Android关机流程简析

作者: 世界的尽头是编制 | 来源:发表于2019-05-23 13:42 被阅读0次
关机有几种方式:按键、重启或者其他模式

在关机过程中,主要做了三件事:
1.发送关机广播
2.关闭一些主要服务进程
3.通过PowerManagerService调用底层进行关机
有的模块可能需要监听手机关机事件,所以在关机时发送关机广播,通知相关模块处理。


接下来主要讲解按键响应关机整个流程:

PhoneWindowManager.java | framework\base\services\core\java\com\android\server\policy

    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        // Hold a wake lock until the power key is released.
        if (!mPowerKeyWakeLock.isHeld()) {
            mPowerKeyWakeLock.acquire();
        }
        ......
         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;
           //........
            }
        }
    }

当按下电源按键的时候会有不同的响应,可能什么都不做,也可能直接关机,没有给用户任何提醒. 或者弹出dialog让用户自己选择. 下面将主要讲解弹出dialog关机的情况。

GlobalActions.java | framework\base\services\core\java\com\android\server\policy

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();
        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();//shutDownRing();//显示全局Dailog
            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;
        }
    }

延伸:

可以在调用showGlobalActionsInternal()方法显示全局Dailog之前做一些定制化处理,比如随机播放关机铃声,如shutDownRing()方法:

private void shutDownRing(){
        //Merged by lefty.lan @20180803 for shutdown_ring, start
        Random random = new Random();
        int num = random.nextInt(4)%(4-1+1) + 1;
        MediaPlayer mPlayer = new MediaPlayer();
        try {
            if(mPlayer != null){
                switch (num) {
                case 1:
                    mPlayer.setDataSource("/system/media/01.wav");
                    break;
                case 2:
                    mPlayer.setDataSource("/system/media/02.wav");
                    break;
                case 3:
                    mPlayer.setDataSource("/system/media/03.wav");
                    break;
                case 4:
                    mPlayer.setDataSource("/system/media/04.wav");
                    break;
                }
            mPlayer.prepare();
            mPlayer.start();
            try{
                Thread.sleep(2000);
                } catch (Exception e) {
                //e.printStackTrace();
            }
            }
        } catch (Exception e) {
        e.printStackTrace();
        }

        showGlobalActionsInternal();

        if(mPlayer != null){
        mPlayer.stop();
        }
        //end
    }

言归正传

    void showGlobalActionsInternal() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);//关闭系统Dailog
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);//创建Dailog
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();//keyguard是否在显示
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());//显示Dailog
        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
        }
    }

在GlobalActions中如果已经存在mDialog就将原来的dismiss掉,之后发送消息重新show出来,否则就新创建出来一个显示。

    /**
     * Show the global actions dialog (creating if necessary)
     * @param keyguardShowing True if keyguard is showing
     */
    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = isDeviceProvisioned;
        if (mDialog != null) {
            mDialog.dismiss();
            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();//创建Dailog
        prepareDialog();//准备Dailog

        // 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();//显示Dailog
            mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);

            //add by lefty_lan,2018-7-3 13:48:55
            //mWindowManagerFuncs.shutdown(true);
            // try
            // {
            // Thread.sleep(2000);
            // } catch (Exception e) {
            //  //e.printStackTrace();
            //  }
            //mWindowManagerFuncs.shutdown(false);
        }
    }

通过在(frameworks\base\core\res\res\values)config.xml中的actionslist记录的长按power键可以选择执行的动作,遍历actions列表进行匹配动作为其创建对应的Action对象.之后就创建dialog并返回。

<!-- Defines the default set of global actions. Actions may still be disabled or hidden based
         on the current state of the device.
         Each item must be one of the following strings:
         "power" = Power off
         "settings" = An action to launch settings
         "airplane" = Airplane mode toggle
         "bugreport" = Take bug report, if available
         "silent" = silent mode
         "users" = list of users
         -->
    <string-array translatable="false" name="config_globalActionsList">
        <item>power</item>
        <item>bugreport</item>
        <item>users</item>
    </string-array>

    /**
     * Create the global actions dialog.
     * @return A new dialog.
     */
    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());
            }
            // Add by Avar.wu @20161021 to add reboot action, start
            else if(GLOBAL_ACTION_KEY_REBOOT.equals(actionKey)) {
                mItems.add(new RebootAction());
            // end
            // Add by Avar.wu @20170303 to add region-screenshot action
            } else if (GLOBAL_ACTION_KEY_REGION_SCREENSHOT.equals(actionKey)) {
                mItems.add(new ScreenShotAction());
            // end
            } else {
                Log.e(TAG, "Invalid global action key " + actionKey);
            }
            // Add here so we don't add more than one.
            addedKeys.add(actionKey);//最后将actionKey放入列表, 不重复处理
        }

        mAdapter = new MyAdapter();

        AlertParams params = new AlertParams(mContext);
        params.mAdapter = mAdapter;
        params.mOnClickListener = this;
        params.mForceInverseBackground = true;

        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;//返回Dailog
    }

当进行关机操作时就会调用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 */);    //关机
        }
    }

由于WindowManagerService实现了接口WindowManagerFuncs, 所以就会调用到WMS中的shutdown函数. 进而调用ShutdownThread中去实现关机功能。

    // Called by window manager policy.  Not exposed externally.
    @Override
    public void shutdown(boolean confirm) {
        ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
    }

ShutdownThread.java | framework\base\services\core\java\com\android\server\power

    /**
     * Request a clean shutdown, waiting for subsystems to clean up their
     * state etc.  Must be called from a Looper thread in which its UI
     * is shown.
     *
     * @param context Context used to display the shutdown progress dialog.
     * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
     * @param confirm true if user confirmation is needed before shutting down.
     */
    public static void shutdown(final Context context, String reason, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        mReason = reason;

        Log.d(TAG, "!!! Request to shutdown !!!");

        if (mSpew) {
            StackTraceElement[] stack = new Throwable().getStackTrace();
            for (StackTraceElement element : stack)
            {
                Log.d(TAG, "    |----" + element.toString());
            }
        }

        if (SystemProperties.getBoolean("ro.monkey", false)) {
            Log.d(TAG, "Cannot request to shutdown when Monkey is running, returning.");
            return;
        }

        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);
        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();
            }
            bConfirmForAnimation = confirm;
            Log.d(TAG, "PowerOff dialog doesn't exist. Create it first");
            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();

            //add by lefty_lan,2018-7-3 13:49:22
            //beginShutdownSequence(context);
        } else {
            beginShutdownSequence(context);//如果不需要确认就直接关机
        }
    }


摘录自:https://blog.csdn.net/fu_kevin0606/article/details/54586009

在关机的时候如果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中,然后经过内核进行关机。

相关文章

网友评论

    本文标题:Android关机流程简析

    本文链接:https://www.haomeiwen.com/subject/wevvzqtx.html