下图详细阐释了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关机的更多相关文章

  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. [tmux] Create collections of panes using tmux windows

    In tmux, a window is a collection of panes. Creating multiple windows is a great way to organize you ...

  2. Linux中mv重命名作用及打包war压缩文件及分配权限

    1.Linux中的重命名文件使用mv命令 touch a.txt 新建一个文件 mv a.txt b.txt 重命名文件为b.txt mkdir abc 新建一个目录 mv abc abd 重命名文件 ...

  3. Database Vault注冊

    默认情况下,在安装好Database Vault组件的Oracle HOme下创建的数据库是没有注冊Database Vault的. 1)停EM.监听.数据库 2)启用Database Vault 在 ...

  4. 短文评估【安徽省选2003】- hash / trie

    题目分析 其实是hash/trie裸题,讲一下hash的做法:如果其小写状态是第一次出现则加入集合,同时将小写状态加入小写单词的hash表,最后查时查出出现次数即可. code #include< ...

  5. 【无旋 treap】例题

    [bzoj3223]文艺平衡树 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[ ...

  6. Java Service Wrapper 使用(windows)

    1       简介 最近项目中需要做一个Windows系统服务,记录一下使用过程. Java Service Wrapper 可以将Java程序包装成系统服务,这样就可以随着系统的运行而自动运行.J ...

  7. Error: unrecognized flag -version

    按照网上的教程http://www.ituring.com.cn/article/13473 来确认node.js是否成功安装. 须要在命令行输入:node -version. 但是我在命令行输入:n ...

  8. Android分享介绍

    一.使用系统分享 public void execShare(Activity context,String title,String text){ Intent intent = new Inten ...

  9. 让C#语言充当自身脚本!——.NET中的动态编译

    原文:让C#语言充当自身脚本!--.NET中的动态编译 代码的动态编译并执行是.NET平台提供给我们的很强大的一个工具,用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码 ...

  10. 数字电路基本概念 —— fan-in/fan-out

    0. 从模拟电路到数字电路 数字电路抗干扰能力强: 模拟电路会随着信号的传输而放大,这是因为模拟电路中信号几乎完全将真实信号按比例表现为电压或者电流的形式: 模拟电路是数字电路的基础 74LS283 ...