Merge "Add test config to libstatspull_test and libstatssocket_test" into rvc-dev
diff --git a/adb/Android.bp b/adb/Android.bp
index cb5c1df..f8e5b38 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -567,7 +567,6 @@
 cc_binary {
     name: "adbd",
     defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"],
-    stl: "libc++_static",
     recovery_available: true,
     apex_available: ["com.android.adbd"],
 
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 092a866..f2de0d2 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -155,6 +155,14 @@
     *buf = '\0';
 }
 
+static unique_fd send_command(const std::vector<std::string>& cmd_args, std::string* error) {
+    if (is_abb_exec_supported()) {
+        return send_abb_exec_command(cmd_args, error);
+    } else {
+        return unique_fd(adb_connect(android::base::Join(cmd_args, " "), error));
+    }
+}
+
 static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
     printf("Performing Streamed Install\n");
 
@@ -227,12 +235,7 @@
         cmd_args.push_back("--apex");
     }
 
-    unique_fd remote_fd;
-    if (use_abb_exec) {
-        remote_fd = send_abb_exec_command(cmd_args, &error);
-    } else {
-        remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
-    }
+    unique_fd remote_fd = send_command(cmd_args, &error);
     if (remote_fd < 0) {
         fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
         return 1;
@@ -299,7 +302,7 @@
 }
 
 template <class TimePoint>
-static int msBetween(TimePoint start, TimePoint end) {
+static int ms_between(TimePoint start, TimePoint end) {
     return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
 }
 
@@ -308,7 +311,7 @@
     const auto start = clock::now();
     int first_apk = -1;
     int last_apk = -1;
-    std::vector<std::string_view> args = {"package"sv};
+    incremental::Args passthrough_args = {};
     for (int i = 0; i < argc; ++i) {
         const auto arg = std::string_view(argv[i]);
         if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) {
@@ -316,12 +319,11 @@
             if (first_apk == -1) {
                 first_apk = i;
             }
-        } else if (arg.starts_with("install-"sv)) {
+        } else if (arg.starts_with("install"sv)) {
             // incremental installation command on the device is the same for all its variations in
             // the adb, e.g. install-multiple or install-multi-package
-            args.push_back("install"sv);
         } else {
-            args.push_back(arg);
+            passthrough_args.push_back(arg);
         }
     }
 
@@ -342,13 +344,13 @@
     }
 
     printf("Performing Incremental Install\n");
-    auto server_process = incremental::install(files, silent);
+    auto server_process = incremental::install(files, passthrough_args, silent);
     if (!server_process) {
         return -1;
     }
 
     const auto end = clock::now();
-    printf("Install command complete in %d ms\n", msBetween(start, end));
+    printf("Install command complete in %d ms\n", ms_between(start, end));
 
     if (wait) {
         (*server_process).wait();
@@ -357,9 +359,9 @@
     return 0;
 }
 
-static std::pair<InstallMode, std::optional<InstallMode>> calculateInstallMode(
-        InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incrementalRequest) {
-    if (incrementalRequest == CmdlineOption::Enable) {
+static std::pair<InstallMode, std::optional<InstallMode>> calculate_install_mode(
+        InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incremental_request) {
+    if (incremental_request == CmdlineOption::Enable) {
         if (fastdeploy) {
             error_exit(
                     "--incremental and --fast-deploy options are incompatible. "
@@ -368,30 +370,30 @@
     }
 
     if (modeFromArgs != INSTALL_DEFAULT) {
-        if (incrementalRequest == CmdlineOption::Enable) {
+        if (incremental_request == CmdlineOption::Enable) {
             error_exit("--incremental is not compatible with other installation modes");
         }
         return {modeFromArgs, std::nullopt};
     }
 
-    if (incrementalRequest != CmdlineOption::Disable && !is_abb_exec_supported()) {
-        if (incrementalRequest == CmdlineOption::None) {
-            incrementalRequest = CmdlineOption::Disable;
+    if (incremental_request != CmdlineOption::Disable && !is_abb_exec_supported()) {
+        if (incremental_request == CmdlineOption::None) {
+            incremental_request = CmdlineOption::Disable;
         } else {
             error_exit("Device doesn't support incremental installations");
         }
     }
-    if (incrementalRequest == CmdlineOption::None) {
+    if (incremental_request == CmdlineOption::None) {
         // check if the host is ok with incremental by default
         if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) {
             using namespace android::base;
             auto val = ParseBool(incrementalFromEnv);
             if (val == ParseBoolResult::kFalse) {
-                incrementalRequest = CmdlineOption::Disable;
+                incremental_request = CmdlineOption::Disable;
             }
         }
     }
-    if (incrementalRequest == CmdlineOption::None) {
+    if (incremental_request == CmdlineOption::None) {
         // still ok: let's see if the device allows using incremental by default
         // it starts feeling like we're looking for an excuse to not to use incremental...
         std::string error;
@@ -407,17 +409,17 @@
             using namespace android::base;
             auto val = ParseBool(buf);
             if (val == ParseBoolResult::kFalse) {
-                incrementalRequest = CmdlineOption::Disable;
+                incremental_request = CmdlineOption::Disable;
             }
         }
     }
 
-    if (incrementalRequest == CmdlineOption::Enable) {
+    if (incremental_request == CmdlineOption::Enable) {
         // explicitly requested - no fallback
         return {INSTALL_INCREMENTAL, std::nullopt};
     }
     const auto bestMode = best_install_mode();
-    if (incrementalRequest == CmdlineOption::None) {
+    if (incremental_request == CmdlineOption::None) {
         // no opinion - use incremental, fallback to regular on a failure.
         return {INSTALL_INCREMENTAL, bestMode};
     }
@@ -425,57 +427,75 @@
     return {bestMode, std::nullopt};
 }
 
-int install_app(int argc, const char** argv) {
-    std::vector<int> processedArgIndices;
-    InstallMode installMode = INSTALL_DEFAULT;
-    bool use_fastdeploy = false;
-    bool is_reinstall = false;
-    bool wait = false;
-    auto incremental_request = CmdlineOption::None;
-    FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+static std::vector<const char*> parse_install_mode(std::vector<const char*> argv,
+                                                   InstallMode* install_mode,
+                                                   CmdlineOption* incremental_request,
+                                                   bool* incremental_wait) {
+    *install_mode = INSTALL_DEFAULT;
+    *incremental_request = CmdlineOption::None;
+    *incremental_wait = false;
 
-    for (int i = 1; i < argc; i++) {
-        if (argv[i] == "--streaming"sv) {
-            processedArgIndices.push_back(i);
-            installMode = INSTALL_STREAM;
-        } else if (argv[i] == "--no-streaming"sv) {
-            processedArgIndices.push_back(i);
-            installMode = INSTALL_PUSH;
-        } else if (argv[i] == "-r"sv) {
-            // Note that this argument is not added to processedArgIndices because it
-            // must be passed through to pm
-            is_reinstall = true;
-        } else if (argv[i] == "--fastdeploy"sv) {
-            processedArgIndices.push_back(i);
-            use_fastdeploy = true;
-        } else if (argv[i] == "--no-fastdeploy"sv) {
-            processedArgIndices.push_back(i);
-            use_fastdeploy = false;
-        } else if (argv[i] == "--force-agent"sv) {
-            processedArgIndices.push_back(i);
-            agent_update_strategy = FastDeploy_AgentUpdateAlways;
-        } else if (argv[i] == "--date-check-agent"sv) {
-            processedArgIndices.push_back(i);
-            agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
-        } else if (argv[i] == "--version-check-agent"sv) {
-            processedArgIndices.push_back(i);
-            agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
-        } else if (strlen(argv[i]) >= "--incr"sv.size() && "--incremental"sv.starts_with(argv[i])) {
-            processedArgIndices.push_back(i);
-            incremental_request = CmdlineOption::Enable;
-        } else if (strlen(argv[i]) >= "--no-incr"sv.size() &&
-                   "--no-incremental"sv.starts_with(argv[i])) {
-            processedArgIndices.push_back(i);
-            incremental_request = CmdlineOption::Disable;
-        } else if (argv[i] == "--wait"sv) {
-            processedArgIndices.push_back(i);
-            wait = true;
+    std::vector<const char*> passthrough;
+    for (auto&& arg : argv) {
+        if (arg == "--streaming"sv) {
+            *install_mode = INSTALL_STREAM;
+        } else if (arg == "--no-streaming"sv) {
+            *install_mode = INSTALL_PUSH;
+        } else if (strlen(arg) >= "--incr"sv.size() && "--incremental"sv.starts_with(arg)) {
+            *incremental_request = CmdlineOption::Enable;
+        } else if (strlen(arg) >= "--no-incr"sv.size() && "--no-incremental"sv.starts_with(arg)) {
+            *incremental_request = CmdlineOption::Disable;
+        } else if (arg == "--wait"sv) {
+            *incremental_wait = true;
+        } else {
+            passthrough.push_back(arg);
         }
     }
+    return passthrough;
+}
 
-    auto [primaryMode, fallbackMode] =
-            calculateInstallMode(installMode, use_fastdeploy, incremental_request);
-    if ((primaryMode == INSTALL_STREAM || fallbackMode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+static std::vector<const char*> parse_fast_deploy_mode(
+        std::vector<const char*> argv, bool* use_fastdeploy,
+        FastDeploy_AgentUpdateStrategy* agent_update_strategy) {
+    *use_fastdeploy = false;
+    *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+    std::vector<const char*> passthrough;
+    for (auto&& arg : argv) {
+        if (arg == "--fastdeploy"sv) {
+            *use_fastdeploy = true;
+        } else if (arg == "--no-fastdeploy"sv) {
+            *use_fastdeploy = false;
+        } else if (arg == "--force-agent"sv) {
+            *agent_update_strategy = FastDeploy_AgentUpdateAlways;
+        } else if (arg == "--date-check-agent"sv) {
+            *agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+        } else if (arg == "--version-check-agent"sv) {
+            *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+        } else {
+            passthrough.push_back(arg);
+        }
+    }
+    return passthrough;
+}
+
+int install_app(int argc, const char** argv) {
+    InstallMode install_mode = INSTALL_DEFAULT;
+    auto incremental_request = CmdlineOption::None;
+    bool incremental_wait = false;
+
+    bool use_fastdeploy = false;
+    FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+    auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request,
+                                          &incremental_wait);
+    auto passthrough_argv =
+            parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy);
+
+    auto [primary_mode, fallback_mode] =
+            calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+    if ((primary_mode == INSTALL_STREAM ||
+         fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
         best_install_mode() == INSTALL_PUSH) {
         error_exit("Attempting to use streaming install on unsupported device");
     }
@@ -489,19 +509,12 @@
     }
     fastdeploy_set_agent_update_strategy(agent_update_strategy);
 
-    std::vector<const char*> passthrough_argv;
-    for (int i = 0; i < argc; i++) {
-        if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
-            processedArgIndices.end()) {
-            passthrough_argv.push_back(argv[i]);
-        }
-    }
     if (passthrough_argv.size() < 2) {
         error_exit("install requires an apk argument");
     }
 
-    auto runInstallMode = [&](InstallMode installMode, bool silent) {
-        switch (installMode) {
+    auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+        switch (install_mode) {
             case INSTALL_PUSH:
                 return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
                                           use_fastdeploy);
@@ -510,20 +523,20 @@
                                             use_fastdeploy);
             case INSTALL_INCREMENTAL:
                 return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
-                                               wait, silent);
+                                               incremental_wait, silent);
             case INSTALL_DEFAULT:
             default:
-                return 1;
+                error_exit("invalid install mode");
         }
     };
-    auto res = runInstallMode(primaryMode, fallbackMode.has_value());
-    if (res && fallbackMode.value_or(primaryMode) != primaryMode) {
-        res = runInstallMode(*fallbackMode, false);
+    auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+    if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+        res = run_install_mode(*fallback_mode, false);
     }
     return res;
 }
 
-int install_multiple_app(int argc, const char** argv) {
+static int install_multiple_app_streamed(int argc, const char** argv) {
     // Find all APK arguments starting at end.
     // All other arguments passed through verbatim.
     int first_apk = -1;
@@ -548,24 +561,27 @@
 
     if (first_apk == -1) error_exit("need APK file on command line");
 
-    std::string install_cmd;
-    if (best_install_mode() == INSTALL_PUSH) {
-        install_cmd = "exec:pm";
-    } else {
-        install_cmd = "exec:cmd package";
-    }
+    const bool use_abb_exec = is_abb_exec_supported();
+    const std::string install_cmd =
+            use_abb_exec ? "package"
+                         : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
 
-    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
-                                                  install_cmd.c_str(), total_size);
+    std::vector<std::string> cmd_args = {install_cmd, "install-create", "-S",
+                                         std::to_string(total_size)};
+    cmd_args.reserve(first_apk + 4);
     for (int i = 1; i < first_apk; i++) {
-        cmd += " " + escape_arg(argv[i]);
+        if (use_abb_exec) {
+            cmd_args.push_back(argv[i]);
+        } else {
+            cmd_args.push_back(escape_arg(argv[i]));
+        }
     }
 
     // Create install session
     std::string error;
     char buf[BUFSIZ];
     {
-        unique_fd fd(adb_connect(cmd, &error));
+        unique_fd fd = send_command(cmd_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
             return EXIT_FAILURE;
@@ -587,6 +603,7 @@
         fputs(buf, stderr);
         return EXIT_FAILURE;
     }
+    const auto session_id_str = std::to_string(session_id);
 
     // Valid session, now stream the APKs
     bool success = true;
@@ -599,10 +616,15 @@
             goto finalize_session;
         }
 
-        std::string cmd =
-                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
-                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
-                                            session_id, android::base::Basename(file).c_str());
+        std::vector<std::string> cmd_args = {
+                install_cmd,
+                "install-write",
+                "-S",
+                std::to_string(sb.st_size),
+                session_id_str,
+                android::base::Basename(file),
+                "-",
+        };
 
         unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
         if (local_fd < 0) {
@@ -612,7 +634,7 @@
         }
 
         std::string error;
-        unique_fd remote_fd(adb_connect(cmd, &error));
+        unique_fd remote_fd = send_command(cmd_args, &error);
         if (remote_fd < 0) {
             fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
             success = false;
@@ -637,10 +659,13 @@
 
 finalize_session:
     // Commit session if we streamed everything okay; otherwise abandon.
-    std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
-                                                      success ? "commit" : "abandon", session_id);
+    std::vector<std::string> service_args = {
+            install_cmd,
+            success ? "install-commit" : "install-abandon",
+            session_id_str,
+    };
     {
-        unique_fd fd(adb_connect(service, &error));
+        unique_fd fd = send_command(service_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
             return EXIT_FAILURE;
@@ -659,6 +684,44 @@
     return EXIT_SUCCESS;
 }
 
+int install_multiple_app(int argc, const char** argv) {
+    InstallMode install_mode = INSTALL_DEFAULT;
+    auto incremental_request = CmdlineOption::None;
+    bool incremental_wait = false;
+    bool use_fastdeploy = false;
+
+    auto passthrough_argv = parse_install_mode({argv + 1, argv + argc}, &install_mode,
+                                               &incremental_request, &incremental_wait);
+
+    auto [primary_mode, fallback_mode] =
+            calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+    if ((primary_mode == INSTALL_STREAM ||
+         fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+        best_install_mode() == INSTALL_PUSH) {
+        error_exit("Attempting to use streaming install on unsupported device");
+    }
+
+    auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+        switch (install_mode) {
+            case INSTALL_PUSH:
+            case INSTALL_STREAM:
+                return install_multiple_app_streamed(passthrough_argv.size(),
+                                                     passthrough_argv.data());
+            case INSTALL_INCREMENTAL:
+                return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
+                                               incremental_wait, silent);
+            case INSTALL_DEFAULT:
+            default:
+                error_exit("invalid install mode");
+        }
+    };
+    auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+    if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+        res = run_install_mode(*fallback_mode, false);
+    }
+    return res;
+}
+
 int install_multi_package(int argc, const char** argv) {
     // Find all APK arguments starting at end.
     // All other arguments passed through verbatim.
@@ -683,23 +746,31 @@
         fprintf(stderr, "adb: multi-package install is not supported on this device\n");
         return EXIT_FAILURE;
     }
-    std::string install_cmd = "exec:cmd package";
 
-    std::string multi_package_cmd =
-            android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+    const bool use_abb_exec = is_abb_exec_supported();
+    const std::string install_cmd = use_abb_exec ? "package" : "exec:cmd package";
+
+    std::vector<std::string> multi_package_cmd_args = {install_cmd, "install-create",
+                                                       "--multi-package"};
+
+    multi_package_cmd_args.reserve(first_package + 4);
     for (int i = 1; i < first_package; i++) {
-        multi_package_cmd += " " + escape_arg(argv[i]);
+        if (use_abb_exec) {
+            multi_package_cmd_args.push_back(argv[i]);
+        } else {
+            multi_package_cmd_args.push_back(escape_arg(argv[i]));
+        }
     }
 
     if (apex_found) {
-        multi_package_cmd += " --staged";
+        multi_package_cmd_args.emplace_back("--staged");
     }
 
     // Create multi-package install session
     std::string error;
     char buf[BUFSIZ];
     {
-        unique_fd fd(adb_connect(multi_package_cmd, &error));
+        unique_fd fd = send_command(multi_package_cmd_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
             return EXIT_FAILURE;
@@ -721,6 +792,7 @@
         fputs(buf, stderr);
         return EXIT_FAILURE;
     }
+    const auto parent_session_id_str = std::to_string(parent_session_id);
 
     fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
 
@@ -728,17 +800,30 @@
 
     // Valid session, now create the individual sessions and stream the APKs
     int success = EXIT_FAILURE;
-    std::string individual_cmd =
-            android::base::StringPrintf("%s install-create", install_cmd.c_str());
-    std::string all_session_ids = "";
+    std::vector<std::string> individual_cmd_args = {install_cmd, "install-create"};
     for (int i = 1; i < first_package; i++) {
-        individual_cmd += " " + escape_arg(argv[i]);
+        if (use_abb_exec) {
+            individual_cmd_args.push_back(argv[i]);
+        } else {
+            individual_cmd_args.push_back(escape_arg(argv[i]));
+        }
     }
     if (apex_found) {
-        individual_cmd += " --staged";
+        individual_cmd_args.emplace_back("--staged");
     }
-    std::string individual_apex_cmd = individual_cmd + " --apex";
-    std::string cmd = "";
+
+    std::vector<std::string> individual_apex_cmd_args;
+    if (apex_found) {
+        individual_apex_cmd_args = individual_cmd_args;
+        individual_apex_cmd_args.emplace_back("--apex");
+    }
+
+    std::vector<std::string> add_session_cmd_args = {
+            install_cmd,
+            "install-add-session",
+            parent_session_id_str,
+    };
+
     for (int i = first_package; i < argc; i++) {
         const char* file = argv[i];
         char buf[BUFSIZ];
@@ -746,9 +831,9 @@
             unique_fd fd;
             // Create individual install session
             if (android::base::EndsWithIgnoreCase(file, ".apex")) {
-                fd.reset(adb_connect(individual_apex_cmd, &error));
+                fd = send_command(individual_apex_cmd_args, &error);
             } else {
-                fd.reset(adb_connect(individual_cmd, &error));
+                fd = send_command(individual_cmd_args, &error);
             }
             if (fd < 0) {
                 fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
@@ -771,6 +856,7 @@
             fputs(buf, stderr);
             goto finalize_multi_package_session;
         }
+        const auto session_id_str = std::to_string(session_id);
 
         fprintf(stdout, "Created child session ID %d.\n", session_id);
         session_ids.push_back(session_id);
@@ -785,10 +871,15 @@
                 goto finalize_multi_package_session;
             }
 
-            std::string cmd = android::base::StringPrintf(
-                    "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
-                    static_cast<uint64_t>(sb.st_size), session_id, i,
-                    android::base::Basename(split).c_str());
+            std::vector<std::string> cmd_args = {
+                    install_cmd,
+                    "install-write",
+                    "-S",
+                    std::to_string(sb.st_size),
+                    session_id_str,
+                    android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+                    "-",
+            };
 
             unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
             if (local_fd < 0) {
@@ -797,7 +888,7 @@
             }
 
             std::string error;
-            unique_fd remote_fd(adb_connect(cmd, &error));
+            unique_fd remote_fd = send_command(cmd_args, &error);
             if (remote_fd < 0) {
                 fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
                 goto finalize_multi_package_session;
@@ -816,13 +907,11 @@
                 goto finalize_multi_package_session;
             }
         }
-        all_session_ids += android::base::StringPrintf(" %d", session_id);
+        add_session_cmd_args.push_back(std::to_string(session_id));
     }
 
-    cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
-                                      parent_session_id, all_session_ids.c_str());
     {
-        unique_fd fd(adb_connect(cmd, &error));
+        unique_fd fd = send_command(add_session_cmd_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
             goto finalize_multi_package_session;
@@ -831,7 +920,8 @@
     }
 
     if (strncmp("Success", buf, 7)) {
-        fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+        fprintf(stderr, "adb: failed to link sessions (%s)\n",
+                android::base::Join(add_session_cmd_args, " ").c_str());
         fputs(buf, stderr);
         goto finalize_multi_package_session;
     }
@@ -841,11 +931,14 @@
 
 finalize_multi_package_session:
     // Commit session if we streamed everything okay; otherwise abandon
-    std::string service =
-            android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
-                                        success == 0 ? "commit" : "abandon", parent_session_id);
+    std::vector<std::string> service_args = {
+            install_cmd,
+            success == 0 ? "install-commit" : "install-abandon",
+            parent_session_id_str,
+    };
+
     {
-        unique_fd fd(adb_connect(service, &error));
+        unique_fd fd = send_command(service_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
             return EXIT_FAILURE;
@@ -866,10 +959,13 @@
     session_ids.push_back(parent_session_id);
     // try to abandon all remaining sessions
     for (std::size_t i = 0; i < session_ids.size(); i++) {
-        service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
-                                              session_ids[i]);
+        std::vector<std::string> service_args = {
+                install_cmd,
+                "install-abandon",
+                std::to_string(session_ids[i]),
+        };
         fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
-        unique_fd fd(adb_connect(service, &error));
+        unique_fd fd = send_command(service_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
             continue;
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index f56ef9a..c8f69c3 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -93,12 +93,10 @@
 
 // Send install-incremental to the device along with properly configured file descriptors in
 // streaming format. Once connection established, send all fs-verity tree bytes.
-static unique_fd start_install(const Files& files, bool silent) {
+static unique_fd start_install(const Files& files, const Args& passthrough_args, bool silent) {
     std::vector<std::string> command_args{"package", "install-incremental"};
+    command_args.insert(command_args.end(), passthrough_args.begin(), passthrough_args.end());
 
-    // fd's with positions at the beginning of fs-verity
-    std::vector<unique_fd> signature_fds;
-    signature_fds.reserve(files.size());
     for (int i = 0, size = files.size(); i < size; ++i) {
         const auto& file = files[i];
 
@@ -118,8 +116,6 @@
         auto file_desc = StringPrintf("%s:%lld:%d:%s:1", android::base::Basename(file).c_str(),
                                       (long long)st.st_size, i, signature.c_str());
         command_args.push_back(std::move(file_desc));
-
-        signature_fds.push_back(std::move(signature_fd));
     }
 
     std::string error;
@@ -150,8 +146,8 @@
     return true;
 }
 
-std::optional<Process> install(const Files& files, bool silent) {
-    auto connection_fd = start_install(files, silent);
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent) {
+    auto connection_fd = start_install(files, passthrough_args, silent);
     if (connection_fd < 0) {
         if (!silent) {
             fprintf(stderr, "adb: failed to initiate installation on device.\n");
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
index 1fb1e0b..40e928a 100644
--- a/adb/client/incremental.h
+++ b/adb/client/incremental.h
@@ -26,9 +26,10 @@
 namespace incremental {
 
 using Files = std::vector<std::string>;
+using Args = std::vector<std::string_view>;
 
 bool can_install(const Files& files);
-std::optional<Process> install(const Files& files, bool silent);
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent);
 
 enum class Result { Success, Failure, None };
 Result wait_for_installation(int read_fd);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 7fff05a..a663871 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -231,7 +231,14 @@
                 offset += write_size;
             }
         }
-        SubmitWrites();
+
+        // Wake up the worker thread to submit writes.
+        uint64_t notify = 1;
+        ssize_t rc = adb_write(worker_event_fd_.get(), &notify, sizeof(notify));
+        if (rc < 0) {
+            PLOG(FATAL) << "failed to notify worker eventfd to submit writes";
+        }
+
         return true;
     }
 
@@ -443,6 +450,9 @@
                 }
 
                 ReadEvents();
+
+                std::lock_guard<std::mutex> lock(write_mutex_);
+                SubmitWrites();
             }
         });
     }
@@ -626,8 +636,6 @@
         write_requests_.erase(it);
         size_t outstanding_writes = --writes_submitted_;
         LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
-
-        SubmitWrites();
     }
 
     IoWriteBlock CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset, size_t len,
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 884856d..cf0f1ac 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -117,8 +117,10 @@
         "device/main.cpp",
         "device/usb.cpp",
         "device/usb_client.cpp",
+        "device/tcp_client.cpp",
         "device/utility.cpp",
         "device/variables.cpp",
+        "socket.cpp",
     ],
 
     shared_libs: [
@@ -143,11 +145,13 @@
     ],
 
     static_libs: [
+        "libgtest_prod",
         "libhealthhalutils",
         "libsnapshot_nobinder",
     ],
 
     header_libs: [
+        "avb_headers",
         "libsnapshot_headers",
     ]
 }
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b8eee4a..2553353 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -625,7 +625,7 @@
         if (!sm) {
             return device->WriteFail("Unable to create SnapshotManager");
         }
-        if (!sm->HandleImminentDataWipe()) {
+        if (!sm->FinishMergeInRecovery()) {
             return device->WriteFail("Unable to finish snapshot merge");
         }
     } else {
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index bb085c5..1b0859f 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
@@ -28,6 +29,7 @@
 
 #include "constants.h"
 #include "flashing.h"
+#include "tcp_client.h"
 #include "usb_client.h"
 
 using android::fs_mgr::EnsurePathUnmounted;
@@ -60,11 +62,16 @@
               {FB_CMD_GSI, GsiHandler},
               {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
       }),
-      transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
       health_hal_(get_health_service()),
       fastboot_hal_(IFastboot::getService()),
       active_slot_("") {
+    if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") {
+        transport_ = std::make_unique<ClientTcpTransport>();
+    } else {
+        transport_ = std::make_unique<ClientUsbTransport>();
+    }
+
     if (boot_control_hal_) {
         boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
     }
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 7e7e507..a5f1223 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -31,6 +31,7 @@
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
+#include <libavb/libavb.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <libsnapshot/snapshot.h>
@@ -122,6 +123,27 @@
     }
 }
 
+static void CopyAVBFooter(std::vector<char>* data, const uint64_t block_device_size) {
+    if (data->size() < AVB_FOOTER_SIZE) {
+        return;
+    }
+    std::string footer;
+    uint64_t footer_offset = data->size() - AVB_FOOTER_SIZE;
+    for (int idx = 0; idx < AVB_FOOTER_MAGIC_LEN; idx++) {
+        footer.push_back(data->at(footer_offset + idx));
+    }
+    if (0 != footer.compare(AVB_FOOTER_MAGIC)) {
+        return;
+    }
+
+    // copy AVB footer from end of data to end of block device
+    uint64_t original_data_size = data->size();
+    data->resize(block_device_size, 0);
+    for (int idx = 0; idx < AVB_FOOTER_SIZE; idx++) {
+        data->at(block_device_size - 1 - idx) = data->at(original_data_size - 1 - idx);
+    }
+}
+
 int Flash(FastbootDevice* device, const std::string& partition_name) {
     PartitionHandle handle;
     if (!OpenPartition(device, partition_name, &handle)) {
@@ -131,8 +153,14 @@
     std::vector<char> data = std::move(device->download_data());
     if (data.size() == 0) {
         return -EINVAL;
-    } else if (data.size() > get_block_device_size(handle.fd())) {
+    }
+    uint64_t block_device_size = get_block_device_size(handle.fd());
+    if (data.size() > block_device_size) {
         return -EOVERFLOW;
+    } else if (data.size() < block_device_size &&
+               (partition_name == "boot" || partition_name == "boot_a" ||
+                partition_name == "boot_b")) {
+        CopyAVBFooter(&data, block_device_size);
     }
     WipeOverlayfsForPartition(device, partition_name);
     return FlashBlockDevice(handle.fd(), data);
diff --git a/fastboot/device/tcp_client.cpp b/fastboot/device/tcp_client.cpp
new file mode 100644
index 0000000..ec5e1e3
--- /dev/null
+++ b/fastboot/device/tcp_client.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tcp_client.h"
+#include "constants.h"
+
+#include <android-base/errors.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+static constexpr int kDefaultPort = 5554;
+static constexpr int kProtocolVersion = 1;
+static constexpr int kHandshakeTimeoutMs = 2000;
+static constexpr size_t kHandshakeLength = 4;
+
+// Extract the big-endian 8-byte message length into a 64-bit number.
+static uint64_t ExtractMessageLength(const void* buffer) {
+    uint64_t ret = 0;
+    for (int i = 0; i < 8; ++i) {
+        ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);
+    }
+    return ret;
+}
+
+// Encode the 64-bit number into a big-endian 8-byte message length.
+static void EncodeMessageLength(uint64_t length, void* buffer) {
+    for (int i = 0; i < 8; ++i) {
+        reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);
+    }
+}
+
+ClientTcpTransport::ClientTcpTransport() {
+    service_ = Socket::NewServer(Socket::Protocol::kTcp, kDefaultPort);
+
+    // A workaround to notify recovery to continue its work.
+    android::base::SetProperty("sys.usb.ffs.ready", "1");
+}
+
+ssize_t ClientTcpTransport::Read(void* data, size_t len) {
+    if (len > SSIZE_MAX) {
+        return -1;
+    }
+
+    size_t total_read = 0;
+    do {
+        // Read a new message
+        while (message_bytes_left_ == 0) {
+            if (socket_ == nullptr) {
+                ListenFastbootSocket();
+            }
+
+            char buffer[8];
+            if (socket_->ReceiveAll(buffer, 8, 0) == 8) {
+                message_bytes_left_ = ExtractMessageLength(buffer);
+            } else {
+                // If connection is closed by host, Receive will return 0 immediately.
+                socket_.reset(nullptr);
+                // In DATA phase, return error.
+                if (downloading_) {
+                    return -1;
+                }
+            }
+        }
+
+        size_t read_length = len - total_read;
+        if (read_length > message_bytes_left_) {
+            read_length = message_bytes_left_;
+        }
+        ssize_t bytes_read =
+                socket_->ReceiveAll(reinterpret_cast<char*>(data) + total_read, read_length, 0);
+        if (bytes_read == -1) {
+            socket_.reset(nullptr);
+            return -1;
+        } else {
+            message_bytes_left_ -= bytes_read;
+            total_read += bytes_read;
+        }
+    // There are more than one DATA phases if the downloading buffer is too
+    // large, like a very big system image. All of data phases should be
+    // received until the whole buffer is filled in that case.
+    } while (downloading_ && total_read < len);
+
+    return total_read;
+}
+
+ssize_t ClientTcpTransport::Write(const void* data, size_t len) {
+    if (socket_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+
+    // Use multi-buffer writes for better performance.
+    char header[8];
+    EncodeMessageLength(len, header);
+
+    if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, len}})) {
+        socket_.reset(nullptr);
+        return -1;
+    }
+
+    // In DATA phase
+    if (android::base::StartsWith(reinterpret_cast<const char*>(data), RESPONSE_DATA)) {
+        downloading_ = true;
+    } else {
+        downloading_ = false;
+    }
+
+    return len;
+}
+
+int ClientTcpTransport::Close() {
+    if (socket_ == nullptr) {
+        return -1;
+    }
+    socket_.reset(nullptr);
+
+    return 0;
+}
+
+int ClientTcpTransport::Reset() {
+    return Close();
+}
+
+void ClientTcpTransport::ListenFastbootSocket() {
+    while (true) {
+        socket_ = service_->Accept();
+
+        // Handshake
+        char buffer[kHandshakeLength + 1];
+        buffer[kHandshakeLength] = '\0';
+        if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) !=
+            kHandshakeLength) {
+            PLOG(ERROR) << "No Handshake message received";
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        if (memcmp(buffer, "FB", 2) != 0) {
+            PLOG(ERROR) << "Unrecognized initialization message";
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        int version = 0;
+        if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
+            LOG(ERROR) << "Unknown TCP protocol version " << buffer + 2
+                       << ", our version: " << kProtocolVersion;
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion));
+        if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {
+            PLOG(ERROR) << "Failed to send initialization message";
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        break;
+    }
+}
diff --git a/fastboot/device/tcp_client.h b/fastboot/device/tcp_client.h
new file mode 100644
index 0000000..32e9834
--- /dev/null
+++ b/fastboot/device/tcp_client.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "socket.h"
+#include "transport.h"
+
+class ClientTcpTransport : public Transport {
+  public:
+    ClientTcpTransport();
+    ~ClientTcpTransport() override = default;
+
+    ssize_t Read(void* data, size_t len) override;
+    ssize_t Write(const void* data, size_t len) override;
+    int Close() override;
+    int Reset() override;
+
+  private:
+    void ListenFastbootSocket();
+
+    std::unique_ptr<Socket> service_;
+    std::unique_ptr<Socket> socket_;
+    uint64_t message_bytes_left_ = 0;
+    bool downloading_ = false;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientTcpTransport);
+};
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index e56ffcf..5a14b63 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -54,7 +54,9 @@
     while (total < length) {
         ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);
 
-        if (bytes == -1) {
+        // Returns 0 only when the peer has disconnected because our requested length is not 0. So
+        // we return immediately to avoid dead loop here.
+        if (bytes <= 0) {
             if (total == 0) {
                 return -1;
             }
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 76e3955..b8385d3 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -454,7 +454,8 @@
                << entry.encryption_options;
         return;
     }
-    if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) != 0) {
+    if ((options.flags &
+         (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) != 0) {
         // We can only use this policy on ext4 if the "stable_inodes" feature
         // is set on the filesystem, otherwise shrinking will break encrypted files.
         if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index ea9c957..9046132 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -52,17 +52,27 @@
 using DmTargetZero = android::dm::DmTargetZero;
 using DmTargetLinear = android::dm::DmTargetLinear;
 
-static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
-                                           const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const CreateLogicalPartitionParams& params,
                                            const LpMetadataBlockDevice& block_device,
                                            const std::string& super_device, std::string* result) {
     // If the super device is the source of this block device's metadata,
     // make sure we use the correct super device (and not just "super",
     // which might not exist.)
     std::string name = GetBlockDevicePartitionName(block_device);
-    std::string dev_string = opener.GetDeviceString(name);
-    if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
-        dev_string = opener.GetDeviceString(super_device);
+    if (android::base::StartsWith(name, "dm-")) {
+        // Device-mapper nodes are not normally allowed in LpMetadata, since
+        // they are not consistent across reboots. However for the purposes of
+        // testing it's useful to handle them. For example when running DSUs,
+        // userdata is a device-mapper device, and some stacking will result
+        // when using libfiemap.
+        *result = "/dev/block/" + name;
+        return true;
+    }
+
+    auto opener = params.partition_opener;
+    std::string dev_string = opener->GetDeviceString(name);
+    if (GetMetadataSuperBlockDevice(*params.metadata) == &block_device) {
+        dev_string = opener->GetDeviceString(super_device);
     }
 
     // Note: device-mapper will not accept symlinks, so we must use realpath
@@ -93,8 +103,8 @@
             case LP_TARGET_TYPE_LINEAR: {
                 const auto& block_device = params.metadata->block_devices[extent.target_source];
                 std::string dev_string;
-                if (!GetPhysicalPartitionDevicePath(*params.partition_opener, *params.metadata,
-                                                    block_device, super_device, &dev_string)) {
+                if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,
+                                                    &dev_string)) {
                     LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
                     return false;
                 }
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 6717922..3ee742f 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -51,6 +51,7 @@
 using android::fs_mgr::GetPartitionName;
 
 static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
+static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test";
 
 std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
     auto metadata_dir = "/metadata/gsi/" + dir_prefix;
@@ -135,10 +136,13 @@
     return !!FindPartition(*metadata.get(), name);
 }
 
+static bool IsTestDir(const std::string& path) {
+    return android::base::StartsWith(path, kTestImageMetadataDir) ||
+           android::base::StartsWith(path, kOtaTestImageMetadataDir);
+}
+
 static bool IsUnreliablePinningAllowed(const std::string& path) {
-    return android::base::StartsWith(path, "/data/gsi/dsu/") ||
-           android::base::StartsWith(path, "/data/gsi/test/") ||
-           android::base::StartsWith(path, "/data/gsi/ota/test/");
+    return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
 }
 
 FiemapStatus ImageManager::CreateBackingImage(
@@ -174,8 +178,7 @@
     // if device-mapper is stacked in some complex way not supported by
     // FiemapWriter.
     auto device_path = GetDevicePathForFile(fw.get());
-    if (android::base::StartsWith(device_path, "/dev/block/dm-") &&
-        !android::base::StartsWith(metadata_dir_, kTestImageMetadataDir)) {
+    if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
         LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
 
         fw = {};
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 5388b44..6663391 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -131,132 +131,6 @@
     ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
 }
 
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
-  public:
-    ImageTest() : dm_(DeviceMapper::Instance()) {}
-
-    void SetUp() override {
-        manager_ = ImageManager::Open(kMetadataPath, gDataPath);
-        ASSERT_NE(manager_, nullptr);
-
-        manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
-        submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
-        ASSERT_NE(submanager_, nullptr);
-
-        submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
-        // Ensure that metadata is cleared in between runs.
-        submanager_->RemoveAllImages();
-        manager_->RemoveAllImages();
-
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        base_name_ = tinfo->name();
-        test_image_name_ = base_name_ + "-base";
-        wrapper_device_name_ = base_name_ + "-wrapper";
-
-        ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
-        ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
-    }
-
-    void TearDown() override {
-        submanager_->UnmapImageDevice(test_image_name_);
-        umount(gDataMountPath.c_str());
-        dm_.DeleteDeviceIfExists(wrapper_device_name_);
-        manager_->UnmapImageDevice(base_name_);
-        manager_->DeleteBackingImage(base_name_);
-    }
-
-  protected:
-    bool DoFormat(const std::string& device) {
-        // clang-format off
-        std::vector<std::string> mkfs_args = {
-            "/system/bin/mke2fs",
-            "-F",
-            "-b 4096",
-            "-t ext4",
-            "-m 0",
-            "-O has_journal",
-            device,
-            ">/dev/null",
-            "2>/dev/null",
-            "</dev/null",
-        };
-        // clang-format on
-        auto command = android::base::Join(mkfs_args, " ");
-        return system(command.c_str()) == 0;
-    }
-
-    std::unique_ptr<ImageManager> manager_;
-    std::unique_ptr<ImageManager> submanager_;
-
-    DeviceMapper& dm_;
-    std::string base_name_;
-    std::string base_device_;
-    std::string test_image_name_;
-    std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
-    ASSERT_TRUE(DoFormat(base_device_));
-    ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
-    std::string path;
-    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
-    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
-    GTEST_SKIP() << "WIP failure b/148874852";
-#endif
-    // Create a simple wrapper around the base device that we'll mount from
-    // instead. This will simulate the code paths for dm-crypt/default-key/bow
-    // and force us to use device-mapper rather than loop devices.
-    uint64_t device_size = 0;
-    {
-        unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
-        ASSERT_GE(fd, 0);
-        device_size = get_block_device_size(fd);
-        ASSERT_EQ(device_size, kTestImageSize * 16);
-    }
-    uint64_t num_sectors = device_size / 512;
-
-    auto& dm = DeviceMapper::Instance();
-
-    DmTable table;
-    table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
-    ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
-    // Format and mount.
-    std::string wrapper_device;
-    ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
-    ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
-    ASSERT_TRUE(DoFormat(wrapper_device));
-    ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
-    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
-    std::set<std::string> backing_devices;
-    auto init = [&](std::set<std::string> devices) -> bool {
-        backing_devices = std::move(devices);
-        return true;
-    };
-
-    std::string path;
-    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
-    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
-    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-    ASSERT_TRUE(submanager_->MapAllImages(init));
-    ASSERT_FALSE(backing_devices.empty());
-    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-}
-
 bool Mkdir(const std::string& path) {
     if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
         std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 4505382..2288674 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -124,6 +124,64 @@
     return true;
 }
 
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+    bool found = false;
+    const uint8_t* desc_partition_name;
+    auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
+
+    for (const auto& vbmeta : vbmeta_images) {
+        size_t num_descriptors;
+        std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
+                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+        if (!descriptors || num_descriptors < 1) {
+            continue;
+        }
+
+        for (size_t n = 0; n < num_descriptors && !found; n++) {
+            AvbDescriptor desc;
+            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+                LWARNING << "Descriptor[" << n << "] is invalid";
+                continue;
+            }
+            if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
+                desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
+                if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
+                                                               hash_desc.get())) {
+                    continue;
+                }
+                if (hash_desc->partition_name_len != partition_name.length()) {
+                    continue;
+                }
+                // Notes that desc_partition_name is not NUL-terminated.
+                std::string hash_partition_name((const char*)desc_partition_name,
+                                                hash_desc->partition_name_len);
+                if (hash_partition_name == partition_name) {
+                    found = true;
+                }
+            }
+        }
+
+        if (found) break;
+    }
+
+    if (!found) {
+        LERROR << "Hash descriptor not found: " << partition_name;
+        return nullptr;
+    }
+
+    hash_desc->partition_name = partition_name;
+
+    const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
+    hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
+
+    const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
+    hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
+
+    return hash_desc;
+}
+
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
     bool found = false;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 09c786a..e8f7c39 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -40,6 +40,9 @@
 std::string GetAvbPropertyDescriptor(const std::string& key,
                                      const std::vector<VBMetaData>& vbmeta_images);
 
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
 // AvbHashtreeDescriptor to dm-verity table setup.
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
index f82f83d..1c14cc0 100644
--- a/fs_mgr/libfs_avb/fs_avb_util.cpp
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -74,5 +74,15 @@
     return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
 }
 
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hash descriptor.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+                                                       VBMetaData&& vbmeta) {
+    if (!vbmeta.size()) return nullptr;
+
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+    return GetHashDescriptor(avb_partition_name, vbmeta_images);
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
index ec8badb..3f37bd7 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -32,9 +32,20 @@
                                                 std::string* out_avb_partition_name,
                                                 VBMetaVerifyResult* out_verify_result);
 
+// Loads the single vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+        const std::string& image_path, const std::string& partition_name,
+        const std::string& expected_public_key_blob, bool allow_verification_error,
+        bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+        bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);
+
 // Gets the hashtree descriptor for avb_partition_name from the vbmeta.
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& avb_partition_name, VBMetaData&& vbmeta);
 
+// Gets the hash descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+                                                       VBMetaData&& vbmeta);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
index bd638e6..f2aa7cc 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/types.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -55,6 +55,12 @@
 
 std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
 
+struct FsAvbHashDescriptor : AvbHashDescriptor {
+    std::string partition_name;
+    std::string salt;
+    std::string digest;
+};
+
 struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
     std::string partition_name;
     std::string salt;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 1daa83b..ac8a25e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -223,6 +223,10 @@
     // optional callback fires periodically to query progress via GetUpdateState.
     bool HandleImminentDataWipe(const std::function<void()>& callback = {});
 
+    // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
+    // but does not expect a data wipe after.
+    bool FinishMergeInRecovery();
+
     // This method is only allowed in recovery and is used as a helper to
     // initialize the snapshot devices as a requirement to mount a snapshotted
     // /system in recovery.
@@ -536,11 +540,16 @@
     bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
                                       const std::function<bool()>& callback);
 
+    // Return device string of a mapped image, or if it is not available, the mapped image path.
+    bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
+                                          std::string* device_string_or_mapped_path);
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
     std::unique_ptr<IImageManager> images_;
     bool has_local_image_manager_ = false;
+    bool in_factory_data_reset_ = false;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c9fa28e..d4a3f62 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -577,8 +577,16 @@
         return false;
     }
 
+    auto other_suffix = device_->GetOtherSlotSuffix();
+
     auto& dm = DeviceMapper::Instance();
     for (const auto& snapshot : snapshots) {
+        if (android::base::EndsWith(snapshot, other_suffix)) {
+            // Allow the merge to continue, but log this unexpected case.
+            LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
+            continue;
+        }
+
         // The device has to be mapped, since everything should be merged at
         // the same time. This is a fairly serious error. We could forcefully
         // map everything here, but it should have been mapped during first-
@@ -1008,6 +1016,15 @@
 }
 
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+    // It's not possible to remove update state in recovery, so write an
+    // indicator that cleanup is needed on reboot. If a factory data reset
+    // was requested, it doesn't matter, everything will get wiped anyway.
+    // To make testing easier we consider a /data wipe as cleaned up.
+    if (device_->IsRecovery() && !in_factory_data_reset_) {
+        WriteUpdateState(lock, UpdateState::MergeCompleted);
+        return;
+    }
+
     RemoveAllUpdateState(lock);
 }
 
@@ -1674,7 +1691,7 @@
         return false;
     }
     std::string cow_device;
-    if (!dm.GetDeviceString(cow_name, &cow_device)) {
+    if (!GetMappedImageDeviceStringOrPath(cow_name, &cow_device)) {
         LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
         return false;
     }
@@ -1771,7 +1788,7 @@
     // If the COW image exists, append it as the last extent.
     if (snapshot_status.cow_file_size() > 0) {
         std::string cow_image_device;
-        if (!dm.GetDeviceString(cow_image_name, &cow_image_device)) {
+        if (!GetMappedImageDeviceStringOrPath(cow_image_name, &cow_image_device)) {
             LOG(ERROR) << "Cannot determine major/minor for: " << cow_image_name;
             return false;
         }
@@ -2347,7 +2364,6 @@
         const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
     CHECK(lock);
 
-    auto& dm = DeviceMapper::Instance();
     CreateLogicalPartitionParams cow_params{
             .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
             .metadata = exported_target_metadata,
@@ -2372,7 +2388,7 @@
         }
 
         std::string cow_path;
-        if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
+        if (!images_->GetMappedImageDevice(cow_name, &cow_path)) {
             LOG(ERROR) << "Cannot determine path for " << cow_name;
             return Return::Error();
         }
@@ -2528,7 +2544,43 @@
         }
         return true;
     };
-    if (!ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback)) {
+
+    in_factory_data_reset_ = true;
+    bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+    in_factory_data_reset_ = false;
+
+    if (!ok) {
+        return false;
+    }
+
+    // Nothing should be depending on partitions now, so unmap them all.
+    if (!UnmapAllPartitions()) {
+        LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+    }
+    return true;
+}
+
+bool SnapshotManager::FinishMergeInRecovery() {
+    if (!device_->IsRecovery()) {
+        LOG(ERROR) << "Data wipes are only allowed in recovery.";
+        return false;
+    }
+
+    auto mount = EnsureMetadataMounted();
+    if (!mount || !mount->HasDevice()) {
+        return false;
+    }
+
+    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_path = device_->GetSuperDevice(slot_number);
+    if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+        LOG(ERROR) << "Unable to map partitions to complete merge.";
+        return false;
+    }
+
+    UpdateState state = ProcessUpdateState();
+    if (state != UpdateState::MergeCompleted) {
+        LOG(ERROR) << "Merge returned unexpected status: " << state;
         return false;
     }
 
@@ -2685,5 +2737,24 @@
     return true;
 }
 
+bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
+                                                       std::string* device_string_or_mapped_path) {
+    auto& dm = DeviceMapper::Instance();
+    // Try getting the device string if it is a device mapper device.
+    if (dm.GetState(device_name) != DmDeviceState::INVALID) {
+        return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+    }
+
+    // Otherwise, get path from IImageManager.
+    if (!images_->GetMappedImageDevice(device_name, device_string_or_mapped_path)) {
+        return false;
+    }
+
+    LOG(WARNING) << "Calling GetMappedImageDevice with local image manager; device "
+                 << (device_string_or_mapped_path ? *device_string_or_mapped_path : "(nullptr)")
+                 << "may not be available in first stage init! ";
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index f82c082..9ca2412 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1454,6 +1454,52 @@
     ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
 }
 
+// Test that a merge does not clear the snapshot state in fastboot.
+TEST_F(SnapshotUpdateTest, MergeInFastboot) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(MapUpdateSnapshots());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    init = nullptr;
+
+    // Initiate the merge and then immediately stop it to simulate a reboot.
+    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_TRUE(new_sm->InitiateMerge());
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a reboot into recovery.
+    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    test_device->set_recovery(true);
+    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+    ASSERT_TRUE(new_sm->FinishMergeInRecovery());
+
+    auto mount = new_sm->EnsureMetadataMounted();
+    ASSERT_TRUE(mount && mount->HasDevice());
+    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+
+    // Finish the merge in a normal boot.
+    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    init = SnapshotManager::NewForFirstStageMount(test_device.release());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    init = nullptr;
+
+    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
+}
+
 // Test that after an OTA, before a merge, we can wipe data in recovery.
 TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
     // Execute the first update.
diff --git a/init/README.md b/init/README.md
index 726c0cc..b70366b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -644,7 +644,8 @@
 `wait <path> [ <timeout> ]`
 > Poll for the existence of the given file and return when found,
   or the timeout has been reached. If timeout is not specified it
-  currently defaults to five seconds.
+  currently defaults to five seconds. The timeout value can be
+  fractional seconds, specified in floating point notation.
 
 `wait_for_prop <name> <value>`
 > Wait for system property _name_ to be _value_. Properties are expanded
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 200bfff..149a766 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -49,6 +49,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -1065,11 +1066,12 @@
 static Result<void> do_wait(const BuiltinArguments& args) {
     auto timeout = kCommandRetryTimeout;
     if (args.size() == 3) {
-        int timeout_int;
-        if (!android::base::ParseInt(args[2], &timeout_int)) {
+        double timeout_double;
+        if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
             return Error() << "failed to parse timeout";
         }
-        timeout = std::chrono::seconds(timeout_int);
+        timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(
+                std::chrono::duration<double>(timeout_double));
     }
 
     if (wait_for_file(args[1].c_str(), timeout) != 0) {
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index d62ecb0..d1a84f3 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -25,6 +25,7 @@
 #include <sys/time.h>
 
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 
@@ -205,8 +206,8 @@
 
 Result<void> check_wait(const BuiltinArguments& args) {
     if (args.size() == 3 && !args[2].empty()) {
-        int timeout_int;
-        if (!android::base::ParseInt(args[2], &timeout_int)) {
+        double timeout_double;
+        if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
             return Error() << "failed to parse timeout";
         }
     }
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index cae53f4..cfa0d99 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -16,6 +16,7 @@
 
 #include "first_stage_console.h"
 
+#include <stdio.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
@@ -87,8 +88,18 @@
     _exit(127);
 }
 
-bool FirstStageConsole(const std::string& cmdline) {
-    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+int FirstStageConsole(const std::string& cmdline) {
+    auto pos = cmdline.find("androidboot.first_stage_console=");
+    if (pos != std::string::npos) {
+        int val = 0;
+        if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
+            return FirstStageConsoleParam::DISABLED;
+        }
+        if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+            return val;
+        }
+    }
+    return FirstStageConsoleParam::DISABLED;
 }
 
 }  // namespace init
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index 7485339..8f36a7c 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -21,8 +21,15 @@
 namespace android {
 namespace init {
 
+enum FirstStageConsoleParam {
+    DISABLED = 0,
+    CONSOLE_ON_FAILURE = 1,
+    IGNORE_FAILURE = 2,
+    MAX_PARAM_VALUE = IGNORE_FAILURE,
+};
+
 void StartConsole();
-bool FirstStageConsole(const std::string& cmdline);
+int FirstStageConsole(const std::string& cmdline);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 5eca644..1a608f6 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -200,16 +200,16 @@
     }
 
     Modprobe m({"/lib/modules"}, module_load_file);
-    auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
+    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
     if (!m.LoadListedModules(!want_console)) {
-        if (want_console) {
+        if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
         } else {
             LOG(FATAL) << "Failed to load kernel modules";
         }
     }
 
-    if (want_console) {
+    if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
         StartConsole();
     }
 
diff --git a/init/init.cpp b/init/init.cpp
index 6465df1..29859c5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -509,7 +509,9 @@
 // Set the UDC controller for the ConfigFS USB Gadgets.
 // Read the UDC controller in use from "/sys/class/udc".
 // In case of multiple UDC controllers select the first one.
-static void set_usb_controller() {
+static void SetUsbController() {
+    static auto controller_set = false;
+    if (controller_set) return;
     std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
     if (!dir) return;
 
@@ -518,6 +520,7 @@
         if (dp->d_name[0] == '.') continue;
 
         SetProperty("sys.usb.controller", dp->d_name);
+        controller_set = true;
         break;
     }
 }
@@ -772,7 +775,7 @@
     fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
     MountHandler mount_handler(&epoll);
-    set_usb_controller();
+    SetUsbController();
 
     const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
@@ -879,6 +882,7 @@
         }
         if (!IsShuttingDown()) {
             HandleControlMessages();
+            SetUsbController();
         }
     }
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index e89f74a..ffd58a3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -66,8 +66,6 @@
 #include "sigchld_handler.h"
 #include "util.h"
 
-#define PROC_SYSRQ "/proc/sysrq-trigger"
-
 using namespace std::literals;
 
 using android::base::boot_clock;
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index 485188b..76460a5 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -29,6 +29,7 @@
 #include <cutils/android_reboot.h>
 
 #include "capabilities.h"
+#include "reboot_utils.h"
 
 namespace android {
 namespace init {
@@ -138,6 +139,9 @@
         LOG(ERROR) << backtrace->FormatFrameData(i);
     }
     if (init_fatal_panic) {
+        LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
+        android::base::WriteStringToFile("c", PROC_SYSRQ);
+        LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";
         _exit(signal_number);
     }
     RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 878ad96..05bb9ae 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -18,6 +18,8 @@
 
 #include <string>
 
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
 namespace android {
 namespace init {
 
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index dc989a0..c4cfa6f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -48,6 +48,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_defaults {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 9ed7927..24110ee 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -33,6 +33,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
     native_bridge_supported: true,
     export_include_dirs: ["include"],
     target: {
@@ -59,6 +60,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
 
     export_include_dirs: ["include"],
 
@@ -142,6 +144,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
     native_bridge_supported: true,
     srcs: [
         "config_utils.cpp",
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index ce0c3c8..33ae13d 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -26,4 +26,5 @@
     export_include_dirs: ["include"],
     shared_libs: ["android.hardware.graphics.allocator@2.0"],
     header_libs: ["libhardware_headers"],
+    min_sdk_version: "29",
 }
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index 9ecdd4f..64de00e 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -37,25 +37,22 @@
 
 #include "dhcpmsg.h"
 
-int fatal();
+int fatal(const char*);
 
-int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
-{
-    int s;
-    struct sockaddr_ll bindaddr;
+int open_raw_socket(const char* ifname __unused, uint8_t hwaddr[ETH_ALEN], int if_index) {
+    int s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+    if (s < 0) return fatal("socket(PF_PACKET)");
 
-    if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
-        return fatal("socket(PF_PACKET)");
-    }
-
-    memset(&bindaddr, 0, sizeof(bindaddr));
-    bindaddr.sll_family = AF_PACKET;
-    bindaddr.sll_protocol = htons(ETH_P_IP);
-    bindaddr.sll_halen = ETH_ALEN;
+    struct sockaddr_ll bindaddr = {
+            .sll_family = AF_PACKET,
+            .sll_protocol = htons(ETH_P_IP),
+            .sll_ifindex = if_index,
+            .sll_halen = ETH_ALEN,
+    };
     memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
-    bindaddr.sll_ifindex = if_index;
 
     if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+        close(s);
         return fatal("Cannot bind raw socket to interface");
     }
 
diff --git a/libnetutils/packet.h b/libnetutils/packet.h
index aade392..66186fc 100644
--- a/libnetutils/packet.h
+++ b/libnetutils/packet.h
@@ -17,7 +17,9 @@
 #ifndef _WIFI_PACKET_H_
 #define _WIFI_PACKET_H_
 
-int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
+#include <linux/if_ether.h>
+
+int open_raw_socket(const char* ifname, uint8_t hwaddr[ETH_ALEN], int if_index);
 int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
                 uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
 int receive_packet(int s, struct dhcp_msg *msg);
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 2c1b255..bda11e9 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -17,6 +17,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
 }
 
 cc_library {
@@ -60,4 +61,5 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
 }
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 766ea0f..ccc6f62 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -89,15 +89,15 @@
         "test_vendor.cpp",
     ],
     static_libs: [
+        "libbase",
         "libgmock",
+        "liblog",
+        "libjsoncpp",
         "libjsonpbverify",
         "libjsonpbparse",
         "libprocessgroup_proto",
     ],
     shared_libs: [
-        "libbase",
-        "liblog",
-        "libjsoncpp",
         "libprotobuf-cpp-full",
     ],
     test_suites: [
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 0c9a2b8..15b0d89 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -51,6 +51,12 @@
             enabled: false,
         },
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 // Tests
diff --git a/libstats/push_compat/StatsEventCompat.cpp b/libstats/push_compat/StatsEventCompat.cpp
index e1a86ae..c17ca61 100644
--- a/libstats/push_compat/StatsEventCompat.cpp
+++ b/libstats/push_compat/StatsEventCompat.cpp
@@ -28,17 +28,8 @@
 using android::base::GetProperty;
 
 const static int kStatsEventTag = 1937006964;
-
-/* Checking ro.build.version.release is fragile, as the release field is
- * an opaque string without structural guarantees. However, testing confirms
- * that on Q devices, the property is "10," and on R, it is "R." Until
- * android_get_device_api_level() is updated, this is the only solution.
- *
- * TODO(b/146019024): migrate to android_get_device_api_level()
- */
 const bool StatsEventCompat::mPlatformAtLeastR =
-        GetProperty("ro.build.version.codename", "") == "R" ||
-        android_get_device_api_level() > __ANDROID_API_Q__;
+        android_get_device_api_level() >= __ANDROID_API_R__;
 
 // initializations of static class variables
 bool StatsEventCompat::mAttemptedLoad = false;
diff --git a/libstats/push_compat/tests/StatsEventCompat_test.cpp b/libstats/push_compat/tests/StatsEventCompat_test.cpp
index dcb3797..2a70db5 100644
--- a/libstats/push_compat/tests/StatsEventCompat_test.cpp
+++ b/libstats/push_compat/tests/StatsEventCompat_test.cpp
@@ -21,16 +21,7 @@
 
 using android::base::GetProperty;
 
-/* Checking ro.build.version.release is fragile, as the release field is
- * an opaque string without structural guarantees. However, testing confirms
- * that on Q devices, the property is "10," and on R, it is "R." Until
- * android_get_device_api_level() is updated, this is the only solution.
- *
- *
- * TODO(b/146019024): migrate to android_get_device_api_level()
- */
-const static bool mPlatformAtLeastR = GetProperty("ro.build.version.codename", "") == "R" ||
-                                      android_get_device_api_level() > __ANDROID_API_Q__;
+const static bool mPlatformAtLeastR = android_get_device_api_level() >= __ANDROID_API_R__;
 
 TEST(StatsEventCompatTest, TestDynamicLoading) {
     StatsEventCompat event;
diff --git a/libstats/socket/stats_buffer_writer.c b/libstats/socket/stats_buffer_writer.c
index 74acb20..549acdc 100644
--- a/libstats/socket/stats_buffer_writer.c
+++ b/libstats/socket/stats_buffer_writer.c
@@ -50,24 +50,16 @@
 int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
     int ret = 1;
 
-#ifdef __ANDROID__
-    bool statsdEnabled = property_get_bool("ro.statsd.enable", true);
-#else
-    bool statsdEnabled = false;
-#endif
+    struct iovec vecs[2];
+    vecs[0].iov_base = (void*)&kStatsEventTag;
+    vecs[0].iov_len = sizeof(kStatsEventTag);
+    vecs[1].iov_base = buffer;
+    vecs[1].iov_len = size;
 
-    if (statsdEnabled) {
-        struct iovec vecs[2];
-        vecs[0].iov_base = (void*)&kStatsEventTag;
-        vecs[0].iov_len = sizeof(kStatsEventTag);
-        vecs[1].iov_base = buffer;
-        vecs[1].iov_len = size;
+    ret = __write_to_statsd(vecs, 2);
 
-        ret = __write_to_statsd(vecs, 2);
-
-        if (ret < 0) {
-            note_log_drop(ret, atomId);
-        }
+    if (ret < 0) {
+        note_log_drop(ret, atomId);
     }
 
     return ret;
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index ff886fd..db61669 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -8,6 +8,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
     export_include_dirs: ["include"],
 
     target: {
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 627f0d4..3b98bab 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -43,6 +43,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_test {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 5efe03f..9c1621b 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -180,6 +180,7 @@
     struct ifa_cacheinfo *cacheinfo = nullptr;
     char addrstr[INET6_ADDRSTRLEN] = "";
     char ifname[IFNAMSIZ] = "";
+    uint32_t flags;
 
     if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
         return false;
@@ -194,6 +195,9 @@
     // For log messages.
     const char *msgtype = rtMessageName(type);
 
+    // First 8 bits of flags. In practice will always be overridden when parsing IFA_FLAGS below.
+    flags = ifaddr->ifa_flags;
+
     struct rtattr *rta;
     int len = IFA_PAYLOAD(nh);
     for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
@@ -242,6 +246,9 @@
             }
 
             cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
+
+        } else if (rta->rta_type == IFA_FLAGS) {
+            flags = *(uint32_t*)RTA_DATA(rta);
         }
     }
 
@@ -256,7 +263,7 @@
     mSubsystem = strdup("net");
     asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen);
     asprintf(&mParams[1], "INTERFACE=%s", ifname);
-    asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
+    asprintf(&mParams[2], "FLAGS=%u", flags);
     asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
     asprintf(&mParams[4], "IFINDEX=%u", ifaddr->ifa_index);
 
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 1f3ba59..563c2c2 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -135,6 +135,12 @@
             exclude_shared_libs: ["libdexfile_support"],
         },
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 // Static library without DEX support to avoid dependencies on the ART APEX.
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 7405c96..3a30a9e 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -22,6 +22,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
 
     header_libs: [
         "liblog_headers",
@@ -168,6 +169,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_library {
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1202c15..1d899ab 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -195,7 +195,7 @@
     int prot = PROT_READ;
     if (!readOnly) prot |= PROT_WRITE;
 
-    void* ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
+    void* ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset);
     if (ptr == MAP_FAILED) {
         if (errno == EINVAL && length == 0) {
             ptr = nullptr;
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
index 576d89b..9f7ce85 100644
--- a/libutils/FileMap_test.cpp
+++ b/libutils/FileMap_test.cpp
@@ -32,3 +32,23 @@
     ASSERT_EQ(0u, m.getDataLength());
     ASSERT_EQ(4096, m.getDataOffset());
 }
+
+TEST(FileMap, large_offset) {
+    // Make sure that an offset > INT32_MAX will not fail the create
+    // function. See http://b/155662887.
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+
+    off64_t offset = INT32_MAX + 1024LL;
+
+    // Make the temporary file large enough to pass the mmap.
+    ASSERT_EQ(offset, lseek64(tf.fd, offset, SEEK_SET));
+    char value = 0;
+    ASSERT_EQ(1, write(tf.fd, &value, 1));
+
+    android::FileMap m;
+    ASSERT_TRUE(m.create("test", tf.fd, offset, 0, true));
+    ASSERT_STREQ("test", m.getFileName());
+    ASSERT_EQ(0u, m.getDataLength());
+    ASSERT_EQ(offset, m.getDataOffset());
+}
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index dee577e..6002567 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -19,12 +19,20 @@
 
 #include <unistd.h>
 
+#if !defined(__MINGW32__)
+#include <sys/mman.h>
+#endif
+
 #if defined(__APPLE__)
 
 /* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-
+static_assert(sizeof(off_t) >= 8, "This code requires that Mac OS have at least a 64-bit off_t.");
 typedef off_t off64_t;
 
+static inline void* mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) {
+    return mmap(addr, length, prot, flags, fd, offset);
+}
+
 static inline off64_t lseek64(int fd, off64_t offset, int whence) {
     return lseek(fd, offset, whence);
 }
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 1bbffaf..553136a 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -109,6 +109,12 @@
             enabled: true,
         },
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 // Tests.