深入解析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关机的更多相关文章
- 解析Android消息处理机制:Handler/Thread/Looper & MessageQueue
解析Android消息处理机制 ——Handler/Thread/Looper & MessageQueue Keywords: Android Message HandlerThread L ...
- 【转】android 电池(二):android关机充电流程、充电画面显示
关键词:android 电池关机充电 androidboot.mode charger关机充电 充电画面显示 平台信息:内核:linux2.6/linux3.0系统:android/android4. ...
- Android关机流程源码分析
上一篇文章Android 开关机动画显示源码分析详细介绍了开关机动画的显示过程,Android系统开机时,在启动SurfaceFlinger服务过程中通过Android属性系统方式来启动bootani ...
- 解析android framework下利用app_process来调用java写的命令及示例
解析android framework下利用app_process来调用java写的命令及示例 在android SDK的framework/base/cmds目录下了,有不少目录,这些目的最终都是b ...
- 解析 Android Things 技术原理
2012 年 6 月,由 IoT-GSI(Global Standards Initiative on Internet of Things)发布的白皮书“ITU-T Y.4000/Y.2060”[1 ...
- 如何解析android访问webservice返回的SoapObject数据(可用)
怎么解析android访问webservice返回的SoapObject数据 本帖最后由 kkDragon123 于 2013-03-26 15:50:07 编辑 我的数据如下:mingdanResp ...
- 源代码解析Android中View的layout布局过程
Android中的Veiw从内存中到呈如今UI界面上须要依次经历三个阶段:量算 -> 布局 -> 画图,关于View的量算.布局.画图的整体机制可參见博文 < Android中Vie ...
- HexDump.java解析,android 16进制转换
HexDump.java解析android 16进制转换 package com.android.internal.util; public class HexDump { private final ...
- android 电池(二):android关机充电流程、充电画面显示【转】
本文转载自:http://blog.csdn.net/xubin341719/article/details/8498580 上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下a ...
随机推荐
- pycharm Zooming in the Editor
https://www.jetbrains.com/help/pycharm/zooming-in-the-editor.html To enable changing font size in th ...
- alloc init初始化后对象依然还在父视图
self.TableView=[[UITableView alloc]init]; ........2个cell //下面但方法和addsubviews方法不一样 [self.view insertS ...
- 《学习opencv》笔记——关于一些画图的函数
画图函数 (1)直线cvLine函数 其结构 void cvLine(//画直线 CvArr* array,//画布图像 CvPoint pt1,//起始点 CvPoint pt2,//终点 CvSc ...
- 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 ...
- 【t034】Matrix67的派对
Time Limit: 1 second Memory Limit: 1 MB [问题描述] Matrix67发现身高接近的人似乎更合得来.Matrix67举办的派对共有N(1<=N<=1 ...
- 利用SendMessage实现窗口拖动
原文:利用SendMessage实现窗口拖动 利用SendMessage实现窗口拖动 周银辉 想想以前用跟踪鼠标位 ...
- 使用RpcLite构建SOA/Web服务(Full .Net Framework)
使用RpcLite构建SOA/Web服务(Full .Net Framework) SOA框架系列 1. 使用RpcLite构建SOA/Web服务 2. 使用RpcLite构建SOA/Web服务(Fu ...
- FreeBSD 5.0中强制访问控制机制的使用与源代码分析【转】
本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容.这一部分讲述要将MAC框架与 ...
- 策略模式的JS实现
var S = function (salary) { return salary * 4; }; var A = function (salary) { return salary * 3; }; ...
- 经典书单 —— 语言/算法/机器学习/深度学习/AI/CV/PGM
0.0 计算机科学 <Lex 与 Yacc> Think Complexity(使用 Python 语言) GitHub - AllenDowney/ThinkComplexity: Co ...