Android Power OFF Sequence / How Android Shuts Down works ?

This post tries to brief about how android shutdown / poweroff sequence is with respect to the android open source code. We tried to take logs for reference to identify source code and below is the related logcat messages,

[bash] ShutdownThread: Notifying thread to start shutdown longPressBehavior=1 ShutdownThread: Sending shutdown broadcast… SyncManager: Writing sync state before shutdown… EntropyMixer: Writing entropy… MyPppoeService: onReceive :android.intent.action.ACTION_SHUTDOWN ShutdownThread: Shutting down activity manager… AppOps : Writing app ops before shutdown… UsageStatsService: User[0] Flushing usage stats to disk BatteryStats: Writing battery stats before shutdown… ProcessStatsService: Writing process stats before shutdown… ProcessStatsService: Prepared write state in 3ms ShutdownThread: Shutting down package manager… ShutdownThread: Disabling Bluetooth… ShutdownThread: not disable Bluetooth for BT wakeup ShutdownThread: Waiting for NFC, Bluetooth and Radio… ShutdownThread: Bluetooth turned off. ShutdownThread: NFC, Radio and Bluetooth shutdown complete. ShutdownThread: Shutting down MountService MountService: Shutting down VoldConnector: SND -> {7 volume shutdown} VoldConnector: RCV <- {651 emulated 5} VoldConnector: RCV <- {651 emulated 0} VoldConnector: RCV <- {651 emulated 8} VoldConnector: RCV <- {659 emulated} VoldConnector: RCV <- {200 7 Command succeeded} VoldConnector: NDC Command {7 volume shutdown} took too long (567ms) ShutdownThread: Result code 0 from MountService.shutdown MountService: Volume emulated broadcasting ejecting to UserHandle{0} MountService: Volume emulated broadcasting unmounted to UserHandle{0} MountService: Volume emulated broadcasting bad_removal to UserHandle{0} ProductTestReciver: onReceive…action:android.intent.action.MEDIA_UNMOUNTED ProductTestService: handleMessage { when=-1ms what=4098 target=net.amtc.service.stb.producttest.ProductTestService$1 } MediaScannerReceiver: action: android.intent.action.MEDIA_UNMOUNTED path: /storage/emulated/0 ExternalStorage: After updating volumes, found 0 active roots ShutdownThread: Performing low-level shutdown..- [/bash]

Above logs shows that it ShutdownThread starts the actual shutting down process, The source code reference for the ShutdownThread is as below,

frameworks/base/services/core/java/com/android/server/power/
├── Notifier.java
├── PowerManagerService.java
├── ScreenOnBlocker.java
├── ShutdownThread.java
├── SuspendBlocker.java
└── WirelessChargerDetector.java

The power off button is mapped to string as below,

vim frameworks/base/core/res/res/values/strings.xml 
[bash] <!– Button to turn off the phone, within the Phone Options dialog –> <string name="power_off"&gt;Power off&lt;/string> [/bash]
vim frameworks/base/core/java/android/os/PowerManager.java 
[bash] /** * Turn off the device. * * @param confirm If true, shows a shutdown confirmation dialog. * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. * @param wait If true, this call waits for the shutdown to complete and does not return. * * @hide */ public void shutdown(boolean confirm, String reason, boolean wait) { try { mService.shutdown(confirm, reason, wait); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } [/bash]
$vim frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java 
[bash] /** * Shuts down the device. * * @param confirm If true, shows a shutdown confirmation dialog. * @param wait If true, this call waits for the shutdown to complete and does not return. */ @Override // Binder call public void shutdown(boolean confirm, String reason, boolean wait) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); final long ident = Binder.clearCallingIdentity(); try { shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait); } finally { Binder.restoreCallingIdentity(ident); } } [/bash] [bash] private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm, final String reason, boolean wait) { if (mHandler == null || !mSystemReady) { if (RescueParty.isAttemptingFactoryReset()) { // If we’re stuck in a really low-level reboot loop, and a // rescue party is trying to prompt the user for a factory data // reset, we must GET TO DA CHOPPA! PowerManagerService.lowLevelReboot(reason); } else { throw new IllegalStateException("Too early to call shutdown() or reboot()"); } } Runnable runnable = new Runnable() { @Override public void run() { synchronized (this) { if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) { ShutdownThread.rebootSafeMode(getUiContext(), confirm); } else if (haltMode == HALT_MODE_REBOOT) { ShutdownThread.reboot(getUiContext(), reason, confirm); } else { ShutdownThread.shutdown(getUiContext(), reason, confirm); } } } }; // ShutdownThread must run on a looper capable of displaying the UI. Message msg = Message.obtain(UiThread.getHandler(), runnable); msg.setAsynchronous(true); UiThread.getHandler().sendMessage(msg); // PowerManager.reboot() is documented not to return so just wait for the inevitable. if (wait) { synchronized (runnable) { while (true) { try { runnable.wait(); } catch (InterruptedException e) { } } } } } [/bash]

This calls the ShutdownThread.shutdown() function as,

$ vim frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java 
[bash] /** * Request a clean shutdown, waiting for subsystems to clean up their * state etc. Must be called from a Looper thread in which its UI * is shown. * * @param context Context used to display the shutdown progress dialog. This must be a context * suitable for displaying UI (aka Themable). * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. * @param confirm true if user confirmation is needed before shutting down. */ public static void shutdown(final Context context, String reason, boolean confirm) { mReboot = false; mRebootSafeMode = false; mReason = reason; shutdownInner(context, confirm); } [/bash] [bash] private static void shutdownInner(final Context context, boolean confirm) { // ShutdownThread is called from many places, so best to verify here that the context passed // in is themed. context.assertRuntimeOverlayThemable(); // ensure that only one thread is trying to power down. // any additional calls are just returned synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Request to shutdown already running, returning."); return; } } final int longPressBehavior = context.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); final int resourceId = mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_confirm : (longPressBehavior == 2 ? com.android.internal.R.string.shutdown_confirm_question : com.android.internal.R.string.shutdown_confirm); Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); if (confirm) { final CloseDialogReceiver closer = new CloseDialogReceiver(context); if (sConfirmDialog != null) { sConfirmDialog.dismiss(); } sConfirmDialog = new AlertDialog.Builder(context) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) .setMessage(resourceId) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { beginShutdownSequence(context); } }) .setNegativeButton(com.android.internal.R.string.no, null) .create(); closer.dialog = sConfirmDialog; sConfirmDialog.setOnDismissListener(closer); sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); sConfirmDialog.show(); } else { beginShutdownSequence(context); } } [/bash]

Above function at the end starts the shutdown sequence by calling function beginShutdownSequence(context),

[bash] // static instance of this thread private static final ShutdownThread sInstance = new ShutdownThread(); private static void beginShutdownSequence(Context context) { synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Shutdown sequence already running, returning."); return; } sIsStarted = true; } sInstance.mProgressDialog = showShutdownDialog(context); sInstance.mContext = context; sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); // make sure we never fall asleep again sInstance.mCpuWakeLock = null; try { sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); sInstance.mCpuWakeLock.setReferenceCounted(false); sInstance.mCpuWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mCpuWakeLock = null; } // also make sure the screen stays on for better user experience sInstance.mScreenWakeLock = null; if (sInstance.mPowerManager.isScreenOn()) { try { sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); sInstance.mScreenWakeLock.setReferenceCounted(false); sInstance.mScreenWakeLock.acquire(); } catch (SecurityException e) { Log.w(TAG, "No permission to acquire wake lock", e); sInstance.mScreenWakeLock = null; } } if (SecurityLog.isLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN); } // start the thread that initiates shutdown sInstance.mHandler = new Handler() { }; sInstance.start(); } [/bash]

The above call “sInstance.start” starts the shutdown thread,

[bash] /** * Makes sure we handle the shutdown gracefully. * Shuts off power regardless of radio state if the allotted time has passed. */ public void run() { TimingsTraceLog shutdownTimingLog = newTimingsLog(); shutdownTimingLog.traceBegin("SystemServerShutdown"); metricShutdownStart(); metricStarted(METRIC_SYSTEM_SERVER); BroadcastReceiver br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // We don’t allow apps to cancel this, so ignore the result. actionDone(); } }; /* * Write a system property in case the system_server reboots before we * get to the actual hardware restart. If that happens, we’ll retry at * the beginning of the SystemServer startup. */ { String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); } /* * If we are rebooting into safe mode, write a system property * indicating so. */ if (mRebootSafeMode) { SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); } metricStarted(METRIC_SEND_BROADCAST); shutdownTimingLog.traceBegin("SendShutdownBroadcast"); Log.i(TAG, "Sending shutdown broadcast…"); // First send the high-level shut down broadcast. mActionDone = false; Intent intent = new Intent(Intent.ACTION_SHUTDOWN); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null); final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; synchronized (mActionDoneSync) { while (!mActionDone) { long delay = endTime – SystemClock.elapsedRealtime(); if (delay &lt;= 0) { Log.w(TAG, "Shutdown broadcast timed out"); break; } else if (mRebootHasProgressBar) { int status = (int)((MAX_BROADCAST_TIME – delay) * 1.0 * BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME); sInstance.setRebootProgress(status, null); } try { mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS)); } catch (InterruptedException e) { } } } if (mRebootHasProgressBar) { sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); } shutdownTimingLog.traceEnd(); // SendShutdownBroadcast metricEnded(METRIC_SEND_BROADCAST); Log.i(TAG, "Shutting down activity manager…"); shutdownTimingLog.traceBegin("ShutdownActivityManager"); metricStarted(METRIC_AM); final IActivityManager am = IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); if (am != null) { try { am.shutdown(MAX_BROADCAST_TIME); } catch (RemoteException e) { } } if (mRebootHasProgressBar) { sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); } shutdownTimingLog.traceEnd();// ShutdownActivityManager metricEnded(METRIC_AM); Log.i(TAG, "Shutting down package manager…"); shutdownTimingLog.traceBegin("ShutdownPackageManager"); metricStarted(METRIC_PM); final PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package"); if (pm != null) { pm.shutdown(); } if (mRebootHasProgressBar) { sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); } shutdownTimingLog.traceEnd(); // ShutdownPackageManager metricEnded(METRIC_PM); // Shutdown radios. shutdownTimingLog.traceBegin("ShutdownRadios"); metricStarted(METRIC_RADIOS); shutdownRadios(MAX_RADIO_WAIT_TIME); if (mRebootHasProgressBar) { sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); } shutdownTimingLog.traceEnd(); // ShutdownRadios metricEnded(METRIC_RADIOS); if (mRebootHasProgressBar) { sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); // If it’s to reboot to install an update and uncrypt hasn’t been // done yet, trigger it now. uncrypt(); } shutdownTimingLog.traceEnd(); // SystemServerShutdown metricEnded(METRIC_SYSTEM_SERVER); saveMetrics(mReboot, mReason); // Remaining work will be done by init, including vold shutdown rebootOrShutdown(mContext, mReboot, mReason); } [/bash]

As we can see the last function called is “rebootOrShutdown” with a message as “Remaining work will be done by init, including vold shutdown”

[bash] /** * Do not call this directly. Use {@link #reboot(Context, String, boolean)} * or {@link #shutdown(Context, String, boolean)} instead. * * @param context Context used to vibrate or null without vibration * @param reboot true to reboot or false to shutdown * @param reason reason for reboot/shutdown */ public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { if (reboot) { Log.i(TAG, "Rebooting, reason: " + reason); PowerManagerService.lowLevelReboot(reason); Log.e(TAG, "Reboot failed, will attempt shutdown instead"); reason = null; } else if (SHUTDOWN_VIBRATE_MS &gt; 0 &amp;&amp; context != null) { // vibrate before shutting down Vibrator vibrator = new SystemVibrator(context); try { vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES); } catch (Exception e) { // Failure to vibrate shouldn’t interrupt shutdown. Just log it. Log.w(TAG, "Failed to vibrate during shutdown.", e); } // vibrator is asynchronous so we need to wait to avoid shutting down too soon. try { Thread.sleep(SHUTDOWN_VIBRATE_MS); } catch (InterruptedException unused) { } } // Shutdown power Log.i(TAG, "Performing low-level shutdown…"); PowerManagerService.lowLevelShutdown(reason); } [/bash]

Above function call to “PowerManagerService.lowLevelShutdown(reason)” starts the shutdown procedure at very low / system level as,

$ vim frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java 
[bash] /** * Low-level function turn the device off immediately, without trying * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. * * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. */ public static void lowLevelShutdown(String reason) { if (reason == null) { reason = ""; } SystemProperties.set("sys.powerctl", "shutdown," + reason); } [/bash]

Actual Action Taken by Android when above “SystemProperties.set(“sys.powerctl”, “shutdown,” + reason)” is called is same as when we type command “setprop sys.powerctl shutdown”

$ vim system/core/init/init.cpp 
[bash] 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. // In non-thermal-shutdown case, ‘shutdown’ trigger will be fired to let device specific // commands to be executed. if (name == "sys.powerctl") { // 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); if (waiting_for_prop) { if (wait_prop_name == name &amp;&amp; wait_prop_value == value) { LOG(INFO) &lt;&lt; "Wait for property ‘" &lt;&lt; wait_prop_name &lt;&lt; "=" &lt;&lt; wait_prop_value &lt;&lt; "’ took " &lt;&lt; *waiting_for_prop; ResetWaitForProp(); } } } [/bash] [bash] while (true) { // By default, sleep until something happens. auto epoll_timeout = std::optional<std::chrono::milliseconds>{}; if (do_shutdown && !shutting_down) { do_shutdown = false; if (HandlePowerctlMessage(shutdown_command)) { shutting_down = true; } } [/bash]
$ vim system/core/init/reboot.cpp 
[bash] bool HandlePowerctlMessage(const std::string& command) { if (cmd_params.size() > 3) { command_invalid = true; } else if (cmd_params[0] == "shutdown") { cmd = ANDROID_RB_POWEROFF; if (cmd_params.size() == 2) { if (cmd_params[1] == "userrequested") { // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED. // Run fsck once the file system is remounted in read-only mode. run_fsck = true; } else if (cmd_params[1] == "thermal") { // Turn off sources of heat immediately. TurnOffBacklight(); // run_fsck is false to avoid delay cmd = ANDROID_RB_THERMOFF; } } } LOG(INFO) &lt;&lt; "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 BuiltinArguments&amp;) { DoReboot(cmd, command, reboot_target, run_fsck); return Success(); }; ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done"); } [/bash]

The above function calls “DoReboot”

[bash] //* Reboot / shutdown the system. // cmd ANDROID_RB_* as defined in android_reboot.h // reason Reason string like "reboot", "shutdown,userrequested" // rebootTarget Reboot target string like "bootloader". Otherwise, it should be an // empty string. // runFsck Whether to run fsck after umount is done. // static void DoReboot(unsigned int cmd, const std::string&amp; reason, const std::string&amp; rebootTarget, bool runFsck) { Timer t; LOG(INFO) &lt;&lt; "Reboot start, reason: " &lt;&lt; reason &lt;&lt; ", rebootTarget: " &lt;&lt; rebootTarget; // Ensure last reboot reason is reduced to canonical // alias reported in bootloader or system boot reason. size_t skip = 0; std::vector&lt;std::string&gt; reasons = Split(reason, ","); if (reasons.size() &gt;= 2 &amp;&amp; reasons[0] == "reboot" &amp;&amp; (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" || reasons[1] == "hard" || reasons[1] == "warm")) { skip = strlen("reboot,"); } property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip); sync(); bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF; auto shutdown_timeout = 0ms; if (!SHUTDOWN_ZERO_TIMEOUT) { constexpr unsigned int shutdown_timeout_default = 6; constexpr unsigned int max_thermal_shutdown_timeout = 3; auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default); if (is_thermal_shutdown &amp;&amp; shutdown_timeout_final &gt; max_thermal_shutdown_timeout) { shutdown_timeout_final = max_thermal_shutdown_timeout; } shutdown_timeout = std::chrono::seconds(shutdown_timeout_final); } LOG(INFO) &lt;&lt; "Shutdown timeout: " &lt;&lt; shutdown_timeout.count() &lt;&lt; " ms"; // keep debugging tools until non critical ones are all gone. const std::set&lt;std::string&gt; kill_after_apps{"tombstoned", "logd", "adbd"}; // watchdogd is a vendor specific component but should be alive to complete shutdown safely. const std::set&lt;std::string&gt; to_starts{"watchdogd"}; for (const auto&amp; s : ServiceList::GetInstance()) { if (kill_after_apps.count(s-&gt;name())) { s-&gt;SetShutdownCritical(); } else if (to_starts.count(s-&gt;name())) { if (auto result = s-&gt;Start(); !result) { LOG(ERROR) &lt;&lt; "Could not start shutdown ‘to_start’ service ‘" &lt;&lt; s-&gt;name() &lt;&lt; "’: " &lt;&lt; result.error(); } s-&gt;SetShutdownCritical(); } else if (s-&gt;IsShutdownCritical()) { // Start shutdown critical service if not started. if (auto result = s-&gt;Start(); !result) { LOG(ERROR) &lt;&lt; "Could not start shutdown critical service ‘" &lt;&lt; s-&gt;name() &lt;&lt; "’: " &lt;&lt; result.error(); } } } // remaining operations (specifically fsck) may take a substantial duration if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) { TurnOffBacklight(); } Service* bootAnim = ServiceList::GetInstance().FindService("bootanim"); Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger"); if (bootAnim != nullptr &amp;&amp; surfaceFlinger != nullptr &amp;&amp; surfaceFlinger-&gt;IsRunning()) { bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false); if (do_shutdown_animation) { property_set("service.bootanim.exit", "0"); // Could be in the middle of animation. Stop and start so that it can pick // up the right mode. bootAnim-&gt;Stop(); } for (const auto&amp; service : ServiceList::GetInstance()) { if (service-&gt;classnames().count("animation") == 0) { continue; } // start all animation classes if stopped. if (do_shutdown_animation) { service-&gt;Start().IgnoreError(); } service-&gt;SetShutdownCritical(); // will not check animation class separately } if (do_shutdown_animation) { bootAnim-&gt;Start().IgnoreError(); surfaceFlinger-&gt;SetShutdownCritical(); bootAnim-&gt;SetShutdownCritical(); } } // optional shutdown step // 1. terminate all services except shutdown critical ones. wait for delay to finish if (shutdown_timeout &gt; 0ms) { LOG(INFO) &lt;&lt; "terminating init services"; // Ask all services to terminate except shutdown critical ones. for (const auto&amp; s : ServiceList::GetInstance().services_in_shutdown_order()) { if (!s-&gt;IsShutdownCritical()) s-&gt;Terminate(); } int service_count = 0; // Only wait up to half of timeout here auto termination_wait_timeout = shutdown_timeout / 2; while (t.duration() &lt; termination_wait_timeout) { ReapAnyOutstandingChildren(); service_count = 0; for (const auto&amp; s : ServiceList::GetInstance()) { // Count the number of services running except shutdown critical. // Exclude the console as it will ignore the SIGTERM signal // and not exit. // Note: SVC_CONSOLE actually means "requires console" but // it is only used by the shell. if (!s-&gt;IsShutdownCritical() &amp;&amp; s-&gt;pid() != 0 &amp;&amp; (s-&gt;flags() &amp; SVC_CONSOLE) == 0) { service_count++; } } if (service_count == 0) { // All terminable services terminated. We can exit early. break; } // Wait a bit before recounting the number or running services. std::this_thread::sleep_for(50ms); } LOG(INFO) &lt;&lt; "Terminating running services took " &lt;&lt; t &lt;&lt; " with remaining services:" &lt;&lt; service_count; } // minimum safety steps before restarting // 2. kill all services except ones that are necessary for the shutdown sequence. for (const auto&amp; s : ServiceList::GetInstance().services_in_shutdown_order()) { if (!s-&gt;IsShutdownCritical()) s-&gt;Stop(); } SubcontextTerminate(); ReapAnyOutstandingChildren(); // 3. send volume shutdown to vold Service* voldService = ServiceList::GetInstance().FindService("vold"); if (voldService != nullptr &amp;&amp; voldService-&gt;IsRunning()) { ShutdownVold(); voldService-&gt;Stop(); } else { LOG(INFO) &lt;&lt; "vold not running, skipping vold shutdown"; } // logcat stopped here for (const auto&amp; s : ServiceList::GetInstance().services_in_shutdown_order()) { if (kill_after_apps.count(s-&gt;name())) s-&gt;Stop(); } // 4. sync, try umount, and optionally run fsck for user shutdown { Timer sync_timer; LOG(INFO) &lt;&lt; "sync() before umount…"; sync(); LOG(INFO) &lt;&lt; "sync() before umount took" &lt;&lt; sync_timer; } // 5. drop caches and disable zram backing device, if exist KillZramBackingDevice(); UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout – t.duration()); // Follow what linux shutdown is doing: one more sync with little bit delay { Timer sync_timer; LOG(INFO) &lt;&lt; "sync() after umount…"; sync(); LOG(INFO) &lt;&lt; "sync() after umount took" &lt;&lt; sync_timer; } if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms); LogShutdownTime(stat, &amp;t); // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it. RebootSystem(cmd, rebootTarget); abort(); } [/bash]
$ vim system/core/init/reboot_utils.cpp 
[bash] void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string&amp; rebootTarget) { LOG(INFO) &lt;&lt; "Reboot ending, jumping to kernel"; if (!IsRebootCapable()) { // On systems where init does not have the capability of rebooting the // device, just exit cleanly. exit(0); } switch (cmd) { case ANDROID_RB_POWEROFF: reboot(RB_POWER_OFF); break; case ANDROID_RB_RESTART2: syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str()); break; case ANDROID_RB_THERMOFF: reboot(RB_POWER_OFF); break; } // In normal case, reboot should not return. PLOG(ERROR) &lt;&lt; "reboot call returned"; abort(); } [/bash]
$ vim bionic/libc/bionic/reboot.cpp 
[bash] #include <unistd.h> #include <sys/reboot.h> extern "C" int __reboot(int, int, int, void*); int reboot(int mode) { return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, nullptr); } [/bash]
$ vim bionic/libc/arch-arm/syscalls/__reboot.S 
[bash] #include <private/bionic_asm.h> ENTRY(__reboot) mov ip, r7 .cfi_register r7, ip ldr r7, =__NR_reboot swi #0 mov r7, ip .cfi_restore r7 cmn r0, #(MAX_ERRNO + 1) bxls lr neg r0, r0 b __set_errno_internal END(__reboot) [/bash]

As we can see, reboot is a 142 number system call,

$ vim bionic/libc/kernel/uapi/asm-generic/unistd.h 
[bash]#define __NR_reboot 142 [/bash]

Below code is from kernel source code,

[bash] common/include/uapi/asm-generic/unistd.h:#define __NR_reboot 142 common/include/uapi/asm-generic/unistd.h:__SYSCALL(__NR_reboot, sys_reboot) [/bash]

This system call is defined in kernel_source/kernel/reboot.c as below,

[bash] /* * Reboot system call: for obvious reasons only root may call it, * and even root needs to set up some magic numbers in the registers * so that some mistake won’t make this reboot the whole machine. * You can also set the meaning of the ctrl-alt-del-key here. * * reboot doesn’t sync: do that yourself before calling this. */ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { switch (cmd) { case LINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); do_exit(0); break; } } <!– /wp:html –> <!– wp:html –> [bash] /** * kernel_power_off – power_off the system * * Shutdown everything and perform a clean system power_off. */ void kernel_power_off(void) { kernel_shutdown_prepare(SYSTEM_POWER_OFF); if (pm_power_off_prepare) pm_power_off_prepare(); migrate_to_reboot_cpu(); syscore_shutdown(); pr_emerg("Power down\n"); kmsg_dump(KMSG_DUMP_POWEROFF); machine_power_off(); } EXPORT_SYMBOL_GPL(kernel_power_off); [/bash]

As we can see in above “machine_power_off” is the function which will actually interface the hardware architecture specific code with the kernel core code, [ For arm64 the code is in arch/arm64/kernel/process. where as for 32 bit ARM platforms its in arch/arm/kernel/reboot.c]

[bash] /* * Power-off simply requires that the secondary CPUs stop performing any * activity (executing tasks, handling interrupts). smp_send_stop() * achieves this. When the system power is turned off, it will take all CPUs * with it. */ void machine_power_off(void) { local_irq_disable(); smp_send_stop(); if (pm_power_off) pm_power_off(); } [/bash]

same file has definition of pm_power_off as,

[bash] /* * Function pointers to optional machine specific functions */ void (*pm_power_off)(void); EXPORT_SYMBOL_GPL(pm_power_off); [/bash]

Now, if we take Qualcomm MSM hardware as an example, this pm_power_off has been initialised in drivers/power/reset/msm-poweroff.c as,

[bash] static void do_msm_poweroff(void) { /* TODO: Add poweroff capability */ } static int msm_restart_probe(struct platform_device *pdev) { pm_power_off = do_msm_poweroff; return 0; } static const struct of_device_id of_msm_restart_match[] = { { .compatible = "qcom,pshold", }, {}, }; MODULE_DEVICE_TABLE(of, of_msm_restart_match); static struct platform_driver msm_restart_driver = { .probe = msm_restart_probe, .driver = { .name = "msm-restart", .of_match_table = of_match_ptr(of_msm_restart_match), }, }; static int __init msm_restart_init(void) { return platform_driver_register(&msm_restart_driver); } device_initcall(msm_restart_init); [/bash]

Leave a Comment