zoukankan      html  css  js  c++  java
  • 深入解析Android关机

    下图详细阐释了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关机流程总结

    完。

  • 相关阅读:
    ST L3 测试用例
    ST L2 系统测试流程
    ST L1 软件测试基础
    软件测试 Part5 使用测试文档
    软件测试 Part4 测试的补充
    Python Day37 python多线程标准模块concurrent.futures
    Python Day36 python多线程
    Python Day35进程池,回调函数
    Python Day34 python并发编程之多进程
    Python Day32 进程
  • 原文地址:https://www.cnblogs.com/zhangyubao/p/7016982.html
Copyright © 2011-2022 走看看