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

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

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

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

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

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

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

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

mPowerLongPress 线程的实现如下:

  1. 1 private final Runnable mPowerLongPress = new Runnable() {
  2. 2 @Override
  3. 3 public void run() {
  4. 4 // The context isn't read
  5. 5 if (mLongPressOnPowerBehavior < 0) {
  6. 6 mLongPressOnPowerBehavior = mContext.getResources().getInteger(
  7. 7 com.android.internal.R.integer.config_longPressOnPowerBehavior);
  8. 8 }
  9. 9 int resolvedBehavior = mLongPressOnPowerBehavior;
  10. 10 if (FactoryTest.isLongPressOnPowerOffEnabled()) {
  11. 11 resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
  12. 12 }
  13. 13
  14. 14 switch (resolvedBehavior) {
  15. 15 case LONG_PRESS_POWER_NOTHING:
  16. 16 break;
  17. 17 case LONG_PRESS_POWER_GLOBAL_ACTIONS:
  18. 18 mPowerKeyHandled = true;
  19. 19 if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
  20. 20 performAuditoryFeedbackForAccessibilityIfNeed();
  21. 21 }
  22. 22 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
  23. 23 showGlobalActionsDialog();
  24. 24 break;
  25. 25 case LONG_PRESS_POWER_SHUT_OFF:
  26. 26 case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
  27. 27 mPowerKeyHandled = true;
  28. 28 performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
  29. 29 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
  30. 30 mWindowManagerFuncs.shutdown(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF);
  31. 31 break;
  32. 32 }
  33. 33 }
  34. 34 };

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

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

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

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

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

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

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

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

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

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

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

完。

深入解析Android关机的更多相关文章

  1. 解析Android消息处理机制:Handler/Thread/Looper & MessageQueue

    解析Android消息处理机制 ——Handler/Thread/Looper & MessageQueue Keywords: Android Message HandlerThread L ...

  2. 【转】android 电池(二):android关机充电流程、充电画面显示

    关键词:android 电池关机充电 androidboot.mode charger关机充电 充电画面显示 平台信息:内核:linux2.6/linux3.0系统:android/android4. ...

  3. Android关机流程源码分析

    上一篇文章Android 开关机动画显示源码分析详细介绍了开关机动画的显示过程,Android系统开机时,在启动SurfaceFlinger服务过程中通过Android属性系统方式来启动bootani ...

  4. 解析android framework下利用app_process来调用java写的命令及示例

    解析android framework下利用app_process来调用java写的命令及示例 在android SDK的framework/base/cmds目录下了,有不少目录,这些目的最终都是b ...

  5. 解析 Android Things 技术原理

    2012 年 6 月,由 IoT-GSI(Global Standards Initiative on Internet of Things)发布的白皮书“ITU-T Y.4000/Y.2060”[1 ...

  6. 如何解析android访问webservice返回的SoapObject数据(可用)

    怎么解析android访问webservice返回的SoapObject数据 本帖最后由 kkDragon123 于 2013-03-26 15:50:07 编辑 我的数据如下:mingdanResp ...

  7. 源代码解析Android中View的layout布局过程

    Android中的Veiw从内存中到呈如今UI界面上须要依次经历三个阶段:量算 -> 布局 -> 画图,关于View的量算.布局.画图的整体机制可參见博文 < Android中Vie ...

  8. HexDump.java解析,android 16进制转换

    HexDump.java解析android 16进制转换 package com.android.internal.util; public class HexDump { private final ...

  9. android 电池(二):android关机充电流程、充电画面显示【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/8498580 上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下a ...

随机推荐

  1. pycharm Zooming in the Editor

    https://www.jetbrains.com/help/pycharm/zooming-in-the-editor.html To enable changing font size in th ...

  2. alloc init初始化后对象依然还在父视图

    self.TableView=[[UITableView alloc]init]; ........2个cell //下面但方法和addsubviews方法不一样 [self.view insertS ...

  3. 《学习opencv》笔记——关于一些画图的函数

    画图函数 (1)直线cvLine函数 其结构 void cvLine(//画直线 CvArr* array,//画布图像 CvPoint pt1,//起始点 CvPoint pt2,//终点 CvSc ...

  4. Mybatis找不到参数错误:There is no getter for property named 'categoryId' in 'class java.lang.Integer'。

    Mybatis找不到参数错误:There is no getter for property named 'categoryId' in 'class java.lang.Integer'. 错误Li ...

  5. 【t034】Matrix67的派对

    Time Limit: 1 second Memory Limit: 1 MB [问题描述] Matrix67发现身高接近的人似乎更合得来.Matrix67举办的派对共有N(1<=N<=1 ...

  6. 利用SendMessage实现窗口拖动

    原文:利用SendMessage实现窗口拖动 利用SendMessage实现窗口拖动                                            周银辉 想想以前用跟踪鼠标位 ...

  7. 使用RpcLite构建SOA/Web服务(Full .Net Framework)

    使用RpcLite构建SOA/Web服务(Full .Net Framework) SOA框架系列 1. 使用RpcLite构建SOA/Web服务 2. 使用RpcLite构建SOA/Web服务(Fu ...

  8. FreeBSD 5.0中强制访问控制机制的使用与源代码分析【转】

    本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容.这一部分讲述要将MAC框架与 ...

  9. 策略模式的JS实现

    var S = function (salary) { return salary * 4; }; var A = function (salary) { return salary * 3; }; ...

  10. 经典书单 —— 语言/算法/机器学习/深度学习/AI/CV/PGM

    0.0 计算机科学 <Lex 与 Yacc> Think Complexity(使用 Python 语言) GitHub - AllenDowney/ThinkComplexity: Co ...