diff --git a/init/action.cpp b/init/action.cpp index 69003917b..4a3ab4816 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -326,6 +326,13 @@ void ActionManager::DumpState() const { } } +void ActionManager::ClearQueue() { + // We are shutting down so don't claim the oneshot builtin actions back + current_executing_actions_ = {}; + event_queue_ = {}; + current_command_ = 0; +} + bool ActionParser::ParseSection(std::vector&& args, const std::string& filename, int line, std::string* err) { std::vector triggers(args.begin() + 1, args.end()); diff --git a/init/action.h b/init/action.h index 5cb50a741..ad15f3f42 100644 --- a/init/action.h +++ b/init/action.h @@ -104,6 +104,7 @@ class ActionManager { void ExecuteOneCommand(); bool HasMoreCommands() const; void DumpState() const; + void ClearQueue(); private: ActionManager(ActionManager const&) = delete; diff --git a/init/builtins.cpp b/init/builtins.cpp index 00ffbc3d2..75a8f1972 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -119,7 +119,7 @@ static int reboot_into_recovery(const std::vector& options) { LOG(ERROR) << "failed to set bootloader message: " << err; return -1; } - DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false); + property_set("sys.powerctl", "reboot,recovery"); return 0; } diff --git a/init/init.cpp b/init/init.cpp index 501298cd5..c4a3eadf3 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -94,6 +94,7 @@ static int epoll_fd = -1; static std::unique_ptr waiting_for_prop(nullptr); static std::string wait_prop_name; static std::string wait_prop_value; +static bool shutting_down; void DumpState() { ServiceManager::GetInstance().DumpState(); @@ -158,21 +159,31 @@ bool start_waiting_for_property(const char *name, const char *value) return true; } +void ResetWaitForProp() { + wait_prop_name.clear(); + wait_prop_value.clear(); + waiting_for_prop.reset(); +} + void property_changed(const std::string& name, const std::string& value) { // If the property is sys.powerctl, we bypass the event queue and immediately handle it. // This is to ensure that init will always and immediately shutdown/reboot, regardless of // if there are other pending events to process or if init is waiting on an exec service or // waiting on a property. - if (name == "sys.powerctl") HandlePowerctlMessage(value); + // 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; + } + } if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value); if (waiting_for_prop) { if (wait_prop_name == name && wait_prop_value == value) { - wait_prop_name.clear(); - wait_prop_value.clear(); LOG(INFO) << "Wait for property took " << *waiting_for_prop; - waiting_for_prop.reset(); + ResetWaitForProp(); } } } @@ -1166,7 +1177,7 @@ int main(int argc, char** argv) { am.ExecuteOneCommand(); } if (!(waiting_for_prop || sm.IsWaitingForExec())) { - restart_processes(); + if (!shutting_down) restart_processes(); // If there's a process that needs restarting, wake up in time for that. if (process_needs_restart_at != 0) { diff --git a/init/init.h b/init/init.h index 479b77173..aaab523ee 100644 --- a/init/init.h +++ b/init/init.h @@ -44,6 +44,8 @@ bool start_waiting_for_property(const char *name, const char *value); void DumpState(); +void ResetWaitForProp(); + } // namespace init } // namespace android diff --git a/init/reboot.cpp b/init/reboot.cpp index 34c98a740..ec1ddd6f3 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -49,6 +49,7 @@ #include #include "capabilities.h" +#include "init.h" #include "property_service.h" #include "service.h" @@ -490,6 +491,9 @@ bool HandlePowerctlMessage(const std::string& command) { } } else if (command == "thermal-shutdown") { // no additional parameter allowed cmd = ANDROID_RB_THERMOFF; + // Do not queue "shutdown" trigger since we want to shutdown immediately + DoReboot(cmd, command, reboot_target, run_fsck); + return true; } else { command_invalid = true; } @@ -498,7 +502,26 @@ bool HandlePowerctlMessage(const std::string& command) { return false; } - DoReboot(cmd, command, reboot_target, run_fsck); + LOG(INFO) << "Clear action queue and start shutdown trigger"; + ActionManager::GetInstance().ClearQueue(); + // Queue shutdown trigger first + ActionManager::GetInstance().QueueEventTrigger("shutdown"); + // Queue built-in shutdown_done + auto shutdown_handler = [cmd, command, reboot_target, + run_fsck](const std::vector&) { + DoReboot(cmd, command, reboot_target, run_fsck); + return 0; + }; + ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done"); + + // Skip wait for prop if it is in progress + ResetWaitForProp(); + + // Skip wait for exec if it is in progress + if (ServiceManager::GetInstance().IsWaitingForExec()) { + ServiceManager::GetInstance().ClearExecWait(); + } + return true; } diff --git a/init/service.cpp b/init/service.cpp index cb854f54b..a2c4b50f1 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -1152,6 +1152,15 @@ void ServiceManager::ReapAnyOutstandingChildren() { } } +void ServiceManager::ClearExecWait() { + // Clear EXEC flag if there is one pending + // And clear the wait flag + for (const auto& s : services_) { + s->UnSetExec(); + } + exec_waiter_.reset(); +} + bool ServiceParser::ParseSection(std::vector&& args, const std::string& filename, int line, std::string* err) { if (args.size() < 3) { diff --git a/init/service.h b/init/service.h index 3c7dc742c..0cc16839b 100644 --- a/init/service.h +++ b/init/service.h @@ -89,6 +89,7 @@ class Service { void DumpState() const; void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; } bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; } + void UnSetExec() { flags_ &= ~SVC_EXEC; } const std::string& name() const { return name_; } const std::set& classnames() const { return classnames_; } @@ -186,7 +187,7 @@ class Service { }; class ServiceManager { -public: + public: static ServiceManager& GetInstance(); // Exposed for testing @@ -208,8 +209,9 @@ public: void ReapAnyOutstandingChildren(); void RemoveService(const Service& svc); void DumpState() const; + void ClearExecWait(); -private: + private: // Cleans up a child process that exited. // Returns true iff a children was cleaned up. bool ReapOneProcess(); diff --git a/init/util.cpp b/init/util.cpp index 4b1894f8c..479179e41 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -370,6 +370,7 @@ bool expand_props(const std::string& src, std::string* dst) { void panic() { LOG(ERROR) << "panic: rebooting to bootloader"; + // Do not queue "shutdown" trigger since we want to shutdown immediately DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false); } diff --git a/rootdir/init.rc b/rootdir/init.rc index fff3c89de..2069fc61a 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -678,6 +678,10 @@ on property:security.perf_harden=0 on property:security.perf_harden=1 write /proc/sys/kernel/perf_event_paranoid 3 +# on shutdown +# In device's init.rc, this trigger can be used to do device-specific actions +# before shutdown. e.g disable watchdog and mask error handling + ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd