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,


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/ 
[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/ 
[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/ 
[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(; final int resourceId = mRebootSafeMode ? : (longPressBehavior == 2 ? :; 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 ? : .setMessage(resourceId) .setPositiveButton(, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { beginShutdownSequence(context); } }) .setNegativeButton(, null) .create(); closer.dialog = sConfirmDialog; sConfirmDialog.setOnDismissListener(closer); sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);; } 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/ 
[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("", 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