下图详细阐释了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. 海思hi3716c机顶盒接usb摄像头和usb无线耳机时,无线耳机有时没有声音

    两个USB设备各自是: A:USB摄像头带录音功能,但不带放音功能. B:USB无线耳机是使用USB转2.4G的无线耳机. 详细现象: 1, A,B两者同一时候插上机顶盒,并开机进入android,此 ...

  2. .NET Core微服务之路:不断更新中的目录 (v0.43)

    原文:.NET Core微服务之路:不断更新中的目录 (v0.43) 微服务架构,对于从事JAVA架构的童鞋来说,早已不是什么新鲜的事儿,他们有鼎鼎大名的Spring Cloud这样的全家桶框架支撑, ...

  3. AlphaImageLoader用法

    在 IE6 中,能够非常方便地利用 img 的 src 属性,实现本地图片预览,然而在 IE7 中,这样的办法却行不通.须要用 AlphaImageLoader. AlphaImageLoader 说 ...

  4. 二维高斯滤波器(gauss filter)的实现

    我们以一个二维矩阵表示二元高斯滤波器,显然此二维矩阵的具体形式仅于其形状(shape)有关: def gauss_filter(kernel_shape): 为实现二维高斯滤波器,需要首先定义二元高斯 ...

  5. java生成二维码,读取(解析)二维码图片

    二维码分为好多种,我们最常用的是qrcode类型的二维码,以下有三种生成方式以及解析方式: 附所需jar包或者js地址 第一种:依赖qrcode.jar import java.awt.Color; ...

  6. _Decoder_Interface_init xxxxxx in amrFileCodec.o

    Undefined symbols for architecture arm64: "_Decoder_Interface_init", referenced from: Deco ...

  7. 学习鸟哥的Linux私房菜笔记(7)——文件查找与文件管理1

    一.可执行文件的搜索 which 显示一个可执行文件的完整路径 按照alias->$PATH的顺序查找 查看系统的环境变量 whereis 搜索一个可执行工具及其相关配置.帮助 slocate ...

  8. Java访问修饰符(转)

    类.方法.成员变量和局部变量的可用修饰符 修饰符 类 成员方法 构造方法 成员变量 局部变量 abstract(抽象的) √ √ - - - static (静态的) - √ - √ - public ...

  9. CodeForces 659E New Reform (图的遍历判环)

    Description Berland has n cities connected by m bidirectional roads. No road connects a city to itse ...

  10. sitecore-CMS

    安装sitecore数据库和客户端到本机   (提前先装好数据库和IIS) 安装教程下载:http://download.csdn.net/detail/qq1162195421/6436799 安装 ...