下图详细阐释了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. [Postgres] Group and Aggregate Data in Postgres

    How can we see a histogram of movies on IMDB with a particular rating? Or how much movies grossed at ...

  2. [React] Recompose: Override Styles & Elements Types in React

    When we move from CSS to defining styles inside components we lose the ability to override styles wi ...

  3. Hierarchical Tree Traversal in Graphics Pipeline Stages

    BACKGROUND Many algorithms on a graphics processing unit (GPU) may benefit from doing a query in a h ...

  4. 使用PowerDesigner15在win7下的系统MySQL p相反roject(一)

    使用PowerDesigner15在win7下的系统MySQL 相反project 1.首先.安装下面的驱动 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv ...

  5. QT类库与Delphi VCL类库的体系结构对比——两者十分类似!

    今天在看QT对象内存管理的一篇文章时:http://blog.csdn.net/dbzhang800/article/details/6300025想到了一个问题:就是QT类库体系结构与Delphi类 ...

  6. erlang app 文件

    http://hje.iteye.com/blog/1211734 应用的概念¶ 当我们写了实现特定功能的代码之后,我们可能想将代码转成一个 应用 (application),这是可以作为一个单元启动 ...

  7. 【u127】台阶问题

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 有N级的台阶,你一开始在底部,每次可以向上迈最多K级台阶(最少1级),问到达第N级台阶有多少种不同方式 ...

  8. TensorFlow 学习(五)—— Session

    A Session object encapsulates the environment in which Tensor objects are evaluated. 一个会话对象(session ...

  9. 查询系统状态 内存大小 cpu信息 设备负载情况

    1.1 查看内存状态 /proc/meminfo里面存放着内存的信息 查看内存命令(包括虚拟内存swap): free -h (低版本系统可能不支持-h) 或者 free -m (以mb单位显示) a ...

  10. cordova APP 检查更新

    原文:cordova APP 检查更新 //升级程序 .factory('UpdateService', function ($rootScope, $cordovaAppVersion, $cord ...