在长按power键时系统会弹出对话框,让用户选择关机, 重启或者其他模式. 在本文中重点讲解系统关机流程. 让大家了解在系统关机过程都做了哪些事情,而导致关机慢又有那些主要的原因.在Android7.0 PowerManagerService亮灭屏(一)一文中有对按power键传输讲解, 长按power键是在Java层的PhoneWindowManager.java中进行处理的.
- private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
- //..........
- // When interactive, we're already awake.
- // Wait for a long press or for the button to be released to decide what to do.
- if (hasLongPressOnPowerBehavior()) { //是否存在长按事件
- Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
- msg.setAsynchronous(true); //如果是长按power键就发送MSG_POWER_LONG_PRESS
- mHandler.sendMessageDelayed(msg, //发送delay消息, delay 500ms给用户一些时间长按power键显示关机dialog, 同时delay时间也可以通过config.xml中配置
- ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
- }
- //........
- }
延迟消息最终会在PolicyHandler的handleMessage中处理长按事件.
- private class PolicyHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- //......
- case MSG_POWER_LONG_PRESS:
- powerLongPress(); //处理长按事件
- break;
- //........
- }
- }
- }
- static final int LONG_PRESS_POWER_NOTHING = 0; //长按power键什么都不做
- static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1; //为全局动作, 显示关机dialog
- static final int LONG_PRESS_POWER_SHUT_OFF = 2; //只有关机一个选项
- static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3; //直接关机不用确认
- private void powerLongPress() {
- final int behavior = getResolvedLongPressOnPowerBehavior(); //获取长按power键要做的事情
- switch (behavior) {
- case LONG_PRESS_POWER_NOTHING: //返回,什么都不做
- break;
- case LONG_PRESS_POWER_GLOBAL_ACTIONS:
- mPowerKeyHandled = true;
- if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
- performAuditoryFeedbackForAccessibilityIfNeed();
- }
- showGlobalActionsInternal(); //显示全局的dialog
- 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(behavior == LONG_PRESS_POWER_SHUT_OFF);
- break;
- }
- }
在显示关机dialog时需要将其他的系统dialogs都关闭了, 优先处理关机dialog, 之后就创建mGlobalActions显示dialog.\
- void showGlobalActionsInternal() {
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); //关闭系统dialogs
- if (mGlobalActions == null) {
- mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); //创建GlobalActions
- }
- final boolean keyguardShowing = isKeyguardShowingAndNotOccluded(); //keyguard是否在显示
- mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned()); //显示dialog
- if (keyguardShowing) {
- // since it took two seconds of long press to bring this up,
- // poke the wake lock so they have some time to see the dialog.
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false); //通知power发生了一次用户时间, 让用户可以看到dialog
- }
- }
- public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
- mKeyguardShowing = keyguardShowing; //keyguard正在显示
- mDeviceProvisioned = isDeviceProvisioned;
- if (mDialog != null) {
- mDialog.dismiss(); //dismiss dialog
- mDialog = null;
- // Show delayed, so that the dismiss of the previous dialog completes
- mHandler.sendEmptyMessage(MESSAGE_SHOW);
- } else {
- handleShow(); //如果不存在mDialog, 就调用handleShow处理
- }
- }
- private void handleShow() {
- awakenIfNecessary();
- mDialog = createDialog(); //创建mDialog对象
- prepareDialog(); //准备dialog
- // If we only have 1 item and it's a simple press action, just do this action.
- if (mAdapter.getCount() == 1
- && mAdapter.getItem(0) instanceof SinglePressAction
- && !(mAdapter.getItem(0) instanceof LongPressAction)) {
- ((SinglePressAction) mAdapter.getItem(0)).onPress(); //调用onPress函数
- } else {
- WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
- attrs.setTitle("GlobalActions");
- mDialog.getWindow().setAttributes(attrs);
- mDialog.show(); //显示dialog
- mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
- }
- }
- private GlobalActionsDialog createDialog() {
- mItems = new ArrayList<Action>();
- String[] defaultActions = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_globalActionsList); //获取config.xml中的action list记录在defaultActions数组中
- ArraySet<String> addedKeys = new ArraySet<String>();
- for (int i = 0; i < defaultActions.length; i++) { //遍历defaultActions列表
- String actionKey = defaultActions[i];
- if (addedKeys.contains(actionKey)) { //如果action已经在addedKeys列表中就不再添加了
- // If we already have added this, don't add it again.
- continue;
- }
- if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) { //对action list中actio进行匹配,如果能匹配上就创建对应对象放入mItems列表中
- mItems.add(new PowerAction()); //长按power键动作
- } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
- mItems.add(mAirplaneModeOn);
- } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
- if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
- mItems.add(new BugReportAction());
- }
- } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
- if (mShowSilentToggle) {
- mItems.add(mSilentModeAction);
- }
- } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
- if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
- addUsersToMenu(mItems);
- }
- } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
- mItems.add(getSettingsAction());
- } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
- mItems.add(getLockdownAction());
- } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
- mItems.add(getVoiceAssistAction());
- } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
- mItems.add(getAssistAction());
- } else {
- Log.e(TAG, "Invalid global action key " + actionKey);
- }
- // Add here so we don't add more than one.
- addedKeys.add(actionKey); //最后将actionKey放入列表, 不重复处理
- }
- GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params); //创建dialog
- dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. 当点击dialog外面,就会将dialog取消掉
- dialog.getListView().setItemsCanFocus(true);
- dialog.getListView().setLongClickable(true);
- dialog.getListView().setOnItemLongClickListener(
- new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
- long id) {
- final Action action = mAdapter.getItem(position);
- if (action instanceof LongPressAction) {
- return ((LongPressAction) action).onLongPress();
- }
- return false;
- }
- });
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- dialog.setOnDismissListener(this);
- return dialog; //将新建的dialog返回
- }
当进行关机操作时就会调用PowerAction的onPress函数, 进行调用WindowManagerFuncs接口中的shutdown函数, shutdown函数的具体实现在WindowManagerService中.
- private final class PowerAction extends SinglePressAction implements LongPressAction {
- private PowerAction() {
- super(com.android.internal.R.drawable.ic_lock_power_off,
- R.string.global_action_power_off);
- }
- @Override
- public boolean onLongPress() {
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
- mWindowManagerFuncs.rebootSafeMode(true);
- return true;
- }
- return false;
- }
- //.............
- @Override
- public void onPress() {
- // shutdown by making sure radio and power are handled accordingly.
- mWindowManagerFuncs.shutdown(false /* confirm */); //关机
- }
- }
- // Called by window manager policy. Not exposed externally.
- @Override
- public void shutdown(boolean confirm) {
- ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
- }
在关机过程中,主要做了三件事:
1.发送关机广播
2.关闭一些主要服务进程
3.通过PowerManagerService调用底层进行关机
有的模块可能需要监听手机关机事件,所以在关机时发送关机广播,通知相关模块处理。
而在关机过程中为了不损坏手机性能,记录当前一些状态,需要将一些模块服务进程先关闭,然后才进行关机。在本问题将着重讲解该过程。
最后,调用power进行关机,实际就是在SystemProperties中设置相关数据,让底层进行读取,执行Builtins.c的do_powerctl函数,最终通过bionic的reboot.cpp 调用kernel中kernel_power_off进行关机。
- public static void shutdown(final Context context, String reason, boolean confirm) {
- mReboot = false;
- mRebootSafeMode = false; //不是重启
- mReason = reason; //记录关机的原因
- shutdownInner(context, confirm);
- }
- 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); //获取长按资源id
- 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)//需要再次确认关机, 创建dialog
- .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); //如果不需要确认就直接关机
- }
- }
在关机过程中需要关闭的各模块如下:
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