Android Framework层Power键关机流程

xiaoxiao2021-02-28  101

一:Android处理Power按键长按操作

    在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManager开始分析,在分析前我这里打印了该方法的堆栈调用信息。大家可以参考一下。

public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {

    ......

    android.util.Log.d("BILL",android.util.Log.getStackTraceString(new Throwable()));

    ......

}

1-13 19:35:32.458 D/BILL    (  718): Java.lang.Throwable

01-13 19:35:32.458 D/BILL    (  718): at com.android.internal.policy.impl.PhoneWindowManager.interceptKeyBeforeDispatching(PhoneWindowManager.java:2224)

01-13 19:35:32.458 D/BILL    (  718): at com.android.server.wm.InputMonitor.interceptKeyBeforeDispatching(InputMonitor.java:501)

01-13 19:35:32.458 D/BILL    (  718): at com.android.server.input.InputManagerService.interceptKeyBeforeDispatching(InputManagerService.java:1383)

01-13 19:35:32.458 D/BILL    (  718): at dalvik.system.NativeStart.run(Native Method)

调用流程如下(只贴出关键代码):

interceptKeyBeforeDispatching()-->interceptPowerKeyDown()-->mPowerLongPress.run()

1>

public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {

......

       case KeyEvent.KEYCODE_POWER: {                 result &= ~ACTION_PASS_TO_USER;                 if (down) {                     mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn, event.getDownTime(),                             isImmersiveMode(mLastSystemUiFlags));                     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()) {                                 // Pressing Power while there's a ringing incoming                                 // call should silence the ringer.                                 telephonyService.silenceRinger();                             /// M: [ALPS00093981] @{                             } else if ((isScreenOn                                 ||  mScreenOffReason == OFF_BECAUSE_OF_PROX_SENSOR)                             /// @}                                 && (mIncallPowerBehavior                                     & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0                                     && telephonyService.isOffhook()) {                                 // Otherwise, if "Power button ends call" is enabled,                                 // the Power button will hang up any current active call.                                 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;             }

......

}

注!红色为判断长按(down),蓝色为判断短按(up)。

2>

    private void interceptPowerKeyDown(boolean handled) {         mPowerKeyHandled = handled;         if (!handled) {             mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());         }     }

3>

private final Runnable mPowerLongPress = new Runnable() { ......             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; ...... };

注:上述代码中下划线即弹出(关机、重启、飞行模式等选项)的对话框。

二,关机流程

    从前一篇博文我们知道,当用户长按Power键时会弹出(关机、重启,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框。那么从选项对话框到关机确认对话框又是一个什么流程呢。下面我们在简单分析一下:

    showGlobalActionsDialog()-->showDialog()-->handleShow()-->createDialog()-->onPress()-->shutdown()

PhoneWindowManager.Java     void showGlobalActionsDialog() {         ......         mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());         ......     } GlobalActions.java     public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {         ......         handleShow();         ......     }     private void handleShow() {         ......         mDialog = createDialog();         ......     }     private GlobalActionsDialog createDialog(){         ......         mItems = new ArrayList<Action>();         // first: power off         mItems.add(             new SinglePressAction(                     com.Android.internal.R.drawable.uirom_ic_lock_power_off,                     R.string.global_action_power_off) {                 public void onPress() {                     // shutdown by making sure radio and power are handled accordingly.                     mWindowManagerFuncs.shutdown(true);                 }                 public boolean onLongPress() {                     mWindowManagerFuncs.rebootSafeMode(true);                     return true;                 }                 public boolean showDuringKeyguard() {                     return true;                 }                 public boolean showBeforeProvisioning() {                     return true;                 }             });         ......     } 上述代码中的mWindowManagerFuncs实际上是WindowManagerService的对象,该对象有PhoneWindowManager的init的方法传入GlobalActions的构造函数中,并在上述代码中进行调用。下面这一行代码是调用的关键代码。 mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);

下面是弹出“关机确认对话框”的堆栈: 01-16 18:08:21.497 D/bill    (  720): java.lang.Throwable

01-16 18:08:21.497 D/bill    (  720): at com.android.server.power.ShutdownThread.shutdown(ShutdownThread.java:175)

01-16 18:08:21.497 D/bill    (  720): at com.android.server.wm.WindowManagerService.shutdown(WindowManagerService.java:5783)01-16 18:08:21.497 D/bill    (  720):at com.android.internal.policy.impl.GlobalActions$2.onPress(GlobalActions.java:352)//WindowManagerService实现了接口WindowsManagerFuncs

01-16 18:08:21.497 D/bill    (  720): at com.android.internal.policy.impl.GlobalActions.onClick(GlobalActions.java:581)

01-16 18:08:21.497 D/bill    (  720): at com.android.internal.app.AlertController$AlertParams$3.onItemClick(AlertController.java:952)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AdapterView.performItemClick(AdapterView.java:299)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AbsListView.performItemClick(AbsListView.java:1152)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AbsListView$PerformClick.run(AbsListView.java:3014)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AbsListView$3.run(AbsListView.java:3865)

01-16 18:08:21.497 D/bill    (  720): at android.os.Handler.handleCallback(Handler.java:808)

01-16 18:08:21.497 D/bill    (  720): at android.os.Handler.dispatchMessage(Handler.java:103)

01-16 18:08:21.497 D/bill    (  720): at android.os.Looper.loop(Looper.java:193)

01-16 18:08:21.497 D/bill    (  720): at android.os.HandlerThread.run(HandlerThread.java:61)

从这里(shutdown())我们正式进入关机流程的关键。

shutdown()<ShutdownThread.java> --->shutdownInner() --->beginShutdownSequence()--->run()--->rebootOrShutdown()--->lowLevelShutdown()<PowerManagerService.java>---> 源码来自:https://github.com/android/platform_frameworks_base/blob/master/services/java/com/android/server/power/ShutdownThread.java

[java]  view plain  copy  print ? public static void shutdown(final Context context, boolean confirm) {          mReboot = false;          mRebootSafeMode = false;          shutdownInner(context, confirm);      }   注! 

    参数2:confir;关机操作前是否需要用户进行确认 static void shutdownInner(final Context context, boolean confirm) {         // ensure that only one thread is trying to power down.         // any additional calls are just returned         synchronized (sIsStartedGuard) {             if (sIsStarted) {                 Log.d(TAG, "Request to shutdown already running, returning.");                 return;             }         }         final int longPressBehavior = context.getResources().getInteger(                         com.android.internal.R.integer.config_longPressOnPowerBehavior);

        //longPressBehavior的值标示当前长按Power操作意向(关机、重启。。。)         final int resourceId = mRebootSafeMode                 ? com.android.internal.R.string.reboot_safemode_confirm                 : (longPressBehavior == 2                         ? com.android.internal.R.string.shutdown_confirm_question                         : com.android.internal.R.string.shutdown_confirm);         Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);         if (confirm) {             final CloseDialogReceiver closer = new CloseDialogReceiver(context);             if (sConfirmDialog != null) {                 sConfirmDialog.dismiss();             }             sConfirmDialog = new AlertDialog.Builder(context)                     .setTitle(mRebootSafeMode                             ? com.android.internal.R.string.reboot_safemode_title                             : com.android.internal.R.string.power_off)                     .setMessage(resourceId)                     .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {                         public void onClick(DialogInterface dialog, int which) {                             beginShutdownSequence(context);                         }                     })                     .setNegativeButton(com.android.internal.R.string.no, null)                     .create();             closer.dialog = sConfirmDialog;             sConfirmDialog.setOnDismissListener(closer);             sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);             sConfirmDialog.show();         } else {             beginShutdownSequence(context);         }     } 注:上述代码中,如果需要用户确认关机操作,则会弹出对话框,在对话框的确认按钮被触发时,调用beginShutdownSequence()方法继续关机流程。如果无需用户确认,则直接调用beginShutdownSequence()进入下一个关机流程节点。

在beginShutdownSequence()有些手机厂商常常会在这里添加一些定制功能,例如在对话框中添加“下次快速开机”,定制关机动画等等。随后会根据不同平台进行讲解。下面这张图是Android原生系统的关机画面(对应下面加粗显示的代码):

 private static void  beginShutdownSequence (Context context) {         synchronized (sIsStartedGuard) {             if (sIsStarted) {                 Log.d(TAG, "Shutdown sequence already running, returning.");                 return;             }             sIsStarted = true;         }         // throw up an indeterminate system dialog to indicate radio is         // shutting down.         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);         // make sure we never fall asleep again         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;         }         // also make sure the screen stays on for better user experience         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;             }         }         // start the thread that initiates shutdown         sInstance.mHandler = new Handler() {         };          sInstance.start();⑤     } 注解!

①上述红色代码中的作用主要是为了防止手机进入休眠状态,从代码中我们看到,此时通过PowerManager的newWakeLock方法生成了PowerManager.WakeLock对象。newWakeLock()是PowerManager中最为常用的方法,该对象是一种锁机制,通过该对象可以控制设备的电源状态。在生成WakeLock实例时通过第一个参数的传入只开控制获取不同的WakeLock,主要是不同的lock对CPU,屏幕,键盘灯有不同的影响。如下:

PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度

②Wake Lock 是一种锁的机制,只要有人拿着这个锁,系统九五案发进入休眠,可以被用户动态程序和内核获得,这个锁可以使有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了,或者超时了,内核就会启动休眠的那套机制来进入休眠。PowerManager.WakeLock有加锁和解锁的两种状态,加锁的方式有两种,一种是永久的锁住,这样的锁除非是显示的放开,否则是不会解锁的,所以这种锁用起来要非常小心,第二种锁是超时锁,这种锁会在锁住一段时间后自动解锁。

sInstance.mCpuWakeLock.setReferenceCounted(false);是设置锁的方式为永久的锁住。

③sInstance.mCpuWakeLock.acquire(); 加锁 ④上述蓝色代码的作用是为了保证用户体验,保持屏幕、键盘的亮度 ⑤接着启动关机线程,进入关机流程的下一个节点。 /**      * Makes sure we handle the shutdown gracefully.      * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.      */     public void run() {         BroadcastReceiver br = new BroadcastReceiver() {             @Override public void onReceive(Context context, Intent intent) {                 actionDone();//这里用于接受关机广播,actionDone()方法主要是防止应用程序取消关机操作。             }         };         /*          * Write a system property in case the system_server reboots before we          * get to the actual hardware restart. If that happens, we'll retry at          * the beginning of the SystemServer startup.          */         {             String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");             SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);         }         /*          * If we are rebooting into safe mode, write a system property          * indicating so.          */         if (mRebootSafeMode) {             SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");         }         Log.i(TAG, "Sending shutdown broadcast...");                  // First send the high-level shut down 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);//关闭ActivityManagerService             } catch (RemoteException e) {             }         }         Log.i(TAG, "Shutting down package manager...");         final PackageManagerService pm = (PackageManagerService)             ServiceManager.getService("package");         if (pm != null) {             pm.shutdown();//关闭PackageManagerService服务         }         // 关闭Radios         shutdownRadios(MAX_RADIO_WAIT_TIME);         // Shutdown MountService to ensure media is in a safe state         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");         // Set initial variables and time out time.         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);//关闭MountService                 } 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()     public static void rebootOrShutdown(boolean reboot, String reason) {         if (reboot) {             Log.i(TAG, "Rebooting, reason: " + reason);             PowerManagerService.lowLevelReboot(reason);             Log.e(TAG, "Reboot failed, will attempt shutdown instead");         } else if (SHUTDOWN_VIBRATE_MS > 0) {             // vibrate before shutting down             Vibrator vibrator = new SystemVibrator();             try {                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);//关机震动             } catch (Exception e) {                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.                 Log.w(TAG, "Failed to vibrate during shutdown.", e);             }             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.             try {                 Thread.sleep(SHUTDOWN_VIBRATE_MS);             } catch (InterruptedException unused) {             }         }         // Shutdown power         Log.i(TAG, "Performing low-level shutdown...");         PowerManagerService.lowLevelShutdown();//关闭电源     } }      /**      * Low-level function turn the device off immediately, without trying      * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.      */     public static void lowLevelShutdown() {         SystemProperties.set("sys.powerctl", "shutdown");//这里通过修改Android属性进行关机     } 注:上述代码中,红色加粗部分为关机关键代码,我也可以通过adb 命令进行修改Android系统的属性进行关机,具体命令如下

adb shell setprop sys.powerctl shutdown

转载请注明原文地址: https://www.6miu.com/read-64654.html

最新回复(0)