深入解析Android關機
轉自https://www.importnew.com/6356.html
Android 關機順序
- 當我們長按電源按鈕時,手機裏究竟發了什麼?
- 什麼又是關機順序?
- Android的關機順序與Linux桌麵係統有何不同?
- 如何更改關機菜單?
當我們說起Android的關機順序時,許多諸如此類的問題便會湧進我們的腦袋。 不過,在繼續閱讀之前,建議您首先能對開機順序有一個了解開機順序文章。
Android是基於Linux內核的開源操作係統。盡管x86(x86 是一係列計算機微處理器指令集及其架構的統稱,這種架構基於Intel 8086 CPU)是大多數Linux係統所采用的處理器架構,然而絕大多數Android係統卻運行於ARM架構之上(ARM,又稱Advanced RISC Machine,其前身為Acorn RISC Machine),除了來自Intel的Xolo設備。這種移動設備使用了Atom 1. 6Ghz x86處理器。 但不論哪種架構,Android的關機順序都區別於Linux的桌麵係統,如Ubuntu、Fedora等。 本文主要介紹Android的關機順序, 如果想更多地了解Linux桌麵係統的關機順序,請參考Linux的啟動與關閉流程。
下圖詳細闡釋了Android的關機順序。

第一步: 按住電源按鈕半秒鍾(500ms)。
第二步: 之後,PhoneWindowManager.java 將捕獲長按電源按鈕這一事件並調用“interceptKeyBeforeQueueing”方法。
下麵是處理長按電源鍵事件的代碼片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
/** {@inheritDoc} */ @Override public
int interceptKeyBeforeQueueing(KeyEvent event,
int policyFlags,
boolean isScreenOn) {
.... .... .... case
KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
if
(down) {
if
(isScreenOn && !mPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) ==
0 ) {
mPowerKeyTriggered =
true ;
mPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
}
ITelephony telephonyService = getTelephonyService();
boolean
hungUp = false ;
if
(telephonyService != null ) {
try
{
if
(telephonyService.isRinging()) {
// 如果在來電響鈴時按下電源鍵,則係統將關閉來電提示
telephonyService.silenceRinger();
}
else if
((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) !=
0
&& telephonyService.isOffhook()) {
// 如果處在通話中且電源鍵掛斷選項已啟用,則按下電源鍵會結束當前通話
hungUp = telephonyService.endCall();
}
}
catch (RemoteException ex) {
Log.w(TAG,
"ITelephony threw RemoteException" , ex);
}
}
interceptPowerKeyDown(!isScreenOn || hungUp
|| mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
}
else {
mPowerKeyTriggered =
false ;
cancelPendingScreenshotChordAction();
if
(interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
}
mPendingPowerKeyUpCanceled =
false ;
}
break ;
}
.... .... .... } |
上麵的代碼包含了對多種情形下對長按電源鍵時間的處理,例如靜默來電響鈴、屏幕截圖以及關閉電源等。 係統將根據電源鍵被按住的時間長短以及相關按鍵的使用情況來決定如何恰當地處理當前的用戶操作。 當電源鍵被按下且沒有截屏操作觸發時interceptPowerKeyDown 將被調用,這時其他的按鍵響應(其他按鍵響應指 interceptKeyBeforeQueueing 中其他cases)將不會被觸發。
下麵的代碼展示了 interceptPowerKeyDown 函數內容, 函數將注冊一個回調函數,在500毫秒超時事件(ViewConfiguration#getGlobalActionKeyTimeout())觸發時啟動 mPowerLongPress 線程。
1
2
3
4
5
6
|
private
void interceptPowerKeyDown( boolean
handled) {
mPowerKeyHandled = handled;
if
(!handled) {
mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
}
} |
mPowerLongPress 線程的實現如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
private
final Runnable mPowerLongPress = new
Runnable() {
@Override
public
void run() {
// The context isn't read
if
(mLongPressOnPowerBehavior < 0 ) {
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
}
int
resolvedBehavior = mLongPressOnPowerBehavior;
if
(FactoryTest.isLongPressOnPowerOffEnabled()) {
resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
}
switch
(resolvedBehavior) {
case
LONG_PRESS_POWER_NOTHING:
break ;
case
LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled =
true ;
if
(!performHapticFeedbackLw( null , HapticFeedbackConstants.LONG_PRESS,
false )) {
performAuditoryFeedbackForAccessibilityIfNeed();
}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
showGlobalActionsDialog();
break ;
case
LONG_PRESS_POWER_SHUT_OFF:
case
LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled =
true ;
performHapticFeedbackLw( null , HapticFeedbackConstants.LONG_PRESS,
false );
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF);
break ;
}
}
};
|
第三步: 由上麵代碼的Switch分支可知,當程序進去Long_Press_Power_Global_Options時控製將移交給 GlobalActions 類, 該模塊則負責顯示關機選項的對話框,這些選項在各Android發行版(各OEM廠商定製的Android係統, 不同的手機型號和不同版本的Android係統)中不盡相同,通常包括關閉電源、飛行模式和屏幕截圖。也可能包括其他一些選項按鍵。GlobalActions 類實現了一個showdialog方法,該方法將根據當前係統支持的菜單內容來創建這個對話框。
1
2
3
4
5
6
7
8
9
10
11
|
void
showGlobalActionsDialog() {
if
(mGlobalActions == null ) {
mGlobalActions =
new GlobalActions(mContext, mWindowManagerFuncs);
}
final
boolean keyguardShowing = keyguardIsShowingTq();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
if
(keyguardShowing) {
// 由於激活關機對話框需要長按電源鍵兩秒以上,所以當對話框顯示之後,屏幕的喚醒狀態將被鎖定,以方便用戶瀏覽對話框中內容
mKeyguardMediator.userActivity();
}
} |

第四步: 若用戶選擇“關閉電源“,則對係統的控製將交回給 PhoneWindowManager, 然後由PhoneWindowManager 啟動關閉流程。
第五步: 整個關機過程起始於ShutdownThread模塊中的shutdowninner方法。該方法首先創建一個確認對話框給用戶, 用戶可以選擇確認關機或是取消關機操作。 如果用戶選擇確認,則係統將真正進入關機流程。

第六步: 如上所述,當用戶點擊確認按鈕後beginShutdownSequence方法將被調用以啟動關機順序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
private
static void beginShutdownSequence(Context context) {
synchronized
(sIsStartedGuard) {
if
(sIsStarted) {
Log.d(TAG,
"Shutdown sequence already running, returning." );
return ;
}
sIsStarted =
true ;
}
// 顯示正在關閉電源的對話框
ProgressDialog pd =
new ProgressDialog(context);
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate( true );
pd.setCancelable( false );
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
pd.show();
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
// 阻止CPU進入休眠狀態
sInstance.mCpuWakeLock =
null ;
try
{
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG +
"-cpu" );
sInstance.mCpuWakeLock.setReferenceCounted( false );
sInstance.mCpuWakeLock.acquire();
}
catch (SecurityException e) {
Log.w(TAG,
"No permission to acquire wake lock" , e);
sInstance.mCpuWakeLock =
null ;
}
// 電源關閉前一直保持屏幕喚醒狀態,以便提升用戶體驗
sInstance.mScreenWakeLock =
null ;
if
(sInstance.mPowerManager.isScreenOn()) {
try
{
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.FULL_WAKE_LOCK, TAG +
"-screen" );
sInstance.mScreenWakeLock.setReferenceCounted( false );
sInstance.mScreenWakeLock.acquire();
}
catch (SecurityException e) {
Log.w(TAG,
"No permission to acquire wake lock" , e);
sInstance.mScreenWakeLock =
null ;
}
}
// 啟動負責關機順序的線程
sInstance.mHandler =
new Handler() {
};
sInstance.start();
}
|
運行函數,啟動實際的關機流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
public
void run() {
BroadcastReceiver br =
new BroadcastReceiver() {
@Override
public void
onReceive(Context context, Intent intent) {
// We don't allow apps to cancel this, so ignore the result.
actionDone();
}
};
/*
* 寫入一個係統參數,以防Android係統中的System Server
* (一個運行於Dalvik虛擬機與真實係統內核間的server,負責虛擬機與內核的通信)在真實硬件重啟前完成重啟。
* 當上述情況發生時, 則在System Server完成啟動後重試之前的重啟操作。
*/
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
/*
* 寫入一個係統參數以便重啟後進入安全模式
*/
if
(mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY,
"1" );
}
Log.i(TAG,
"Sending shutdown broadcast..." );
// 關閉移動通信
mActionDone =
false ;
Intent intent =
new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL,
null , br, mHandler,
0 , null ,
null );
final
long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized
(mActionDoneSync) {
while
(!mActionDone) {
long
delay = endTime - SystemClock.elapsedRealtime();
if
(delay <= 0 ) {
Log.w(TAG,
"Shutdown broadcast timed out" );
break ;
}
try
{
mActionDoneSync.wait(delay);
}
catch (InterruptedException e) {
}
}
}
Log.i(TAG,
"Shutting down activity manager..." );
final
IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService( "activity" ));
if
(am != null ) {
try
{
am.shutdown(MAX_BROADCAST_TIME);
}
catch (RemoteException e) {
}
}
// 關閉移動通信
shutdownRadios(MAX_RADIO_WAIT_TIME);
// 安全移除外部存儲卡
IMountShutdownObserver observer =
new IMountShutdownObserver.Stub() {
public
void onShutDownComplete( int
statusCode) throws
RemoteException {
Log.w(TAG,
"Result code " + statusCode +
" from MountService.shutdown" );
actionDone();
}
};
Log.i(TAG,
"Shutting down MountService" );
// 初始化變量,並設置關機超時時限
mActionDone =
false ;
final
long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
synchronized
(mActionDoneSync) {
try
{
final
IMountService mount = IMountService.Stub.asInterface(
ServiceManager.checkService( "mount" ));
if
(mount != null ) {
mount.shutdown(observer);
}
else {
Log.w(TAG,
"MountService unavailable for shutdown" );
}
}
catch (Exception e) {
Log.e(TAG,
"Exception during MountService shutdown" , e);
}
while
(!mActionDone) {
long
delay = endShutTime - SystemClock.elapsedRealtime();
if
(delay <= 0 ) {
Log.w(TAG,
"Shutdown wait timed out" );
break ;
}
try
{
mActionDoneSync.wait(delay);
}
catch (InterruptedException e) {
}
}
}
rebootOrShutdown(mReboot, mRebootReason);
}
|
第七步: 當rebootOrShutdown方法被調用時,係統控製權首先轉至底層函數 nativeShutdown(在com_android_server_power_PowerManagerService。cpp中定義) 並最終調用android_reboot函數(定義於android_reboot.c中)來完成整個關機順序
1
2
3
|
static
void nativeShutdown(JNIEnv *env, jclass clazz) {
android_reboot(ANDROID_RB_POWEROFF,
0 , 0 );
|
最後更新:2017-04-03 07:57:08