首页 > 代码库 > 深入解析Android关机

深入解析Android关机

下图详细阐释了Android的关机顺序。

 

第一步: 按住电源按钮半秒钟(500ms)。

第二步: 之后,PhoneWindowManager.java 将捕获长按电源按钮这一事件并调用“interceptKeyBeforeQueueing”方法。

下面是处理长按电源键事件的代码片段

技术分享
 1     /** {@inheritDoc} */ 
 2     @Override 
 3     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { 
 4     .... 
 5     .... 
 6     .... 
 7     case KeyEvent.KEYCODE_POWER: { 
 8          result &= ~ACTION_PASS_TO_USER; 
 9            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                                // 如果在来电响铃时按下电源键,则系统将关闭来电提示 
22                                 telephonyService.silenceRinger(); 
23                            } else if ((mIncallPowerBehavior 
24                                     & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 
25                                    && telephonyService.isOffhook()) { 
26                                 // 如果处在通话中且电源键挂断选项已启用,则按下电源键会结束当前通话 
27                                 hungUp = telephonyService.endCall(); 
28                            } 
29                        } catch (RemoteException ex) { 
30                             Log.w(TAG, "ITelephony threw RemoteException", ex); 
31                        } 
32                    } 
33                    interceptPowerKeyDown(!isScreenOn || hungUp 
34                            || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered); 
35                } else { 
36                    mPowerKeyTriggered = false; 
37                    cancelPendingScreenshotChordAction(); 
38                    if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) { 
39                        result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; 
40                    } 
41                    mPendingPowerKeyUpCanceled = false; 
42                } 
43               break; 
44           } 
45     .... 
46     .... 
47     .... 
48     } 
技术分享

 

上面的代码包含了对多种情形下对长按电源键时间的处理,例如静默来电响铃、屏幕截图以及关闭电源等。 系统将根据电源键被按住的时间长短以及相关按 键的使用情况来决定如何恰当地处理当前的用户操作。 当电源键被按下且没有截屏操作触发时interceptPowerKeyDown 将被调用,这时其 他的按键响应(其他按键响应指 interceptKeyBeforeQueueing 中其他cases)将不会被触发。

下面的代码展示了 interceptPowerKeyDown 函数内容, 函数将注册一个回调函数,在500毫秒超时事件 (ViewConfiguration#getGlobalActionKeyTimeout())触发时启动 mPowerLongPress 线程。

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

 

mPowerLongPress 线程的实现如下:

技术分享
 1     private final Runnable mPowerLongPress = new Runnable() { 
 2             @Override 
 3             public void run() { 
 4                 // The context isn‘t read 
 5                 if (mLongPressOnPowerBehavior < 0) { 
 6                     mLongPressOnPowerBehavior = mContext.getResources().getInteger( 
 7                             com.android.internal.R.integer.config_longPressOnPowerBehavior); 
 8                 } 
 9                 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         }; 
技术分享

 

第三步: 由上面代码的Switch分支可知,当程序进去 Long_Press_Power_Global_Options时控制将移交给 GlobalActions 类, 该模块则负责显示关机选项的对话 框,这些选项在各Android发行版(各OEM厂商定制的Android系统, 不同的手机型号和不同版本的Android系统)中不尽相同,通常包括 关闭电源、飞行模式和屏幕截图。也可能包括其他一些选项按键。GlobalActions 类实现了一个showdialog方法,该方法将根据当前系统 支持的菜单内容来创建这个对话框。

技术分享
 1 void showGlobalActionsDialog() { 
 2     if (mGlobalActions == null) { 
 3         mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); 
 4     } 
 5     final boolean keyguardShowing = keyguardIsShowingTq(); 
 6     mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned()); 
 7     if (keyguardShowing) { 
 8          // 由于激活关机对话框需要长按电源键两秒以上,所以当对话框显示之后,屏幕的唤醒状态将被锁定,以方便用户浏览对话框中内容 
 9         mKeyguardMediator.userActivity(); 
10     } 
11 } 
技术分享

 

技术分享

第四步: 若用户选择“关闭电源“,则对系统的控制将交回给 PhoneWindowManager, 然后由PhoneWindowManager 启动关闭流程。

第五步: 整个关机过程起始于ShutdownThread模块中的shutdowninner方法。该方法首先创建一个确认对话框给用户, 用户可以选择确认关机或是取消关机操作。 如果用户选择确认,则系统将真正进入关机流程。

技术分享

第六步: 如上所述,当用户点击确认按钮后beginShutdownSequence方法将被调用以启动关机顺序。

技术分享
 1     private static void beginShutdownSequence(Context context) { 
 2             synchronized (sIsStartedGuard) { 
 3                 if (sIsStarted) { 
 4                     Log.d(TAG, "Shutdown sequence already running, returning."); 
 5                     return; 
 6                 } 
 7                 sIsStarted = true; 
 8             } 
 9       
10             // 显示正在关闭电源的对话框 
11             ProgressDialog pd = new ProgressDialog(context); 
12             pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 
13             pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 
14             pd.setIndeterminate(true); 
15             pd.setCancelable(false); 
16      
17     pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 
18             pd.show(); 
19             sInstance.mContext = context; 
20             sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 
21             // 阻止CPU进入休眠状态 
22             sInstance.mCpuWakeLock = null; 
23             try { 
24                 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( 
25                         PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); 
26                 sInstance.mCpuWakeLock.setReferenceCounted(false); 
27                 sInstance.mCpuWakeLock.acquire(); 
28             } catch (SecurityException e) { 
29                 Log.w(TAG, "No permission to acquire wake lock", e); 
30                 sInstance.mCpuWakeLock = null; 
31             } 
32             // 电源关闭前一直保持屏幕唤醒状态,以便提升用户体验 
33             sInstance.mScreenWakeLock = null; 
34             if (sInstance.mPowerManager.isScreenOn()) { 
35                 try { 
36                     sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( 
37                             PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); 
38                    sInstance.mScreenWakeLock.setReferenceCounted(false); 
39                     sInstance.mScreenWakeLock.acquire(); 
40                 } catch (SecurityException e) { 
41                     Log.w(TAG, "No permission to acquire wake lock", e); 
42                     sInstance.mScreenWakeLock = null; 
43                 } 
44             } 
45             // 启动负责关机顺序的线程 
46             sInstance.mHandler = new Handler() { 
47             }; 
48             sInstance.start(); 
49         } 
技术分享

 

运行函数,启动实际的关机流程

技术分享
  1     public void run() { 
  2             BroadcastReceiver br = new BroadcastReceiver() { 
  3                 @Override public void onReceive(Context context, Intent intent) { 
  4                     // We don‘t allow apps to cancel this, so ignore the result. 
  5                     actionDone(); 
  6                 } 
  7             }; 
  8       
  9             /* 
 10              *  写入一个系统参数,以防Android系统中的System Server 
 11              * (一个运行于Dalvik虚拟机与真实系统内核间的server,负责虚拟机与内核的通信)在真实硬件重启前完成重启。 
 12              * 当上述情况发生时, 则在System Server完成启动后重试之前的重启操作。 
 13              */ 
 14             { 
 15                 String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : ""); 
 16                 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 
 17             } 
 18       
 19             /* 
 20              * 写入一个系统参数以便重启后进入安全模式 
 21              */ 
 22             if (mRebootSafeMode) { 
 23                 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 
 24             } 
 25       
 26             Log.i(TAG, "Sending shutdown broadcast..."); 
 27       
 28             // 关闭移动通信 
 29             mActionDone = false; 
 30             Intent intent = new Intent(Intent.ACTION_SHUTDOWN); 
 31             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 
 32             mContext.sendOrderedBroadcastAsUser(intent, 
 33                     UserHandle.ALL, null, br, mHandler, 0, null, null); 
 34       
 35             final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; 
 36             synchronized (mActionDoneSync) { 
 37                 while (!mActionDone) { 
 38                     long delay = endTime - SystemClock.elapsedRealtime(); 
 39                     if (delay <= 0) { 
 40                         Log.w(TAG, "Shutdown broadcast timed out"); 
 41                         break; 
 42                     } 
 43                     try { 
 44                         mActionDoneSync.wait(delay); 
 45                     } catch (InterruptedException e) { 
 46                     } 
 47                 } 
 48             } 
 49       
 50             Log.i(TAG, "Shutting down activity manager..."); 
 51       
 52             final IActivityManager am = 
 53                 ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); 
 54             if (am != null) { 
 55                 try { 
 56                     am.shutdown(MAX_BROADCAST_TIME); 
 57                 } catch (RemoteException e) { 
 58                 } 
 59             } 
 60       
 61             // 关闭移动通信 
 62             shutdownRadios(MAX_RADIO_WAIT_TIME); 
 63       
 64             // 安全移除外部存储卡 
 65             IMountShutdownObserver observer = new IMountShutdownObserver.Stub() { 
 66                 public void onShutDownComplete(int statusCode) throws RemoteException { 
 67                     Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown"); 
 68                     actionDone(); 
 69                 } 
 70             }; 
 71       
 72             Log.i(TAG, "Shutting down MountService"); 
 73       
 74             // 初始化变量,并设置关机超时时限 
 75             mActionDone = false; 
 76             final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; 
 77             synchronized (mActionDoneSync) { 
 78                 try { 
 79                     final IMountService mount = IMountService.Stub.asInterface( 
 80                             ServiceManager.checkService("mount")); 
 81                     if (mount != null) { 
 82                         mount.shutdown(observer); 
 83                     } else { 
 84                         Log.w(TAG, "MountService unavailable for shutdown"); 
 85                     } 
 86                 } catch (Exception e) { 
 87                     Log.e(TAG, "Exception during MountService shutdown", e); 
 88                 } 
 89                 while (!mActionDone) { 
 90                     long delay = endShutTime - SystemClock.elapsedRealtime(); 
 91                     if (delay <= 0) { 
 92                         Log.w(TAG, "Shutdown wait timed out"); 
 93                         break; 
 94                     } 
 95                     try { 
 96                         mActionDoneSync.wait(delay); 
 97                     } catch (InterruptedException e) { 
 98                     } 
 99                 } 
100             } 
101       
102             rebootOrShutdown(mReboot, mRebootReason); 
103         } 
技术分享

 

第七步: 当rebootOrShutdown方法被调用时,系统控制权首先转至底层函 数 nativeShutdown(在com_android_server_power_PowerManagerService。cpp中定义) 并 最终调用android_reboot函数(定义于android_reboot.c中)来完成整个关机顺序

1     static void nativeShutdown(JNIEnv *env, jclass clazz) { 
2         android_reboot(ANDROID_RB_POWEROFF, 0, 0); 
3     }

注: 目前的Android版本的 rebootOrShutdown 的實現跟上面的不同。是通過調用PowerManagerService.lowLevelShutdown()修改屬性"sys.powerctl"的值實現的。可以參考: 设备驱动-----Android关机流程总结

 

完。

深入解析Android关机