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