- What happened when I long press power button ?
- What is shutdown sequence ?
- How is it different from desktop linux shutdown sequence?
- How to change shutdown menu ?
Many questions pop-up in mind when we think about Android shutdown sequence. Before you read about shutdown sequence I suggest you to read about boot sequence article.
Android is linux based open source operating system, x86 (x86 is a series of computer microprocessor instruction set architectures based on the Intel 8086 CPU.) is most likely system where linux kernel is deployed however all Android devices are running on ARM process (ARM (formerly Advanced RISC Machine, which was formerly Acorn RISC Machine)) except Intel’s Xolo device (http://xolo.in/xolo-x900-features). Xolo comes with Atom 1.6 GHz x86 processor. Android shutdown sequence is different from desktop linux like ubuntu, fedora, etc.
In this article I am going to explain shutdown sequence for Android only. Please refer “Linux Boot and Shutdown Process” for details of desktop linux shutdown process.
Following diagram illustrate shutdown sequence in detail.
Step 1: Long Press Power Button for 500ms.
Step 2: PhoneWindowManager.java identify Power Button long press and call method named “interceptKeyBeforeQueueing”.
Following code display power key snippet from the function.
03 | public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { |
07 | case KeyEvent.KEYCODE_POWER: { |
08 | result &= ~ACTION_PASS_TO_USER; |
10 | if (isScreenOn && !mPowerKeyTriggered |
11 | && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0 ) { |
12 | mPowerKeyTriggered = true ; |
13 | mPowerKeyTime = event.getDownTime(); |
14 | interceptScreenshotChord(); |
16 | ITelephony telephonyService = getTelephonyService(); |
17 | boolean hungUp = false ; |
18 | if (telephonyService != null ) { |
20 | if (telephonyService.isRinging()) { |
23 | telephonyService.silenceRinger(); |
24 | } else if ((mIncallPowerBehavior |
25 | & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 |
26 | && telephonyService.isOffhook()) { |
29 | hungUp = telephonyService.endCall(); |
31 | } catch (RemoteException ex) { |
32 | Log.w(TAG, "ITelephony threw RemoteException" , ex); |
35 | interceptPowerKeyDown(!isScreenOn || hungUp |
36 | || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered); |
38 | mPowerKeyTriggered = false ; |
39 | cancelPendingScreenshotChordAction(); |
40 | if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) { |
41 | result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; |
43 | mPendingPowerKeyUpCanceled = false ; |
Above code handle multiple options like silence ringtone, take screenshots and power off. It will identify appropriate option based on time duration and other key’s status. It will call “interceptPowerKeyDown” option by eliminate other options.
Following code display interceptPowerKeyDown function. It will wait for 500 millisecond (ViewConfiguration#getGlobalActionKeyTimeout())
then call mPowerLongPress Thread.
1 | private void interceptPowerKeyDown( boolean handled) { |
2 | mPowerKeyHandled = handled; |
4 | mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout()); |
Following code represent mPowerLongPress thread
01 | private final Runnable mPowerLongPress = new Runnable() { |
05 | if (mLongPressOnPowerBehavior < 0 ) { |
06 | mLongPressOnPowerBehavior = mContext.getResources().getInteger( |
07 | com.android.internal.R.integer.config_longPressOnPowerBehavior); |
09 | int resolvedBehavior = mLongPressOnPowerBehavior; |
10 | if (FactoryTest.isLongPressOnPowerOffEnabled()) { |
11 | resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM; |
14 | switch (resolvedBehavior) { |
15 | case LONG_PRESS_POWER_NOTHING: |
17 | case LONG_PRESS_POWER_GLOBAL_ACTIONS: |
18 | mPowerKeyHandled = true ; |
19 | if (!performHapticFeedbackLw( null , HapticFeedbackConstants.LONG_PRESS, false )) { |
20 | performAuditoryFeedbackForAccessibilityIfNeed(); |
22 | sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); |
23 | showGlobalActionsDialog(); |
25 | case LONG_PRESS_POWER_SHUT_OFF: |
26 | case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: |
27 | mPowerKeyHandled = true ; |
28 | performHapticFeedbackLw( null , HapticFeedbackConstants.LONG_PRESS, false ); |
29 | sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); |
30 | mWindowManagerFuncs.shutdown(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF); |
Step 3: Controls goes to GlobalActions.java which is responsible to display dialogbox with various options like (Power Off, Airplan mode, Take screenshot and few toggle buttons), This dialog box has different options are per your OEM provider, model and Android OS version. GlobalAction class has method named showdialog() which is responsible to create object of Dialogbox with options.
01 | void showGlobalActionsDialog() { |
02 | if (mGlobalActions == null ) { |
03 | mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); |
05 | final boolean keyguardShowing = keyguardIsShowingTq(); |
06 | mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned()); |
07 | if (keyguardShowing) { |
10 | mKeyguardMediator.userActivity(); |
Step 4: If user select “Power Off” option from the dialogbox then control again goes back to PhoneWindowManager, It will start shutdown process.
Step 5: Shutdown process initiate from ShutdownThread.java file’s shoutdowninner() function, It wil display confirmation dialog with ok / cancel button, If user select ok option then actual shutdown process starts.
Step 6: beginShutdownSequence() function called when user select OK option from the dialog.
01 | private static void beginShutdownSequence(Context context) { |
02 | synchronized (sIsStartedGuard) { |
04 | Log.d(TAG, "Shutdown sequence already running, returning." ); |
12 | ProgressDialog pd = new ProgressDialog(context); |
13 | pd.setTitle(context.getText(com.android.internal.R.string.power_off)); |
14 | pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); |
15 | pd.setIndeterminate( true ); |
16 | pd.setCancelable( false ); |
17 | pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); |
21 | sInstance.mContext = context; |
22 | sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); |
25 | sInstance.mCpuWakeLock = null ; |
27 | sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( |
28 | PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu" ); |
29 | sInstance.mCpuWakeLock.setReferenceCounted( false ); |
30 | sInstance.mCpuWakeLock.acquire(); |
31 | } catch (SecurityException e) { |
32 | Log.w(TAG, "No permission to acquire wake lock" , e); |
33 | sInstance.mCpuWakeLock = null ; |
37 | sInstance.mScreenWakeLock = null ; |
38 | if (sInstance.mPowerManager.isScreenOn()) { |
40 | sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( |
41 | PowerManager.FULL_WAKE_LOCK, TAG + "-screen" ); |
42 | sInstance.mScreenWakeLock.setReferenceCounted( false ); |
43 | sInstance.mScreenWakeLock.acquire(); |
44 | } catch (SecurityException e) { |
45 | Log.w(TAG, "No permission to acquire wake lock" , e); |
46 | sInstance.mScreenWakeLock = null ; |
51 | sInstance.mHandler = new Handler() { |
Run method, start actual shutdown process
002 | BroadcastReceiver br = new BroadcastReceiver() { |
003 | @Override public void onReceive(Context context, Intent intent) { |
023 | if (mRebootSafeMode) { |
024 | SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1" ); |
027 | Log.i(TAG, "Sending shutdown broadcast..." ); |
031 | Intent intent = new Intent(Intent.ACTION_SHUTDOWN); |
032 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); |
033 | mContext.sendOrderedBroadcastAsUser(intent, |
034 | UserHandle.ALL, null , br, mHandler, 0 , null , null ); |
036 | final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; |
037 | synchronized (mActionDoneSync) { |
038 | while (!mActionDone) { |
039 | long delay = endTime - SystemClock.elapsedRealtime(); |
041 | Log.w(TAG, "Shutdown broadcast timed out" ); |
045 | mActionDoneSync.wait(delay); |
046 | } catch (InterruptedException e) { |
051 | Log.i(TAG, "Shutting down activity manager..." ); |
053 | final IActivityManager am = |
054 | ActivityManagerNative.asInterface(ServiceManager.checkService( "activity" )); |
057 | am.shutdown(MAX_BROADCAST_TIME); |
058 | } catch (RemoteException e) { |
063 | shutdownRadios(MAX_RADIO_WAIT_TIME); |
066 | IMountShutdownObserver observer = new IMountShutdownObserver.Stub() { |
067 | public void onShutDownComplete( int statusCode) throws RemoteException { |
068 | Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown" ); |
073 | Log.i(TAG, "Shutting down MountService" ); |
077 | final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; |
078 | synchronized (mActionDoneSync) { |
080 | final IMountService mount = IMountService.Stub.asInterface( |
081 | ServiceManager.checkService( "mount" )); |
083 | mount.shutdown(observer); |
085 | Log.w(TAG, "MountService unavailable for shutdown" ); |
087 | } catch (Exception e) { |
088 | Log.e(TAG, "Exception during MountService shutdown" , e); |
090 | while (!mActionDone) { |
091 | long delay = endShutTime - SystemClock.elapsedRealtime(); |
093 | Log.w(TAG, "Shutdown wait timed out" ); |
097 | mActionDoneSync.wait(delay); |
098 | } catch (InterruptedException e) { |
103 | rebootOrShutdown(mReboot, mRebootReason); |
Step 7: With rebootOrShutdown() method controls transfer to the native function of com_android_server_power_PowerManagerService.cpp file, and finally control goes to android_reboot.c file which is final step of shutdown sequence.
1 | static void nativeShutdown(JNIEnv *env, jclass clazz) { |
2 | android_reboot(ANDROID_RB_POWEROFF, 0 , 0 ); |