In Depth : Android Shutdown Sequence

来源:互联网 发布:linux桌面环境比较 编辑:程序博客网 时间:2024/05/16 10:21
  • 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.

Android Shutdown Sequence

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.

01/** {@inheritDoc} */
02@Override
03public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
04....
05....
06....
07case KeyEvent.KEYCODE_POWER: {
08     result &= ~ACTION_PASS_TO_USER;
09       if (down) {
10         if (isScreenOn && !mPowerKeyTriggered
11               && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
12                   mPowerKeyTriggered = true;
13                   mPowerKeyTime = event.getDownTime();
14                   interceptScreenshotChord();
15            }
16               ITelephony telephonyService = getTelephonyService();
17                boolean hungUp = false;
18               if (telephonyService != null) {
19                   try {
20                       if (telephonyService.isRinging()) {
21                           // Pressing Power while there's a ringing incoming
22                           // call should silence the ringer.
23                            telephonyService.silenceRinger();
24                       else if ((mIncallPowerBehavior
25                                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
26                               && telephonyService.isOffhook()) {
27                            // Otherwise, if "Power button ends call" is enabled,
28                           // the Power button will hang up any current active call.
29                            hungUp = telephonyService.endCall();
30                       }
31                   catch (RemoteException ex) {
32                        Log.w(TAG, "ITelephony threw RemoteException", ex);
33                   }
34               }
35               interceptPowerKeyDown(!isScreenOn || hungUp
36                       || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
37           else {
38               mPowerKeyTriggered = false;
39               cancelPendingScreenshotChordAction();
40               if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
41                   result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
42               }
43               mPendingPowerKeyUpCanceled = false;
44           }
45          break;
46      }
47....
48....
49....
50}

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.

1private void interceptPowerKeyDown(boolean handled) {
2  mPowerKeyHandled = handled;
3  if (!handled) {
4       mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
5  }
6}

Following code represent mPowerLongPress thread

01private final Runnable mPowerLongPress = new Runnable() {
02        @Override
03        public void run() {
04            // The context isn't read
05            if (mLongPressOnPowerBehavior < 0) {
06                mLongPressOnPowerBehavior = mContext.getResources().getInteger(
07                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
08            }
09            int resolvedBehavior = mLongPressOnPowerBehavior;
10            if (FactoryTest.isLongPressOnPowerOffEnabled()) {
11                resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
12            }
13 
14            switch (resolvedBehavior) {
15            case LONG_PRESS_POWER_NOTHING:
16                break;
17            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
18                mPowerKeyHandled = true;
19                if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
20                    performAuditoryFeedbackForAccessibilityIfNeed();
21                }
22                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
23                showGlobalActionsDialog();
24                break;
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);
31                break;
32            }
33        }
34    };

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.

01void showGlobalActionsDialog() {
02    if (mGlobalActions == null) {
03        mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
04    }
05    final boolean keyguardShowing = keyguardIsShowingTq();
06    mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
07    if (keyguardShowing) {
08         // since it took two seconds of long press to bring this up,
09        // poke the wake lock so they have some time to see the dialog.
10        mKeyguardMediator.userActivity();
11    }
12}

Screenshot_2013-08-16-10-47-09

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.

Screenshot_2013-08-16-10-47-29

Step 6: beginShutdownSequence() function called when user select OK option from the dialog.

01private static void beginShutdownSequence(Context context) {
02        synchronized (sIsStartedGuard) {
03            if (sIsStarted) {
04                Log.d(TAG, "Shutdown sequence already running, returning.");
05                return;
06            }
07            sIsStarted = true;
08        }
09 
10        // throw up an indeterminate system dialog to indicate radio is
11        // shutting down.
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);
18 
19        pd.show();
20 
21        sInstance.mContext = context;
22        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
23 
24        // make sure we never fall asleep again
25        sInstance.mCpuWakeLock = null;
26        try {
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;
34        }
35 
36        // also make sure the screen stays on for better user experience
37        sInstance.mScreenWakeLock = null;
38        if (sInstance.mPowerManager.isScreenOn()) {
39            try {
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;
47            }
48        }
49 
50        // start the thread that initiates shutdown
51        sInstance.mHandler = new Handler() {
52        };
53        sInstance.start();
54    }

Run method, start actual shutdown process 

001public void run() {
002        BroadcastReceiver br = new BroadcastReceiver() {
003            @Override public void onReceive(Context context, Intent intent) {
004                // We don't allow apps to cancel this, so ignore the result.
005                actionDone();
006            }
007        };
008 
009        /*
010         * Write a system property in case the system_server reboots before we
011         * get to the actual hardware restart. If that happens, we'll retry at
012         * the beginning of the SystemServer startup.
013         */
014        {
015            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
016            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
017        }
018 
019        /*
020         * If we are rebooting into safe mode, write a system property
021         * indicating so.
022         */
023        if (mRebootSafeMode) {
024            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
025        }
026 
027        Log.i(TAG, "Sending shutdown broadcast...");
028 
029        // First send the high-level shut down broadcast.
030        mActionDone = false;
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, 0nullnull);
035 
036        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
037        synchronized (mActionDoneSync) {
038            while (!mActionDone) {
039                long delay = endTime - SystemClock.elapsedRealtime();
040                if (delay <= 0) {
041                    Log.w(TAG, "Shutdown broadcast timed out");
042                    break;
043                }
044                try {
045                    mActionDoneSync.wait(delay);
046                catch (InterruptedException e) {
047                }
048            }
049        }
050 
051        Log.i(TAG, "Shutting down activity manager...");
052 
053        final IActivityManager am =
054            ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
055        if (am != null) {
056            try {
057                am.shutdown(MAX_BROADCAST_TIME);
058            catch (RemoteException e) {
059            }
060        }
061 
062        // Shutdown radios.
063        shutdownRadios(MAX_RADIO_WAIT_TIME);
064 
065        // Shutdown MountService to ensure media is in a safe state
066        IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
067            public void onShutDownComplete(int statusCode) throws RemoteException {
068                Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
069                actionDone();
070            }
071        };
072 
073        Log.i(TAG, "Shutting down MountService");
074 
075        // Set initial variables and time out time.
076        mActionDone = false;
077        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
078        synchronized (mActionDoneSync) {
079            try {
080                final IMountService mount = IMountService.Stub.asInterface(
081                        ServiceManager.checkService("mount"));
082                if (mount != null) {
083                    mount.shutdown(observer);
084                else {
085                    Log.w(TAG, "MountService unavailable for shutdown");
086                }
087            catch (Exception e) {
088                Log.e(TAG, "Exception during MountService shutdown", e);
089            }
090            while (!mActionDone) {
091                long delay = endShutTime - SystemClock.elapsedRealtime();
092                if (delay <= 0) {
093                    Log.w(TAG, "Shutdown wait timed out");
094                    break;
095                }
096                try {
097                    mActionDoneSync.wait(delay);
098                catch (InterruptedException e) {
099                }
100            }
101        }
102 
103        rebootOrShutdown(mReboot, mRebootReason);
104    }

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.

1static void nativeShutdown(JNIEnv *env, jclass clazz) {
2    android_reboot(ANDROID_RB_POWEROFF, 00);
3}
原创粉丝点击