Android7.0关机流程分析

在长按power键时系统会弹出对话框,让用户选择关机, 重启或者其他模式. 在本文中重点讲解系统关机流程. 让大家了解在系统关机过程都做了哪些事情,而导致关机慢又有那些主要的原因.在Android7.0 PowerManagerService亮灭屏(一)一文中有对按power键传输讲解, 长按power键是在Java层的PhoneWindowManager.java中进行处理的.

[java]  view plain  copy
  1. private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {  
  2.             //..........  
  3.             // When interactive, we're already awake.  
  4.             // Wait for a long press or for the button to be released to decide what to do.  
  5.             if (hasLongPressOnPowerBehavior()) {     //是否存在长按事件  
  6.                 Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);  
  7.                 msg.setAsynchronous(true);     //如果是长按power键就发送MSG_POWER_LONG_PRESS  
  8.                 mHandler.sendMessageDelayed(msg,   //发送delay消息, delay 500ms给用户一些时间长按power键显示关机dialog, 同时delay时间也可以通过config.xml中配置  
  9.                         ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());  
  10.             }  
  11.           //........  
  12. }  

延迟消息最终会在PolicyHandler的handleMessage中处理长按事件.

[java]  view plain  copy
  1. private class PolicyHandler extends Handler {  
  2.     @Override  
  3.     public void handleMessage(Message msg) {  
  4.           //......  
  5.             case MSG_POWER_LONG_PRESS:  
  6.                 powerLongPress();    //处理长按事件  
  7.                 break;  
  8.        //........  
  9.         }  
  10.     }  
  11. }  
当长按power键时可能会有不同的behavior,可能什么都不做, 也可能直接关机,没有给用户任何提醒. 或者弹出dialog让用户自己选择. 下面将主要讲解弹出dialog关机的情况.

[java]  view plain  copy
  1. static final int LONG_PRESS_POWER_NOTHING = 0;       //长按power键什么都不做  
  2. static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;     //为全局动作, 显示关机dialog  
  3. static final int LONG_PRESS_POWER_SHUT_OFF = 2;    //只有关机一个选项  
  4. static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;   //直接关机不用确认  
  5.   
  6. private void powerLongPress() {  
  7.     final int behavior = getResolvedLongPressOnPowerBehavior();  //获取长按power键要做的事情  
  8.     switch (behavior) {  
  9.     case LONG_PRESS_POWER_NOTHING:   //返回,什么都不做  
  10.         break;  
  11.     case LONG_PRESS_POWER_GLOBAL_ACTIONS:  
  12.         mPowerKeyHandled = true;  
  13.         if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {  
  14.             performAuditoryFeedbackForAccessibilityIfNeed();  
  15.         }  
  16.         showGlobalActionsInternal();    //显示全局的dialog  
  17.         break;  
  18.     case LONG_PRESS_POWER_SHUT_OFF:  
  19.     case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:   //直接关机  
  20.         mPowerKeyHandled = true;  
  21.         performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);  
  22.         sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);  
  23.         mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);  
  24.         break;  
  25.     }  
  26. }  

在显示关机dialog时需要将其他的系统dialogs都关闭了, 优先处理关机dialog, 之后就创建mGlobalActions显示dialog.\

[java]  view plain  copy
  1. void showGlobalActionsInternal() {  
  2.     sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);   //关闭系统dialogs  
  3.     if (mGlobalActions == null) {  
  4.         mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);  //创建GlobalActions  
  5.     }  
  6.     final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();   //keyguard是否在显示  
  7.     mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());   //显示dialog  
  8.     if (keyguardShowing) {  
  9.         // since it took two seconds of long press to bring this up,  
  10.         // poke the wake lock so they have some time to see the dialog.  
  11.         mPowerManager.userActivity(SystemClock.uptimeMillis(), false);  //通知power发生了一次用户时间, 让用户可以看到dialog  
  12.     }  
  13. }  
在GlobalActions中如果已经存在mDialog就将原来的dismiss掉,之后发送消息重新show出来,否则就新创建出来一个显示.

[java]  view plain  copy
  1. public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {  
  2.     mKeyguardShowing = keyguardShowing;    //keyguard正在显示  
  3.     mDeviceProvisioned = isDeviceProvisioned;     
  4.     if (mDialog != null) {  
  5.         mDialog.dismiss();    //dismiss dialog  
  6.         mDialog = null;  
  7.         // Show delayed, so that the dismiss of the previous dialog completes  
  8.         mHandler.sendEmptyMessage(MESSAGE_SHOW);  
  9.     } else {  
  10.         handleShow();   //如果不存在mDialog, 就调用handleShow处理  
  11.     }  
  12. }  
  13.   
  14. private void handleShow() {  
  15.     awakenIfNecessary();  
  16.     mDialog = createDialog();   //创建mDialog对象  
  17.     prepareDialog();   //准备dialog  
  18.   
  19.     // If we only have 1 item and it's a simple press action, just do this action.  
  20.     if (mAdapter.getCount() == 1  
  21.             && mAdapter.getItem(0instanceof SinglePressAction  
  22.             && !(mAdapter.getItem(0instanceof LongPressAction)) {  
  23.         ((SinglePressAction) mAdapter.getItem(0)).onPress();    //调用onPress函数  
  24.     } else {  
  25.         WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();  
  26.         attrs.setTitle("GlobalActions");  
  27.         mDialog.getWindow().setAttributes(attrs);  
  28.         mDialog.show();     //显示dialog  
  29.         mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);  
  30.     }  
  31. }  
通过在config.xml中的actions list记录的长按power键可以选择执行的动作, 遍历actions列表进行匹配动作为其创建对应的Action对象.之后就创建dialog并返回.

[java]  view plain  copy
  1. private GlobalActionsDialog createDialog() {  
  2.   
  3.     mItems = new ArrayList<Action>();  
  4.     String[] defaultActions = mContext.getResources().getStringArray(  
  5.             com.android.internal.R.array.config_globalActionsList);   //获取config.xml中的action list记录在defaultActions数组中  
  6.   
  7.     ArraySet<String> addedKeys = new ArraySet<String>();  
  8.     for (int i = 0; i < defaultActions.length; i++) {  //遍历defaultActions列表  
  9.         String actionKey = defaultActions[i];  
  10.         if (addedKeys.contains(actionKey)) {   //如果action已经在addedKeys列表中就不再添加了  
  11.             // If we already have added this, don't add it again.  
  12.             continue;  
  13.         }  
  14.         if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {     //对action list中actio进行匹配,如果能匹配上就创建对应对象放入mItems列表中  
  15.             mItems.add(new PowerAction());   //长按power键动作  
  16.         } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {  
  17.             mItems.add(mAirplaneModeOn);  
  18.         } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {  
  19.             if (Settings.Global.getInt(mContext.getContentResolver(),  
  20.                     Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {  
  21.                 mItems.add(new BugReportAction());  
  22.             }  
  23.         } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {  
  24.             if (mShowSilentToggle) {  
  25.                 mItems.add(mSilentModeAction);  
  26.             }  
  27.         } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {  
  28.             if (SystemProperties.getBoolean("fw.power_user_switcher"false)) {  
  29.                 addUsersToMenu(mItems);  
  30.             }  
  31.         } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {  
  32.             mItems.add(getSettingsAction());  
  33.         } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {  
  34.             mItems.add(getLockdownAction());  
  35.         } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {  
  36.             mItems.add(getVoiceAssistAction());  
  37.         } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {  
  38.             mItems.add(getAssistAction());  
  39.         }  else {  
  40.             Log.e(TAG, "Invalid global action key " + actionKey);  
  41.         }  
  42.         // Add here so we don't add more than one.  
  43.         addedKeys.add(actionKey);   //最后将actionKey放入列表, 不重复处理  
  44.     }  
  45.   
  46.     GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);  //创建dialog  
  47.     dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. 当点击dialog外面,就会将dialog取消掉  
  48.   
  49.     dialog.getListView().setItemsCanFocus(true);  
  50.     dialog.getListView().setLongClickable(true);  
  51.     dialog.getListView().setOnItemLongClickListener(  
  52.             new AdapterView.OnItemLongClickListener() {  
  53.                 @Override  
  54.                 public boolean onItemLongClick(AdapterView<?> parent, View view, int position,  
  55.                         long id) {  
  56.                     final Action action = mAdapter.getItem(position);  
  57.                     if (action instanceof LongPressAction) {  
  58.                         return ((LongPressAction) action).onLongPress();  
  59.                     }  
  60.                     return false;  
  61.                 }  
  62.     });  
  63.     dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);  
  64.   
  65.     dialog.setOnDismissListener(this);  
  66.   
  67.     return dialog;    //将新建的dialog返回  
  68. }  

当进行关机操作时就会调用PowerAction的onPress函数, 进行调用WindowManagerFuncs接口中的shutdown函数, shutdown函数的具体实现在WindowManagerService中.

[java]  view plain  copy
  1. private final class PowerAction extends SinglePressAction implements LongPressAction {  
  2.     private PowerAction() {  
  3.         super(com.android.internal.R.drawable.ic_lock_power_off,  
  4.             R.string.global_action_power_off);  
  5.     }  
  6.   
  7.     @Override  
  8.     public boolean onLongPress() {  
  9.         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);  
  10.         if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {  
  11.             mWindowManagerFuncs.rebootSafeMode(true);  
  12.             return true;  
  13.         }  
  14.         return false;  
  15.     }  
  16.   
  17.    //.............  
  18.   
  19.     @Override  
  20.     public void onPress() {  
  21.         // shutdown by making sure radio and power are handled accordingly.  
  22.         mWindowManagerFuncs.shutdown(false /* confirm */);    //关机  
  23.     }  
  24. }  
由于WindowManagerService实现了接口WindowManagerFuncs, 所以就会调用到WMS中的shutdown函数. 进而调用ShutdownThread中去实现关机功能.

[java]  view plain  copy
  1. // Called by window manager policy.  Not exposed externally.  
  2. @Override  
  3. public void shutdown(boolean confirm) {  
  4.     ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);  
  5. }  
关机过程的主要实现在ShutdownThread.java中
在关机过程中,主要做了三件事:
1.发送关机广播
2.关闭一些主要服务进程
3.通过PowerManagerService调用底层进行关机
有的模块可能需要监听手机关机事件,所以在关机时发送关机广播,通知相关模块处理。
而在关机过程中为了不损坏手机性能,记录当前一些状态,需要将一些模块服务进程先关闭,然后才进行关机。在本问题将着重讲解该过程。
最后,调用power进行关机,实际就是在SystemProperties中设置相关数据,让底层进行读取,执行Builtins.c的do_powerctl函数,最终通过bionic的reboot.cpp 调用kernel中kernel_power_off进行关机。


[java]  view plain  copy
  1. public static void shutdown(final Context context, String reason, boolean confirm) {  
  2.     mReboot = false;  
  3.     mRebootSafeMode = false;   //不是重启  
  4.     mReason = reason;   //记录关机的原因  
  5.     shutdownInner(context, confirm);  
  6. }  
  7.   
  8. static void shutdownInner(final Context context, boolean confirm) {  
  9.     // ensure that only one thread is trying to power down.  
  10.     // any additional calls are just returned   
  11.     synchronized (sIsStartedGuard) {   //保证只有一个关机线程  
  12.         if (sIsStarted) {  
  13.             Log.d(TAG, "Request to shutdown already running, returning.");  
  14.             return;  
  15.         }  
  16.     }  
  17.   
  18.     final int longPressBehavior = context.getResources().getInteger(  
  19.                     com.android.internal.R.integer.config_longPressOnPowerBehavior);   //获取长按资源id  
  20.     final int resourceId = mRebootSafeMode  
  21.             ? com.android.internal.R.string.reboot_safemode_confirm  
  22.             : (longPressBehavior == 2         //关机确认信息  
  23.                     ? com.android.internal.R.string.shutdown_confirm_question  
  24.                     : com.android.internal.R.string.shutdown_confirm);  
  25.   
  26.     Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);  
  27.   
  28.     if (confirm) {      
  29.         final CloseDialogReceiver closer = new CloseDialogReceiver(context);  
  30.         if (sConfirmDialog != null) {      
  31.             sConfirmDialog.dismiss();  
  32.         }  
  33.         sConfirmDialog = new AlertDialog.Builder(context)//需要再次确认关机, 创建dialog  
  34.                 .setTitle(mRebootSafeMode  
  35.                         ? com.android.internal.R.string.reboot_safemode_title  
  36.                         : com.android.internal.R.string.power_off)  
  37.                 .setMessage(resourceId)  
  38.                 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {  
  39.                     public void onClick(DialogInterface dialog, int which) {  
  40.                         beginShutdownSequence(context);     //确认关机  
  41.                     }  
  42.                 })  
  43.                 .setNegativeButton(com.android.internal.R.string.no, null)  //不关机了  
  44.                 .create();  
  45.         closer.dialog = sConfirmDialog;  
  46.         sConfirmDialog.setOnDismissListener(closer);  
  47.         sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);  
  48.         sConfirmDialog.show();  
  49.     } else {  
  50.         beginShutdownSequence(context);   //如果不需要确认就直接关机  
  51.     }  
  52. }  
关闭主要模块
在关机过程中需要关闭的各模块如下:
ActivityManagerService
PackageManagerService
phone
NFC Bluetooth Radio 
ICC
MountService
在关机过程中发送完关机广播后,首先关闭的就是AMS。在AMS中做了如下几件事情:
1.调用input进行冻结屏幕,不能再转屏。
2.保存一些状态,如:app,battery状态等
shutdown package就是将apk使用时间写入文件package-usage.list中 。
如果手机中没有SIM卡的话,关闭phone会很快,如果安装了SIM卡需要在关机时发送AT命令先将协议栈关闭,所以该过程可能会成为耗时点。
而关闭NFC Bluetooth Radio,只用将其disable就可以了。
shutdown ICC其实就是,关闭SIM卡
在shutdown Mountservice时由于不同的存储方案,会导致关机时间有很大差别。并且当手机有SD卡时,需要先卸载SD卡,所以当插上SD卡关机速度会慢一点。

在关机的时候如果sdcard处于MOUNTED状态下,就需要发送消息H_UNMOUNT_PM_UPDATE将sdcard进行unmount操作。
如果sdcard为emulated模拟内卡,就不需要进行unmount操作,所以模拟内卡比物理内卡的手机关机速度要快很多。
在进行unmount操作时,需要先调用PackageManagerService进行更新一下包状态,删除安装在sd卡上的软件。完成之后回调到MountService中。
之后需要向底层发送命令查询占用sd卡的进程(如数据库,slog所在进程),然后调用AMS将占用sd卡的进程杀死。由于占用sd卡的进程比较顽强,可能杀死后还会起来,所以杀完后再进行查询占用sd卡的进程。如果所查进程数仍然不为NULL,就会再次杀,最多尝试杀四次,如果还杀不死就调用底层vold进行强制杀死。故,如果占用sd卡的进程较多,这部分就会较耗时。
上层的准备工作处理完毕后,就需要向底层发送命令来进行卸载sd卡。
底层在卸载sd卡的过程中,要将sd卡的状态改变发广播到上层,通知MountSevice。
sd卡状态由Mounted变为Unmounting时,为了给上层framework一定的反应时间,底层在发送广播后睡了1s。
之后开始umount操作,如果在上层没有将占用sd卡的进程杀死,就会在底层强制杀进程。系统原生给了10次机会,每次umount失败一次系统都会睡1s之后在尝试umount,如果在最后一次仍然没有成功就强制杀进程。所以重新umount的次数越多耗时就会越长。
系统关机
当Shutdown MountService完成之后,回调至ShutdownThread中,将线程唤醒,继续往下执行。
在关机之前获得振动器震动手机,由于震动手机与调用power关机异步进行,所以为了避免关机太快来不及震动,上层会在调用用振动器后睡500ms。
最后调到power中,然后经过内核进行关机。

原文地址:http://blog.csdn.net/fu_kevin0606/article/details/54586009

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值