Understanding Android Shutdown Sequence: PowerOff Source Code Explained in Detail

This post tries to describe what really happens when we power off the Android device using poweroff key.

Lets take the logcat, while we try to turn off the android device using long press of power off key ..

$ adb shell logcat | grep ShutdownThread
ShutdownThread: Notifying thread to start shutdown longPressBehavior=1
ShutdownThread: mLeastShutDownAnimationTime=1500
ShutdownThread: isSupportUserSwitchPoweroffAniFeature = false
ShutdownThread: isUserEnabledShutdownAni = false
ShutdownThread: isSupportPoweroffAni =true
ShutdownThread: Shutdown animation type: OneImege
ShutdownThread: Get CDA fail!!
ShutdownThread: getCDAshutAniFromIdFinal path=system/product/CDA/600WW/power-off-image/shutanimation.zip
ShutdownThread: CDAshutAni=system/product/CDA/600WW/power-off-image/shutanimation.zip
ShutdownThread: shutdownAnim.exists()=false
ShutdownThread: checkAnimationFileExist=false
ShutdownThread: Attempting to use SysUI shutdown UI
ShutdownThread: SysUI handling shutdown UI
PowerManagerService:   PARTIAL_WAKE_LOCK              'ShutdownThread-cpu' ACQ=0 (uid=1000 pid=1559)
PowerManagerService:   FULL_WAKE_LOCK                 'ShutdownThread-screen' (uid=1000 pid=1559)
ShutdownThread: shutdownTimingStart start timming = 25000
ShutdownThread: ===>>> turn off screen
ShutdownThread: WindowManager persist.sys.PreventPowerkey-1=off
ShutdownThread: I set prevent power key for shutdown
ShutdownThread: WindowManager persist.sys.PreventPowerkey-2=on
ShutdownThread: Sending shutdown broadcast...
ShutdownThread: Shutting down activity manager...
ShutdownThread: Shutting down package manager...
ShutdownThread: Turning off cellular radios...
ShutdownThread: Waiting for Radio...
ShutdownThread: Radio turned off.
ShutdownThread: Radio shutdown complete.
ShutdownThread: Shutdown critical subsyslist is :modem : 
ShutdownThread: Waiting for a maximum of 10000ms
ShutdownThread: Vendor subsystem(s) shutdown successful
ShutdownThread: mSupportShutDownVibrate=false
ShutdownThread: Performing low-level shutdown...
ShutdownThread: beginAnimationTime=0

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

$ tree frameworks/base/services/core/java/com/android/server/power/
├── AttentionDetector.java
├── batterysaver
│ ├── BatterySaverController.java
│ ├── BatterySaverLocationPlugin.java
│├── BatterySaverPolicy.java
│ ├── BatterySaverStateMachine.java
│ ├── BatterySavingStats.java
│ ├── CpuFrequencies.java
│ ├── FileUpdater.java
│ └── OWNERS
├── Notifier.java
├── OWNERS
├── PowerManagerService.java
├── PowerManagerShellCommand.java
├── ScreenOnBlocker.java
├── ShutdownThread.java
├── SuspendBlocker.java
├── ThermalManagerService.java
└── WirelessChargerDetector.java

The string for power off dialog comes from frameworks/base/core/res/res/values/strings.xml as,

<!-- Power Dialog -->
    <!-- Button to turn off the phone, within the Phone Options dialog -->
    <string name="power_off" Power off>
[bash] [/bash]

Now, we will explore the JAVA source code which starts the shutdown as,

frameworks/base/core/java/android/os/PowerManager.java

/**
     * Turn off the device.
     *
     * @param confirm If true, shows a shutdown confirmation dialog.
     * @param reason code to pass to android_reboot() (e.g. &quot;userrequested&quot;), 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();
        }
    }

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

 /**
         * 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);
            }
        }
 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(&quot;Too early to call shutdown() or reboot()&quot;);
            }
        }

        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) {
                    }
                }
            }
        }
    }

This calls the ShutdownThread.shutdown() function as,

frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

 /**
     * 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. &quot;userrequested&quot;), 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, &quot;Request to shutdown already running, returning.&quot;);
                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, &quot;Notifying thread to start shutdown longPressBehavior=&quot; + 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);
        }
    }

As you can now see, above code prints the log message

ShutdownThread: Notifying thread to start shutdown longPressBehavior=1

in logcat as we seen earlier at the beginning of the post. Now we will continue with the actual function getting called next is beginShutdownSequence

 private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, &quot;Shutdown sequence already running, returning.&quot;);
                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 + &quot;-cpu&quot;);
            sInstance.mCpuWakeLock.setReferenceCounted(false);
            sInstance.mCpuWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, &quot;No permission to acquire wake lock&quot;, 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 + &quot;-screen&quot;);
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, &quot;No permission to acquire wake lock&quot;, 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();
    }
/**
     * 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(&quot;SystemServerShutdown&quot;);
        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 ? &quot;1&quot; : &quot;0&quot;) + (mReason != null ? mReason : &quot;&quot;);
            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, &quot;1&quot;);
        }

        metricStarted(METRIC_SEND_BROADCAST);
        shutdownTimingLog.traceBegin(&quot;SendShutdownBroadcast&quot;);
        Log.i(TAG, &quot;Sending shutdown broadcast...&quot;);

        // 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 &amp;lt;= 0) {
                    Log.w(TAG, &quot;Shutdown broadcast timed out&quot;);
                    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, &quot;Shutting down activity manager...&quot;);
        shutdownTimingLog.traceBegin(&quot;ShutdownActivityManager&quot;);
        metricStarted(METRIC_AM);

        final IActivityManager am =
                IActivityManager.Stub.asInterface(ServiceManager.checkService(&quot;activity&quot;));
        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, &quot;Shutting down package manager...&quot;);
        shutdownTimingLog.traceBegin(&quot;ShutdownPackageManager&quot;);
        metricStarted(METRIC_PM);

        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService(&quot;package&quot;);
        if (pm != null) {
            pm.shutdown();
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd(); // ShutdownPackageManager
        metricEnded(METRIC_PM);

        // Shutdown radios.
        shutdownTimingLog.traceBegin(&quot;ShutdownRadios&quot;);
        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);
    }
/**
     * 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, &quot;Rebooting, reason: &quot; + reason);
            PowerManagerService.lowLevelReboot(reason);
            Log.e(TAG, &quot;Reboot failed, will attempt shutdown instead&quot;);
            reason = null;
        } else if (SHUTDOWN_VIBRATE_MS &amp;gt; 0 &amp;amp;&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, &quot;Failed to vibrate during shutdown.&quot;, 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, &quot;Performing low-level shutdown...&quot;);
        PowerManagerService.lowLevelShutdown(reason);
    }

From now, actual low level shutdown gets started, and it calls above code

// Shutdown power
    Log.i(TAG, "Performing low-level shutdown...");
    PowerManagerService.lowLevelShutdown(reason);

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

/**
     * 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. &quot;userrequested&quot;), or null.
     */
    public static void lowLevelShutdown(String reason) {
        if (reason == null) {
            reason = &quot;&quot;;
        }
        SystemProperties.set(&quot;sys.powerctl&quot;, &quot;shutdown,&quot; + reason);
    }

Actual Action Taken by Android when it calls “setprop sys.powerctl shutdown” as can be seen above..

This low level property set code reaches to system/core/libcutils/android_reboot.c and calls as,

int android_reboot(int cmd, int flags UNUSED, const char *arg)
{
        case ANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;
}

bionic/libc/bionic/reboot.cpp

int reboot(int mode) {
  return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
}

bionic/libc/arch-arm/syscalls/__reboot.S

ENTRY(__reboot)
    mov     ip, r7
    ldr     r7, =__NR_reboot
    swi     #0
    mov     r7, ip
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno_internal
END(__reboot)

This reboot system call is declared at bionic/libc/kernel/uapi/asm-generic/unistd.h

#define __NR_reboot 142

Below code is from kernel source code

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

[bash][/bash]

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

[/bash] [bash] SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { case LINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); do_exit(0); break; }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]

arch/arm/kernel/process.c

void machine_power_off(void)
{
local_irq_disable();
smp_send_stop();
if (pm_power_off)
            pm_power_off();
}

same file has definition of pm_power_off as,

void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);

Leave a Comment