深入解析Android關機
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”方法。
下面是處理長按電源鍵事件的代碼片段
- /** {@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 線程。
- private void interceptPowerKeyDown(boolean handled) {
- mPowerKeyHandled = handled;
- if (!handled) {
- mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
- }
- }
mPowerLongPress 線程的實現如下:
- 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方法,該方法將根據當前系統支持的菜單內容來創建這個對話框。
- 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方法將被調用以啟動關機順序。
- 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();
- }
運行函數,啟動實際的關機流程
- 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中)來完成整個關機順序
- static void nativeShutdown(JNIEnv *env, jclass clazz) {
- android_reboot(ANDROID_RB_POWEROFF, 0, 0);
- }
原文鏈接: javacodegeeks 翻譯: ImportNew.com - 靳禹
譯文鏈接: http://www.importnew.com/6356.html