Add a skeleton of userspace reboot

This CL only draws boundaries between userspace and full reboots, and
adds some functionality that will be required for userspace reboot:

* Whenever device is shutting down is now controlled in reboot.cpp,
  since during userspace reboot this state can change.
* Now it's also possible to restart handling of control messages inside
  property service. In case of userspace reboot, init will restart it
  after stopping post-data services.
* New userspace-reboot-requested trigger is added similar to shutdown
  one for full reboot.

Test: adb reboot
Test: adb reboot userspace
Bug: 135984674
Change-Id: Id55a53ba781d2b90ce40449037b6d8d47e72c476
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 30836d2..41965a1 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -50,7 +50,9 @@
 #include <private/android_filesystem_config.h>
 #include <selinux/selinux.h>
 
+#include "action.h"
 #include "action_manager.h"
+#include "builtin_arguments.h"
 #include "init.h"
 #include "property_service.h"
 #include "reboot_utils.h"
@@ -71,6 +73,8 @@
 namespace android {
 namespace init {
 
+static bool shutting_down = false;
+
 // represents umount status during reboot / shutdown.
 enum UmountStat {
     /* umount succeeded. */
@@ -655,12 +659,58 @@
     abort();
 }
 
-bool HandlePowerctlMessage(const std::string& command) {
+static void EnterShutdown() {
+    shutting_down = true;
+    // Skip wait for prop if it is in progress
+    ResetWaitForProp();
+    // Clear EXEC flag if there is one pending
+    for (const auto& s : ServiceList::GetInstance()) {
+        s->UnSetExec();
+    }
+    // We no longer process messages about properties changing coming from property service, so we
+    // need to tell property service to stop sending us these messages, otherwise it'll fill the
+    // buffers and block indefinitely, causing future property sets, including those that init makes
+    // during shutdown in Service::NotifyStateChange() to also block indefinitely.
+    SendStopSendingMessagesMessage();
+}
+
+static void LeaveShutdown() {
+    shutting_down = false;
+    SendStartSendingMessagesMessage();
+}
+
+static void DoUserspaceReboot() {
+    // Triggering userspace-reboot-requested will result in a bunch of set_prop
+    // actions. We should make sure, that all of them are propagated before
+    // proceeding with userspace reboot.
+    // TODO(b/135984674): implement proper synchronization logic.
+    std::this_thread::sleep_for(500ms);
+    EnterShutdown();
+    // TODO(b/135984674): tear down post-data services
+    LeaveShutdown();
+    // TODO(b/135984674): remount userdata
+    ActionManager::GetInstance().QueueEventTrigger("userspace-reboot-resume");
+}
+
+static void HandleUserspaceReboot() {
+    LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
+    auto& am = ActionManager::GetInstance();
+    am.ClearQueue();
+    am.QueueEventTrigger("userspace-reboot-requested");
+    auto handler = [](const BuiltinArguments&) {
+        DoUserspaceReboot();
+        return Result<void>{};
+    };
+    am.QueueBuiltinAction(handler, "userspace-reboot");
+}
+
+void HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
     std::vector<std::string> cmd_params = Split(command, ",");
     std::string reboot_target = "";
     bool run_fsck = false;
     bool command_invalid = false;
+    bool userspace_reboot = false;
 
     if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
@@ -680,6 +730,10 @@
         cmd = ANDROID_RB_RESTART2;
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
+            if (reboot_target == "userspace") {
+                LOG(INFO) << "Userspace reboot requested";
+                userspace_reboot = true;
+            }
             // adb reboot fastboot should boot into bootloader for devices not
             // supporting logical partitions.
             if (reboot_target == "fastboot" &&
@@ -706,7 +760,7 @@
                     strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
                     if (std::string err; !write_bootloader_message(boot, &err)) {
                         LOG(ERROR) << "Failed to set bootloader message: " << err;
-                        return false;
+                        return;
                     }
                 }
             } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
@@ -719,7 +773,7 @@
                 std::string err;
                 if (!write_bootloader_message(options, &err)) {
                     LOG(ERROR) << "Failed to set bootloader message: " << err;
-                    return false;
+                    return;
                 }
                 reboot_target = "recovery";
             }
@@ -734,7 +788,12 @@
     }
     if (command_invalid) {
         LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
-        return false;
+        return;
+    }
+
+    if (userspace_reboot) {
+        HandleUserspaceReboot();
+        return;
     }
 
     LOG(INFO) << "Clear action queue and start shutdown trigger";
@@ -748,21 +807,11 @@
     };
     ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
 
-    // Skip wait for prop if it is in progress
-    ResetWaitForProp();
+    EnterShutdown();
+}
 
-    // Clear EXEC flag if there is one pending
-    for (const auto& s : ServiceList::GetInstance()) {
-        s->UnSetExec();
-    }
-
-    // We no longer process messages about properties changing coming from property service, so we
-    // need to tell property service to stop sending us these messages, otherwise it'll fill the
-    // buffers and block indefinitely, causing future property sets, including those that init makes
-    // during shutdown in Service::NotifyStateChange() to also block indefinitely.
-    SendStopSendingMessagesMessage();
-
-    return true;
+bool IsShuttingDown() {
+    return shutting_down;
 }
 
 }  // namespace init