Merge "Add adbd_test to presubmit test mapping"
diff --git a/adb/Android.bp b/adb/Android.bp
index 2a88de5..00e98fe 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -25,6 +25,7 @@
         "-Wvla",
     ],
     rtti: true,
+    cpp_std: "gnu++17",
 
     use_version_lib: true,
 
@@ -41,7 +42,10 @@
 
     target: {
         android: {
-            cflags: ["-DADB_HOST=0"],
+            cflags: [
+                "-DADB_HOST=0",
+                "-Wthread-safety",
+            ],
         },
 
         host: {
@@ -311,6 +315,8 @@
         "daemon/auth.cpp",
         "daemon/jdwp_service.cpp",
         "daemon/usb.cpp",
+        "daemon/usb_ffs.cpp",
+        "daemon/usb_legacy.cpp",
     ],
 
     local_include_dirs: [
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 62e8908..9c0eeca 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -73,39 +73,6 @@
         android::base::GetExecutablePath().c_str());
 }
 
-void fatal(const char *fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    char buf[1024];
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
-    fprintf(stderr, "error: %s\n", buf);
-#else
-    LOG(ERROR) << "error: " << buf;
-#endif
-
-    va_end(ap);
-    abort();
-}
-
-void fatal_errno(const char* fmt, ...) {
-    int err = errno;
-    va_list ap;
-    va_start(ap, fmt);
-    char buf[1024];
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
-    fprintf(stderr, "error: %s: %s\n", buf, strerror(err));
-#else
-    LOG(ERROR) << "error: " << buf << ": " << strerror(err);
-#endif
-
-    va_end(ap);
-    abort();
-}
-
 uint32_t calculate_apacket_checksum(const apacket* p) {
     uint32_t sum = 0;
     for (size_t i = 0; i < p->msg.data_length; ++i) {
@@ -118,7 +85,7 @@
 {
     apacket* p = new apacket();
     if (p == nullptr) {
-      fatal("failed to allocate an apacket");
+        LOG(FATAL) << "failed to allocate an apacket";
     }
 
     memset(&p->msg, 0, sizeof(p->msg));
diff --git a/adb/adb.h b/adb/adb.h
index f434e2d..8c37c4b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -59,7 +59,7 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 40
+#define ADB_SERVER_VERSION 41
 
 using TransportId = uint64_t;
 class atransport;
@@ -124,9 +124,6 @@
 
 void print_packet(const char* label, apacket* p);
 
-void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
-void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
-
 void handle_packet(apacket* p, atransport* t);
 
 int launch_server(const std::string& socket_spec);
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 35017f0..ad77064 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -57,11 +57,11 @@
 void close_stdin() {
     int fd = unix_open(kNullFileName, O_RDONLY);
     if (fd == -1) {
-        fatal_errno("failed to open %s", kNullFileName);
+        PLOG(FATAL) << "failed to open " << kNullFileName;
     }
 
     if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
-        fatal_errno("failed to redirect stdin to %s", kNullFileName);
+        PLOG(FATAL) << "failed to redirect stdin to " << kNullFileName;
     }
     unix_close(fd);
 }
@@ -186,6 +186,48 @@
     return line;
 }
 
+std::string dump_header(const amessage* msg) {
+    unsigned command = msg->command;
+    int len = msg->data_length;
+    char cmd[9];
+    char arg0[12], arg1[12];
+    int n;
+
+    for (n = 0; n < 4; n++) {
+        int b = (command >> (n * 8)) & 255;
+        if (b < 32 || b >= 127) break;
+        cmd[n] = (char)b;
+    }
+    if (n == 4) {
+        cmd[4] = 0;
+    } else {
+        // There is some non-ASCII name in the command, so dump the hexadecimal value instead
+        snprintf(cmd, sizeof cmd, "%08x", command);
+    }
+
+    if (msg->arg0 < 256U)
+        snprintf(arg0, sizeof arg0, "%d", msg->arg0);
+    else
+        snprintf(arg0, sizeof arg0, "0x%x", msg->arg0);
+
+    if (msg->arg1 < 256U)
+        snprintf(arg1, sizeof arg1, "%d", msg->arg1);
+    else
+        snprintf(arg1, sizeof arg1, "0x%x", msg->arg1);
+
+    return android::base::StringPrintf("[%s] arg0=%s arg1=%s (len=%d) ", cmd, arg0, arg1, len);
+}
+
+std::string dump_packet(const char* name, const char* func, const apacket* p) {
+    std::string result = name;
+    result += ": ";
+    result += func;
+    result += ": ";
+    result += dump_header(&p->msg);
+    result += dump_hex(p->payload.data(), p->payload.size());
+    return result;
+}
+
 std::string perror_str(const char* msg) {
     return android::base::StringPrintf("%s: %s", msg, strerror(errno));
 }
@@ -274,18 +316,6 @@
     return android_dir;
 }
 
-int syntax_error(const char* fmt, ...) {
-    fprintf(stderr, "adb: usage: ");
-
-    va_list ap;
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-
-    fprintf(stderr, "\n");
-    return 1;
-}
-
 std::string GetLogFilePath() {
 #if defined(_WIN32)
     const char log_name[] = "adb.log";
@@ -295,13 +325,13 @@
     DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
     if (nchars >= arraysize(temp_path) || nchars == 0) {
         // If string truncation or some other error.
-        fatal("cannot retrieve temporary file path: %s\n",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "cannot retrieve temporary file path: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     std::string temp_path_utf8;
     if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
-        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+        PLOG(FATAL) << "cannot convert temporary file path from UTF-16 to UTF-8";
     }
 
     return temp_path_utf8 + log_name;
@@ -311,3 +341,33 @@
     return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
 #endif
 }
+
+[[noreturn]] static void error_exit_va(int error, const char* fmt, va_list va) {
+    fflush(stdout);
+    fprintf(stderr, "%s: ", android::base::Basename(android::base::GetExecutablePath()).c_str());
+
+    vfprintf(stderr, fmt, va);
+
+    if (error != 0) {
+        fprintf(stderr, ": %s", strerror(error));
+    }
+
+    putc('\n', stderr);
+    fflush(stderr);
+
+    exit(EXIT_FAILURE);
+}
+
+void error_exit(const char* fmt, ...) {
+    va_list va;
+    va_start(va, fmt);
+    error_exit_va(0, fmt, va);
+    va_end(va);
+}
+
+void perror_exit(const char* fmt, ...) {
+    va_list va;
+    va_start(va, fmt);
+    error_exit_va(errno, fmt, va);
+    va_end(va);
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f6ce8e2..6d12225 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _ADB_UTILS_H_
-#define _ADB_UTILS_H_
+#pragma once
 
 #include <condition_variable>
 #include <mutex>
@@ -24,7 +23,7 @@
 
 #include <android-base/macros.h>
 
-int syntax_error(const char*, ...) __attribute__((__format__(__printf__, 1, 2)));
+#include "adb.h"
 
 void close_stdin();
 
@@ -42,12 +41,17 @@
 std::string escape_arg(const std::string& s);
 
 std::string dump_hex(const void* ptr, size_t byte_count);
+std::string dump_header(const amessage* msg);
+std::string dump_packet(const char* name, const char* func, const apacket* p);
 
 std::string perror_str(const char* msg);
 
+[[noreturn]] void error_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+[[noreturn]] void perror_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+
 bool set_file_block_mode(int fd, bool block);
 
-extern int adb_close(int fd);
+int adb_close(int fd);
 
 // Given forward/reverse targets, returns true if they look sane. If an error is found, fills
 // |error| and returns false.
@@ -90,5 +94,3 @@
 };
 
 std::string GetLogFilePath();
-
-#endif
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
index 00c2315..e56ef5a 100755
--- a/adb/benchmark_device.py
+++ b/adb/benchmark_device.py
@@ -60,8 +60,6 @@
     if device == None:
         device = adb.get_device()
 
-    lock_max(device)
-
     remote_path = "/dev/null"
     local_path = "/tmp/adb_benchmark_temp"
 
@@ -69,7 +67,7 @@
         f.truncate(file_size_mb * 1024 * 1024)
 
     speeds = list()
-    for _ in range(0, 5):
+    for _ in range(0, 10):
         begin = time.time()
         device.push(local=local_path, remote=remote_path)
         end = time.time()
@@ -81,15 +79,13 @@
     if device == None:
         device = adb.get_device()
 
-    lock_max(device)
-
     remote_path = "/data/local/tmp/adb_benchmark_temp"
     local_path = "/tmp/adb_benchmark_temp"
 
     device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
                   "count=" + str(file_size_mb)])
     speeds = list()
-    for _ in range(0, 5):
+    for _ in range(0, 10):
         begin = time.time()
         device.pull(remote=remote_path, local=local_path)
         end = time.time()
@@ -101,10 +97,8 @@
     if device == None:
         device = adb.get_device()
 
-    lock_max(device)
-
     speeds = list()
-    for _ in range(0, 5):
+    for _ in range(0, 10):
         begin = time.time()
         device.shell(["dd", "if=/dev/zero", "bs=1m",
                       "count=" + str(file_size_mb)])
@@ -114,7 +108,10 @@
     analyze("shell %dMiB" % file_size_mb, speeds)
 
 def main():
-    benchmark_pull()
+    device = adb.get_device()
+    unlock(device)
+    benchmark_push(device)
+    benchmark_pull(device)
 
 if __name__ == "__main__":
     main()
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index d69dbef..cf22efa 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -38,14 +38,22 @@
 
 static constexpr int kFastDeployMinApi = 24;
 
-static bool _use_legacy_install() {
+static bool can_use_feature(const char* feature) {
     FeatureSet features;
     std::string error;
     if (!adb_get_feature_set(&features, &error)) {
         fprintf(stderr, "error: %s\n", error.c_str());
         return true;
     }
-    return !CanUseFeature(features, kFeatureCmd);
+    return CanUseFeature(features, feature);
+}
+
+static bool use_legacy_install() {
+    return !can_use_feature(kFeatureCmd);
+}
+
+static bool is_apex_supported() {
+    return can_use_feature(kFeatureApex);
 }
 
 static int pm_command(int argc, const char** argv) {
@@ -102,7 +110,7 @@
 }
 
 int uninstall_app(int argc, const char** argv) {
-    if (_use_legacy_install()) {
+    if (use_legacy_install()) {
         return uninstall_app_legacy(argc, argv);
     }
     return uninstall_app_streamed(argc, argv);
@@ -133,8 +141,21 @@
 
     // The last argument must be the APK file
     const char* file = argv[argc - 1];
-    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
-        return syntax_error("filename doesn't end .apk: %s", file);
+    if (!android::base::EndsWithIgnoreCase(file, ".apk") &&
+        !android::base::EndsWithIgnoreCase(file, ".apex")) {
+        error_exit("filename doesn't end .apk or .apex: %s", file);
+    }
+
+    bool is_apex = false;
+    if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+        is_apex = true;
+    }
+    if (is_apex && !is_apex_supported()) {
+        error_exit(".apex is not supported on the target device");
+    }
+
+    if (is_apex && use_fastdeploy) {
+        error_exit("--fastdeploy doesn't support .apex files");
     }
 
     if (use_fastdeploy == true) {
@@ -177,6 +198,10 @@
         // do last to override any user specified value
         cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
 
+        if (is_apex) {
+            cmd += " --apex";
+        }
+
         int remoteFd = adb_connect(cmd, &error);
         if (remoteFd < 0) {
             fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
@@ -218,13 +243,16 @@
     // All other arguments passed through verbatim.
     int last_apk = -1;
     for (int i = argc - 1; i >= 0; i--) {
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
+            error_exit("APEX packages are only compatible with Streamed Install");
+        }
         if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
             last_apk = i;
             break;
         }
     }
 
-    if (last_apk == -1) return syntax_error("need APK file on command line");
+    if (last_apk == -1) error_exit("need APK file on command line");
 
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
@@ -303,15 +331,15 @@
     }
 
     if (installMode == INSTALL_DEFAULT) {
-        if (_use_legacy_install()) {
+        if (use_legacy_install()) {
             installMode = INSTALL_PUSH;
         } else {
             installMode = INSTALL_STREAM;
         }
     }
 
-    if (installMode == INSTALL_STREAM && _use_legacy_install() == true) {
-        return syntax_error("Attempting to use streaming install on unsupported deivce.");
+    if (installMode == INSTALL_STREAM && use_legacy_install() == true) {
+        error_exit("Attempting to use streaming install on unsupported device");
     }
 
     if (use_fastdeploy == true && is_reinstall == false) {
@@ -359,6 +387,9 @@
     uint64_t total_size = 0;
     for (int i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
+            error_exit("APEX packages are not compatible with install-multiple");
+        }
 
         if (android::base::EndsWithIgnoreCase(file, ".apk") ||
             android::base::EndsWithIgnoreCase(file, ".dm")) {
@@ -370,10 +401,10 @@
         }
     }
 
-    if (first_apk == -1) return syntax_error("need APK file on command line");
+    if (first_apk == -1) error_exit("need APK file on command line");
 
     std::string install_cmd;
-    if (_use_legacy_install()) {
+    if (use_legacy_install()) {
         install_cmd = "exec:pm";
     } else {
         install_cmd = "exec:cmd package";
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index fe98737..8ca44e8 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -197,7 +197,7 @@
 };
 
 int Bugreport::DoIt(int argc, const char** argv) {
-    if (argc > 2) return syntax_error("adb bugreport [PATH]");
+    if (argc > 2) error_exit("usage: adb bugreport [PATH]");
 
     // Gets bugreportz version.
     std::string bugz_stdout, bugz_stderr;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 07b5747..3124852 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -71,8 +71,7 @@
 static std::string product_file(const std::string& file) {
     const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
     if (ANDROID_PRODUCT_OUT == nullptr) {
-        fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
-        exit(1);
+        error_exit("product directory not specified; set $ANDROID_PRODUCT_OUT");
     }
     return std::string{ANDROID_PRODUCT_OUT} + OS_PATH_SEPARATOR_STR + file;
 }
@@ -318,7 +317,7 @@
 #ifdef _WIN32
         old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
         if (old_stdin_mode == -1) {
-            fatal_errno("could not set stdin to binary");
+            PLOG(FATAL) << "could not set stdin to binary";
         }
 #endif
     }
@@ -327,7 +326,7 @@
     if (outFd == STDOUT_FILENO) {
         old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
         if (old_stdout_mode == -1) {
-            fatal_errno("could not set stdout to binary");
+            PLOG(FATAL) << "could not set stdout to binary";
         }
     }
 #endif
@@ -338,7 +337,7 @@
         stdin_raw_restore();
 #ifdef _WIN32
         if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
-            fatal_errno("could not restore stdin mode");
+            PLOG(FATAL) << "could not restore stdin mode";
         }
 #endif
     }
@@ -346,7 +345,7 @@
 #ifdef _WIN32
     if (outFd == STDOUT_FILENO) {
         if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
-            fatal_errno("could not restore stdout mode");
+            PLOG(FATAL) << "could not restore stdout mode";
         }
     }
 #endif
@@ -660,9 +659,9 @@
 
 static int adb_shell(int argc, const char** argv) {
     FeatureSet features;
-    std::string error;
-    if (!adb_get_feature_set(&features, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
+    std::string error_message;
+    if (!adb_get_feature_set(&features, &error_message)) {
+        fprintf(stderr, "error: %s\n", error_message.c_str());
         return 1;
     }
 
@@ -685,7 +684,7 @@
         switch (opt) {
             case 'e':
                 if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
-                    return syntax_error("-e requires a single-character argument or 'none'");
+                    error_exit("-e requires a single-character argument or 'none'");
                 }
                 escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
                 break;
@@ -933,31 +932,29 @@
  */
 static int ppp(int argc, const char** argv) {
 #if defined(_WIN32)
-    fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
-    return -1;
+    error_exit("adb %s not implemented on Win32", argv[0]);
+    __builtin_unreachable();
 #else
-    if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
+    if (argc < 2) error_exit("usage: adb %s <adb service name> [ppp opts]", argv[0]);
 
     const char* adb_service_name = argv[1];
-    std::string error;
-    int fd = adb_connect(adb_service_name, &error);
+    std::string error_message;
+    int fd = adb_connect(adb_service_name, &error_message);
     if (fd < 0) {
-        fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
-        return 1;
+        error_exit("could not open adb service %s: %s", adb_service_name, error_message.c_str());
     }
 
     pid_t pid = fork();
+    if (pid == -1) {
+        perror_exit("fork failed");
+    }
 
-    if (pid < 0) {
-        perror("from fork()");
-        return 1;
-    } else if (pid == 0) {
-        int err;
+    if (pid == 0) {
+        // child side
         int i;
-        const char **ppp_args;
 
         // copy args
-        ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+        const char** ppp_args = (const char**)alloca(sizeof(char*) * argc + 1);
         ppp_args[0] = "pppd";
         for (i = 2 ; i < argc ; i++) {
             //argv[2] and beyond become ppp_args[1] and beyond
@@ -965,25 +962,18 @@
         }
         ppp_args[i-1] = nullptr;
 
-        // child side
-
         dup2(fd, STDIN_FILENO);
         dup2(fd, STDOUT_FILENO);
         adb_close(STDERR_FILENO);
         adb_close(fd);
 
-        err = execvp("pppd", (char * const *)ppp_args);
-
-        if (err < 0) {
-            perror("execing pppd");
-        }
-        exit(-1);
-    } else {
-        // parent side
-
-        adb_close(fd);
-        return 0;
+        execvp("pppd", (char* const*)ppp_args);
+        perror_exit("exec pppd failed");
     }
+
+    // parent side
+    adb_close(fd);
+    return 0;
 #endif /* !defined(_WIN32) */
 }
 
@@ -1161,7 +1151,7 @@
     /* find, extract, and use any -f argument */
     for (int i = 1; i < argc; i++) {
         if (!strcmp("-f", argv[i])) {
-            if (i == argc - 1) return syntax_error("backup -f passed with no filename");
+            if (i == argc - 1) error_exit("backup -f passed with no filename");
             filename = argv[i+1];
             for (int j = i+2; j <= argc; ) {
                 argv[i++] = argv[j++];
@@ -1173,7 +1163,7 @@
 
     // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
     // a list of packages is required.
-    if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
+    if (argc < 2) error_exit("backup either needs a list of packages or -all/-shared");
 
     adb_unlink(filename);
     int outFd = adb_creat(filename, 0640);
@@ -1209,7 +1199,7 @@
 }
 
 static int restore(int argc, const char** argv) {
-    if (argc != 2) return syntax_error("restore requires an argument");
+    if (argc != 2) error_exit("restore requires an argument");
 
     const char* filename = argv[1];
     int tarFd = adb_open(filename, O_RDONLY);
@@ -1263,8 +1253,7 @@
             } else if (!strcmp(*arg, "--")) {
                 ignore_flags = true;
             } else {
-                syntax_error("unrecognized option '%s'", *arg);
-                exit(1);
+                error_exit("unrecognized option '%s'", *arg);
             }
         }
         ++arg;
@@ -1342,7 +1331,7 @@
             /* this is a special flag used only when the ADB client launches the ADB Server */
             is_daemon = true;
         } else if (!strcmp(argv[0], "--reply-fd")) {
-            if (argc < 2) return syntax_error("--reply-fd requires an argument");
+            if (argc < 2) error_exit("--reply-fd requires an argument");
             const char* reply_fd_str = argv[1];
             argc--;
             argv++;
@@ -1355,7 +1344,7 @@
             if (isdigit(argv[0][2])) {
                 serial = argv[0] + 2;
             } else {
-                if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
+                if (argc < 2 || argv[0][2] != '\0') error_exit("-s requires an argument");
                 serial = argv[1];
                 argc--;
                 argv++;
@@ -1371,7 +1360,7 @@
             }
             transport_id = strtoll(id, const_cast<char**>(&id), 10);
             if (*id != '\0') {
-                return syntax_error("invalid transport id");
+                error_exit("invalid transport id");
             }
         } else if (!strcmp(argv[0],"-d")) {
             transport_type = kTransportUsb;
@@ -1381,7 +1370,7 @@
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return syntax_error("-H requires an argument");
+                if (argc < 2) error_exit("-H requires an argument");
                 server_host_str = argv[1];
                 argc--;
                 argv++;
@@ -1390,7 +1379,7 @@
             }
         } else if (!strncmp(argv[0], "-P", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return syntax_error("-P requires an argument");
+                if (argc < 2) error_exit("-P requires an argument");
                 server_port_str = argv[1];
                 argc--;
                 argv++;
@@ -1398,7 +1387,7 @@
                 server_port_str = argv[0] + 2;
             }
         } else if (!strcmp(argv[0], "-L")) {
-            if (argc < 2) return syntax_error("-L requires an argument");
+            if (argc < 2) error_exit("-L requires an argument");
             server_socket_str = argv[1];
             argc--;
             argv++;
@@ -1411,7 +1400,7 @@
     }
 
     if ((server_host_str || server_port_str) && server_socket_str) {
-        return syntax_error("-L is incompatible with -H or -P");
+        error_exit("-L is incompatible with -H or -P");
     }
 
     // If -L, -H, or -P are specified, ignore environment variables.
@@ -1428,11 +1417,10 @@
         server_port_str = server_port_str ? server_port_str : getenv("ANDROID_ADB_SERVER_PORT");
         if (server_port_str && strlen(server_port_str) > 0) {
             if (!android::base::ParseInt(server_port_str, &server_port, 1, 65535)) {
-                fprintf(stderr,
-                        "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive"
-                        " number less than 65535. Got \"%s\"\n",
+                error_exit(
+                        "$ANDROID_ADB_SERVER_PORT must be a positive number less than 65535: "
+                        "got \"%s\"",
                         server_port_str);
-                exit(1);
             }
         }
 
@@ -1444,7 +1432,7 @@
             rc = asprintf(&temp, "tcp:%d", server_port);
         }
         if (rc < 0) {
-            fatal("failed to allocate server socket specification");
+            LOG(FATAL) << "failed to allocate server socket specification";
         }
         server_socket_str = temp;
     }
@@ -1506,7 +1494,7 @@
         } else if (argc == 2 && !strcmp(argv[1], "-l")) {
             listopt = argv[1];
         } else {
-            return syntax_error("adb devices [-l]");
+            error_exit("adb devices [-l]");
         }
 
         std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1514,13 +1502,13 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
+        if (argc != 2) error_exit("usage: adb connect <host>[:<port>]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "disconnect")) {
-        if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
+        if (argc > 2) error_exit("usage: adb disconnect [<host>[:<port>]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
@@ -1535,7 +1523,7 @@
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
 
-        if (argc < 2) return syntax_error("adb %s command", argv[0]);
+        if (argc < 2) error_exit("usage: adb %s command", argv[0]);
 
         std::string cmd = "exec:";
         cmd += argv[1];
@@ -1565,17 +1553,17 @@
         return adb_kill_server() ? 0 : 1;
     }
     else if (!strcmp(argv[0], "sideload")) {
-        if (argc != 2) return syntax_error("sideload requires an argument");
+        if (argc != 2) error_exit("sideload requires an argument");
         if (adb_sideload_host(argv[1])) {
             return 1;
         } else {
             return 0;
         }
     } else if (!strcmp(argv[0], "tcpip")) {
-        if (argc != 2) return syntax_error("tcpip requires an argument");
+        if (argc != 2) error_exit("tcpip requires an argument");
         int port;
         if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
-            return syntax_error("tcpip: invalid port: %s", argv[1]);
+            error_exit("tcpip: invalid port: %s", argv[1]);
         }
         return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
     }
@@ -1607,7 +1595,7 @@
     } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
         bool reverse = !strcmp(argv[0], "reverse");
         --argc;
-        if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
+        if (argc < 1) error_exit("%s requires an argument", argv[0]);
         ++argv;
 
         // Determine the <host-prefix> for this command.
@@ -1626,46 +1614,44 @@
             }
         }
 
-        std::string cmd, error;
+        std::string cmd, error_message;
         if (strcmp(argv[0], "--list") == 0) {
-            if (argc != 1) return syntax_error("--list doesn't take any arguments");
+            if (argc != 1) error_exit("--list doesn't take any arguments");
             return adb_query_command(host_prefix + ":list-forward");
         } else if (strcmp(argv[0], "--remove-all") == 0) {
-            if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
+            if (argc != 1) error_exit("--remove-all doesn't take any arguments");
             cmd = host_prefix + ":killforward-all";
         } else if (strcmp(argv[0], "--remove") == 0) {
             // forward --remove <local>
-            if (argc != 2) return syntax_error("--remove requires an argument");
+            if (argc != 2) error_exit("--remove requires an argument");
             cmd = host_prefix + ":killforward:" + argv[1];
         } else if (strcmp(argv[0], "--no-rebind") == 0) {
             // forward --no-rebind <local> <remote>
-            if (argc != 3) return syntax_error("--no-rebind takes two arguments");
-            if (forward_targets_are_valid(argv[1], argv[2], &error)) {
+            if (argc != 3) error_exit("--no-rebind takes two arguments");
+            if (forward_targets_are_valid(argv[1], argv[2], &error_message)) {
                 cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
             }
         } else {
             // forward <local> <remote>
-            if (argc != 2) return syntax_error("forward takes two arguments");
-            if (forward_targets_are_valid(argv[0], argv[1], &error)) {
+            if (argc != 2) error_exit("forward takes two arguments");
+            if (forward_targets_are_valid(argv[0], argv[1], &error_message)) {
                 cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
             }
         }
 
-        if (!error.empty()) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
+        if (!error_message.empty()) {
+            error_exit("error: %s", error_message.c_str());
         }
 
-        int fd = adb_connect(cmd, &error);
-        if (fd < 0 || !adb_status(fd, &error)) {
+        int fd = adb_connect(cmd, &error_message);
+        if (fd < 0 || !adb_status(fd, &error_message)) {
             adb_close(fd);
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
+            error_exit("error: %s", error_message.c_str());
         }
 
         // Server or device may optionally return a resolved TCP port number.
         std::string resolved_port;
-        if (ReadProtocolString(fd, &resolved_port, &error) && !resolved_port.empty()) {
+        if (ReadProtocolString(fd, &resolved_port, &error_message) && !resolved_port.empty()) {
             printf("%s\n", resolved_port.c_str());
         }
 
@@ -1674,7 +1660,7 @@
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
-        if (argc != 2) return syntax_error("ls requires an argument");
+        if (argc != 2) error_exit("ls requires an argument");
         return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
@@ -1684,7 +1670,7 @@
         const char* dst = nullptr;
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
-        if (srcs.empty() || !dst) return syntax_error("push requires an argument");
+        if (srcs.empty() || !dst) error_exit("push requires an argument");
         return do_sync_push(srcs, dst, sync) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
@@ -1693,19 +1679,19 @@
         const char* dst = ".";
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
-        if (srcs.empty()) return syntax_error("pull requires an argument");
+        if (srcs.empty()) error_exit("pull requires an argument");
         return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
-        if (argc < 2) return syntax_error("install requires an argument");
+        if (argc < 2) error_exit("install requires an argument");
         return install_app(argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
-        if (argc < 2) return syntax_error("install-multiple requires an argument");
+        if (argc < 2) error_exit("install-multiple requires an argument");
         return install_multiple_app(argc, argv);
     }
     else if (!strcmp(argv[0], "uninstall")) {
-        if (argc < 2) return syntax_error("uninstall requires an argument");
+        if (argc < 2) error_exit("uninstall requires an argument");
         return uninstall_app(argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
@@ -1719,7 +1705,7 @@
         } else if (argc == 2) {
             src = argv[1];
         } else {
-            return syntax_error("adb sync [-l] [PARTITION]");
+            error_exit("usage: adb sync [-l] [PARTITION]");
         }
 
         if (src.empty()) src = "all";
@@ -1734,7 +1720,8 @@
                 if (!do_sync_sync(src_dir, "/" + partition, list_only)) return 1;
             }
         }
-        return found ? 0 : syntax_error("don't know how to sync %s partition", src.c_str());
+        if (!found) error_exit("don't know how to sync %s partition", src.c_str());
+        return 0;
     }
     /* passthrough commands */
     else if (!strcmp(argv[0],"get-state") ||
@@ -1765,7 +1752,7 @@
         return restore(argc, argv);
     }
     else if (!strcmp(argv[0], "keygen")) {
-        if (argc != 2) return syntax_error("keygen requires an argument");
+        if (argc != 2) error_exit("keygen requires an argument");
         // Always print key generation information for keygen command.
         adb_trace_enable(AUTH);
         return adb_auth_keygen(argv[1]);
@@ -1780,7 +1767,7 @@
         return adb_connect_command("host:track-devices");
     } else if (!strcmp(argv[0], "raw")) {
         if (argc != 2) {
-            return syntax_error("adb raw SERVICE");
+            error_exit("usage: adb raw SERVICE");
         }
         return adb_connect_command(argv[1]);
     }
@@ -1823,11 +1810,11 @@
                 std::string err;
                 return adb_query_command("host:reconnect-offline");
             } else {
-                return syntax_error("adb reconnect [device|offline]");
+                error_exit("usage: adb reconnect [device|offline]");
             }
         }
     }
 
-    syntax_error("unknown command %s", argv[0]);
-    return 1;
+    error_exit("unknown command %s", argv[0]);
+    __builtin_unreachable();
 }
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 183a1fa..45f3cca 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -27,7 +27,9 @@
 #include "client/file_sync_client.h"
 #include "commandline.h"
 #include "fastdeploycallbacks.h"
-#include "utils/String16.h"
+#include "sysdeps.h"
+
+#include "adb_utils.h"
 
 static constexpr long kRequiredAgentVersion = 0x00000001;
 
@@ -72,13 +74,14 @@
 static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
     std::string adb_dir = android::base::GetExecutableDirectory();
     if (adb_dir.empty()) {
-        fatal("Could not determine location of adb!");
+        error_exit("Could not determine location of adb!");
     }
 
     if (g_use_localagent) {
         const char* product_out = getenv("ANDROID_PRODUCT_OUT");
         if (product_out == nullptr) {
-            fatal("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined", local_path);
+            error_exit("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined",
+                       local_path);
         }
         return android::base::StringPrintf("%s%s", product_out, local_path);
     } else {
@@ -103,10 +106,10 @@
                 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
         int ret = send_shell_command(chmodCommand);
         if (ret != 0) {
-            fatal("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+            error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
         }
     } else {
-        fatal("Error pushing agent files to device");
+        error_exit("Error pushing agent files to device");
     }
 
     return true;
@@ -136,8 +139,8 @@
 
     agent_version = get_agent_version();
     if (agent_version != kRequiredAgentVersion) {
-        fatal("After update agent version remains incorrect! Expected %ld but version is %ld",
-              kRequiredAgentVersion, agent_version);
+        error_exit("After update agent version remains incorrect! Expected %ld but version is %ld",
+                   kRequiredAgentVersion, agent_version);
     }
 }
 
@@ -153,26 +156,28 @@
 }
 
 static std::string get_packagename_from_apk(const char* apkPath) {
+#undef open
     std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+#define open ___xxx_unix_open
     if (zipFile == nullptr) {
-        fatal("Could not open %s", apkPath);
+        perror_exit("Could not open %s", apkPath);
     }
     android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
     if (entry == nullptr) {
-        fatal("Could not find AndroidManifest.xml inside %s", apkPath);
+        error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
     }
     uint32_t manifest_len = 0;
     if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
-        fatal("Could not read AndroidManifest.xml inside %s", apkPath);
+        error_exit("Could not read AndroidManifest.xml inside %s", apkPath);
     }
     std::vector<char> manifest_data(manifest_len);
     if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
-        fatal("Could not uncompress AndroidManifest.xml inside %s", apkPath);
+        error_exit("Could not uncompress AndroidManifest.xml inside %s", apkPath);
     }
     android::ResXMLTree tree;
     android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
     if (setto_status != android::OK) {
-        fatal("Could not parse AndroidManifest.xml inside %s", apkPath);
+        error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
     }
     android::ResXMLParser::event_code_t code;
     while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
@@ -213,7 +218,7 @@
                 break;
         }
     }
-    fatal("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
+    error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
 }
 
 void extract_metadata(const char* apkPath, FILE* outputFp) {
@@ -227,7 +232,7 @@
     DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
     int returnCode = send_shell_command(extractCommand, false, &cb);
     if (returnCode != 0) {
-        fatal("Executing %s returned %d\n", extractCommand.c_str(), returnCode);
+        error_exit("Executing %s returned %d", extractCommand.c_str(), returnCode);
     }
 }
 
@@ -236,8 +241,9 @@
         // This should never happen on a Windows machine
         const char* host_out = getenv("ANDROID_HOST_OUT");
         if (host_out == nullptr) {
-            fatal("Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT is not "
-                  "defined");
+            error_exit(
+                    "Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT "
+                    "is not defined");
         }
         return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
                                            host_out);
@@ -245,7 +251,7 @@
 
     std::string adb_dir = android::base::GetExecutableDirectory();
     if (adb_dir.empty()) {
-        fatal("Could not locate deploypatchgenerator.jar");
+        error_exit("Could not locate deploypatchgenerator.jar");
     }
     return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
                                        adb_dir.c_str());
@@ -257,7 +263,7 @@
             patchPath);
     int returnCode = system(generatePatchCommand.c_str());
     if (returnCode != 0) {
-        fatal("Executing %s returned %d\n", generatePatchCommand.c_str(), returnCode);
+        error_exit("Executing %s returned %d", generatePatchCommand.c_str(), returnCode);
     }
 }
 
@@ -276,7 +282,7 @@
     std::vector<const char*> srcs = {patchPath};
     bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
     if (!push_ok) {
-        fatal("Error pushing %s to %s returned\n", patchPath, patchDevicePath.c_str());
+        error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
     }
 
     std::string applyPatchCommand =
@@ -285,7 +291,7 @@
 
     int returnCode = send_shell_command(applyPatchCommand);
     if (returnCode != 0) {
-        fatal("Executing %s returned %d\n", applyPatchCommand.c_str(), returnCode);
+        error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
     }
 }
 
@@ -299,7 +305,7 @@
     std::vector<const char*> srcs{patchPath};
     bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
     if (!push_ok) {
-        fatal("Error pushing %s to %s returned\n", patchPath, patchDevicePath.c_str());
+        error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
     }
 
     std::vector<unsigned char> applyOutputBuffer;
@@ -316,6 +322,6 @@
                                         patchDevicePath.c_str(), argsString.c_str());
     int returnCode = send_shell_command(applyPatchCommand);
     if (returnCode != 0) {
-        fatal("Executing %s returned %d\n", applyPatchCommand.c_str(), returnCode);
+        error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
     }
 }
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index cb9bcfa..f0f9a80 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -325,12 +325,12 @@
         memset(st, 0, sizeof(*st));
         if (have_stat_v2_) {
             if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
-                fatal_errno("protocol fault: failed to read stat response");
+                PLOG(FATAL) << "protocol fault: failed to read stat response";
             }
 
             if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
-                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
-                            msg.stat_v2.id);
+                PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+                            << msg.stat_v2.id;
             }
 
             if (msg.stat_v2.error != 0) {
@@ -351,12 +351,12 @@
             return true;
         } else {
             if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
-                fatal_errno("protocol fault: failed to read stat response");
+                PLOG(FATAL) << "protocol fault: failed to read stat response";
             }
 
             if (msg.stat_v1.id != ID_LSTAT_V1) {
-                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
-                            msg.stat_v1.id);
+                PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+                            << msg.stat_v1.id;
             }
 
             if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
@@ -894,7 +894,8 @@
     //
     // TODO(b/25457350): We don't preserve permissions on directories.
     // TODO: Find all of the leaves and `mkdir -p` them instead?
-    if (CanUseFeature(sc.Features(), kFeatureShell2)) {
+    if (!CanUseFeature(sc.Features(), kFeatureFixedPushMkdir) &&
+        CanUseFeature(sc.Features(), kFeatureShell2)) {
         SilentStandardStreamsCallbackInterface cb;
         std::string cmd = "mkdir";
         for (const auto& dir : directory_list) {
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index a7e454d..fb581a6 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -42,13 +42,13 @@
     const std::string log_file_path(GetLogFilePath());
     int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
     if (fd == -1) {
-        fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
+        PLOG(FATAL) << "cannot open " << log_file_path;
     }
     if (dup2(fd, STDOUT_FILENO) == -1) {
-        fatal("cannot redirect stdout: %s", strerror(errno));
+        PLOG(FATAL) << "cannot redirect stdout";
     }
     if (dup2(fd, STDERR_FILENO) == -1) {
-        fatal("cannot redirect stderr: %s", strerror(errno));
+        PLOG(FATAL) << "cannot redirect stderr";
     }
     unix_close(fd);
 
@@ -81,10 +81,10 @@
     // This also keeps stderr unbuffered when it is redirected to adb.log.
     if (is_daemon) {
         if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
-            fatal("cannot make stdout unbuffered: %s", strerror(errno));
+            PLOG(FATAL) << "cannot make stdout unbuffered";
         }
         if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
-            fatal("cannot make stderr unbuffered: %s", strerror(errno));
+            PLOG(FATAL) << "cannot make stderr unbuffered";
         }
     }
 
@@ -137,7 +137,7 @@
     while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
            INSTALL_STATUS_OK) {
         if (std::chrono::steady_clock::now() - start > 0.5s) {
-            fatal("could not install *smartsocket* listener: %s", error.c_str());
+            LOG(FATAL) << "could not install *smartsocket* listener: " << error;
         }
 
         std::this_thread::sleep_for(100ms);
@@ -153,7 +153,7 @@
         // setsid will fail with EPERM if it's already been a lead process of new session.
         // Ignore such error.
         if (setsid() == -1 && errno != EPERM) {
-            fatal("setsid() failed: %s", strerror(errno));
+            PLOG(FATAL) << "setsid() failed";
         }
 #endif
 
@@ -171,19 +171,19 @@
             const DWORD bytes_to_write = arraysize(ack) - 1;
             DWORD written = 0;
             if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
-                fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
-                      android::base::SystemErrorCodeToString(GetLastError()).c_str());
+                LOG(FATAL) << "cannot write ACK to handle " << ack_reply_handle
+                           << android::base::SystemErrorCodeToString(GetLastError());
             }
             if (written != bytes_to_write) {
-                fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
-                      written);
+                LOG(FATAL) << "cannot write " << bytes_to_write << " bytes of ACK: only wrote "
+                           << written << " bytes";
             }
             CloseHandle(ack_reply_handle);
 #else
             // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
             // "OKAY".
             if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
-                fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+                PLOG(FATAL) << "error writing ACK to fd " << ack_reply_fd;
             }
             unix_close(ack_reply_fd);
 #endif
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 00aeb9b..cfa5cf4 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -212,8 +212,8 @@
     const HINSTANCE instance = GetModuleHandleW(nullptr);
     if (!instance) {
         // This is such a common API call that this should never fail.
-        fatal("GetModuleHandleW failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "GetModuleHandleW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     WNDCLASSEXW wndclass;
@@ -223,15 +223,15 @@
     wndclass.hInstance = instance;
     wndclass.lpszClassName = kPowerNotificationWindowClassName;
     if (!RegisterClassExW(&wndclass)) {
-        fatal("RegisterClassExW failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "RegisterClassExW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
                          L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
                          instance, nullptr)) {
-        fatal("CreateWindowExW failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "CreateWindowExW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     MSG msg;
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 8d28c49..2a6418a 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -32,6 +32,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "fdevent.h"
 
 /* TODO:
@@ -78,7 +79,7 @@
         const char* command = "screencap";
         const char *args[2] = {command, nullptr};
         execvp(command, (char**)args);
-        exit(1);
+        perror_exit("exec screencap failed");
     }
 
     adb_close(fds[1]);
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index 78e4cd5..3213f69 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -62,3 +62,5 @@
 };
 
 usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
+bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out,
+                     android::base::unique_fd* bulk_in);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index fe79acd..1363976 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -136,7 +136,7 @@
         this->fde = fdevent_create(socket, jdwp_process_event, this);
 
         if (!this->fde) {
-            fatal("could not create fdevent for new JDWP process");
+            LOG(FATAL) << "could not create fdevent for new JDWP process";
         }
 
         /* start by waiting for the PID */
@@ -200,7 +200,7 @@
     // Message is length-prefixed with 4 hex digits in ASCII.
     static constexpr size_t header_len = 4;
     if (bufferlen < header_len) {
-        fatal("invalid JDWP process list buffer size: %zu", bufferlen);
+        LOG(FATAL) << "invalid JDWP process list buffer size: " << bufferlen;
     }
 
     char head[header_len + 1];
@@ -395,7 +395,7 @@
 
         auto proc = std::make_unique<JdwpProcess>(s);
         if (!proc) {
-            fatal("failed to allocate JdwpProcess");
+            LOG(FATAL) << "failed to allocate JdwpProcess";
         }
 
         _jdwp_list.emplace_back(std::move(proc));
@@ -454,7 +454,7 @@
     JdwpSocket* s = new JdwpSocket();
 
     if (!s) {
-        fatal("failed to allocate JdwpSocket");
+        LOG(FATAL) << "failed to allocate JdwpSocket";
     }
 
     install_local_socket(s);
@@ -531,7 +531,7 @@
 asocket* create_jdwp_tracker_service_socket(void) {
     auto t = std::make_unique<JdwpTracker>();
     if (!t) {
-        fatal("failed to allocate JdwpTracker");
+        LOG(FATAL) << "failed to allocate JdwpTracker";
     }
 
     memset(t.get(), 0, sizeof(asocket));
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 9d495b0..f603d13 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,528 +18,568 @@
 
 #include "sysdeps.h"
 
-#include <dirent.h>
 #include <errno.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/functionfs.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
-#include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <algorithm>
-#include <atomic>
-#include <chrono>
-#include <condition_variable>
+#include <linux/usb/functionfs.h>
+#include <sys/eventfd.h>
+
+#include <array>
+#include <future>
+#include <memory>
 #include <mutex>
-#include <thread>
+#include <optional>
+#include <vector>
+
+#include <asyncio/AsyncIO.h>
 
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/properties.h>
+#include <android-base/thread_annotations.h>
 
-#include "adb.h"
-#include "adbd/usb.h"
+#include <adbd/usb.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
+#include "types.h"
 
-using namespace std::chrono_literals;
+using android::base::StringPrintf;
 
-#define MAX_PACKET_SIZE_FS 64
-#define MAX_PACKET_SIZE_HS 512
-#define MAX_PACKET_SIZE_SS 1024
+static constexpr size_t kUsbReadQueueDepth = 16;
+static constexpr size_t kUsbReadSize = 16384;
 
-#define USB_FFS_BULK_SIZE 16384
+static constexpr size_t kUsbWriteQueueDepth = 16;
 
-// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
-#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
-
-#define cpu_to_le16(x) htole16(x)
-#define cpu_to_le32(x) htole32(x)
-
-static unique_fd& dummy_fd = *new unique_fd();
-
-struct func_desc {
-    struct usb_interface_descriptor intf;
-    struct usb_endpoint_descriptor_no_audio source;
-    struct usb_endpoint_descriptor_no_audio sink;
-} __attribute__((packed));
-
-struct ss_func_desc {
-    struct usb_interface_descriptor intf;
-    struct usb_endpoint_descriptor_no_audio source;
-    struct usb_ss_ep_comp_descriptor source_comp;
-    struct usb_endpoint_descriptor_no_audio sink;
-    struct usb_ss_ep_comp_descriptor sink_comp;
-} __attribute__((packed));
-
-struct desc_v1 {
-    struct usb_functionfs_descs_head_v1 {
-        __le32 magic;
-        __le32 length;
-        __le32 fs_count;
-        __le32 hs_count;
-    } __attribute__((packed)) header;
-    struct func_desc fs_descs, hs_descs;
-} __attribute__((packed));
-
-struct desc_v2 {
-    struct usb_functionfs_descs_head_v2 header;
-    // The rest of the structure depends on the flags in the header.
-    __le32 fs_count;
-    __le32 hs_count;
-    __le32 ss_count;
-    __le32 os_count;
-    struct func_desc fs_descs, hs_descs;
-    struct ss_func_desc ss_descs;
-    struct usb_os_desc_header os_header;
-    struct usb_ext_compat_desc os_desc;
-} __attribute__((packed));
-
-static struct func_desc fs_descriptors = {
-    .intf = {
-        .bLength = sizeof(fs_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(fs_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
-    },
-    .sink = {
-        .bLength = sizeof(fs_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
-    },
-};
-
-static struct func_desc hs_descriptors = {
-    .intf = {
-        .bLength = sizeof(hs_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(hs_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
-    },
-    .sink = {
-        .bLength = sizeof(hs_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
-    },
-};
-
-static struct ss_func_desc ss_descriptors = {
-    .intf = {
-        .bLength = sizeof(ss_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(ss_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
-    },
-    .source_comp = {
-        .bLength = sizeof(ss_descriptors.source_comp),
-        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-        .bMaxBurst = 4,
-    },
-    .sink = {
-        .bLength = sizeof(ss_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
-    },
-    .sink_comp = {
-        .bLength = sizeof(ss_descriptors.sink_comp),
-        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-        .bMaxBurst = 4,
-    },
-};
-
-struct usb_ext_compat_desc os_desc_compat = {
-    .bFirstInterfaceNumber = 0,
-    .Reserved1 = cpu_to_le32(1),
-    .CompatibleID = {0},
-    .SubCompatibleID = {0},
-    .Reserved2 = {0},
-};
-
-static struct usb_os_desc_header os_desc_header = {
-    .interface = cpu_to_le32(1),
-    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
-    .bcdVersion = cpu_to_le32(1),
-    .wIndex = cpu_to_le32(4),
-    .bCount = cpu_to_le32(1),
-    .Reserved = cpu_to_le32(0),
-};
-
-#define STR_INTERFACE_ "ADB Interface"
-
-static const struct {
-    struct usb_functionfs_strings_head header;
-    struct {
-        __le16 code;
-        const char str1[sizeof(STR_INTERFACE_)];
-    } __attribute__((packed)) lang0;
-} __attribute__((packed)) strings = {
-    .header = {
-        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
-        .length = cpu_to_le32(sizeof(strings)),
-        .str_count = cpu_to_le32(1),
-        .lang_count = cpu_to_le32(1),
-    },
-    .lang0 = {
-        cpu_to_le16(0x0409), /* en-us */
-        STR_INTERFACE_,
-    },
-};
-
-static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
-    aiob->iocb.resize(num_bufs);
-    aiob->iocbs.resize(num_bufs);
-    aiob->events.resize(num_bufs);
-    aiob->num_submitted = 0;
-    for (unsigned i = 0; i < num_bufs; i++) {
-        aiob->iocbs[i] = &aiob->iocb[i];
-    }
-    memset(&aiob->ctx, 0, sizeof(aiob->ctx));
-    if (io_setup(num_bufs, &aiob->ctx)) {
-        D("[ aio: got error on io_setup (%d) ]", errno);
+static const char* to_string(enum usb_functionfs_event_type type) {
+    switch (type) {
+        case FUNCTIONFS_BIND:
+            return "FUNCTIONFS_BIND";
+        case FUNCTIONFS_UNBIND:
+            return "FUNCTIONFS_UNBIND";
+        case FUNCTIONFS_ENABLE:
+            return "FUNCTIONFS_ENABLE";
+        case FUNCTIONFS_DISABLE:
+            return "FUNCTIONFS_DISABLE";
+        case FUNCTIONFS_SETUP:
+            return "FUNCTIONFS_SETUP";
+        case FUNCTIONFS_SUSPEND:
+            return "FUNCTIONFS_SUSPEND";
+        case FUNCTIONFS_RESUME:
+            return "FUNCTIONFS_RESUME";
     }
 }
 
-static int getMaxPacketSize(int ffs_fd) {
-    usb_endpoint_descriptor desc;
-    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
-        D("[ could not get endpoint descriptor! (%d) ]", errno);
-        return MAX_PACKET_SIZE_HS;
-    } else {
-        return desc.wMaxPacketSize;
+enum class TransferDirection : uint64_t {
+    READ = 0,
+    WRITE = 1,
+};
+
+struct TransferId {
+    TransferDirection direction : 1;
+    uint64_t id : 63;
+
+    TransferId() : TransferId(TransferDirection::READ, 0) {}
+
+  private:
+    TransferId(TransferDirection direction, uint64_t id) : direction(direction), id(id) {}
+
+  public:
+    explicit operator uint64_t() const {
+        uint64_t result;
+        static_assert(sizeof(*this) == sizeof(result));
+        memcpy(&result, this, sizeof(*this));
+        return result;
     }
-}
 
-static bool init_functionfs(struct usb_handle* h) {
-    LOG(INFO) << "initializing functionfs";
+    static TransferId read(uint64_t id) { return TransferId(TransferDirection::READ, id); }
+    static TransferId write(uint64_t id) { return TransferId(TransferDirection::WRITE, id); }
 
-    ssize_t ret;
-    struct desc_v1 v1_descriptor;
-    struct desc_v2 v2_descriptor;
+    static TransferId from_value(uint64_t value) {
+        TransferId result;
+        memcpy(&result, &value, sizeof(value));
+        return result;
+    }
+};
 
-    v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
-    v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
-    v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
-                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
-    v2_descriptor.fs_count = 3;
-    v2_descriptor.hs_count = 3;
-    v2_descriptor.ss_count = 5;
-    v2_descriptor.os_count = 1;
-    v2_descriptor.fs_descs = fs_descriptors;
-    v2_descriptor.hs_descs = hs_descriptors;
-    v2_descriptor.ss_descs = ss_descriptors;
-    v2_descriptor.os_header = os_desc_header;
-    v2_descriptor.os_desc = os_desc_compat;
+struct IoBlock {
+    bool pending;
+    struct iocb control;
+    Block payload;
 
-    if (h->control < 0) { // might have already done this before
-        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
-        h->control.reset(adb_open(USB_FFS_ADB_EP0, O_WRONLY));
-        if (h->control < 0) {
-            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
-            goto err;
+    TransferId id() const { return TransferId::from_value(control.aio_data); }
+};
+
+struct ScopedAioContext {
+    ScopedAioContext() = default;
+    ~ScopedAioContext() { reset(); }
+
+    ScopedAioContext(ScopedAioContext&& move) { reset(move.release()); }
+    ScopedAioContext(const ScopedAioContext& copy) = delete;
+
+    ScopedAioContext& operator=(ScopedAioContext&& move) {
+        reset(move.release());
+        return *this;
+    }
+    ScopedAioContext& operator=(const ScopedAioContext& copy) = delete;
+
+    static ScopedAioContext Create(size_t max_events) {
+        aio_context_t ctx = 0;
+        if (io_setup(max_events, &ctx) != 0) {
+            PLOG(FATAL) << "failed to create aio_context_t";
+        }
+        ScopedAioContext result;
+        result.reset(ctx);
+        return result;
+    }
+
+    aio_context_t release() {
+        aio_context_t result = context_;
+        context_ = 0;
+        return result;
+    }
+
+    void reset(aio_context_t new_context = 0) {
+        if (context_ != 0) {
+            io_destroy(context_);
         }
 
-        ret = adb_write(h->control.get(), &v2_descriptor, sizeof(v2_descriptor));
-        if (ret < 0) {
-            v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
-            v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
-            v1_descriptor.header.fs_count = 3;
-            v1_descriptor.header.hs_count = 3;
-            v1_descriptor.fs_descs = fs_descriptors;
-            v1_descriptor.hs_descs = hs_descriptors;
-            D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
-            ret = adb_write(h->control.get(), &v1_descriptor, sizeof(v1_descriptor));
-            if (ret < 0) {
-                D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
-                goto err;
+        context_ = new_context;
+    }
+
+    aio_context_t get() { return context_; }
+
+  private:
+    aio_context_t context_ = 0;
+};
+
+struct UsbFfsConnection : public Connection {
+    UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
+                     std::promise<void> destruction_notifier)
+        : stopped_(false),
+          destruction_notifier_(std::move(destruction_notifier)),
+          control_fd_(std::move(control)),
+          read_fd_(std::move(read)),
+          write_fd_(std::move(write)) {
+        LOG(INFO) << "UsbFfsConnection constructed";
+        event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        aio_context_ = ScopedAioContext::Create(kUsbReadQueueDepth + kUsbWriteQueueDepth);
+    }
+
+    ~UsbFfsConnection() {
+        LOG(INFO) << "UsbFfsConnection being destroyed";
+        Stop();
+        monitor_thread_.join();
+        destruction_notifier_.set_value();
+    }
+
+    virtual bool Write(std::unique_ptr<apacket> packet) override final {
+        LOG(DEBUG) << "USB write: " << dump_header(&packet->msg);
+        Block header(sizeof(packet->msg));
+        memcpy(header.data(), &packet->msg, sizeof(packet->msg));
+
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
+        if (!packet->payload.empty()) {
+            write_requests_.push_back(
+                    CreateWriteBlock(std::move(packet->payload), next_write_id_++));
+        }
+        SubmitWrites();
+        return true;
+    }
+
+    virtual void Start() override final { StartMonitor(); }
+
+    virtual void Stop() override final {
+        if (stopped_.exchange(true)) {
+            return;
+        }
+        stopped_ = true;
+        uint64_t notify = 1;
+        ssize_t rc = adb_write(event_fd_.get(), &notify, sizeof(notify));
+        if (rc < 0) {
+            PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+        }
+        CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+    }
+
+  private:
+    void StartMonitor() {
+        // This is a bit of a mess.
+        // It's possible for io_submit to end up blocking, if we call it as the endpoint
+        // becomes disabled. Work around this by having a monitor thread to listen for functionfs
+        // lifecycle events. If we notice an error condition (either we've become disabled, or we
+        // were never enabled in the first place), we send interruption signals to the worker thread
+        // until it dies, and then report failure to the transport via HandleError, which will
+        // eventually result in the transport being destroyed, which will result in UsbFfsConnection
+        // being destroyed, which unblocks the open thread and restarts this entire process.
+        static constexpr int kInterruptionSignal = SIGUSR1;
+        static std::once_flag handler_once;
+        std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
+
+        monitor_thread_ = std::thread([this]() {
+            adb_thread_setname("UsbFfs-monitor");
+
+            bool bound = false;
+            bool started = false;
+            bool running = true;
+            while (running) {
+                if (!bound || !started) {
+                    adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
+                    int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
+                    if (rc == -1) {
+                        PLOG(FATAL) << "poll on USB control fd failed";
+                    } else if (rc == 0) {
+                        // Something in the kernel presumably went wrong.
+                        // Close our endpoints, wait for a bit, and then try again.
+                        aio_context_.reset();
+                        read_fd_.reset();
+                        write_fd_.reset();
+                        control_fd_.reset();
+                        std::this_thread::sleep_for(5s);
+                        HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
+                        return;
+                    }
+                }
+
+                struct usb_functionfs_event event;
+                if (TEMP_FAILURE_RETRY(adb_read(control_fd_.get(), &event, sizeof(event))) !=
+                    sizeof(event)) {
+                    PLOG(FATAL) << "failed to read functionfs event";
+                }
+
+                LOG(INFO) << "USB event: "
+                          << to_string(static_cast<usb_functionfs_event_type>(event.type));
+
+                switch (event.type) {
+                    case FUNCTIONFS_BIND:
+                        CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+                        bound = true;
+                        break;
+
+                    case FUNCTIONFS_ENABLE:
+                        CHECK(!started) << "received FUNCTIONFS_ENABLE while already running?";
+                        started = true;
+                        StartWorker();
+                        break;
+
+                    case FUNCTIONFS_DISABLE:
+                        running = false;
+                        break;
+                }
+            }
+
+            pthread_t worker_thread_handle = worker_thread_.native_handle();
+            while (true) {
+                int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+                if (rc != 0) {
+                    LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+                    break;
+                }
+
+                std::this_thread::sleep_for(100ms);
+
+                rc = pthread_kill(worker_thread_handle, 0);
+                if (rc == 0) {
+                    continue;
+                } else if (rc == ESRCH) {
+                    break;
+                } else {
+                    LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+                }
+            }
+
+            worker_thread_.join();
+
+            aio_context_.reset();
+            read_fd_.reset();
+            write_fd_.reset();
+        });
+    }
+
+    void StartWorker() {
+        worker_thread_ = std::thread([this]() {
+            adb_thread_setname("UsbFfs-worker");
+            for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
+                read_requests_[i] = CreateReadBlock(next_read_id_++);
+                SubmitRead(&read_requests_[i]);
+            }
+
+            while (!stopped_) {
+                uint64_t dummy;
+                ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+                if (rc == -1) {
+                    PLOG(FATAL) << "failed to read from eventfd";
+                } else if (rc == 0) {
+                    LOG(FATAL) << "hit EOF on eventfd";
+                }
+
+                WaitForEvents();
+            }
+        });
+    }
+
+    void PrepareReadBlock(IoBlock* block, uint64_t id) {
+        block->pending = false;
+        block->payload.resize(kUsbReadSize);
+        block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+        block->control.aio_nbytes = block->payload.size();
+    }
+
+    IoBlock CreateReadBlock(uint64_t id) {
+        IoBlock block;
+        PrepareReadBlock(&block, id);
+        block.control.aio_rw_flags = 0;
+        block.control.aio_lio_opcode = IOCB_CMD_PREAD;
+        block.control.aio_reqprio = 0;
+        block.control.aio_fildes = read_fd_.get();
+        block.control.aio_offset = 0;
+        block.control.aio_flags = IOCB_FLAG_RESFD;
+        block.control.aio_resfd = event_fd_.get();
+        return block;
+    }
+
+    void WaitForEvents() {
+        static constexpr size_t kMaxEvents = kUsbReadQueueDepth + kUsbWriteQueueDepth;
+        struct io_event events[kMaxEvents];
+        struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
+        int rc = io_getevents(aio_context_.get(), 0, kMaxEvents, events, &timeout);
+        if (rc == -1) {
+            HandleError(StringPrintf("io_getevents failed while reading: %s", strerror(errno)));
+            return;
+        }
+
+        for (int event_idx = 0; event_idx < rc; ++event_idx) {
+            auto& event = events[event_idx];
+            TransferId id = TransferId::from_value(event.data);
+
+            if (event.res < 0) {
+                std::string error =
+                        StringPrintf("%s %" PRIu64 " failed with error %s",
+                                     id.direction == TransferDirection::READ ? "read" : "write",
+                                     id.id, strerror(-event.res));
+                HandleError(error);
+                return;
+            }
+
+            if (id.direction == TransferDirection::READ) {
+                HandleRead(id, event.res);
+            } else {
+                HandleWrite(id);
+            }
+        }
+    }
+
+    void HandleRead(TransferId id, int64_t size) {
+        uint64_t read_idx = id.id % kUsbReadQueueDepth;
+        IoBlock* block = &read_requests_[read_idx];
+        block->pending = false;
+        block->payload.resize(size);
+
+        // Notification for completed reads can be received out of order.
+        if (block->id().id != needed_read_id_) {
+            LOG(VERBOSE) << "read " << block->id().id << " completed while waiting for "
+                         << needed_read_id_;
+            return;
+        }
+
+        for (uint64_t id = needed_read_id_;; ++id) {
+            size_t read_idx = id % kUsbReadQueueDepth;
+            IoBlock* current_block = &read_requests_[read_idx];
+            if (current_block->pending) {
+                break;
+            }
+            ProcessRead(current_block);
+            ++needed_read_id_;
+        }
+    }
+
+    void ProcessRead(IoBlock* block) {
+        if (!block->payload.empty()) {
+            if (!incoming_header_.has_value()) {
+                CHECK_EQ(sizeof(amessage), block->payload.size());
+                amessage msg;
+                memcpy(&msg, block->payload.data(), sizeof(amessage));
+                LOG(DEBUG) << "USB read:" << dump_header(&msg);
+                incoming_header_ = msg;
+            } else {
+                size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
+                Block payload = std::move(block->payload);
+                CHECK_LE(payload.size(), bytes_left);
+                incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
+            }
+
+            if (incoming_header_->data_length == incoming_payload_.size()) {
+                auto packet = std::make_unique<apacket>();
+                packet->msg = *incoming_header_;
+
+                // TODO: Make apacket contain an IOVector so we don't have to coalesce.
+                packet->payload = incoming_payload_.coalesce();
+                read_callback_(this, std::move(packet));
+
+                incoming_header_.reset();
+                incoming_payload_.clear();
             }
         }
 
-        ret = adb_write(h->control.get(), &strings, sizeof(strings));
-        if (ret < 0) {
-            D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
-            goto err;
+        PrepareReadBlock(block, block->id().id + kUsbReadQueueDepth);
+        SubmitRead(block);
+    }
+
+    void SubmitRead(IoBlock* block) {
+        block->pending = true;
+        struct iocb* iocb = &block->control;
+        if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+            HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
+            return;
         }
-        //Signal only when writing the descriptors to ffs
-        android::base::SetProperty("sys.usb.ffs.ready", "1");
     }
 
-    h->bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
-    if (h->bulk_out < 0) {
-        PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
-        goto err;
+    void HandleWrite(TransferId id) {
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        auto it =
+                std::find_if(write_requests_.begin(), write_requests_.end(), [id](const auto& req) {
+                    return static_cast<uint64_t>(req->id()) == static_cast<uint64_t>(id);
+                });
+        CHECK(it != write_requests_.end());
+
+        write_requests_.erase(it);
+        size_t outstanding_writes = --writes_submitted_;
+        LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
+
+        SubmitWrites();
     }
 
-    h->bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
-    if (h->bulk_in < 0) {
-        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
-        goto err;
+    std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+        auto block = std::make_unique<IoBlock>();
+        block->payload = std::move(payload);
+        block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
+        block->control.aio_rw_flags = 0;
+        block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
+        block->control.aio_reqprio = 0;
+        block->control.aio_fildes = write_fd_.get();
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+        block->control.aio_nbytes = block->payload.size();
+        block->control.aio_offset = 0;
+        block->control.aio_flags = IOCB_FLAG_RESFD;
+        block->control.aio_resfd = event_fd_.get();
+        return block;
     }
 
-    h->read_aiob.fd = h->bulk_out;
-    h->write_aiob.fd = h->bulk_in;
-    h->reads_zero_packets = true;
-    return true;
+    void SubmitWrites() REQUIRES(write_mutex_) {
+        if (writes_submitted_ == kUsbWriteQueueDepth) {
+            return;
+        }
 
-err:
-    h->bulk_in.reset();
-    h->bulk_out.reset();
-    h->control.reset();
-    return false;
-}
+        ssize_t writes_to_submit = std::min(kUsbWriteQueueDepth - writes_submitted_,
+                                            write_requests_.size() - writes_submitted_);
+        CHECK_GE(writes_to_submit, 0);
+        if (writes_to_submit == 0) {
+            return;
+        }
 
-static void usb_ffs_open_thread(usb_handle *usb) {
+        struct iocb* iocbs[kUsbWriteQueueDepth];
+        for (int i = 0; i < writes_to_submit; ++i) {
+            CHECK(!write_requests_[writes_submitted_ + i]->pending);
+            write_requests_[writes_submitted_ + i]->pending = true;
+            iocbs[i] = &write_requests_[writes_submitted_ + i]->control;
+            LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
+        }
+
+        int rc = io_submit(aio_context_.get(), writes_to_submit, iocbs);
+        if (rc == -1) {
+            HandleError(StringPrintf("failed to submit write requests: %s", strerror(errno)));
+            return;
+        } else if (rc != writes_to_submit) {
+            LOG(FATAL) << "failed to submit all writes: wanted to submit " << writes_to_submit
+                       << ", actually submitted " << rc;
+        }
+
+        writes_submitted_ += rc;
+    }
+
+    void HandleError(const std::string& error) {
+        std::call_once(error_flag_, [&]() {
+            error_callback_(this, error);
+            if (!stopped_) {
+                Stop();
+            }
+        });
+    }
+
+    std::thread monitor_thread_;
+    std::thread worker_thread_;
+
+    std::atomic<bool> stopped_;
+    std::promise<void> destruction_notifier_;
+    std::once_flag error_flag_;
+
+    unique_fd event_fd_;
+
+    ScopedAioContext aio_context_;
+    unique_fd control_fd_;
+    unique_fd read_fd_;
+    unique_fd write_fd_;
+
+    std::optional<amessage> incoming_header_;
+    IOVector incoming_payload_;
+
+    std::array<IoBlock, kUsbReadQueueDepth> read_requests_;
+    IOVector read_data_;
+
+    // ID of the next request that we're going to send out.
+    size_t next_read_id_ = 0;
+
+    // ID of the next packet we're waiting for.
+    size_t needed_read_id_ = 0;
+
+    std::mutex write_mutex_;
+    std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
+    size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
+    size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+};
+
+static void usb_ffs_open_thread() {
     adb_thread_setname("usb ffs open");
 
     while (true) {
-        // wait until the USB device needs opening
-        std::unique_lock<std::mutex> lock(usb->lock);
-        while (!usb->open_new_connection) {
-            usb->notify.wait(lock);
-        }
-        usb->open_new_connection = false;
-        lock.unlock();
-
-        while (true) {
-            if (init_functionfs(usb)) {
-                LOG(INFO) << "functionfs successfully initialized";
-                break;
-            }
+        unique_fd control;
+        unique_fd bulk_out;
+        unique_fd bulk_in;
+        if (!open_functionfs(&control, &bulk_out, &bulk_in)) {
             std::this_thread::sleep_for(1s);
-        }
-
-        LOG(INFO) << "registering usb transport";
-        register_usb_transport(usb, nullptr, nullptr, 1);
-    }
-
-    // never gets here
-    abort();
-}
-
-static int usb_ffs_write(usb_handle* h, const void* data, int len) {
-    D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
-
-    const char* buf = static_cast<const char*>(data);
-    int orig_len = len;
-    while (len > 0) {
-        int write_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = adb_write(h->bulk_in, buf, write_len);
-        if (n < 0) {
-            D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
-            return -1;
-        }
-        buf += n;
-        len -= n;
-    }
-
-    D("[ done fd=%d ]", h->bulk_in.get());
-    return orig_len;
-}
-
-static int usb_ffs_read(usb_handle* h, void* data, int len) {
-    D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
-
-    char* buf = static_cast<char*>(data);
-    int orig_len = len;
-    while (len > 0) {
-        int read_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = adb_read(h->bulk_out, buf, read_len);
-        if (n < 0) {
-            D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
-            return -1;
-        }
-        buf += n;
-        len -= n;
-    }
-
-    D("[ done fd=%d ]", h->bulk_out.get());
-    return orig_len;
-}
-
-static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
-    aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
-    bool zero_packet = false;
-
-    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
-    const char* cur_data = reinterpret_cast<const char*>(data);
-    int packet_size = getMaxPacketSize(aiob->fd);
-
-    if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
-        0) {
-        D("[ Failed to madvise: %d ]", errno);
-    }
-
-    for (int i = 0; i < num_bufs; i++) {
-        int buf_len = std::min(len, static_cast<int>(h->io_size));
-        io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
-
-        len -= buf_len;
-        cur_data += buf_len;
-
-        if (len == 0 && buf_len % packet_size == 0 && read) {
-            // adb does not expect the device to send a zero packet after data transfer,
-            // but the host *does* send a zero packet for the device to read.
-            zero_packet = h->reads_zero_packets;
-        }
-    }
-    if (zero_packet) {
-        io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
-                packet_size, 0, read);
-        num_bufs += 1;
-    }
-
-    while (true) {
-        if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
-            PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
-            return -1;
-        }
-        if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
-                                            nullptr)) < num_bufs) {
-            PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
-            return -1;
-        }
-        if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
             continue;
         }
-        int ret = 0;
-        for (int i = 0; i < num_bufs; i++) {
-            if (aiob->events[i].res < 0) {
-                errno = -aiob->events[i].res;
-                PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
-                            << " total bufs " << num_bufs;
-                return -1;
-            }
-            ret += aiob->events[i].res;
-        }
-        return ret;
+
+        atransport* transport = new atransport();
+        transport->serial = "UsbFfs";
+        std::promise<void> destruction_notifier;
+        std::future<void> future = destruction_notifier.get_future();
+        transport->SetConnection(std::make_unique<UsbFfsConnection>(
+                std::move(control), std::move(bulk_out), std::move(bulk_in),
+                std::move(destruction_notifier)));
+        register_transport(transport);
+        future.wait();
     }
 }
 
-static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
-    return usb_ffs_do_aio(h, data, len, true);
-}
-
-static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
-    return usb_ffs_do_aio(h, data, len, false);
-}
-
-static void usb_ffs_kick(usb_handle* h) {
-    int err;
-
-    err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
-    if (err < 0) {
-        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
-    }
-
-    err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
-    if (err < 0) {
-        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
-    }
-
-    // don't close ep0 here, since we may not need to reinitialize it with
-    // the same descriptors again. if however ep1/ep2 fail to re-open in
-    // init_functionfs, only then would we close and open ep0 again.
-    // Ditto the comment in usb_adb_kick.
-    h->kicked = true;
-    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
-    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
-}
-
-static void usb_ffs_close(usb_handle* h) {
-    LOG(INFO) << "closing functionfs transport";
-
-    h->kicked = false;
-    h->bulk_out.reset();
-    h->bulk_in.reset();
-
-    // Notify usb_adb_open_thread to open a new connection.
-    h->lock.lock();
-    h->open_new_connection = true;
-    h->lock.unlock();
-    h->notify.notify_one();
-}
-
-usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size) {
-    usb_handle* h = new usb_handle();
-
-    if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
-        // Devices on older kernels (< 3.18) will not have aio support for ffs
-        // unless backported. Fall back on the non-aio functions instead.
-        h->write = usb_ffs_write;
-        h->read = usb_ffs_read;
-    } else {
-        h->write = usb_ffs_aio_write;
-        h->read = usb_ffs_aio_read;
-        aio_block_init(&h->read_aiob, num_bufs);
-        aio_block_init(&h->write_aiob, num_bufs);
-    }
-    h->io_size = io_size;
-    h->kick = usb_ffs_kick;
-    h->close = usb_ffs_close;
-    return h;
-}
-
+void usb_init_legacy();
 void usb_init() {
-    D("[ usb_init - using FunctionFS ]");
-    dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
-    CHECK_NE(-1, dummy_fd.get());
-
-    std::thread(usb_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)).detach();
-}
-
-int usb_write(usb_handle* h, const void* data, int len) {
-    return h->write(h, data, len);
-}
-
-int usb_read(usb_handle* h, void* data, int len) {
-    return h->read(h, data, len);
-}
-
-int usb_close(usb_handle* h) {
-    h->close(h);
-    return 0;
-}
-
-void usb_kick(usb_handle* h) {
-    h->kick(h);
+    if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
+        usb_init_legacy();
+    } else {
+        std::thread(usb_ffs_open_thread).detach();
+    }
 }
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
new file mode 100644
index 0000000..07b4ba8
--- /dev/null
+++ b/adb/daemon/usb_ffs.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#define USB_FFS_BULK_SIZE 16384
+
+// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
+#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+struct func_desc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct ss_func_desc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_ss_ep_comp_descriptor source_comp;
+    struct usb_endpoint_descriptor_no_audio sink;
+    struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct desc_v1 {
+    struct usb_functionfs_descs_head_v1 {
+        __le32 magic;
+        __le32 length;
+        __le32 fs_count;
+        __le32 hs_count;
+    } __attribute__((packed)) header;
+    struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+struct desc_v2 {
+    struct usb_functionfs_descs_head_v2 header;
+    // The rest of the structure depends on the flags in the header.
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
+    __le32 os_count;
+    struct func_desc fs_descs, hs_descs;
+    struct ss_func_desc ss_descs;
+    struct usb_os_desc_header os_header;
+    struct usb_ext_compat_desc os_desc;
+} __attribute__((packed));
+
+// clang-format off
+static struct func_desc fs_descriptors = {
+    .intf = {
+        .bLength = sizeof(fs_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(fs_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+    },
+    .sink = {
+        .bLength = sizeof(fs_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+    },
+};
+
+static struct func_desc hs_descriptors = {
+    .intf = {
+        .bLength = sizeof(hs_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(hs_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+    },
+    .sink = {
+        .bLength = sizeof(hs_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+    },
+};
+
+static struct ss_func_desc ss_descriptors = {
+    .intf = {
+        .bLength = sizeof(ss_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(ss_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+    },
+    .source_comp = {
+        .bLength = sizeof(ss_descriptors.source_comp),
+        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
+    },
+    .sink = {
+        .bLength = sizeof(ss_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+    },
+    .sink_comp = {
+        .bLength = sizeof(ss_descriptors.sink_comp),
+        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
+    },
+};
+
+struct usb_ext_compat_desc os_desc_compat = {
+    .bFirstInterfaceNumber = 0,
+    .Reserved1 = cpu_to_le32(1),
+    .CompatibleID = {0},
+    .SubCompatibleID = {0},
+    .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+    .interface = cpu_to_le32(1),
+    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+    .bcdVersion = cpu_to_le32(1),
+    .wIndex = cpu_to_le32(4),
+    .bCount = cpu_to_le32(1),
+    .Reserved = cpu_to_le32(0),
+};
+
+#define STR_INTERFACE_ "ADB Interface"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+    .header = {
+        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+        .length = cpu_to_le32(sizeof(strings)),
+        .str_count = cpu_to_le32(1),
+        .lang_count = cpu_to_le32(1),
+    },
+    .lang0 = {
+        cpu_to_le16(0x0409), /* en-us */
+        STR_INTERFACE_,
+    },
+};
+// clang-format on
+
+bool open_functionfs(android::base::unique_fd* out_control, android::base::unique_fd* out_bulk_out,
+                     android::base::unique_fd* out_bulk_in) {
+    unique_fd control, bulk_out, bulk_in;
+    struct desc_v1 v1_descriptor = {};
+    struct desc_v2 v2_descriptor = {};
+
+    v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+    v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+    v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
+    v2_descriptor.fs_count = 3;
+    v2_descriptor.hs_count = 3;
+    v2_descriptor.ss_count = 5;
+    v2_descriptor.os_count = 1;
+    v2_descriptor.fs_descs = fs_descriptors;
+    v2_descriptor.hs_descs = hs_descriptors;
+    v2_descriptor.ss_descs = ss_descriptors;
+    v2_descriptor.os_header = os_desc_header;
+    v2_descriptor.os_desc = os_desc_compat;
+
+    if (out_control->get() < 0) {  // might have already done this before
+        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
+        control.reset(adb_open(USB_FFS_ADB_EP0, O_RDWR));
+        if (control < 0) {
+            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
+            return false;
+        }
+
+        if (adb_write(control.get(), &v2_descriptor, sizeof(v2_descriptor)) < 0) {
+            D("[ %s: Switching to V1_descriptor format errno=%s ]", USB_FFS_ADB_EP0,
+              strerror(errno));
+            v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+            v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+            v1_descriptor.header.fs_count = 3;
+            v1_descriptor.header.hs_count = 3;
+            v1_descriptor.fs_descs = fs_descriptors;
+            v1_descriptor.hs_descs = hs_descriptors;
+            if (adb_write(control.get(), &v1_descriptor, sizeof(v1_descriptor)) < 0) {
+                PLOG(ERROR) << "failed to write USB descriptors";
+                return false;
+            }
+        }
+
+        if (adb_write(control.get(), &strings, sizeof(strings)) < 0) {
+            PLOG(ERROR) << "failed to write USB strings";
+            return false;
+        }
+        // Signal only when writing the descriptors to ffs
+        android::base::SetProperty("sys.usb.ffs.ready", "1");
+    }
+
+    bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
+    if (bulk_out < 0) {
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
+        return false;
+    }
+
+    bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
+    if (bulk_in < 0) {
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
+        return false;
+    }
+
+    *out_control = std::move(control);
+    *out_bulk_in = std::move(bulk_in);
+    *out_bulk_out = std::move(bulk_out);
+    return true;
+}
diff --git a/adb/daemon/usb_legacy.cpp b/adb/daemon/usb_legacy.cpp
new file mode 100644
index 0000000..7ace59d
--- /dev/null
+++ b/adb/daemon/usb_legacy.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG USB
+
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+#include "transport.h"
+
+using namespace std::chrono_literals;
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#define USB_FFS_BULK_SIZE 16384
+
+// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
+#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+
+static unique_fd& dummy_fd = *new unique_fd();
+
+static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
+    aiob->iocb.resize(num_bufs);
+    aiob->iocbs.resize(num_bufs);
+    aiob->events.resize(num_bufs);
+    aiob->num_submitted = 0;
+    for (unsigned i = 0; i < num_bufs; i++) {
+        aiob->iocbs[i] = &aiob->iocb[i];
+    }
+    memset(&aiob->ctx, 0, sizeof(aiob->ctx));
+    if (io_setup(num_bufs, &aiob->ctx)) {
+        D("[ aio: got error on io_setup (%d) ]", errno);
+    }
+}
+
+static int getMaxPacketSize(int ffs_fd) {
+    usb_endpoint_descriptor desc;
+    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+        D("[ could not get endpoint descriptor! (%d) ]", errno);
+        return MAX_PACKET_SIZE_HS;
+    } else {
+        return desc.wMaxPacketSize;
+    }
+}
+
+static bool init_functionfs(struct usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+    if (!open_functionfs(&h->control, &h->bulk_out, &h->bulk_in)) {
+        return false;
+    }
+
+    h->read_aiob.fd = h->bulk_out.get();
+    h->write_aiob.fd = h->bulk_in.get();
+    h->reads_zero_packets = true;
+    return true;
+}
+
+static void usb_legacy_ffs_open_thread(usb_handle* usb) {
+    adb_thread_setname("usb legacy ffs open");
+
+    while (true) {
+        // wait until the USB device needs opening
+        std::unique_lock<std::mutex> lock(usb->lock);
+        while (!usb->open_new_connection) {
+            usb->notify.wait(lock);
+        }
+        usb->open_new_connection = false;
+        lock.unlock();
+
+        while (true) {
+            if (init_functionfs(usb)) {
+                LOG(INFO) << "functionfs successfully initialized";
+                break;
+            }
+            std::this_thread::sleep_for(1s);
+        }
+
+        LOG(INFO) << "registering usb transport";
+        register_usb_transport(usb, nullptr, nullptr, 1);
+    }
+
+    // never gets here
+    abort();
+}
+
+static int usb_ffs_write(usb_handle* h, const void* data, int len) {
+    D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
+
+    const char* buf = static_cast<const char*>(data);
+    int orig_len = len;
+    while (len > 0) {
+        int write_len = std::min(USB_FFS_BULK_SIZE, len);
+        int n = adb_write(h->bulk_in, buf, write_len);
+        if (n < 0) {
+            D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
+            return -1;
+        }
+        buf += n;
+        len -= n;
+    }
+
+    D("[ done fd=%d ]", h->bulk_in.get());
+    return orig_len;
+}
+
+static int usb_ffs_read(usb_handle* h, void* data, int len) {
+    D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
+
+    char* buf = static_cast<char*>(data);
+    int orig_len = len;
+    while (len > 0) {
+        int read_len = std::min(USB_FFS_BULK_SIZE, len);
+        int n = adb_read(h->bulk_out, buf, read_len);
+        if (n < 0) {
+            D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
+            return -1;
+        }
+        buf += n;
+        len -= n;
+    }
+
+    D("[ done fd=%d ]", h->bulk_out.get());
+    return orig_len;
+}
+
+static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
+    aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+    bool zero_packet = false;
+
+    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
+    const char* cur_data = reinterpret_cast<const char*>(data);
+    int packet_size = getMaxPacketSize(aiob->fd);
+
+    if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
+        0) {
+        D("[ Failed to madvise: %d ]", errno);
+    }
+
+    for (int i = 0; i < num_bufs; i++) {
+        int buf_len = std::min(len, static_cast<int>(h->io_size));
+        io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
+
+        len -= buf_len;
+        cur_data += buf_len;
+
+        if (len == 0 && buf_len % packet_size == 0 && read) {
+            // adb does not expect the device to send a zero packet after data transfer,
+            // but the host *does* send a zero packet for the device to read.
+            zero_packet = h->reads_zero_packets;
+        }
+    }
+    if (zero_packet) {
+        io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
+                packet_size, 0, read);
+        num_bufs += 1;
+    }
+
+    while (true) {
+        if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
+            PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
+            return -1;
+        }
+        if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
+                                            nullptr)) < num_bufs) {
+            PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
+            return -1;
+        }
+        if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
+            continue;
+        }
+        int ret = 0;
+        for (int i = 0; i < num_bufs; i++) {
+            if (aiob->events[i].res < 0) {
+                errno = -aiob->events[i].res;
+                PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
+                            << " total bufs " << num_bufs;
+                return -1;
+            }
+            ret += aiob->events[i].res;
+        }
+        return ret;
+    }
+}
+
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, true);
+}
+
+static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, false);
+}
+
+static void usb_ffs_kick(usb_handle* h) {
+    int err;
+
+    err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
+    if (err < 0) {
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
+    }
+
+    err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
+    if (err < 0) {
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
+    }
+
+    // don't close ep0 here, since we may not need to reinitialize it with
+    // the same descriptors again. if however ep1/ep2 fail to re-open in
+    // init_functionfs, only then would we close and open ep0 again.
+    // Ditto the comment in usb_adb_kick.
+    h->kicked = true;
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
+}
+
+static void usb_ffs_close(usb_handle* h) {
+    LOG(INFO) << "closing functionfs transport";
+
+    h->kicked = false;
+    h->bulk_out.reset();
+    h->bulk_in.reset();
+
+    // Notify usb_adb_open_thread to open a new connection.
+    h->lock.lock();
+    h->open_new_connection = true;
+    h->lock.unlock();
+    h->notify.notify_one();
+}
+
+usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size) {
+    usb_handle* h = new usb_handle();
+
+    if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+        // Devices on older kernels (< 3.18) will not have aio support for ffs
+        // unless backported. Fall back on the non-aio functions instead.
+        h->write = usb_ffs_write;
+        h->read = usb_ffs_read;
+    } else {
+        h->write = usb_ffs_aio_write;
+        h->read = usb_ffs_aio_read;
+        aio_block_init(&h->read_aiob, num_bufs);
+        aio_block_init(&h->write_aiob, num_bufs);
+    }
+    h->io_size = io_size;
+    h->kick = usb_ffs_kick;
+    h->close = usb_ffs_close;
+    return h;
+}
+
+void usb_init_legacy() {
+    D("[ usb_init - using legacy FunctionFS ]");
+    dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
+    CHECK_NE(-1, dummy_fd.get());
+
+    std::thread(usb_legacy_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE))
+            .detach();
+}
+
+int usb_write(usb_handle* h, const void* data, int len) {
+    return h->write(h, data, len);
+}
+
+int usb_read(usb_handle* h, void* data, int len) {
+    return h->read(h, data, len);
+}
+
+int usb_close(usb_handle* h) {
+    h->close(h);
+    return 0;
+}
+
+void usb_kick(usb_handle* h) {
+    h->kick(h);
+}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index 5a417e0..8d853c3 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -20,6 +20,7 @@
 #include <mutex>
 #include <thread>
 
+#include "adb_io.h"
 #include "socket.h"
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 1534792..8b07f74 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -78,7 +78,7 @@
 
     // Socket ids should never be 0.
     if (local_socket_next_id == 0) {
-        fatal("local socket id overflow");
+        LOG(FATAL) << "local socket id overflow";
     }
 
     local_socket_list.push_back(s);
@@ -451,7 +451,7 @@
 // Returns a new non-NULL asocket handle.
 asocket* create_remote_socket(unsigned id, atransport* t) {
     if (id == 0) {
-        fatal("invalid remote socket id (0)");
+        LOG(FATAL) << "invalid remote socket id (0)";
     }
     asocket* s = new asocket();
     s->id = id;
@@ -477,9 +477,7 @@
     p->payload.assign(destination, destination + strlen(destination) + 1);
     p->msg.data_length = p->payload.size();
 
-    if (p->msg.data_length > s->get_max_payload()) {
-        fatal("destination oversized");
-    }
+    CHECK_LE(p->msg.data_length, s->get_max_payload());
 
     send_packet(p, s->transport);
 }
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 0a08fbb..8a6541d 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -35,6 +35,7 @@
 #include <cutils/sockets.h>
 
 #include <android-base/errors.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
@@ -46,8 +47,6 @@
 
 #include "sysdeps/uio.h"
 
-extern void fatal(const char *fmt, ...);
-
 /* forward declarations */
 
 typedef const struct FHClassRec_* FHClass;
@@ -98,11 +97,6 @@
 #undef assert
 #endif
 
-#define assert(cond)                                                                       \
-    do {                                                                                   \
-        if (!(cond)) fatal("assertion failed '%s' on %s:%d\n", #cond, __FILE__, __LINE__); \
-    } while (0)
-
 void handle_deleter::operator()(HANDLE h) {
     // CreateFile() is documented to return INVALID_HANDLE_FILE on error,
     // implying that NULL is a valid handle, but this is probably impossible.
@@ -730,8 +724,8 @@
         WSADATA wsaData;
         int rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
         if (rc != 0) {
-            fatal("adb: could not initialize Winsock: %s",
-                  android::base::SystemErrorCodeToString(rc).c_str());
+            LOG(FATAL) << "could not initialize Winsock: "
+                       << android::base::SystemErrorCodeToString(rc);
         }
 
         // Note that we do not call atexit() to register WSACleanup to be called
@@ -1287,11 +1281,11 @@
         }
 
         if (read_count == 0) {   // should be impossible
-            fatal("ReadConsoleInputA returned 0");
+            LOG(FATAL) << "ReadConsoleInputA returned 0";
         }
 
         if (read_count != 1) {   // should be impossible
-            fatal("ReadConsoleInputA did not return one input record");
+            LOG(FATAL) << "ReadConsoleInputA did not return one input record";
         }
 
         // If the console window is resized, emulate SIGWINCH by breaking out
@@ -1309,8 +1303,7 @@
         if ((input_record->EventType == KEY_EVENT) &&
             (input_record->Event.KeyEvent.bKeyDown)) {
             if (input_record->Event.KeyEvent.wRepeatCount == 0) {
-                fatal("ReadConsoleInputA returned a key event with zero repeat"
-                      " count");
+                LOG(FATAL) << "ReadConsoleInputA returned a key event with zero repeat count";
             }
 
             // Got an interesting INPUT_RECORD, so return
@@ -2193,7 +2186,7 @@
     for (int i = 0; i < argc; ++i) {
         std::string arg_narrow;
         if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
-            fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+            PLOG(FATAL) << "cannot convert argument from UTF-16 to UTF-8";
         }
         narrow_args[i] = strdup(arg_narrow.c_str());
     }
@@ -2644,7 +2637,7 @@
         // If _wenviron is null, then -municode probably wasn't used. That
         // linker flag will cause the entry point to setup _wenviron. It will
         // also require an implementation of wmain() (which we provide above).
-        fatal("_wenviron is not set, did you link with -municode?");
+        LOG(FATAL) << "_wenviron is not set, did you link with -municode?";
     }
 
     // Read name/value pairs from UTF-16 _wenviron and write new name/value
diff --git a/adb/test_device.py b/adb/test_device.py
index c3166ff..34f8fd9 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -750,7 +750,7 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    def test_push_empty(self):
+    def disabled_test_push_empty(self):
         """Push an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
diff --git a/adb/transport.cpp b/adb/transport.cpp
index cabd279..03a9f30 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -52,7 +52,6 @@
 #include "fdevent.h"
 #include "sysdeps/chrono.h"
 
-static void register_transport(atransport* transport);
 static void remove_transport(atransport* transport);
 static void transport_unref(atransport* transport);
 
@@ -67,6 +66,8 @@
 const char* const kFeatureStat2 = "stat_v2";
 const char* const kFeatureLibusb = "libusb";
 const char* const kFeaturePushSync = "push_sync";
+const char* const kFeatureApex = "apex";
+const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
 
 namespace {
 
@@ -408,42 +409,6 @@
     fd_.reset();
 }
 
-static std::string dump_packet(const char* name, const char* func, apacket* p) {
-    unsigned command = p->msg.command;
-    int len = p->msg.data_length;
-    char cmd[9];
-    char arg0[12], arg1[12];
-    int n;
-
-    for (n = 0; n < 4; n++) {
-        int b = (command >> (n * 8)) & 255;
-        if (b < 32 || b >= 127) break;
-        cmd[n] = (char)b;
-    }
-    if (n == 4) {
-        cmd[4] = 0;
-    } else {
-        /* There is some non-ASCII name in the command, so dump
-            * the hexadecimal value instead */
-        snprintf(cmd, sizeof cmd, "%08x", command);
-    }
-
-    if (p->msg.arg0 < 256U)
-        snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
-    else
-        snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
-
-    if (p->msg.arg1 < 256U)
-        snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
-    else
-        snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
-
-    std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
-                                                     func, cmd, arg0, arg1, len);
-    result += dump_hex(p->payload.data(), p->payload.size());
-    return result;
-}
-
 void send_packet(apacket* p, atransport* t) {
     p->msg.magic = p->msg.command ^ 0xffffffff;
     // compute a checksum for connection/auth packets for compatibility reasons
@@ -456,7 +421,7 @@
     VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "to remote", p);
 
     if (t == nullptr) {
-        fatal("Transport is null");
+        LOG(FATAL) << "Transport is null";
     }
 
     if (t->Write(p) != 0) {
@@ -562,7 +527,7 @@
 
 asocket* create_device_tracker(bool long_output) {
     device_tracker* tracker = new device_tracker();
-    if (tracker == nullptr) fatal("cannot allocate device tracker");
+    if (tracker == nullptr) LOG(FATAL) << "cannot allocate device tracker";
 
     D("device tracker %p created", tracker);
 
@@ -668,7 +633,7 @@
     }
 
     if (transport_read_action(_fd, &m)) {
-        fatal_errno("cannot read transport registration socket");
+        PLOG(FATAL) << "cannot read transport registration socket";
     }
 
     t = m.transport;
@@ -707,7 +672,7 @@
             return true;
         });
         t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
-            D("%s: connection terminated: %s", t->serial.c_str(), error.c_str());
+            LOG(INFO) << t->serial_name() << ": connection terminated: " << error;
             fdevent_run_on_main_thread([t]() {
                 handle_offline(t);
                 transport_unref(t);
@@ -742,7 +707,7 @@
     int s[2];
 
     if (adb_socketpair(s)) {
-        fatal_errno("cannot open transport registration socketpair");
+        PLOG(FATAL) << "cannot open transport registration socketpair";
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
@@ -766,13 +731,13 @@
 }
 
 /* the fdevent select pump is single threaded */
-static void register_transport(atransport* transport) {
+void register_transport(atransport* transport) {
     tmsg m;
     m.transport = transport;
     m.action = 1;
     D("transport: %s registered", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
-        fatal_errno("cannot write transport registration socket\n");
+        PLOG(FATAL) << "cannot write transport registration socket";
     }
 }
 
@@ -782,7 +747,7 @@
     m.action = 0;
     D("transport: %s removed", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
-        fatal_errno("cannot write transport registration socket\n");
+        PLOG(FATAL) << "cannot write transport registration socket";
     }
 }
 
@@ -794,6 +759,7 @@
     CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
+        LOG(INFO) << "destroying transport " << t->serial_name();
         t->connection()->Stop();
 #if ADB_HOST
         if (t->IsTcpDevice() && !t->kicked()) {
@@ -1043,7 +1009,10 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2, kFeatureCmd, kFeatureStat2,
+        kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir,
+#if ADB_HOST
+                kFeatureApex
+#endif
         // Increment ADB_SERVER_VERSION whenever the feature list changes to
         // make sure that the adb client and server features stay in sync
         // (http://b/24370690).
@@ -1329,6 +1298,7 @@
     register_transport(t);
 }
 
+#if ADB_HOST
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
@@ -1340,6 +1310,7 @@
         return false;
     });
 }
+#endif
 
 bool check_header(apacket* p, atransport* t) {
     if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
diff --git a/adb/transport.h b/adb/transport.h
index f854ce5..9894bdf 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -57,8 +57,12 @@
 extern const char* const kFeatureStat2;
 // The server is running with libusb enabled.
 extern const char* const kFeatureLibusb;
-// The server supports `push --sync`.
+// adbd supports `push --sync`.
 extern const char* const kFeaturePushSync;
+// adbd supports installing .apex packages.
+extern const char* const kFeatureApex;
+// adbd has b/110953234 fixed.
+extern const char* const kFeatureFixedPushMkdir;
 
 TransportId NextTransportId();
 
@@ -362,6 +366,7 @@
 void kick_all_tcp_devices();
 void kick_all_transports();
 
+void register_transport(atransport* transport);
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
 
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 8c628d8..b66f8fa 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,13 +19,16 @@
 #include <gtest/gtest.h>
 
 #include "adb.h"
+#include "fdevent_test.h"
+
+struct TransportTest : public FdeventTest {};
 
 static void DisconnectFunc(void* arg, atransport*) {
     int* count = reinterpret_cast<int*>(arg);
     ++*count;
 }
 
-TEST(transport, RunDisconnects) {
+TEST_F(TransportTest, RunDisconnects) {
     atransport t;
     // RunDisconnects() can be called with an empty atransport.
     t.RunDisconnects();
@@ -49,7 +52,7 @@
     ASSERT_EQ(0, count);
 }
 
-TEST(transport, SetFeatures) {
+TEST_F(TransportTest, SetFeatures) {
     atransport t;
     ASSERT_EQ(0U, t.features().size());
 
@@ -77,8 +80,7 @@
     ASSERT_EQ(0U, t.features().size());
 }
 
-TEST(transport, parse_banner_no_features) {
-    set_main_thread();
+TEST_F(TransportTest, parse_banner_no_features) {
     atransport t;
 
     parse_banner("host::", &t);
@@ -91,7 +93,7 @@
     ASSERT_EQ(std::string(), t.device);
 }
 
-TEST(transport, parse_banner_product_features) {
+TEST_F(TransportTest, parse_banner_product_features) {
     atransport t;
 
     const char banner[] =
@@ -107,9 +109,8 @@
     ASSERT_EQ(std::string("baz"), t.device);
 }
 
-TEST(transport, parse_banner_features) {
+TEST_F(TransportTest, parse_banner_features) {
     atransport t;
-
     const char banner[] =
         "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;"
         "features=woodly,doodly";
@@ -126,7 +127,7 @@
     ASSERT_EQ(std::string("baz"), t.device);
 }
 
-TEST(transport, test_matches_target) {
+TEST_F(TransportTest, test_matches_target) {
     std::string serial = "foo";
     std::string devpath = "/path/to/bar";
     std::string product = "test_product";
@@ -157,7 +158,7 @@
     }
 }
 
-TEST(transport, test_matches_target_local) {
+TEST_F(TransportTest, test_matches_target_local) {
     std::string serial = "100.100.100.100:5555";
 
     atransport t;
diff --git a/base/logging.cpp b/base/logging.cpp
index d60d91d..bd09069 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -53,6 +53,7 @@
 #include <unistd.h>
 #endif
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
@@ -71,14 +72,8 @@
   static char progname[MAX_PATH] = {};
 
   if (first) {
-    CHAR longname[MAX_PATH];
-    DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname));
-    if ((nchars >= arraysize(longname)) || (nchars == 0)) {
-      // String truncation or some other error.
-      strcpy(progname, "<unknown>");
-    } else {
-      strcpy(progname, basename(longname));
-    }
+    snprintf(progname, sizeof(progname), "%s",
+             android::base::Basename(android::base::GetExecutablePath()).c_str());
     first = false;
   }
 
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index bca2337..64e9fb4 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -446,11 +446,13 @@
                                    bool* change) {
     const auto top = overlay + kOverlayTopDir;
 
-    if (!fs_mgr_access(top)) return false;
+    if (!fs_mgr_access(top)) return fs_mgr_overlayfs_teardown_scratch(overlay, change);
 
     auto cleanup_all = mount_point.empty();
-    const auto oldpath = top + (cleanup_all ? "" : ("/" + mount_point));
-    const auto newpath = oldpath + ".teardown";
+    const auto partition_name = android::base::Basename(mount_point);
+    const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
+    const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
+                                     : top + "/." + partition_name + ".teardown";
     auto ret = fs_mgr_rm_all(newpath);
     auto save_errno = errno;
     if (!rename(oldpath.c_str(), newpath.c_str())) {
@@ -476,12 +478,28 @@
         if (!rmdir(top.c_str())) {
             if (change) *change = true;
             cleanup_all = true;
-        } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
+        } else if (errno == ENOTEMPTY) {
+            cleanup_all = true;
+            // cleanup all if the content is all hidden (leading .)
+            std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
+            if (!dir) {
+                PERROR << "opendir " << top;
+            } else {
+                dirent* entry;
+                while ((entry = readdir(dir.get()))) {
+                    if (entry->d_name[0] != '.') {
+                        cleanup_all = false;
+                        break;
+                    }
+                }
+            }
+            errno = save_errno;
+        } else if (errno == ENOENT) {
+            cleanup_all = true;
+            errno = save_errno;
+        } else {
             ret = false;
             PERROR << "rmdir " << top;
-        } else {
-            errno = save_errno;
-            cleanup_all = true;
         }
     }
     if (cleanup_all) ret &= fs_mgr_overlayfs_teardown_scratch(overlay, change);
@@ -589,8 +607,18 @@
     fsrec->fs_type = strdup(mnt_type.c_str());
     fsrec->flags = MS_RELATIME;
     fsrec->fs_options = strdup("");
-    auto mounted = fs_mgr_do_mount_one(fsrec) == 0;
     auto save_errno = errno;
+    auto mounted = fs_mgr_do_mount_one(fsrec) == 0;
+    if (!mounted) {
+        free(fsrec->fs_type);
+        if (mnt_type == "f2fs") {
+            fsrec->fs_type = strdup("ext4");
+        } else {
+            fsrec->fs_type = strdup("f2fs");
+        }
+        mounted = fs_mgr_do_mount_one(fsrec) == 0;
+        if (!mounted) save_errno = errno;
+    }
     setfscreatecon(nullptr);
     if (!mounted) rmdir(kScratchMountPoint.c_str());
     errno = save_errno;
@@ -600,6 +628,7 @@
 const std::string kMkF2fs("/system/bin/make_f2fs");
 const std::string kMkExt4("/system/bin/mke2fs");
 
+// Only a suggestion for _first_ try during mounting
 std::string fs_mgr_overlayfs_scratch_mount_type() {
     if (!access(kMkF2fs.c_str(), X_OK)) return "f2fs";
     if (!access(kMkExt4.c_str(), X_OK)) return "ext4";
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index bbdec5b..4953655 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -47,6 +47,9 @@
     cppflags: [
         "-Wno-unused-parameter",
     ],
+    static_libs: [
+        "libgmock",
+    ],
     shared_libs: [
         "liblp",
         "libbase",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 03fd5f9..2c57a35 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -261,15 +261,20 @@
     // we store a backup copy of everything.
     uint64_t reserved =
             LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
-    uint64_t total_reserved = reserved * 2;
+    uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved * 2;
     if (device_info.size < total_reserved) {
         LERROR << "Attempting to create metadata on a block device that is too small.";
         return false;
     }
 
     // Compute the first free sector, factoring in alignment.
-    uint64_t free_area_start =
-            AlignTo(total_reserved, device_info.alignment, device_info.alignment_offset);
+    uint64_t free_area_start = total_reserved;
+    if (device_info.alignment || device_info.alignment_offset) {
+        free_area_start =
+                AlignTo(free_area_start, device_info.alignment, device_info.alignment_offset);
+    } else {
+        free_area_start = AlignTo(free_area_start, device_info.logical_block_size);
+    }
     uint64_t first_sector = free_area_start / LP_SECTOR_SIZE;
 
     // There must be one logical block of free space remaining (enough for one partition).
@@ -334,7 +339,7 @@
     return nullptr;
 }
 
-PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) const {
+PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
     for (const auto& group : groups_) {
         if (group->name() == group_name) {
             return group.get();
@@ -363,6 +368,57 @@
     }
 }
 
+void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents,
+                                        std::vector<Interval>* free_regions) const {
+    // Convert the extent list into a list of gaps between the extents; i.e.,
+    // the list of ranges that are free on the disk.
+    for (size_t i = 1; i < extents.size(); i++) {
+        const Interval& previous = extents[i - 1];
+        const Interval& current = extents[i];
+
+        uint64_t aligned = AlignSector(previous.end);
+        if (aligned >= current.start) {
+            // There is no gap between these two extents, try the next one.
+            // Note that we check with >= instead of >, since alignment may
+            // bump the ending sector past the beginning of the next extent.
+            continue;
+        }
+
+        // The new interval represents the free space starting at the end of
+        // the previous interval, and ending at the start of the next interval.
+        free_regions->emplace_back(aligned, current.start);
+    }
+}
+
+auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
+    std::vector<Interval> free_regions;
+
+    // Collect all extents in the partition table, then sort them by starting
+    // sector.
+    std::vector<Interval> extents;
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            extents.emplace_back(linear->physical_sector(),
+                                 linear->physical_sector() + extent->num_sectors());
+        }
+    }
+
+    // Add 0-length intervals for the first and last sectors. This will cause
+    // ExtentsToFreeList() to treat the space in between as available.
+    uint64_t last_sector = geometry_.block_device_size / LP_SECTOR_SIZE;
+    extents.emplace_back(geometry_.first_logical_sector, geometry_.first_logical_sector);
+    extents.emplace_back(last_sector, last_sector);
+
+    std::sort(extents.begin(), extents.end());
+
+    ExtentsToFreeList(extents, &free_regions);
+    return free_regions;
+}
+
 bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
     PartitionGroup* group = FindGroup(partition->group_name());
     CHECK(group);
@@ -384,59 +440,7 @@
     uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
     DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
 
-    struct Interval {
-        uint64_t start;
-        uint64_t end;
-
-        Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
-        uint64_t length() const { return end - start; }
-        bool operator<(const Interval& other) const { return start < other.start; }
-    };
-
-    // Collect all extents in the partition table, then sort them by starting
-    // sector.
-    std::vector<Interval> extents;
-    for (const auto& partition : partitions_) {
-        for (const auto& extent : partition->extents()) {
-            LinearExtent* linear = extent->AsLinearExtent();
-            if (!linear) {
-                continue;
-            }
-            extents.emplace_back(linear->physical_sector(),
-                                 linear->physical_sector() + extent->num_sectors());
-        }
-    }
-    std::sort(extents.begin(), extents.end());
-
-    // Convert the extent list into a list of gaps between the extents; i.e.,
-    // the list of ranges that are free on the disk.
-    std::vector<Interval> free_regions;
-    for (size_t i = 1; i < extents.size(); i++) {
-        const Interval& previous = extents[i - 1];
-        const Interval& current = extents[i];
-
-        uint64_t aligned = AlignSector(previous.end);
-        if (aligned >= current.start) {
-            // There is no gap between these two extents, try the next one.
-            // Note that we check with >= instead of >, since alignment may
-            // bump the ending sector past the beginning of the next extent.
-            continue;
-        }
-
-        // The new interval represents the free space starting at the end of
-        // the previous interval, and ending at the start of the next interval.
-        free_regions.emplace_back(aligned, current.start);
-    }
-
-    // Add a final interval representing the remainder of the free space.
-    uint64_t last_free_extent_start =
-            extents.empty() ? geometry_.first_logical_sector : extents.back().end;
-    last_free_extent_start = AlignSector(last_free_extent_start);
-
-    uint64_t last_sector = geometry_.block_device_size / LP_SECTOR_SIZE;
-    if (last_free_extent_start < last_sector) {
-        free_regions.emplace_back(last_free_extent_start, last_sector);
-    }
+    std::vector<Interval> free_regions = GetFreeRegions();
 
     const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
     CHECK_NE(sectors_per_block, 0);
@@ -561,7 +565,7 @@
     return size;
 }
 
-uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
+uint64_t MetadataBuilder::AlignSector(uint64_t sector) const {
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
     uint64_t lba = sector * LP_SECTOR_SIZE;
@@ -620,5 +624,36 @@
     return true;
 }
 
+std::vector<std::string> MetadataBuilder::ListGroups() const {
+    std::vector<std::string> names;
+    for (const auto& group : groups_) {
+        names.emplace_back(group->name());
+    }
+    return names;
+}
+
+void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
+    if (group_name == "default") {
+        // Cannot remove the default group.
+        return;
+    }
+    std::vector<std::string> partition_names;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() == group_name) {
+            partition_names.emplace_back(partition->name());
+        }
+    }
+
+    for (const auto& partition_name : partition_names) {
+        RemovePartition(partition_name);
+    }
+    for (auto iter = groups_.begin(); iter != groups_.end(); iter++) {
+        if ((*iter)->name() == group_name) {
+            groups_.erase(iter);
+            break;
+        }
+    }
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index eb65f89..27ad250 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
+#include <fs_mgr.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
-#include "fs_mgr.h"
+
 #include "utility.h"
 
 using namespace std;
 using namespace android::fs_mgr;
+using ::testing::ElementsAre;
 
 TEST(liblp, BuildBasic) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
@@ -48,8 +51,11 @@
     LinearExtent* extent = system->extents()[0]->AsLinearExtent();
     ASSERT_NE(extent, nullptr);
     EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    // The first logical sector will be (8192+1024*4)/512 = 12.
-    EXPECT_EQ(extent->physical_sector(), 24);
+    // The first logical sector will be:
+    //      (LP_PARTITION_RESERVED_BYTES + 4096*2 + 1024*4) / 512
+    // Or, in terms of sectors (reserved + geometry + metadata):
+    //      (8 + 16 + 8) = 32
+    EXPECT_EQ(extent->physical_sector(), 32);
 
     // Test resizing to the same size.
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -78,7 +84,7 @@
     extent = system->extents()[0]->AsLinearExtent();
     ASSERT_NE(extent, nullptr);
     EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(extent->physical_sector(), 24);
+    EXPECT_EQ(extent->physical_sector(), 32);
 
     // Test shrinking to 0.
     builder->ResizePartition(system, 0);
@@ -148,7 +154,7 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 150);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 174);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -202,7 +208,8 @@
     // maximum size of a metadata blob. Then, we double that space since
     // we store a backup copy of everything.
     static constexpr uint64_t geometry = 4 * 1024;
-    static constexpr uint64_t allocatable = total - (metadata * slots + geometry) * 2;
+    static constexpr uint64_t allocatable =
+            total - (metadata * slots + geometry) * 2 - LP_PARTITION_RESERVED_BYTES;
     EXPECT_EQ(builder->AllocatableSpace(), allocatable);
     EXPECT_EQ(builder->UsedSpace(), 0);
 
@@ -243,11 +250,11 @@
     ASSERT_NE(system2, nullptr);
     ASSERT_NE(vendor1, nullptr);
     EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system1->physical_sector(), 24);
+    EXPECT_EQ(system1->physical_sector(), 32);
     EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system2->physical_sector(), 216);
+    EXPECT_EQ(system2->physical_sector(), 224);
     EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(vendor1->physical_sector(), 152);
+    EXPECT_EQ(vendor1->physical_sector(), 160);
     EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
     EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
 }
@@ -293,7 +300,7 @@
     EXPECT_EQ(geometry.struct_size, sizeof(geometry));
     EXPECT_EQ(geometry.metadata_max_size, 1024);
     EXPECT_EQ(geometry.metadata_slot_count, 2);
-    EXPECT_EQ(geometry.first_logical_sector, 24);
+    EXPECT_EQ(geometry.first_logical_sector, 32);
 
     static const size_t kMetadataSpace =
             ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
@@ -354,9 +361,9 @@
     LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
     LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
     EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system1->physical_sector(), 24);
+    EXPECT_EQ(system1->physical_sector(), 32);
     EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system2->physical_sector(), 216);
+    EXPECT_EQ(system2->physical_sector(), 224);
     EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
 }
 
@@ -381,7 +388,7 @@
     EXPECT_EQ(builder, nullptr);
 
     // No space to store metadata + geometry + one free sector.
-    device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
+    device_info.size += LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2);
     builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
@@ -511,3 +518,57 @@
     EXPECT_FALSE(builder->ResizePartition(partition, 32768));
     EXPECT_EQ(partition->size(), 16384);
 }
+
+constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
+    return x << 30;
+}
+
+TEST(liblp, RemoveAndAddFirstPartition) {
+    auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
+    ASSERT_NE(nullptr, builder);
+    ASSERT_TRUE(builder->AddGroup("foo_a", 5_GiB));
+    ASSERT_TRUE(builder->AddGroup("foo_b", 5_GiB));
+    android::fs_mgr::Partition* p;
+    p = builder->AddPartition("system_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+    p = builder->AddPartition("vendor_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+    p = builder->AddPartition("system_b", "foo_b", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+    p = builder->AddPartition("vendor_b", "foo_b", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+
+    builder->RemovePartition("system_a");
+    builder->RemovePartition("vendor_a");
+    p = builder->AddPartition("system_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 3_GiB));
+    p = builder->AddPartition("vendor_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+}
+
+TEST(liblp, ListGroups) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+
+    std::vector<std::string> groups = builder->ListGroups();
+    ASSERT_THAT(groups, ElementsAre("default", "example"));
+}
+
+TEST(liblp, RemoveGroupAndPartitions) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+    ASSERT_NE(builder->AddPartition("system", "default", 0), nullptr);
+    ASSERT_NE(builder->AddPartition("vendor", "example", 0), nullptr);
+
+    builder->RemoveGroupAndPartitions("example");
+    ASSERT_NE(builder->FindPartition("system"), nullptr);
+    ASSERT_EQ(builder->FindPartition("vendor"), nullptr);
+    ASSERT_THAT(builder->ListGroups(), ElementsAre("default"));
+
+    builder->RemoveGroupAndPartitions("default");
+    ASSERT_NE(builder->FindPartition("system"), nullptr);
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 8716988..dfa37fe 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -28,12 +28,17 @@
 namespace fs_mgr {
 
 std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
-    LpMetadataGeometry geometry;
-    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
         return nullptr;
     }
-    if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << " read failed";
+        return nullptr;
+    }
+    LpMetadataGeometry geometry;
+    if (!ParseGeometry(buffer.get(), &geometry)) {
         return nullptr;
     }
     return ParseMetadata(geometry, fd);
@@ -59,7 +64,7 @@
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
     android::base::unique_fd fd(open(file, O_RDONLY));
     if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
         return nullptr;
     }
     return ReadFromImageFile(fd);
@@ -72,7 +77,7 @@
     std::string everything = geometry + metadata;
 
     if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
-        PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " write " << everything.size() << " bytes failed";
         return false;
     }
     return true;
@@ -81,7 +86,7 @@
 bool WriteToImageFile(const char* file, const LpMetadata& input) {
     android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
     if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
         return false;
     }
     return WriteToImageFile(fd, input);
@@ -106,6 +111,14 @@
         LERROR << "Metadata max size must be a multiple of the block size, " << block_size;
         return;
     }
+    if (LP_METADATA_GEOMETRY_SIZE % block_size != 0) {
+        LERROR << "Geometry size is not a multiple of the block size, " << block_size;
+        return;
+    }
+    if (LP_PARTITION_RESERVED_BYTES % block_size != 0) {
+        LERROR << "Reserved size is not a multiple of the block size, " << block_size;
+        return;
+    }
 
     uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
     if (num_blocks >= UINT_MAX) {
@@ -163,6 +176,11 @@
 }
 
 bool SparseBuilder::Build() {
+    if (sparse_file_add_fill(file_.get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
+        LERROR << "Could not add initial sparse block for reserved zeroes";
+        return false;
+    }
+
     std::string geometry_blob = SerializeGeometry(geometry_);
     std::string metadata_blob = SerializeMetadata(metadata_);
     metadata_blob.resize(geometry_.metadata_max_size);
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 8dbba84..7e07df4 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -199,6 +199,9 @@
     // Find a partition by name. If no partition is found, nullptr is returned.
     Partition* FindPartition(const std::string& name);
 
+    // Find a group by name. If no group is found, nullptr is returned.
+    PartitionGroup* FindGroup(const std::string& name);
+
     // Grow or shrink a partition to the requested size. This size will be
     // rounded UP to the nearest block (512 bytes).
     //
@@ -215,6 +218,12 @@
     uint64_t AllocatableSpace() const;
     uint64_t UsedSpace() const;
 
+    // Return a list of all group names.
+    std::vector<std::string> ListGroups() const;
+
+    // Remove all partitions belonging to a group, then remove the group.
+    void RemoveGroupAndPartitions(const std::string& group_name);
+
     bool GetBlockDeviceInfo(BlockDeviceInfo* info) const;
     bool UpdateBlockDeviceInfo(const BlockDeviceInfo& info);
 
@@ -228,10 +237,23 @@
     bool Init(const LpMetadata& metadata);
     bool GrowPartition(Partition* partition, uint64_t aligned_size);
     void ShrinkPartition(Partition* partition, uint64_t aligned_size);
-    uint64_t AlignSector(uint64_t sector);
-    PartitionGroup* FindGroup(const std::string& group_name) const;
+    uint64_t AlignSector(uint64_t sector) const;
     uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
 
+    struct Interval {
+        uint64_t start;
+        uint64_t end;
+
+        Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+        uint64_t length() const { return end - start; }
+        bool operator<(const Interval& other) const {
+            return (start == other.start) ? end < other.end : start < other.start;
+        }
+    };
+    std::vector<Interval> GetFreeRegions() const;
+    void ExtentsToFreeList(const std::vector<Interval>& extents,
+                           std::vector<Interval>* free_regions) const;
+
     LpMetadataGeometry geometry_;
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 711ff95..89b219c 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
 #define LP_METADATA_HEADER_MAGIC 0x414C5030
 
 /* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 5
+#define LP_METADATA_MAJOR_VERSION 6
 #define LP_METADATA_MINOR_VERSION 0
 
 /* Attributes for the LpMetadataPartition::attributes field.
@@ -72,6 +72,11 @@
 /* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
 #define LP_SECTOR_SIZE 512
 
+/* Amount of space reserved at the start of every super partition to avoid
+ * creating an accidental boot sector.
+ */
+#define LP_PARTITION_RESERVED_BYTES 4096
+
 /* This structure is stored at block 0 in the first 4096 bytes of the
  * partition, and again in the following block. It is never modified and
  * describes how logical partition information can be located.
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 92696f5..2aa41f3 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -376,6 +376,7 @@
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
     unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
 
     unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
     ASSERT_GE(fd, 0);
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 835df9b..43d8076 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -123,11 +123,11 @@
 bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
     if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
         return false;
     }
     if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
-        PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
         return false;
     }
     return ParseGeometry(buffer.get(), geometry);
@@ -136,11 +136,11 @@
 bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
     if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
         return false;
     }
     if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
-        PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
+        PERROR << __PRETTY_FUNCTION__ << " backup read " << LP_METADATA_GEOMETRY_SIZE
                << " bytes failed";
         return false;
     }
@@ -223,7 +223,7 @@
     // First read and validate the header.
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
     if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) {
-        PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " read " << sizeof(metadata->header) << "bytes failed";
         return nullptr;
     }
     if (!ValidateMetadataHeader(metadata->header)) {
@@ -241,7 +241,7 @@
         return nullptr;
     }
     if (!reader->ReadFully(buffer.get(), header.tables_size)) {
-        PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " read " << header.tables_size << "bytes failed";
         return nullptr;
     }
 
@@ -312,7 +312,7 @@
                                                 uint32_t slot_number) {
     int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
     if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
         return nullptr;
     }
     return ParseMetadata(geometry, fd);
@@ -322,7 +322,7 @@
                                                uint32_t slot_number) {
     int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
     if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
         return nullptr;
     }
     return ParseMetadata(geometry, fd);
@@ -335,7 +335,7 @@
     }
 
     if (slot_number >= geometry.metadata_slot_count) {
-        LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+        LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
         return nullptr;
     }
 
@@ -350,7 +350,7 @@
 std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
     android::base::unique_fd fd(open(block_device, O_RDONLY));
     if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
         return nullptr;
     }
     return ReadMetadata(fd, slot_number);
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 2f7692f..0556833 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -57,17 +57,18 @@
 }
 
 int64_t GetPrimaryGeometryOffset() {
-    return 0;
+    return LP_PARTITION_RESERVED_BYTES;
 }
 
 int64_t GetBackupGeometryOffset() {
-    return LP_METADATA_GEOMETRY_SIZE;
+    return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
 }
 
 int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
     CHECK(slot_number < geometry.metadata_slot_count);
 
-    int64_t offset = (LP_METADATA_GEOMETRY_SIZE * 2) + geometry.metadata_max_size * slot_number;
+    int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
+                     geometry.metadata_max_size * slot_number;
     CHECK(offset + geometry.metadata_max_size <=
           int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
     return offset;
@@ -75,7 +76,7 @@
 
 int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
     CHECK(slot_number < geometry.metadata_slot_count);
-    int64_t start = LP_METADATA_GEOMETRY_SIZE * 2 +
+    int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
                     int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
     return start + int64_t(geometry.metadata_max_size * slot_number);
 }
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 46ed2e6..8baf9e7 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -41,13 +41,13 @@
                                    0,
                                    1024 * 1024,
                                    4096};
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 8192);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 8192 + 16384);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 8192 + 16384 * 2);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 8192 + 16384 * 3);
+    static const uint64_t start = LP_PARTITION_RESERVED_BYTES;
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), start + 8192 + 16384);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), start + 8192 + 16384 * 2);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), start + 8192 + 16384 * 3);
 
-    static const uint64_t backup_start = 8192 + 16384 * 4;
-
+    static const uint64_t backup_start = start + 8192 + 16384 * 4;
     EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), backup_start + 16384 * 3);
     EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), backup_start + 16384 * 2);
     EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), backup_start + 16384 * 1);
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 5cf1a2c..f857d8c 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -144,11 +144,11 @@
                                  const std::function<bool(int, const std::string&)>& writer) {
     int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
     if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
         return false;
     }
     if (!writer(fd, blob)) {
-        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
         return false;
     }
     return true;
@@ -160,16 +160,16 @@
     int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
     int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_SET);
     if (abs_offset == (int64_t)-1) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
         return false;
     }
     if (abs_offset >= int64_t(geometry.first_logical_sector) * LP_SECTOR_SIZE) {
-        PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
+        PERROR << __PRETTY_FUNCTION__ << " backup offset " << abs_offset
                << " is within logical partition bounds, sector " << geometry.first_logical_sector;
         return false;
     }
     if (!writer(fd, blob)) {
-        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
         return false;
     }
     return true;
@@ -205,22 +205,33 @@
         return false;
     }
 
-    // Write geometry to the first and last 4096 bytes of the device.
+    // Write zeroes to the first block.
+    std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, zeroes.data(), zeroes.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " write " << zeroes.size() << " bytes failed";
+        return false;
+    }
+
+    // Write geometry to the primary and backup locations.
     std::string blob = SerializeGeometry(metadata.geometry);
     if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: primary geometry";
         return false;
     }
     if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
         return false;
     }
     if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: backup geometry";
         return false;
     }
     if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
         return false;
     }
 
@@ -303,7 +314,7 @@
 bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {
     android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
     if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
         return false;
     }
     if (!FlashPartitionTable(fd, metadata)) {
@@ -317,7 +328,7 @@
                           uint32_t slot_number) {
     android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
     if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device;
         return false;
     }
     if (!UpdatePartitionTable(fd, metadata, slot_number)) {
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
new file mode 100755
index 0000000..d4ca574
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -0,0 +1,264 @@
+#! /bin/bash
+#
+# adb remount tests (overlayfs focus)
+#
+# Conditions:
+#  - Must be a userdebug build.
+#  - Must be in adb mode.
+#  - Kernel must have overlayfs enabled and patched to support override_creds.
+#  - Must have either squashfs, ext4-dedupe or right-sized partitions.
+#  - Minimum expectation system and vender are overlayfs covered partitions.
+#
+
+# Helper Variables
+
+SPACE=" "
+# A _real_ embedded tab character
+TAB="`echo | tr '\n' '\t'`"
+# A _real_ embedded escape character
+ESCAPE="`echo | tr '\n' '\033'`"
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+ORANGE="${ESCAPE}[38;5;255:165:0m"
+NORMAL="${ESCAPE}[0m"
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+  fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+  adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: adb_sh <commands>
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+  adb shell "${@}"
+}
+
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+  adb_sh getprop ${1} 2>&1 </dev/null
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+    false
+  fi
+}
+
+[ "USAGE: adb_su <commands>
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+  adb_sh su root "${@}"
+}
+
+[ "USAGE: adb_cat <file> >stdout
+
+Returns: content of file to stdout with carriage returns skipped,
+         true of the file exists" ]
+adb_cat() {
+    OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
+    retval=${?}
+    echo "${OUTPUT}" | tr -d '\r'
+    return ${retval}
+}
+
+[ "USAGE: adb_reboot
+
+Returns: true if the reboot command succeeded" ]
+adb_reboot() {
+  adb reboot remount-test
+}
+
+[ "USAGE: adb_wait
+
+Returns: waits until the device has returned" ]
+adb_wait() {
+  adb wait-for-device
+}
+
+[ "USAGE: adb_root
+
+Returns: true if device in root state" ]
+adb_root() {
+  adb root >/dev/null </dev/null 2>&1 &&
+  sleep 1 &&
+  adb_wait &&
+  sleep 1
+}
+
+die() {
+  echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
+  exit 1
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+  lval="${1}"
+  rval="${2}"
+  shift 2
+  if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+    if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
+*}" ]; then
+      echo "ERROR: expected \"${lval}\"" >&2
+      echo "       got \"${rval}\"" |
+        sed ': again
+             N
+             s/\(\n\)\([^ ]\)/\1             \2/
+             t again' >&2
+      if [ -n "${*}" ] ; then
+        echo "       ${*}" >&2
+      fi
+    else
+      echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
+    fi
+    return 1
+  fi
+  if [ -n "${*}" ] ; then
+    if [ X"${lval}" != X"${rval}" ]; then
+      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
+        echo "INFO: ok \"${lval}\"" >&2
+        echo "       = \"${rval}\"" |
+          sed ': again
+               N
+               s/\(\n\)\([^ ]\)/\1          \2/
+               t again' >&2
+        if [ -n "${*}" ] ; then
+          echo "      ${*}" >&2
+        fi
+      else
+        echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+      fi
+    else
+      echo "INFO: ok \"${lval}\" ${*}" >&2
+    fi
+  fi
+  return 0
+}
+
+[ "USAGE: check_eq <lval> <rval> [message]
+
+Exits if (regex) lval mismatches rval" ]
+check_eq() {
+  left="${1}"
+  right="${2}"
+  shift 2
+  EXPECT_EQ "${left}" "${right}" ||
+    die "${@}"
+}
+
+[ "USAGE: skip_administrative_mounts
+
+Filters out all administrative (eg: sysfs) mounts" ]
+skip_administrative_mounts() {
+  grep -v -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\|/data/media\) " -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\|data\) "
+}
+
+if [ X"-s" = X"${1}" -a -n "${2}" ]; then
+  export ANDROID_SERIAL="${2}"
+  shift 2
+fi
+
+inFastboot && die "device in fastboot mode"
+inAdb || die "device not in adb mode"
+isDebuggable || die "device not a debug build"
+
+# Do something
+adb_wait || die "wait for device failed"
+adb_sh ls -d /sys/module/overlay </dev/null || die "overlay module not present"
+adb_su ls /sys/module/overlay/parameters/override_creds </dev/null ||
+  die "overlay module can not be used on ANDROID"
+adb_root &&
+  adb_wait &&
+  D=`adb disable-verity 2>&1` ||
+    die "setup for overlay"
+echo "${D}"
+if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+  echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
+fi
+if adb_sh ls -d /data/overlay </dev/null >/dev/null 2>&1; then
+  echo "/data/overlay setup, clearing out" >&2
+  adb_sh rm -rf /data/overlay </dev/null ||
+    die "/data/overlay removal"
+fi
+adb_sh ls -d /cache/overlay </dev/null >/dev/null 2>&1 ||
+  adb_sh ls -d /mnt/scratch/overlay </dev/null >/dev/null 2>&1 ||
+  die "overlay directory setup"
+adb_reboot &&
+  adb_wait &&
+  adb_sh df -k </dev/null | head -1 &&
+  adb_sh df -k </dev/null | grep "^overlay " &&
+  adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
+
+adb_root &&
+  adb_wait &&
+  adb remount &&
+  adb_sh df -k </dev/null | head -1 &&
+  adb_sh df -k </dev/null | grep "^overlay " &&
+  adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  die  "overlay takeover after remount"
+!(adb_sh grep "^overlay " /proc/mounts </dev/null | grep " overlay ro,") &&
+  !(adb_sh grep " rw," /proc/mounts </dev/null |
+  skip_administrative_mounts) ||
+    die "remount overlayfs missed a spot"
+
+adb_su "sed -n '1,/overlay \\/system/p' /proc/mounts" </dev/null |
+  skip_administrative_mounts |
+  grep -v ' \(squashfs\|ext4\|f2fs\) ' &&
+  echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
+  echo "${GREEN}[       OK ]${NORMAL} overlay takeover in first stage init" >&2
+
+# Check something
+A="Hello World! $(date)"
+echo "${A}" | adb_sh "cat - > /system/hello"
+echo "${A}" | adb_sh "cat - > /vendor/hello"
+B="`adb_cat /system/hello`" ||
+  die "sytem hello"
+check_eq "${A}" "${B}" system before reboot
+B="`adb_cat /vendor/hello`" ||
+  die "vendor hello"
+check_eq "${A}" "${B}" vendor before reboot
+adb_reboot &&
+  adb_wait &&
+  B="`adb_cat /system/hello`" ||
+  die "re-read system hello after reboot"
+check_eq "${A}" "${B}" system after reboot
+# Only root can read vendor if sepolicy permissions are as expected
+B="`adb_cat /vendor/hello`" &&
+  die "re-read vendor hello after reboot w/o root"
+check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
+adb_root &&
+  adb_wait &&
+  B="`adb_cat /vendor/hello`" ||
+  die "re-read vendor hello after reboot"
+check_eq "${A}" "${B}" vendor after reboot
+adb remount &&
+  adb_sh rm /system/hello /vendor/hello </dev/null ||
+  die "cleanup hello"
+B="`adb_cat /system/hello`" &&
+  die "re-read system hello after rm"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
+B="`adb_cat /vendor/hello`" &&
+  die "re-read vendor hello after rm"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
+
+echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
diff --git a/healthd/animation.h b/healthd/animation.h
index 562b689..f59fb38 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -20,7 +20,7 @@
 #include <inttypes.h>
 #include <string>
 
-struct GRSurface;
+class GRSurface;
 struct GRFont;
 
 namespace android {
diff --git a/init/README.md b/init/README.md
index f938ccc..6c51b37 100644
--- a/init/README.md
+++ b/init/README.md
@@ -226,6 +226,15 @@
   keycodes are pressed at once, the service will start. This is typically used to start the
   bugreport service.
 
+> This option may take a property instead of a list of keycodes. In this case, only one option is
+  provided: the property name in the typical property expansion format. The property must contain
+  a comma separated list of keycode values or the text 'none' to indicate that
+  this service does not respond to keycodes.
+
+> For example, `keycodes ${some.property.name:-none}` where some.property.name expands
+  to "123,124,125". Since keycodes are handled very early in init,
+  only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
+
 `memcg.limit_in_bytes <value>`
 > Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
   which must be equal or greater than 0.
diff --git a/init/action.cpp b/init/action.cpp
index 11335ca..94ccef2 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -44,9 +44,12 @@
     return function(builtin_arguments);
 }
 
-Command::Command(BuiltinFunction f, bool execute_in_subcontext,
-                 const std::vector<std::string>& args, int line)
-    : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
+Command::Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
+                 int line)
+    : func_(std::move(f)),
+      execute_in_subcontext_(execute_in_subcontext),
+      args_(std::move(args)),
+      line_(line) {}
 
 Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
     if (subcontext) {
@@ -80,7 +83,7 @@
 
 const KeywordFunctionMap* Action::function_map_ = nullptr;
 
-Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
+Result<Success> Action::AddCommand(std::vector<std::string>&& args, int line) {
     if (!function_map_) {
         return Error() << "no function map available";
     }
@@ -88,12 +91,12 @@
     auto function = function_map_->FindFunction(args);
     if (!function) return Error() << function.error();
 
-    commands_.emplace_back(function->second, function->first, args, line);
+    commands_.emplace_back(function->second, function->first, std::move(args), line);
     return Success();
 }
 
-void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
-    commands_.emplace_back(f, false, args, line);
+void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
+    commands_.emplace_back(f, false, std::move(args), line);
 }
 
 std::size_t Action::NumCommands() const {
diff --git a/init/action.h b/init/action.h
index 4f063cc..967c682 100644
--- a/init/action.h
+++ b/init/action.h
@@ -36,7 +36,7 @@
 
 class Command {
   public:
-    Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+    Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
             int line);
 
     Result<Success> InvokeFunc(Subcontext* subcontext) const;
@@ -61,8 +61,8 @@
            const std::string& event_trigger,
            const std::map<std::string, std::string>& property_triggers);
 
-    Result<Success> AddCommand(const std::vector<std::string>& args, int line);
-    void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
+    Result<Success> AddCommand(std::vector<std::string>&& args, int line);
+    void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 22977bb..9de4085 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -47,9 +47,7 @@
 void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
     auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
                                            std::map<std::string, std::string>{});
-    std::vector<std::string> name_vector{name};
-
-    action->AddCommand(func, name_vector, 0);
+    action->AddCommand(func, {name}, 0);
 
     event_queue_.emplace(action.get());
     actions_.emplace_back(std::move(action));
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 1481162..2d497b3 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -19,7 +19,11 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 
-#include "stable_properties.h"
+#if defined(__ANDROID__)
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
 
 using android::base::GetBoolProperty;
 using android::base::StartsWith;
@@ -36,15 +40,7 @@
         return true;
     }
 
-    if (kExportedActionableProperties.count(prop_name) == 1) {
-        return true;
-    }
-    for (const auto& prefix : kPartnerPrefixes) {
-        if (android::base::StartsWith(prop_name, prefix)) {
-            return true;
-        }
-    }
-    return false;
+    return CanReadProperty(subcontext->context(), prop_name);
 }
 
 Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 8866bdc..b85e54a 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -30,6 +30,9 @@
 std::string default_console = "/dev/console";
 
 // property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    return true;
+}
 uint32_t SetProperty(const std::string& key, const std::string& value) {
     android::base::SetProperty(key, value);
     return 0;
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 0af11f6..63ceead 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -39,6 +39,7 @@
 extern std::string default_console;
 
 // property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name);
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                            const std::string& source_context, const ucred& cr, std::string* error);
diff --git a/init/init.cpp b/init/init.cpp
index 42ec88c..b12ba8c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -61,6 +61,10 @@
 #include "ueventd.h"
 #include "util.h"
 
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
@@ -74,6 +78,25 @@
 namespace android {
 namespace init {
 
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+    return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+    LOG(ERROR) << "Main stage (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+    LOG(ERROR) << "Main stage: " << str;
+}
+#endif
+
 static int property_triggers_enabled = 0;
 
 static char qemu[32];
@@ -619,6 +642,10 @@
 }
 
 int main(int argc, char** argv) {
+#if __has_feature(address_sanitizer)
+    __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
     }
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5c8b92a..5328869 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -111,6 +111,22 @@
         LOG(FATAL) << "Failed to load serialized property info file";
     }
 }
+
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    const char* target_context = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
+
+    PropertyAuditData audit_data;
+
+    audit_data.name = name.c_str();
+
+    ucred cr = {.pid = 0, .uid = 0, .gid = 0};
+    audit_data.cr = &cr;
+
+    return selinux_check_access(source_context.c_str(), target_context, "file", "read",
+                                &audit_data) == 0;
+}
+
 static bool CheckMacPerms(const std::string& name, const char* target_context,
                           const char* source_context, const ucred& cr) {
     if (!target_context || !source_context) {
@@ -746,7 +762,7 @@
         return;
     }
 
-    int fd = open(rec->blk_device, O_RDONLY);
+    int fd = open(rec->blk_device, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         PLOG(ERROR) << "error opening block device " << rec->blk_device;
         return;
diff --git a/init/property_service.h b/init/property_service.h
index cacd987..9022f5a 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,6 +26,8 @@
 namespace android {
 namespace init {
 
+bool CanReadProperty(const std::string& source_context, const std::string& name);
+
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/service.cpp b/init/service.cpp
index 0b1425d..7f49423 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -60,6 +60,7 @@
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::ParseInt;
+using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -400,7 +401,7 @@
                   [] (const auto& info) { LOG(INFO) << *info; });
 }
 
-Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
+Result<Success> Service::ParseCapabilities(std::vector<std::string>&& args) {
     capabilities_ = 0;
 
     if (!CapAmbientSupported()) {
@@ -429,29 +430,29 @@
     return Success();
 }
 
-Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
+Result<Success> Service::ParseClass(std::vector<std::string>&& args) {
     classnames_ = std::set<std::string>(args.begin() + 1, args.end());
     return Success();
 }
 
-Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
+Result<Success> Service::ParseConsole(std::vector<std::string>&& args) {
     flags_ |= SVC_CONSOLE;
     console_ = args.size() > 1 ? "/dev/" + args[1] : "";
     return Success();
 }
 
-Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
+Result<Success> Service::ParseCritical(std::vector<std::string>&& args) {
     flags_ |= SVC_CRITICAL;
     return Success();
 }
 
-Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
+Result<Success> Service::ParseDisabled(std::vector<std::string>&& args) {
     flags_ |= SVC_DISABLED;
     flags_ |= SVC_RC_DISABLED;
     return Success();
 }
 
-Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+Result<Success> Service::ParseEnterNamespace(std::vector<std::string>&& args) {
     if (args[1] != "net") {
         return Error() << "Init only supports entering network namespaces";
     }
@@ -461,11 +462,11 @@
     // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
     // present. Therefore, they also require mount namespaces.
     namespace_flags_ |= CLONE_NEWNS;
-    namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
+    namespaces_to_enter_.emplace_back(CLONE_NEWNET, std::move(args[2]));
     return Success();
 }
 
-Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+Result<Success> Service::ParseGroup(std::vector<std::string>&& args) {
     auto gid = DecodeUid(args[1]);
     if (!gid) {
         return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
@@ -482,7 +483,7 @@
     return Success();
 }
 
-Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
+Result<Success> Service::ParsePriority(std::vector<std::string>&& args) {
     priority_ = 0;
     if (!ParseInt(args[1], &priority_,
                   static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
@@ -493,7 +494,7 @@
     return Success();
 }
 
-Result<Success> Service::ParseInterface(const std::vector<std::string>& args) {
+Result<Success> Service::ParseInterface(std::vector<std::string>&& args) {
     const std::string& interface_name = args[1];
     const std::string& instance_name = args[2];
 
@@ -524,7 +525,7 @@
     return Success();
 }
 
-Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
+Result<Success> Service::ParseIoprio(std::vector<std::string>&& args) {
     if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
         return Error() << "priority value must be range 0 - 7";
     }
@@ -542,36 +543,53 @@
     return Success();
 }
 
-Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
-    for (std::size_t i = 1; i < args.size(); i++) {
+Result<Success> Service::ParseKeycodes(std::vector<std::string>&& args) {
+    auto it = args.begin() + 1;
+    if (args.size() == 2 && StartsWith(args[1], "$")) {
+        std::string expanded;
+        if (!expand_props(args[1], &expanded)) {
+            return Error() << "Could not expand property '" << args[1] << "'";
+        }
+
+        // If the property is not set, it defaults to none, in which case there are no keycodes
+        // for this service.
+        if (expanded == "none") {
+            return Success();
+        }
+
+        args = Split(expanded, ",");
+        it = args.begin();
+    }
+
+    for (; it != args.end(); ++it) {
         int code;
-        if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+        if (ParseInt(*it, &code, 0, KEY_MAX)) {
             for (auto& key : keycodes_) {
-                if (key == code) return Error() << "duplicate keycode: " << args[i];
+                if (key == code) return Error() << "duplicate keycode: " << *it;
             }
             keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
         } else {
-            return Error() << "invalid keycode: " << args[i];
+            return Error() << "invalid keycode: " << *it;
         }
     }
     return Success();
 }
 
-Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOneshot(std::vector<std::string>&& args) {
     flags_ |= SVC_ONESHOT;
     return Success();
 }
 
-Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
-    std::vector<std::string> str_args(args.begin() + 1, args.end());
+Result<Success> Service::ParseOnrestart(std::vector<std::string>&& args) {
+    args.erase(args.begin());
     int line = onrestart_.NumCommands() + 1;
-    if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+    if (auto result = onrestart_.AddCommand(std::move(args), line); !result) {
         return Error() << "cannot add Onrestart command: " << result.error();
     }
     return Success();
 }
 
-Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
+Result<Success> Service::ParseNamespace(std::vector<std::string>&& args) {
     for (size_t i = 1; i < args.size(); i++) {
         if (args[i] == "pid") {
             namespace_flags_ |= CLONE_NEWPID;
@@ -586,40 +604,40 @@
     return Success();
 }
 
-Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOomScoreAdjust(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
         return Error() << "oom_score_adjust value must be in range -1000 - +1000";
     }
     return Success();
 }
 
-Result<Success> Service::ParseOverride(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOverride(std::vector<std::string>&& args) {
     override_ = true;
     return Success();
 }
 
-Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
+Result<Success> Service::ParseMemcgSwappiness(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &swappiness_, 0)) {
         return Error() << "swappiness value must be equal or greater than 0";
     }
     return Success();
 }
 
-Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
+Result<Success> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
         return Error() << "limit_in_bytes value must be equal or greater than 0";
     }
     return Success();
 }
 
-Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
+Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
         return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
     }
     return Success();
 }
 
-Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args) {
+Result<Success> Service::ParseProcessRlimit(std::vector<std::string>&& args) {
     auto rlimit = ParseRlimit(args);
     if (!rlimit) return rlimit.error();
 
@@ -627,7 +645,7 @@
     return Success();
 }
 
-Result<Success> Service::ParseRestartPeriod(const std::vector<std::string>& args) {
+Result<Success> Service::ParseRestartPeriod(std::vector<std::string>&& args) {
     int period;
     if (!ParseInt(args[1], &period, 5)) {
         return Error() << "restart_period value must be an integer >= 5";
@@ -636,22 +654,22 @@
     return Success();
 }
 
-Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
-    seclabel_ = args[1];
+Result<Success> Service::ParseSeclabel(std::vector<std::string>&& args) {
+    seclabel_ = std::move(args[1]);
     return Success();
 }
 
-Result<Success> Service::ParseSigstop(const std::vector<std::string>& args) {
+Result<Success> Service::ParseSigstop(std::vector<std::string>&& args) {
     sigstop_ = true;
     return Success();
 }
 
-Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
-    environment_vars_.emplace_back(args[1], args[2]);
+Result<Success> Service::ParseSetenv(std::vector<std::string>&& args) {
+    environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
     return Success();
 }
 
-Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
+Result<Success> Service::ParseShutdown(std::vector<std::string>&& args) {
     if (args[1] == "critical") {
         flags_ |= SVC_SHUTDOWN_CRITICAL;
         return Success();
@@ -659,7 +677,7 @@
     return Error() << "Invalid shutdown option";
 }
 
-Result<Success> Service::ParseTimeoutPeriod(const std::vector<std::string>& args) {
+Result<Success> Service::ParseTimeoutPeriod(std::vector<std::string>&& args) {
     int period;
     if (!ParseInt(args[1], &period, 1)) {
         return Error() << "timeout_period value must be an integer >= 1";
@@ -669,7 +687,7 @@
 }
 
 template <typename T>
-Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
+Result<Success> Service::AddDescriptor(std::vector<std::string>&& args) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
     Result<uid_t> uid = 0;
     Result<gid_t> gid = 0;
@@ -704,26 +722,26 @@
 }
 
 // name type perm [ uid gid context ]
-Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
+Result<Success> Service::ParseSocket(std::vector<std::string>&& args) {
     if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
         !StartsWith(args[2], "seqpacket")) {
         return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
     }
-    return AddDescriptor<SocketInfo>(args);
+    return AddDescriptor<SocketInfo>(std::move(args));
 }
 
 // name type perm [ uid gid context ]
-Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
+Result<Success> Service::ParseFile(std::vector<std::string>&& args) {
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
         return Error() << "file type must be 'r', 'w' or 'rw'";
     }
     if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
         return Error() << "file name must not be relative";
     }
-    return AddDescriptor<FileInfo>(args);
+    return AddDescriptor<FileInfo>(std::move(args));
 }
 
-Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+Result<Success> Service::ParseUser(std::vector<std::string>&& args) {
     auto uid = DecodeUid(args[1]);
     if (!uid) {
         return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
@@ -732,8 +750,9 @@
     return Success();
 }
 
-Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
-    writepid_files_.assign(args.begin() + 1, args.end());
+Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    writepid_files_ = std::move(args);
     return Success();
 }
 
@@ -792,13 +811,13 @@
     return option_parsers;
 }
 
-Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
+Result<Success> Service::ParseLine(std::vector<std::string>&& args) {
     static const OptionParserMap parser_map;
     auto parser = parser_map.FindFunction(args);
 
     if (!parser) return parser.error();
 
-    return std::invoke(*parser, this, args);
+    return std::invoke(*parser, this, std::move(args));
 }
 
 Result<Success> Service::ExecStart() {
diff --git a/init/service.h b/init/service.h
index ee53adf..c7beee9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -75,7 +75,7 @@
     static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
-    Result<Success> ParseLine(const std::vector<std::string>& args);
+    Result<Success> ParseLine(std::vector<std::string>&& args);
     Result<Success> ExecStart();
     Result<Success> Start();
     Result<Success> StartIfNotDisabled();
@@ -125,7 +125,7 @@
     const std::vector<std::string>& args() const { return args_; }
 
   private:
-    using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
+    using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
     class OptionParserMap;
 
     Result<Success> SetUpMountNamespace() const;
@@ -138,39 +138,39 @@
     void KillProcessGroup(int signal);
     void SetProcessAttributes();
 
-    Result<Success> ParseCapabilities(const std::vector<std::string>& args);
-    Result<Success> ParseClass(const std::vector<std::string>& args);
-    Result<Success> ParseConsole(const std::vector<std::string>& args);
-    Result<Success> ParseCritical(const std::vector<std::string>& args);
-    Result<Success> ParseDisabled(const std::vector<std::string>& args);
-    Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
-    Result<Success> ParseGroup(const std::vector<std::string>& args);
-    Result<Success> ParsePriority(const std::vector<std::string>& args);
-    Result<Success> ParseInterface(const std::vector<std::string>& args);
-    Result<Success> ParseIoprio(const std::vector<std::string>& args);
-    Result<Success> ParseKeycodes(const std::vector<std::string>& args);
-    Result<Success> ParseOneshot(const std::vector<std::string>& args);
-    Result<Success> ParseOnrestart(const std::vector<std::string>& args);
-    Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
-    Result<Success> ParseOverride(const std::vector<std::string>& args);
-    Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
-    Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
-    Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
-    Result<Success> ParseNamespace(const std::vector<std::string>& args);
-    Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
-    Result<Success> ParseRestartPeriod(const std::vector<std::string>& args);
-    Result<Success> ParseSeclabel(const std::vector<std::string>& args);
-    Result<Success> ParseSetenv(const std::vector<std::string>& args);
-    Result<Success> ParseShutdown(const std::vector<std::string>& args);
-    Result<Success> ParseSigstop(const std::vector<std::string>& args);
-    Result<Success> ParseSocket(const std::vector<std::string>& args);
-    Result<Success> ParseTimeoutPeriod(const std::vector<std::string>& args);
-    Result<Success> ParseFile(const std::vector<std::string>& args);
-    Result<Success> ParseUser(const std::vector<std::string>& args);
-    Result<Success> ParseWritepid(const std::vector<std::string>& args);
+    Result<Success> ParseCapabilities(std::vector<std::string>&& args);
+    Result<Success> ParseClass(std::vector<std::string>&& args);
+    Result<Success> ParseConsole(std::vector<std::string>&& args);
+    Result<Success> ParseCritical(std::vector<std::string>&& args);
+    Result<Success> ParseDisabled(std::vector<std::string>&& args);
+    Result<Success> ParseEnterNamespace(std::vector<std::string>&& args);
+    Result<Success> ParseGroup(std::vector<std::string>&& args);
+    Result<Success> ParsePriority(std::vector<std::string>&& args);
+    Result<Success> ParseInterface(std::vector<std::string>&& args);
+    Result<Success> ParseIoprio(std::vector<std::string>&& args);
+    Result<Success> ParseKeycodes(std::vector<std::string>&& args);
+    Result<Success> ParseOneshot(std::vector<std::string>&& args);
+    Result<Success> ParseOnrestart(std::vector<std::string>&& args);
+    Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
+    Result<Success> ParseOverride(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
+    Result<Success> ParseNamespace(std::vector<std::string>&& args);
+    Result<Success> ParseProcessRlimit(std::vector<std::string>&& args);
+    Result<Success> ParseRestartPeriod(std::vector<std::string>&& args);
+    Result<Success> ParseSeclabel(std::vector<std::string>&& args);
+    Result<Success> ParseSetenv(std::vector<std::string>&& args);
+    Result<Success> ParseShutdown(std::vector<std::string>&& args);
+    Result<Success> ParseSigstop(std::vector<std::string>&& args);
+    Result<Success> ParseSocket(std::vector<std::string>&& args);
+    Result<Success> ParseTimeoutPeriod(std::vector<std::string>&& args);
+    Result<Success> ParseFile(std::vector<std::string>&& args);
+    Result<Success> ParseUser(std::vector<std::string>&& args);
+    Result<Success> ParseWritepid(std::vector<std::string>&& args);
 
     template <typename T>
-    Result<Success> AddDescriptor(const std::vector<std::string>& args);
+    Result<Success> AddDescriptor(std::vector<std::string>&& args);
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
diff --git a/init/stable_properties.h b/init/stable_properties.h
deleted file mode 100644
index baef833..0000000
--- a/init/stable_properties.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _INIT_STABLE_PROPERTIES_H
-#define _INIT_STABLE_PROPERTIES_H
-
-#include <set>
-#include <string>
-
-namespace android {
-namespace init {
-
-static constexpr const char* kPartnerPrefixes[] = {
-    "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.", "init.svc.odm.", "ro.odm.",
-    "persist.odm.",     "odm.",       "ro.boot.",
-};
-
-static const std::set<std::string> kExportedActionableProperties = {
-    "dev.bootcomplete",
-    "init.svc.console",
-    "init.svc.dumpstatez",
-    "init.svc.mediadrm",
-    "init.svc.surfaceflinger",
-    "init.svc.zygote",
-    "persist.bluetooth.btsnoopenable",
-    "persist.sys.crash_rcu",
-    "persist.sys.usb.usbradio.config",
-    "persist.sys.zram_enabled",
-    "ro.board.platform",
-    "ro.bootmode",
-    "ro.build.type",
-    "ro.crypto.state",
-    "ro.crypto.type",
-    "ro.debuggable",
-    "sys.boot_completed",
-    "sys.boot_from_charger_mode",
-    "sys.retaildemo.enabled",
-    "sys.shutdown.requested",
-    "sys.usb.config",
-    "sys.usb.configfs",
-    "sys.usb.ffs.mtp.ready",
-    "sys.usb.ffs.ready",
-    "sys.user.0.ce_available",
-    "sys.vdso",
-    "vold.decrypt",
-    "vold.post_fs_data_done",
-    "vts.native_server.on",
-    "wlan.driver.status",
-};
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index d2d6ab8..52dd441 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -106,7 +106,7 @@
     return "";
   }
 
-  unwindstack::Elf* elf = map_info->GetElf(process_memory(), true);
+  unwindstack::Elf* elf = map_info->GetElf(process_memory());
 
   std::string name;
   uint64_t func_offset;
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 6e63b74..92fd2a2 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -10,9 +10,11 @@
 
     host_supported: true,
     srcs: ["native_bridge.cc"],
+    header_libs: [
+        "libbase_headers",
+    ],
     shared_libs: [
         "liblog",
-        "libbase",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 5b9ba1c..4ed6e20 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -29,10 +29,12 @@
 
 shared_libraries := \
     liblog \
-    libbase \
     libnativebridge \
     libnativebridge-dummy
 
+header_libraries := \
+    libbase_headers
+
 libnativebridge_tests_common_cflags := \
     -Wall \
     -Werror \
@@ -40,6 +42,7 @@
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -49,6 +52,7 @@
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 56a0679..60ef796 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -40,7 +40,7 @@
 std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
 std::mutex* Elf::cache_lock_;
 
-bool Elf::Init(bool init_gnu_debugdata) {
+bool Elf::Init() {
   load_bias_ = 0;
   if (!memory_) {
     return false;
@@ -54,11 +54,7 @@
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
     interface_->InitHeaders(load_bias_);
-    if (init_gnu_debugdata) {
-      InitGnuDebugdata();
-    } else {
-      gnu_debugdata_interface_.reset(nullptr);
-    }
+    InitGnuDebugdata();
   } else {
     interface_.reset(nullptr);
   }
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index b449c7e..f0ad2b6 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -46,7 +46,7 @@
     }
   }
 
-  Elf* elf = info->GetElf(memory_, true);
+  Elf* elf = info->GetElf(memory_);
   uint64_t ptr;
   // Find first non-empty list (libraries might be loaded multiple times).
   if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) {
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index fe680d7..d6af49e 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -201,7 +201,7 @@
     entry_addr_ = (this->*read_entry_func_)(&start, &size);
 
     Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
-    elf->Init(true);
+    elf->Init();
     if (!elf->valid()) {
       // The data is not formatted in a way we understand, do not attempt
       // to process any other entries.
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 952b332..31337a9 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -100,7 +100,7 @@
       break;
     }
 
-    Elf* elf = map_info->GetElf(process_memory_, true);
+    Elf* elf = map_info->GetElf(process_memory_);
     uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
     uint64_t step_pc = rel_pc;
     uint64_t pc_adjustment;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 52b7535..8527797 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -146,7 +146,7 @@
   return nullptr;
 }
 
-Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory) {
   // Make sure no other thread is trying to add the elf to this map.
   std::lock_guard<std::mutex> guard(mutex_);
 
@@ -175,7 +175,7 @@
   elf.reset(new Elf(memory));
   // If the init fails, keep the elf around as an invalid object so we
   // don't try to reinit the object.
-  elf->Init(init_gnu_debugdata);
+  elf->Init();
 
   if (locked) {
     Elf::CacheAdd(this);
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index e6dd33c..e445a91 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -41,7 +41,7 @@
   }
 
   inline AddressType* Save(uint32_t reg) {
-    if (reg > MAX_REGISTERS) {
+    if (reg >= MAX_REGISTERS) {
       // This should never happen since all currently supported
       // architectures have < 64 total registers.
       abort();
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index ee1cd1a..b8b0ac6 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -155,7 +155,7 @@
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
       }
-      elf = map_info->GetElf(process_memory_, true);
+      elf = map_info->GetElf(process_memory_);
       step_pc = regs_->pc();
       rel_pc = elf->GetRelPc(step_pc, map_info);
       // Everyone except elf data in gdb jit debug maps uses the relative pc.
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 9af859d..24cabf2 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -53,7 +53,7 @@
   Elf(Memory* memory) : memory_(memory) {}
   virtual ~Elf() = default;
 
-  bool Init(bool init_gnu_debugdata);
+  bool Init();
 
   void InitGnuDebugdata();
 
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 9755c48..ff634f2 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -72,7 +72,7 @@
   std::atomic_uint64_t load_bias;
 
   // This function guarantees it will never return nullptr.
-  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+  Elf* GetElf(const std::shared_ptr<Memory>& process_memory);
 
   uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
 
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 1afd4ef..8ed697c 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -82,9 +82,9 @@
   MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
   MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
 
-  Elf* elf1 = info1.GetElf(memory_, true);
+  Elf* elf1 = info1.GetElf(memory_);
   ASSERT_TRUE(elf1->valid());
-  Elf* elf2 = info2.GetElf(memory_, true);
+  Elf* elf2 = info2.GetElf(memory_);
   ASSERT_TRUE(elf2->valid());
 
   if (cache_enabled) {
@@ -132,10 +132,10 @@
   MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
   MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
 
-  Elf* elf0_1 = info0_1.GetElf(memory_, true);
+  Elf* elf0_1 = info0_1.GetElf(memory_);
   ASSERT_TRUE(elf0_1->valid());
   EXPECT_EQ(ARCH_ARM, elf0_1->arch());
-  Elf* elf0_2 = info0_2.GetElf(memory_, true);
+  Elf* elf0_2 = info0_2.GetElf(memory_);
   ASSERT_TRUE(elf0_2->valid());
   EXPECT_EQ(ARCH_ARM, elf0_2->arch());
   EXPECT_EQ(0U, info0_1.elf_offset);
@@ -146,10 +146,10 @@
     EXPECT_NE(elf0_1, elf0_2);
   }
 
-  Elf* elf100_1 = info100_1.GetElf(memory_, true);
+  Elf* elf100_1 = info100_1.GetElf(memory_);
   ASSERT_TRUE(elf100_1->valid());
   EXPECT_EQ(ARCH_X86, elf100_1->arch());
-  Elf* elf100_2 = info100_2.GetElf(memory_, true);
+  Elf* elf100_2 = info100_2.GetElf(memory_);
   ASSERT_TRUE(elf100_2->valid());
   EXPECT_EQ(ARCH_X86, elf100_2->arch());
   EXPECT_EQ(0U, info100_1.elf_offset);
@@ -160,10 +160,10 @@
     EXPECT_NE(elf100_1, elf100_2);
   }
 
-  Elf* elf200_1 = info200_1.GetElf(memory_, true);
+  Elf* elf200_1 = info200_1.GetElf(memory_);
   ASSERT_TRUE(elf200_1->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
-  Elf* elf200_2 = info200_2.GetElf(memory_, true);
+  Elf* elf200_2 = info200_2.GetElf(memory_);
   ASSERT_TRUE(elf200_2->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
   EXPECT_EQ(0U, info200_1.elf_offset);
@@ -174,10 +174,10 @@
     EXPECT_NE(elf200_1, elf200_2);
   }
 
-  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  Elf* elf300_1 = info300_1.GetElf(memory_);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  Elf* elf300_2 = info300_2.GetElf(memory_);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -222,10 +222,10 @@
   MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
   MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
 
-  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  Elf* elf300_1 = info300_1.GetElf(memory_);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  Elf* elf300_2 = info300_2.GetElf(memory_);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -236,10 +236,10 @@
     EXPECT_NE(elf300_1, elf300_2);
   }
 
-  Elf* elf400_1 = info400_1.GetElf(memory_, true);
+  Elf* elf400_1 = info400_1.GetElf(memory_);
   ASSERT_TRUE(elf400_1->valid());
   EXPECT_EQ(ARCH_ARM, elf400_1->arch());
-  Elf* elf400_2 = info400_2.GetElf(memory_, true);
+  Elf* elf400_2 = info400_2.GetElf(memory_);
   ASSERT_TRUE(elf400_2->valid());
   EXPECT_EQ(ARCH_ARM, elf400_2->arch());
   EXPECT_EQ(0x400U, info400_1.elf_offset);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 9a117b2..ccf8927 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -110,7 +110,7 @@
 TEST_F(ElfTest, invalid_memory) {
   Elf elf(memory_);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
 }
 
@@ -122,7 +122,7 @@
   // Corrupt the ELF signature.
   memory_->SetData32(0, 0x7f000000);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
   ASSERT_TRUE(elf.interface() == nullptr);
 
@@ -142,7 +142,7 @@
   InitElf32(EM_PPC);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
@@ -155,7 +155,7 @@
   InitElf64(EM_PPC64);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
@@ -167,7 +167,7 @@
 
   InitElf32(EM_ARM);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -179,7 +179,7 @@
 
   InitElf32(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -191,7 +191,7 @@
 
   InitElf32(EM_386);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -203,7 +203,7 @@
 
   InitElf64(EM_AARCH64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -215,7 +215,7 @@
 
   InitElf64(EM_X86_64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -227,41 +227,13 @@
 
   InitElf64(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
   ASSERT_TRUE(elf.interface() != nullptr);
 }
 
-TEST_F(ElfTest, gnu_debugdata_init_fail32) {
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(false));
-  ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
-  EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
-TEST_F(ElfTest, gnu_debugdata_init_fail64) {
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(false));
-  ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
-  EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
 TEST_F(ElfTest, gnu_debugdata_init32) {
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -269,7 +241,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
@@ -283,7 +255,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 918c028..c6c1c34 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -72,7 +72,7 @@
   MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
@@ -84,7 +84,7 @@
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -98,45 +98,13 @@
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
 }
 
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
-  MapInfo info(nullptr, 0x4000, 0x8000, 0, PROT_READ, "");
-
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(0x4000 + offset, ptr, size);
-                                               });
-
-  Elf* elf = info.GetElf(process_memory_, false);
-  ASSERT_TRUE(elf != nullptr);
-  ASSERT_TRUE(elf->valid());
-  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
-  EXPECT_EQ(ELFCLASS32, elf->class_type());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
-}
-
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
-  MapInfo info(nullptr, 0x6000, 0x8000, 0, PROT_READ, "");
-
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(0x6000 + offset, ptr, size);
-                                               });
-
-  Elf* elf = info.GetElf(process_memory_, false);
-  ASSERT_TRUE(elf != nullptr);
-  ASSERT_TRUE(elf->valid());
-  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
-  EXPECT_EQ(ELFCLASS64, elf->class_type());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
-}
-
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
   MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
@@ -145,7 +113,7 @@
                                                  memory_->SetMemory(0x2000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, true);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -161,7 +129,7 @@
                                                  memory_->SetMemory(0x5000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, true);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -176,20 +144,20 @@
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   info.elf.reset();
   info.end = 0xfff;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Make sure this test is valid.
   info.elf.reset();
   info.end = 0x2000;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 }
@@ -206,7 +174,7 @@
   memcpy(buffer.data(), &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -235,7 +203,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -268,7 +236,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -296,7 +264,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -322,13 +290,13 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   info.elf.reset();
   info.flags = PROT_READ;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -345,20 +313,20 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Set the name to nothing to verify that it still fails.
   info.elf.reset();
   info.name = "";
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_);
   ASSERT_FALSE(elf->valid());
 
   // Change the flags and verify the elf is valid now.
   info.elf.reset();
   info.flags = PROT_READ;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -384,7 +352,7 @@
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
         ;
-      Elf* elf = info.GetElf(process_memory_, false);
+      Elf* elf = info.GetElf(process_memory_);
       elf_in_threads[i] = elf;
     });
     threads.push_back(thread);
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
index 052b5bf..a6bc2c5 100644
--- a/libunwindstack/tests/RegsInfoTest.cpp
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -82,4 +82,11 @@
   }
 }
 
+TEST(RegsInfoTest, invalid_register) {
+  RegsImplFake<uint64_t> regs(64);
+  RegsInfo<uint64_t> info(&regs);
+
+  EXPECT_DEATH(info.Save(RegsInfo<uint64_t>::MAX_REGISTERS), "");
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 266a6db..aebeb95 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -113,7 +113,7 @@
   }
 
   Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 0f01566..4b6f49a 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -173,7 +173,7 @@
   }
 
   Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index f8e3e92..9128430 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -66,7 +66,7 @@
   }
 
   unwindstack::Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", argv[1]);
     return 1;
   }
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 4d39dd9..2c62fca 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -51,13 +51,13 @@
 #define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages"
 #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]"
+    "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
 #define LLK_BLACKLIST_PARENT_PROPERTY  "ro.llk.blacklist.parent"
 #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,ueventd,apexd"
+#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
 /* clang-format on */
 
 __END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 6840ed0..2727aab 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -712,6 +712,7 @@
     if (llkSkipName(std::to_string(procp->pid), llkBlacklistStack)) return false;
     if (llkSkipName(procp->getComm(), llkBlacklistStack)) return false;
     if (llkSkipName(procp->getCmdline(), llkBlacklistStack)) return false;
+    if (llkSkipName(android::base::Basename(procp->getCmdline()), llkBlacklistStack)) return false;
 
     auto kernel_stack = ReadFile(piddir + "/stack");
     if (kernel_stack.empty()) {
@@ -995,13 +996,18 @@
             if (llkSkipName(procp->getCmdline())) {
                 break;
             }
+            if (llkSkipName(android::base::Basename(procp->getCmdline()))) {
+                break;
+            }
 
             auto pprocp = llkTidLookup(ppid);
             if (pprocp == nullptr) {
                 pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
             }
-            if ((pprocp != nullptr) && (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
-                                        llkSkipName(pprocp->getCmdline(), llkBlacklistParent))) {
+            if ((pprocp != nullptr) &&
+                (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
+                 llkSkipName(pprocp->getCmdline(), llkBlacklistParent) ||
+                 llkSkipName(android::base::Basename(pprocp->getCmdline()), llkBlacklistParent))) {
                 break;
             }
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 115b1a3..87bc6ae 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -478,10 +478,10 @@
                     "  -G <size>, --buffer-size=<size>\n"
                     "                  Set size of log ring buffer, may suffix with K or M.\n"
                     "  -L, --last      Dump logs from prior to last reboot\n"
-                    // Leave security (Device Owner only installations) and
-                    // kernel (userdebug and eng) buffers undocumented.
                     "  -b <buffer>, --buffer=<buffer>         Request alternate ring buffer, 'main',\n"
                     "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
+                    "                  Additionally, 'kernel' for userdebug and eng builds, and\n"
+                    "                  'security' for Device Owner installations.\n"
                     "                  Multiple -b parameters or comma separated list of buffers are\n"
                     "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
                     "  -B, --binary    Output the log in binary.\n"
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 13c7af3..9db8c00 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -212,6 +212,10 @@
         cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
         logMask, (int)pid, sequence.nsec(), timeout);
 
+    if (sequence == log_time::EPOCH) {
+        timeout = 0;
+    }
+
     LogTimeEntry::wrlock();
     auto entry = std::make_unique<LogTimeEntry>(
         *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 393e204..aad00ad 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -146,13 +146,7 @@
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
-# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
-bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
-bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
-$(bcp_dep) :
-	$(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
 	@echo "Generate: $< -> $@"
 	@mkdir -p $(dir $@)
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
@@ -161,9 +155,6 @@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
 
-bcp_md5 :=
-bcp_dep :=
-
 # Append PLATFORM_VNDK_VERSION to base name.
 define append_vndk_version
 $(strip \
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 21d234f..025e3c3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -275,6 +275,13 @@
     chown root root /apex
     restorecon /apex
 
+    # Start logd before any other services run to ensure we capture all of their logs.
+    start logd
+    # Start essential services.
+    start servicemanager
+    start hwservicemanager
+    start vndservicemanager
+
 # Healthd can trigger a full boot from charger mode by signaling this
 # property when the power button is held.
 on property:sys.boot_from_charger_mode=1
@@ -331,11 +338,6 @@
     #     /vendor/build.prop and
     #     /factory/factory.prop
     load_system_props
-    # start essential services
-    start logd
-    start servicemanager
-    start hwservicemanager
-    start vndservicemanager
     start vold
     exec - system system -- /system/bin/vdc checkpoint markBootAttempt
 
@@ -487,6 +489,8 @@
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
 
+    mkdir /data/preloads 0775 system system
+
     mkdir /data/vendor 0771 root root
     mkdir /data/vendor_ce 0771 root root
     mkdir /data/vendor_de 0771 root root