Merge "init: disable oneshot_on test"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 98db191..b514368 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1016,8 +1016,12 @@
         if (kill_forward) {
             r = remove_listener(pieces[0].c_str(), transport);
         } else {
-            r = install_listener(pieces[0], pieces[1].c_str(), transport, no_rebind,
-                                 &resolved_tcp_port, &error);
+            int flags = 0;
+            if (no_rebind) {
+                flags |= INSTALL_LISTENER_NO_REBIND;
+            }
+            r = install_listener(pieces[0], pieces[1].c_str(), transport, flags, &resolved_tcp_port,
+                                 &error);
         }
         if (r == INSTALL_STATUS_OK) {
 #if ADB_HOST
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 29909a5..43a9252 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -164,6 +164,15 @@
     }
 }
 
+void enable_daemon_sockets() EXCLUDES(listener_list_mutex) {
+    std::lock_guard<std::mutex> lock(listener_list_mutex);
+    for (auto& l : listener_list) {
+        if (l->connect_to == "*smartsocket*") {
+            fdevent_set(l->fde, FDE_READ);
+        }
+    }
+}
+
 void close_smartsockets() EXCLUDES(listener_list_mutex) {
     std::lock_guard<std::mutex> lock(listener_list_mutex);
     auto pred = [](const std::unique_ptr<alistener>& listener) {
@@ -173,7 +182,7 @@
 }
 
 InstallStatus install_listener(const std::string& local_name, const char* connect_to,
-                               atransport* transport, int no_rebind, int* resolved_tcp_port,
+                               atransport* transport, int flags, int* resolved_tcp_port,
                                std::string* error) EXCLUDES(listener_list_mutex) {
     std::lock_guard<std::mutex> lock(listener_list_mutex);
     for (auto& l : listener_list) {
@@ -184,8 +193,8 @@
                 return INSTALL_STATUS_INTERNAL_ERROR;
             }
 
-            // Can't repurpose a listener if 'no_rebind' is true.
-            if (no_rebind) {
+            // Can't repurpose a listener if INSTALL_LISTENER_NO_REBIND is set
+            if (flags & INSTALL_LISTENER_NO_REBIND) {
                 *error = "cannot rebind";
                 return INSTALL_STATUS_CANNOT_REBIND;
             }
@@ -222,7 +231,9 @@
     } else {
         listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
     }
-    fdevent_set(listener->fde, FDE_READ);
+    if ((flags & INSTALL_LISTENER_DISABLED) == 0) {
+        fdevent_set(listener->fde, FDE_READ);
+    }
 
     listener->transport = transport;
 
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 70a2ee1..354dcc5 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -32,8 +32,11 @@
   INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
 };
 
+inline constexpr int INSTALL_LISTENER_NO_REBIND = 1 << 0;
+inline constexpr int INSTALL_LISTENER_DISABLED = 1 << 1;
+
 InstallStatus install_listener(const std::string& local_name, const char* connect_to,
-                               atransport* transport, int no_rebind, int* resolved_tcp_port,
+                               atransport* transport, int flags, int* resolved_tcp_port,
                                std::string* error);
 
 std::string format_listeners();
@@ -41,6 +44,7 @@
 InstallStatus remove_listener(const char* local_name, atransport* transport);
 void remove_all_listeners(void);
 
+void enable_daemon_sockets();
 void close_smartsockets();
 
 #endif /* __ADB_LISTENERS_H */
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index fe9182e..e4d010c 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -22,11 +22,12 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <algorithm>
-#include <iostream>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/parsebool.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
@@ -39,8 +40,9 @@
 #include "fastdeploy.h"
 #include "incremental.h"
 
+using namespace std::literals;
+
 static constexpr int kFastDeployMinApi = 24;
-static constexpr int kIncrementalMinApi = 29;
 
 namespace {
 
@@ -50,6 +52,8 @@
     INSTALL_STREAM,
     INSTALL_INCREMENTAL,
 };
+
+enum class CmdlineOption { None, Enable, Disable };
 }
 
 static bool can_use_feature(const char* feature) {
@@ -299,28 +303,23 @@
     return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
 }
 
-static int install_app_incremental(int argc, const char** argv) {
-    printf("Performing Incremental Install\n");
+static int install_app_incremental(int argc, const char** argv, bool wait, bool silent) {
     using clock = std::chrono::high_resolution_clock;
     const auto start = clock::now();
     int first_apk = -1;
     int last_apk = -1;
-    std::string cert_path;
-    bool wait = false;
-    std::vector<std::string_view> args = {"package"};
+    std::vector<std::string_view> args = {"package"sv};
     for (int i = 0; i < argc; ++i) {
         const auto arg = std::string_view(argv[i]);
-        if (android::base::EndsWithIgnoreCase(arg, ".apk")) {
+        if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) {
             last_apk = i;
             if (first_apk == -1) {
                 first_apk = i;
             }
-        } else if (arg == "--wait") {
-            wait = true;
-        } else if (arg.starts_with("install-")) {
+        } 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");
+            args.push_back("install"sv);
         } else {
             args.push_back(arg);
         }
@@ -328,16 +327,23 @@
 
     if (first_apk == -1) error_exit("Need at least one APK file on command line");
 
-    const auto afterApk = clock::now();
+    auto files = incremental::Files{argv + first_apk, argv + last_apk + 1};
+    if (silent) {
+        // For a silent installation we want to do the lightweight check first and bail early and
+        // quietly if it fails.
+        if (!incremental::can_install(files)) {
+            return -1;
+        }
+    }
 
-    auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1});
+    printf("Performing Incremental Install\n");
+    auto server_process = incremental::install(files, silent);
     if (!server_process) {
         return -1;
     }
 
     const auto end = clock::now();
-    printf("Install command complete (ms: %d total, %d apk prep, %d install)\n",
-           msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end));
+    printf("Install command complete in %d ms\n", msBetween(start, end));
 
     if (wait) {
         (*server_process).wait();
@@ -346,66 +352,134 @@
     return 0;
 }
 
+static std::pair<InstallMode, std::optional<InstallMode>> calculateInstallMode(
+        InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incrementalRequest) {
+    if (incrementalRequest == CmdlineOption::Enable) {
+        if (fastdeploy) {
+            error_exit(
+                    "--incremental and --fast-deploy options are incompatible. "
+                    "Please choose one");
+        }
+    }
+
+    if (modeFromArgs != INSTALL_DEFAULT) {
+        if (incrementalRequest == 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;
+        } else {
+            error_exit("Device doesn't support incremental installations");
+        }
+    }
+    if (incrementalRequest == 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;
+            }
+        }
+    }
+    if (incrementalRequest == 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;
+        std::vector<std::string> args = {"settings", "get",
+                                         "enable_adb_incremental_install_default"};
+        auto fd = send_abb_exec_command(args, &error);
+        if (!fd.ok()) {
+            fprintf(stderr, "adb: retrieving the default device installation mode failed: %s",
+                    error.c_str());
+        } else {
+            char buf[BUFSIZ] = {};
+            read_status_line(fd.get(), buf, sizeof(buf));
+            using namespace android::base;
+            auto val = ParseBool(buf);
+            if (val == ParseBoolResult::kFalse) {
+                incrementalRequest = CmdlineOption::Disable;
+            }
+        }
+    }
+
+    if (incrementalRequest == CmdlineOption::Enable) {
+        // explicitly requested - no fallback
+        return {INSTALL_INCREMENTAL, std::nullopt};
+    }
+    const auto bestMode = best_install_mode();
+    if (incrementalRequest == CmdlineOption::None) {
+        // no opinion - use incremental, fallback to regular on a failure.
+        return {INSTALL_INCREMENTAL, bestMode};
+    }
+    // incremental turned off - use the regular best mode without a fallback.
+    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;
 
     for (int i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "--streaming")) {
+        if (argv[i] == "--streaming"sv) {
             processedArgIndices.push_back(i);
             installMode = INSTALL_STREAM;
-        } else if (!strcmp(argv[i], "--no-streaming")) {
+        } else if (argv[i] == "--no-streaming"sv) {
             processedArgIndices.push_back(i);
             installMode = INSTALL_PUSH;
-        } else if (!strcmp(argv[i], "-r")) {
+        } 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 (!strcmp(argv[i], "--fastdeploy")) {
+        } else if (argv[i] == "--fastdeploy"sv) {
             processedArgIndices.push_back(i);
             use_fastdeploy = true;
-        } else if (!strcmp(argv[i], "--no-fastdeploy")) {
+        } else if (argv[i] == "--no-fastdeploy"sv) {
             processedArgIndices.push_back(i);
             use_fastdeploy = false;
-        } else if (!strcmp(argv[i], "--force-agent")) {
+        } else if (argv[i] == "--force-agent"sv) {
             processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateAlways;
-        } else if (!strcmp(argv[i], "--date-check-agent")) {
+        } else if (argv[i] == "--date-check-agent"sv) {
             processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
-        } else if (!strcmp(argv[i], "--version-check-agent")) {
+        } else if (argv[i] == "--version-check-agent"sv) {
             processedArgIndices.push_back(i);
             agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
-        } else if (!strcmp(argv[i], "--incremental")) {
+        } else if (strlen(argv[i]) >= "--incr"sv.size() && "--incremental"sv.starts_with(argv[i])) {
             processedArgIndices.push_back(i);
-            installMode = INSTALL_INCREMENTAL;
-        } else if (!strcmp(argv[i], "--no-incremental")) {
+            incremental_request = CmdlineOption::Enable;
+        } else if (strlen(argv[i]) >= "--no-incr"sv.size() &&
+                   "--no-incremental"sv.starts_with(argv[i])) {
             processedArgIndices.push_back(i);
-            installMode = INSTALL_DEFAULT;
+            incremental_request = CmdlineOption::Disable;
+        } else if (argv[i] == "--wait"sv) {
+            processedArgIndices.push_back(i);
+            wait = true;
         }
     }
 
-    if (installMode == INSTALL_INCREMENTAL) {
-        if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) {
-            error_exit("Attempting to use incremental install on unsupported device");
-        }
-    }
-
-    if (installMode == INSTALL_DEFAULT) {
-        installMode = best_install_mode();
-    }
-
-    if (installMode == INSTALL_STREAM && best_install_mode() == INSTALL_PUSH) {
+    auto [primaryMode, fallbackMode] =
+            calculateInstallMode(installMode, use_fastdeploy, incremental_request);
+    if ((primaryMode == INSTALL_STREAM || fallbackMode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+        best_install_mode() == INSTALL_PUSH) {
         error_exit("Attempting to use streaming install on unsupported device");
     }
 
     if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) {
-        printf("Fast Deploy is only compatible with devices of API version %d or higher, "
-               "ignoring.\n",
-               kFastDeployMinApi);
+        fprintf(stderr,
+                "Fast Deploy is only compatible with devices of API version %d or higher, "
+                "ignoring.\n",
+                kFastDeployMinApi);
         use_fastdeploy = false;
     }
     fastdeploy_set_agent_update_strategy(agent_update_strategy);
@@ -421,19 +495,27 @@
         error_exit("install requires an apk argument");
     }
 
-    switch (installMode) {
-        case INSTALL_PUSH:
-            return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
-                                      use_fastdeploy);
-        case INSTALL_STREAM:
-            return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
-                                        use_fastdeploy);
-        case INSTALL_INCREMENTAL:
-            return install_app_incremental(passthrough_argv.size(), passthrough_argv.data());
-        case INSTALL_DEFAULT:
-        default:
-            return 1;
+    auto runInstallMode = [&](InstallMode installMode, bool silent) {
+        switch (installMode) {
+            case INSTALL_PUSH:
+                return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
+                                          use_fastdeploy);
+            case INSTALL_STREAM:
+                return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
+                                            use_fastdeploy);
+            case INSTALL_INCREMENTAL:
+                return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
+                                               wait, silent);
+            case INSTALL_DEFAULT:
+            default:
+                return 1;
+        }
+    };
+    auto res = runInstallMode(primaryMode, fallbackMode.has_value());
+    if (res && fallbackMode.value_or(primaryMode) != primaryMode) {
+        res = runInstallMode(*fallbackMode, false);
     }
+    return res;
 }
 
 int install_multiple_app(int argc, const char** argv) {
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 33a1a37..9765292 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -90,38 +90,58 @@
     return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
 }
 
-// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
-static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
-                                                                   std::string signature_file) {
+// Read, verify and return the signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::vector<char>> read_signature(Size file_size,
+                                                              std::string signature_file,
+                                                              bool silent) {
     signature_file += IDSIG;
 
     struct stat st;
     if (stat(signature_file.c_str(), &st)) {
-        fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
+        if (!silent) {
+            fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
+        }
         return {};
     }
 
     unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
     if (fd < 0) {
-        fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
+        if (!silent) {
+            fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
+        }
         return {};
     }
 
     auto [signature, tree_size] = read_id_sig_headers(fd);
     if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
-        fprintf(stderr,
-                "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
-                signature_file.c_str(), (long long)tree_size, (long long)expected);
+        if (!silent) {
+            fprintf(stderr,
+                    "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+                    signature_file.c_str(), (long long)tree_size, (long long)expected);
+        }
+        return {};
+    }
+
+    return {std::move(fd), std::move(signature)};
+}
+
+// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
+                                                                   std::string signature_file,
+                                                                   bool silent) {
+    auto [fd, signature] = read_signature(file_size, std::move(signature_file), silent);
+    if (!fd.ok()) {
         return {};
     }
 
     size_t base64_len = 0;
     if (!EVP_EncodedLength(&base64_len, signature.size())) {
-        fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
+        if (!silent) {
+            fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
+        }
         return {};
     }
-    std::string encoded_signature;
-    encoded_signature.resize(base64_len);
+    std::string encoded_signature(base64_len, '\0');
     encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
                                              (const uint8_t*)signature.data(), signature.size()));
 
@@ -130,7 +150,7 @@
 
 // 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 std::vector<std::string>& files) {
+static unique_fd start_install(const Files& files, bool silent) {
     std::vector<std::string> command_args{"package", "install-incremental"};
 
     // fd's with positions at the beginning of fs-verity
@@ -141,11 +161,13 @@
 
         struct stat st;
         if (stat(file.c_str(), &st)) {
-            fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+            if (!silent) {
+                fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+            }
             return {};
         }
 
-        auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file);
+        auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file, silent);
         if (!signature_fd.ok()) {
             return {};
         }
@@ -161,15 +183,19 @@
     std::string error;
     auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
     if (connection_fd < 0) {
-        fprintf(stderr, "Failed to run: %s, error: %s\n",
-                android::base::Join(command_args, " ").c_str(), error.c_str());
+        if (!silent) {
+            fprintf(stderr, "Failed to run: %s, error: %s\n",
+                    android::base::Join(command_args, " ").c_str(), error.c_str());
+        }
         return {};
     }
 
     // Pushing verity trees for all installation files.
     for (auto&& local_fd : signature_fds) {
         if (!copy_to_file(local_fd.get(), connection_fd.get())) {
-            fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
+            if (!silent) {
+                fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
+            }
             return {};
         }
     }
@@ -179,10 +205,27 @@
 
 }  // namespace
 
-std::optional<Process> install(std::vector<std::string> files) {
-    auto connection_fd = start_install(files);
+bool can_install(const Files& files) {
+    for (const auto& file : files) {
+        struct stat st;
+        if (stat(file.c_str(), &st)) {
+            return false;
+        }
+
+        auto [fd, _] = read_signature(st.st_size, file, true);
+        if (!fd.ok()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+std::optional<Process> install(const Files& files, bool silent) {
+    auto connection_fd = start_install(files, silent);
     if (connection_fd < 0) {
-        fprintf(stderr, "adb: failed to initiate installation on device.\n");
+        if (!silent) {
+            fprintf(stderr, "adb: failed to initiate installation on device.\n");
+        }
         return {};
     }
 
@@ -194,7 +237,9 @@
     // pipe for child process to write output
     int print_fds[2];
     if (adb_socketpair(print_fds) != 0) {
-        fprintf(stderr, "Failed to create socket pair for child to print to parent\n");
+        if (!silent) {
+            fprintf(stderr, "Failed to create socket pair for child to print to parent\n");
+        }
         return {};
     }
     auto [pipe_read_fd, pipe_write_fd] = print_fds;
@@ -206,7 +251,9 @@
     auto child =
             adb_launch_process(adb_path, std::move(args), {connection_fd.get(), pipe_write_fd});
     if (!child) {
-        fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+        if (!silent) {
+            fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+        }
         return {};
     }
 
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
index 731e6fb..1fb1e0b 100644
--- a/adb/client/incremental.h
+++ b/adb/client/incremental.h
@@ -25,7 +25,10 @@
 
 namespace incremental {
 
-std::optional<Process> install(std::vector<std::string> files);
+using Files = std::vector<std::string>;
+
+bool can_install(const Files& files);
+std::optional<Process> install(const Files& files, bool silent);
 
 enum class Result { Success, Failure, None };
 Result wait_for_installation(int read_fd);
diff --git a/adb/client/incremental_utils.cpp b/adb/client/incremental_utils.cpp
index caadb26..fa501e4 100644
--- a/adb/client/incremental_utils.cpp
+++ b/adb/client/incremental_utils.cpp
@@ -23,6 +23,7 @@
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_writer.h>
 
+#include <array>
 #include <cinttypes>
 #include <numeric>
 #include <unordered_set>
@@ -30,6 +31,8 @@
 #include "adb_trace.h"
 #include "sysdeps.h"
 
+using namespace std::literals;
+
 static constexpr int kBlockSize = 4096;
 
 static constexpr inline int32_t offsetToBlockIndex(int64_t offset) {
@@ -217,16 +220,24 @@
     return {zip, std::move(mapping)};
 }
 
-// TODO(b/151676293): avoid using libziparchive as it reads local file headers
-// which causes additional performance cost. Instead, only read from central directory.
 static std::vector<int32_t> InstallationPriorityBlocks(int fd, int64_t fileSize) {
+    static constexpr std::array<std::string_view, 3> additional_matches = {
+            "resources.arsc"sv, "AndroidManifest.xml"sv, "classes.dex"sv};
     auto [zip, _] = openZipArchive(fd, fileSize);
     if (!zip) {
         return {};
     }
 
+    auto matcher = [](std::string_view entry_name) {
+        if (entry_name.starts_with("lib/"sv) && entry_name.ends_with(".so"sv)) {
+            return true;
+        }
+        return std::any_of(additional_matches.begin(), additional_matches.end(),
+                           [entry_name](std::string_view i) { return i == entry_name; });
+    };
+
     void* cookie = nullptr;
-    if (StartIteration(zip, &cookie) != 0) {
+    if (StartIteration(zip, &cookie, std::move(matcher)) != 0) {
         D("%s failed at StartIteration: %d", __func__, errno);
         return {};
     }
@@ -235,8 +246,12 @@
     ZipEntry entry;
     std::string_view entryName;
     while (Next(cookie, &entry, &entryName) == 0) {
-        if (entryName == "resources.arsc" || entryName == "AndroidManifest.xml" ||
-            entryName.starts_with("lib/")) {
+        if (entryName == "classes.dex"sv) {
+            // Only the head is needed for installation
+            int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
+            appendBlocks(startBlockIndex, 1, &installationPriorityBlocks);
+            D("\tadding to priority blocks: '%.*s' 1", (int)entryName.size(), entryName.data());
+        } else {
             // Full entries are needed for installation
             off64_t entryStartOffset = entry.offset;
             off64_t entryEndOffset =
@@ -248,11 +263,8 @@
             int32_t endBlockIndex = offsetToBlockIndex(entryEndOffset);
             int32_t numNewBlocks = endBlockIndex - startBlockIndex + 1;
             appendBlocks(startBlockIndex, numNewBlocks, &installationPriorityBlocks);
-            D("\tadding to priority blocks: '%.*s'", (int)entryName.size(), entryName.data());
-        } else if (entryName == "classes.dex") {
-            // Only the head is needed for installation
-            int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
-            appendBlocks(startBlockIndex, 1, &installationPriorityBlocks);
+            D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
+              numNewBlocks);
         }
     }
 
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index a85a18c..33e0716 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -139,9 +139,10 @@
     auto start = std::chrono::steady_clock::now();
 
     // If we told a previous adb server to quit because of version mismatch, we can get to this
-    // point before it's finished exiting. Retry for a while to give it some time.
-    while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
-           INSTALL_STATUS_OK) {
+    // point before it's finished exiting. Retry for a while to give it some time. Don't actually
+    // accept any connections until adb_wait_for_device_initialization finishes below.
+    while (install_listener(socket_spec, "*smartsocket*", nullptr, INSTALL_LISTENER_DISABLED,
+                            nullptr, &error) != INSTALL_STATUS_OK) {
         if (std::chrono::steady_clock::now() - start > 0.5s) {
             LOG(FATAL) << "could not install *smartsocket* listener: " << error;
         }
@@ -162,12 +163,14 @@
             PLOG(FATAL) << "setsid() failed";
         }
 #endif
+    }
 
-        // Wait for the USB scan to complete before notifying the parent that we're up.
-        // We need to perform this in a thread, because we would otherwise block the event loop.
-        std::thread notify_thread([ack_reply_fd]() {
-            adb_wait_for_device_initialization();
+    // Wait for the USB scan to complete before notifying the parent that we're up.
+    // We need to perform this in a thread, because we would otherwise block the event loop.
+    std::thread notify_thread([ack_reply_fd]() {
+        adb_wait_for_device_initialization();
 
+        if (ack_reply_fd >= 0) {
             // Any error output written to stderr now goes to adb.log. We could
             // keep around a copy of the stderr fd and use that to write any errors
             // encountered by the following code, but that is probably overkill.
@@ -193,9 +196,13 @@
             }
             unix_close(ack_reply_fd);
 #endif
-        });
-        notify_thread.detach();
-    }
+        }
+        // We don't accept() client connections until this point: this way, clients
+        // can't see wonky state early in startup even if they're connecting directly
+        // to the server instead of going through the adb program.
+        fdevent_run_on_main_thread([] { enable_daemon_sockets(); });
+    });
+    notify_thread.detach();
 
 #if defined(__linux__)
     // Write our location to .android/adb.$PORT, so that older clients can exec us.
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index ca120c6..b8eee4a 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -31,6 +31,7 @@
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
+#include <fs_mgr/roots.h>
 #include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 31fc359..bb085c5 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -16,18 +16,22 @@
 
 #include "fastboot_device.h"
 
+#include <algorithm>
+
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
 #include <healthhalutils/HealthHalUtils.h>
 
-#include <algorithm>
-
 #include "constants.h"
 #include "flashing.h"
 #include "usb_client.h"
 
+using android::fs_mgr::EnsurePathUnmounted;
+using android::fs_mgr::Fstab;
 using ::android::hardware::hidl_string;
 using ::android::hardware::boot::V1_0::IBootControl;
 using ::android::hardware::boot::V1_0::Slot;
@@ -64,6 +68,13 @@
     if (boot_control_hal_) {
         boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
     }
+
+    // Make sure cache is unmounted, since recovery will have mounted it for
+    // logging.
+    Fstab fstab;
+    if (ReadDefaultFstab(&fstab)) {
+        EnsurePathUnmounted(&fstab, "/cache");
+    }
 }
 
 FastbootDevice::~FastbootDevice() {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 5c276b4..1daa83b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -142,7 +142,9 @@
     // be created, and the device must either cancel the OTA (either before
     // rebooting or after rolling back), or merge the OTA.
     // Before calling this function, all snapshots must be mapped.
-    bool FinishedSnapshotWrites();
+    // If |wipe| is set to true, wipe is scheduled after reboot, and snapshots
+    // may need to be merged before wiping.
+    bool FinishedSnapshotWrites(bool wipe);
 
     // Initiate a merge on all snapshot devices. This should only be used after an
     // update has been marked successful after booting.
@@ -452,6 +454,7 @@
 
     std::string GetSnapshotBootIndicatorPath();
     std::string GetRollbackIndicatorPath();
+    std::string GetForwardMergeIndicatorPath();
 
     // Return the name of the device holding the "snapshot" or "snapshot-merge"
     // target. This may not be the final device presented via MapSnapshot(), if
@@ -522,6 +525,17 @@
     bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
                               Slot current_slot, const std::string& name);
 
+    // Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,
+    // allow forward merge on FDR.
+    bool UpdateForwardMergeIndicator(bool wipe);
+
+    // Helper for HandleImminentDataWipe.
+    // Call ProcessUpdateState and handle states with special rules before data wipe. Specifically,
+    // if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if
+    // necessary.
+    bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+                                      const std::function<bool()>& callback);
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index b825805..c9fa28e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -228,10 +228,17 @@
         return false;
     }
 
-    // It's okay if these fail - first-stage init performs a deeper check after
+    // It's okay if these fail:
+    // - For SnapshotBoot and Rollback, first-stage init performs a deeper check after
     // reading the indicator file, so it's not a problem if it still exists
     // after the update completes.
-    std::vector<std::string> files = {GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath()};
+    // - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
+    // matches the incoming update.
+    std::vector<std::string> files = {
+            GetSnapshotBootIndicatorPath(),
+            GetRollbackIndicatorPath(),
+            GetForwardMergeIndicatorPath(),
+    };
     for (const auto& file : files) {
         RemoveFileIfExists(file);
     }
@@ -241,7 +248,7 @@
     return WriteUpdateState(lock, UpdateState::None);
 }
 
-bool SnapshotManager::FinishedSnapshotWrites() {
+bool SnapshotManager::FinishedSnapshotWrites(bool wipe) {
     auto lock = LockExclusive();
     if (!lock) return false;
 
@@ -261,6 +268,10 @@
         return false;
     }
 
+    if (!UpdateForwardMergeIndicator(wipe)) {
+        return false;
+    }
+
     // This file is written on boot to detect whether a rollback occurred. It
     // MUST NOT exist before rebooting, otherwise, we're at risk of deleting
     // snapshots too early.
@@ -992,6 +1003,10 @@
     return metadata_dir_ + "/" + android::base::Basename(kRollbackIndicatorPath);
 }
 
+std::string SnapshotManager::GetForwardMergeIndicatorPath() {
+    return metadata_dir_ + "/allow-forward-merge";
+}
+
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
     RemoveAllUpdateState(lock);
 }
@@ -2438,6 +2453,9 @@
     ss << "Rollback indicator: "
        << (access(GetRollbackIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
        << std::endl;
+    ss << "Forward merge indicator: "
+       << (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
+       << std::endl;
 
     bool ok = true;
     std::vector<std::string> snapshots;
@@ -2504,19 +2522,39 @@
         return false;
     }
 
-    UpdateState state = ProcessUpdateState([&]() -> bool {
+    auto process_callback = [&]() -> bool {
         if (callback) {
             callback();
         }
         return true;
-    });
+    };
+    if (!ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback)) {
+        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::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+                                                   const std::function<bool()>& callback) {
+    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    UpdateState state = ProcessUpdateState(callback);
     LOG(INFO) << "Update state in recovery: " << state;
     switch (state) {
         case UpdateState::MergeFailed:
             LOG(ERROR) << "Unrecoverable merge failure detected.";
             return false;
         case UpdateState::Unverified: {
-            // If an OTA was just applied but has not yet started merging, we
+            // If an OTA was just applied but has not yet started merging:
+            //
+            // - if forward merge is allowed, initiate merge and call
+            // ProcessUpdateState again.
+            //
+            // - if forward merge is not allowed, we
             // have no choice but to revert slots, because the current slot will
             // immediately become unbootable. Rather than wait for the device
             // to reboot N times until a rollback, we proactively disable the
@@ -2526,8 +2564,17 @@
             // as an error here.
             auto slot = GetCurrentSlot();
             if (slot == Slot::Target) {
+                if (allow_forward_merge &&
+                    access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0) {
+                    LOG(INFO) << "Forward merge allowed, initiating merge now.";
+                    return InitiateMerge() &&
+                           ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
+                }
+
                 LOG(ERROR) << "Reverting to old slot since update will be deleted.";
                 device_->SetSlotAsUnbootable(slot_number);
+            } else {
+                LOG(INFO) << "Booting from " << slot << " slot, no action is taken.";
             }
             break;
         }
@@ -2539,11 +2586,6 @@
         default:
             break;
     }
-
-    // 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;
 }
 
@@ -2624,5 +2666,24 @@
     return CreateResult::CREATED;
 }
 
+bool SnapshotManager::UpdateForwardMergeIndicator(bool wipe) {
+    auto path = GetForwardMergeIndicatorPath();
+
+    if (!wipe) {
+        LOG(INFO) << "Wipe is not scheduled. Deleting forward merge indicator.";
+        return RemoveFileIfExists(path);
+    }
+
+    // TODO(b/152094219): Don't forward merge if no CoW file is allocated.
+
+    LOG(INFO) << "Wipe will be scheduled. Allowing forward merge of snapshots.";
+    if (!android::base::WriteStringToFile("1", path)) {
+        PLOG(ERROR) << "Unable to write forward merge indicator: " << path;
+        return false;
+    }
+
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 855451d..f82c082 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -320,7 +320,7 @@
     // Simulate a reboot into the new slot.
     AssertionResult SimulateReboot() {
         lock_ = nullptr;
-        if (!sm->FinishedSnapshotWrites()) {
+        if (!sm->FinishedSnapshotWrites(false)) {
             return AssertionFailure();
         }
         if (!dm_.DeleteDevice("test_partition_b")) {
@@ -424,7 +424,7 @@
 }
 
 TEST_F(SnapshotTest, NoMergeBeforeReboot) {
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Merge should fail, since the slot hasn't changed.
     ASSERT_FALSE(sm->InitiateMerge());
@@ -440,7 +440,7 @@
 }
 
 TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // We didn't change the slot, so we shouldn't need snapshots.
     TestDeviceInfo* info = new TestDeviceInfo(fake_super);
@@ -476,7 +476,7 @@
     lock_ = nullptr;
 
     // Done updating.
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     test_device->set_slot_suffix("_b");
     ASSERT_TRUE(sm->InitiateMerge());
@@ -1007,7 +1007,7 @@
         ASSERT_TRUE(IsPartitionUnchanged(name));
     }
 
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1139,7 +1139,7 @@
         ASSERT_TRUE(IsPartitionUnchanged(name));
     }
 
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1171,7 +1171,7 @@
 // Test that if an update is applied but not booted into, it can be canceled.
 TEST_F(SnapshotUpdateTest, CancelAfterApply) {
     ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
     ASSERT_TRUE(sm->CancelUpdate());
 }
 
@@ -1188,7 +1188,7 @@
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
     ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1295,7 +1295,7 @@
         ASSERT_TRUE(IsPartitionUnchanged(name));
     }
 
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 }
 
 TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
@@ -1324,7 +1324,7 @@
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
     ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1428,7 +1428,7 @@
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
     ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1460,7 +1460,7 @@
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
     ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1485,7 +1485,7 @@
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
     ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1498,7 +1498,46 @@
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
     EXPECT_FALSE(test_device->IsSlotUnbootable(0));
-    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+    EXPECT_FALSE(test_device->IsSlotUnbootable(1));
+}
+
+// Test update package that requests data wipe.
+TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
+    AddOperationForPartitions();
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    // Write some data to target partitions.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
+    }
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a reboot into recovery.
+    auto test_device = new TestDeviceInfo(fake_super, "_b");
+    test_device->set_recovery(true);
+    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+    // Manually mount metadata so that we can call GetUpdateState() below.
+    MountMetadata();
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+    ASSERT_FALSE(test_device->IsSlotUnbootable(1));
+    ASSERT_FALSE(test_device->IsSlotUnbootable(0));
+
+    // Now reboot into new slot.
+    test_device = new TestDeviceInfo(fake_super, "_b");
+    auto init = SnapshotManager::NewForFirstStageMount(test_device);
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    // Verify that we are on the downgraded build.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name)) << name;
+    }
 }
 
 TEST_F(SnapshotUpdateTest, Hashtree) {
@@ -1533,7 +1572,7 @@
     ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
 
     // Finish update.
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
@@ -1569,7 +1608,7 @@
     ASSERT_EQ(1u, table.size());
     EXPECT_TRUE(table[0].IsOverflowSnapshot());
 
-    ASSERT_FALSE(sm->FinishedSnapshotWrites())
+    ASSERT_FALSE(sm->FinishedSnapshotWrites(false))
             << "FinishedSnapshotWrites should detect overflow of CoW device.";
 }
 
@@ -1623,7 +1662,7 @@
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
     ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // Simulate shutting down the device.
     ASSERT_TRUE(UnmapAll());
diff --git a/init/init.cpp b/init/init.cpp
index 20b4176..a9d6301 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -235,15 +235,6 @@
         // action queue.  Instead we set this flag and ensure that shutdown happens before the next
         // command is run in the main init loop.
         auto lock = std::lock_guard{shutdown_command_lock_};
-        if (do_shutdown_) {
-            LOG(ERROR) << "TriggerShutdown called while a previous shutdown command '"
-                       << shutdown_command_ << "' has not been handled";
-            UnwindMainThreadStack();
-        }
-        if (IsShuttingDown()) {
-            LOG(ERROR) << "TriggerShutdown called while init is already shutting down";
-            UnwindMainThreadStack();
-        }
         shutdown_command_ = command;
         do_shutdown_ = true;
         WakeMainInitThread();
@@ -258,12 +249,27 @@
         return {};
     }
 
+    bool do_shutdown() const { return do_shutdown_; }
+
   private:
     std::mutex shutdown_command_lock_;
     std::string shutdown_command_;
     bool do_shutdown_ = false;
 } shutdown_state;
 
+void DebugRebootLogging() {
+    LOG(INFO) << "do_shutdown: " << shutdown_state.do_shutdown()
+              << " IsShuttingDown: " << IsShuttingDown();
+    if (shutdown_state.do_shutdown()) {
+        LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
+        UnwindMainThreadStack();
+    }
+    if (IsShuttingDown()) {
+        LOG(ERROR) << "sys.powerctl set while init is already shutting down";
+        UnwindMainThreadStack();
+    }
+}
+
 void DumpState() {
     ServiceList::GetInstance().DumpState();
     ActionManager::GetInstance().DumpState();
@@ -901,7 +907,9 @@
                 (*function)();
             }
         }
-        HandleControlMessages();
+        if (!IsShuttingDown()) {
+            HandleControlMessages();
+        }
     }
 
     return 0;
diff --git a/init/init.h b/init/init.h
index 27f64e2..4f686cb 100644
--- a/init/init.h
+++ b/init/init.h
@@ -42,6 +42,8 @@
 void PropertyChanged(const std::string& name, const std::string& value);
 bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);
 
+void DebugRebootLogging();
+
 int SecondStageMain(int argc, char** argv);
 
 }  // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 8206522..1e4e127 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -125,7 +125,7 @@
 
 void StopSendingMessages() {
     auto lock = std::lock_guard{accept_messages_lock};
-    accept_messages = true;
+    accept_messages = false;
 }
 
 bool CanReadProperty(const std::string& source_context, const std::string& name) {
@@ -489,6 +489,7 @@
         }
         LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                   << process_log_string;
+        DebugRebootLogging();
     }
 
     // If a process other than init is writing a non-empty value, it means that process is