下图详细阐释了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关机流程总结

完。

最新文章

  1. cookie怎么用
  2. 尚学堂Spring视频教程(一):模拟Spring
  3. C语言宏定义时#(井号)和##(双井号)的用法
  4. KMP - HDU 1711 Number Sequence
  5. ABAP 搜索帮助
  6. Where does beauty come from?
  7. [转载] 一致性问题和Raft一致性算法
  8. web前端网页特效大全导航列表
  9. angularjs入门学习【应用剖析中篇】
  10. iOS 使用GBK编码的hmacMD5算法
  11. 静态变量static
  12. opacity在IE6~8下无效果,解决的办法
  13. 向ibus-table-wubi里添加属于自己的输入法(98五笔)
  14. 14 Fragment 碎片总结
  15. python 全栈开发,Day116(可迭代对象,type创建动态类,偏函数,面向对象的封装,获取外键数据,组合搜索,领域驱动设计(DDD))
  16. EasyUI表格DataGrid前端分页和后端分页的总结
  17. BeanFactory的实现原理
  18. SQL Server中的高可用性(3)----复制 (转载)
  19. Asp.Net MVC分页PageList
  20. SQL 数据库主键 ,外键

热门文章

  1. zxing的使用及优化
  2. PatentTips -- 一种在CoAP网络中注册的方法及装置
  3. 【u026】房间最短路问题
  4. 简单实现的Servlet文件上传,并显示
  5. 【b501】谁拿了最多的奖学金
  6. VS2012经常使用的快捷方式完成最全面
  7. JEECG 什么是商业版本的功能最近添加的好友?
  8. matlab 高级函数 —— colfilt/blockproc (图像)矩阵的分块处理
  9. 利用WPF建立自己的3d gis软件(非axhost方式)(十三)万能的用户层接口,(强大的WPF)
  10. Tinyhttpd - 超轻量型Http Server,使用C语言开发,全部代码只有502行(包括注释),附带一个简单的Client(Qt也有很多第三方HTTP类)