Merge "init: fix crash when reboot is triggered by a builtin"
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 886c572..5f359cf 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -267,7 +267,7 @@
                 "--prompt_and_wipe_data",
                 "--reason=set_policy_failed:"s + args[1]};
             reboot_into_recovery(options);
-            return Error() << "reboot into recovery failed";
+            return Success();
         }
     }
     return Success();
@@ -472,7 +472,7 @@
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
         reboot_into_recovery(options);
-        return Error() << "reboot_into_recovery() failed";
+        return Success();
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
diff --git a/init/init.cpp b/init/init.cpp
index c3e08fb..817b33e 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -79,6 +79,8 @@
 static std::string wait_prop_name;
 static std::string wait_prop_value;
 static bool shutting_down;
+static std::string shutdown_command;
+static bool do_shutdown = false;
 
 std::vector<std::string> late_import_paths;
 
@@ -157,9 +159,16 @@
     // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
     // commands to be executed.
     if (name == "sys.powerctl") {
-        if (HandlePowerctlMessage(value)) {
-            shutting_down = true;
-        }
+        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
+        // because it modifies the contents of the action queue, which can cause the action queue
+        // to get into a bad state if this function is called from a command being executed by the
+        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
+        // command is run in the main init loop.
+        // TODO: once property service is removed from init, this will never happen from a builtin,
+        // but rather from a callback from the property service socket, in which case this hack can
+        // go away.
+        shutdown_command = value;
+        do_shutdown = true;
     }
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
@@ -622,6 +631,13 @@
         // By default, sleep until something happens.
         int epoll_timeout_ms = -1;
 
+        if (do_shutdown && !shutting_down) {
+            do_shutdown = false;
+            if (HandlePowerctlMessage(shutdown_command)) {
+                shutting_down = true;
+            }
+        }
+
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
         }