Android 9关机流程
  R3jekIqJDwNw 2023年11月12日 18 0

一、整体关机流程

framework ->init->kernel

二、关机流程(代码)

本文基于Android 9 ,梳理下正常power键触发的关机流程。 涉及的相关代码路径如下:

//framework

frameworks\base\core\res\res\values\config.xml
frameworks\base\services\core\java\com\android\server\policy\LegacyGlobalActions.java
frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
frameworks\base\services\core\java\com\android\server\policy\PowerAction.java
frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java
frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

//system
system\core\init\init.cpp
system\core\init\reboot.cpp
sdm660\system\core\init\property_service.cpp

//bionic
bionic\libc\bionic\reboot.cpp

//kernel
kernel\msm-4.4\kernel\reboot.c

2.1、关机流程的启动

关机流程启动是在framework,通常由用户长按power键触发,进行待机/关机/重启选择。 frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java

private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
        int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); //进行按键处理队列前需要拦截的按键事件
        if ((actions & ACTION_PASS_TO_USER) != 0) {
            long delayMillis = interceptKeyBeforeDispatching(
                    win, fallbackEvent, policyFlags);
            if (delayMillis == 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    	...
    	// Handle special keys.处理一些特殊(系统全局)按键
        switch (keyCode) {
        	...
        	case KeyEvent.KEYCODE_POWER: {
                // Any activity on the power button stops the accessibility shortcut
                cancelPendingAccessibilityShortcutAction();
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactive);//处理Power键按下动作,关机流程
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);//处理Power键弹起动作,待机流程
                }
                break;
            }
        }
    }

    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        ...
        // Latch power key state to detect screenshot chord.
        //处理由Power触发的截图功能
        if (interactive && !mScreenshotChordPowerKeyTriggered
                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            mScreenshotChordPowerKeyTriggered = true;
            mScreenshotChordPowerKeyTime = event.getDownTime();
            interceptScreenshotChord();
            interceptRingerToggleChord();
        }

		// Stop ringing or end call if configured to do so when power is pressed.
		//Power键静音来电铃声或挂断来电
        TelecomManager telecomManager = getTelecommService();
        boolean hungUp = false;
        if (telecomManager != null) {
            if (telecomManager.isRinging()) {
                // Pressing Power while there's a ringing incoming
                // call should silence the ringer.
                telecomManager.silenceRinger(); //来电静音
            } else if ((mIncallPowerBehavior
                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                    && telecomManager.isInCall() && interactive) {
                // Otherwise, if "Power button ends call" is enabled,
                // the Power button will hang up any current active call.
                hungUp = telecomManager.endCall(); //挂断来电
            }
        }
      
        // If the power key has still not yet been handled, then detect short
        // press, long press, or multi press and decide what to do.
        //如果此时Power按键仍没有被处理,则根据短按、长按、组合按进行处理
        mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
                || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
        if (!mPowerKeyHandled) { //Power键事件未被处理
            if (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()) {
                    if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                        powerLongPress(); //短按Power键处理流程
                    } else {
                    	////Power长按(500ms)处理流程
                        Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageDelayed(msg,
                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

                        if (hasVeryLongPressOnPowerBehavior()) { //Power超长按(3500ms)处理流程
                            Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
                            longMsg.setAsynchronous(true);
                            mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
                        }
                    }
                }
            } else {
            	//由Power键唤醒后处理的流程
                wakeUpFromPowerKey(event.getDownTime());

                if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
                    if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                        powerLongPress();
                    } else {
                        Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageDelayed(msg,
                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

                        if (hasVeryLongPressOnPowerBehavior()) {
                            Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
                            longMsg.setAsynchronous(true);
                            mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
                        }
                    }

                    mBeganFromNonInteractive = true;
                }
                ...
            }
        }
    }

    private class PolicyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            	...
	            case MSG_POWER_LONG_PRESS:
                    powerLongPress();//长按处理流程
                    break;
                case MSG_POWER_VERY_LONG_PRESS:
                    powerVeryLongPress(); //超长按处理流程
                ...
            }
        }
    }

    private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();//获取系统配置power键长按配置行为
        switch (behavior) {
        case LONG_PRESS_POWER_NOTHING:  //长按无响应
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:  //全局长按行为
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            showGlobalActionsInternal();
            break;
        case LONG_PRESS_POWER_SHUT_OFF:  //长按Power键关机
        case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:  //长按Power键(无需确认)立即关机
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
            mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF); //关机
            break;
        case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:  //长按Power键进入语音助手
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            final boolean keyguardActive = mKeyguardDelegate == null
                    ? false
                    : mKeyguardDelegate.isShowing();
            if (!keyguardActive) {
                Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
                if (mAllowStartActivityForLongPressOnPowerDuringSetup) {
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
                } else {
                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
                }
            }
            break;
        }
    }

    private void powerVeryLongPress() {
        switch (mVeryLongPressOnPowerBehavior) {
        	case VERY_LONG_PRESS_POWER_NOTHING:  //Power超长按无响应
            	break;
        	case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS: //Power超长按全局行为
            	mPowerKeyHandled = true;
            	performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            	showGlobalActionsInternal();
            	break;
        }
    }

getResolvedLongPressOnPowerBehavior()上述Power键长按行为配置路径如下,默认为Global actions menu frameworks/base/core/res/res/values/config.xml

<!-- Control the behavior when the user long presses the power button.
            0 - Nothing
            1 - Global actions menu
            2 - Power off (with confirmation)
            3 - Power off (without confirmation)
            4 - Go to voice assist
    -->
    <integer name="config_longPressOnPowerBehavior">1</integer>

frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java

void showGlobalActionsInternal() {
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        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);
        }
    }

frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java

public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
        ...
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = deviceProvisioned;
        mShowing = true;
        if (mGlobalActionsAvailable) { //全局行为可用
            mHandler.postDelayed(mShowTimeout, 5000);
            mGlobalActionsProvider.showGlobalActions();
        } else {
            // SysUI isn't alive, show legacy menu. 显示传统关机菜单
            ensureLegacyCreated();
            mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
        }
    }

到这里有两个分支,关机Dialog有两种样式,都可以进行UI定制化,其中if的case进行定制化在另一篇Android 10关机界面定制有介绍,有兴趣的同学可移步了解。

这里我们按else的case走传统的关机流程。 frameworks\base\services\core\java\com\android\server\policy\LegacyGlobalActions.java

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();
        }
    }

    private void handleShow() {
        awakenIfNecessary();
        mDialog = createDialog();//创建新的对话框,加载关机选项,设置点击选项
        prepareDialog();//更新静音、飞行等各种模式

        ...
            if (mDialog != null) {
                WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
                attrs.setTitle("LegacyGlobalActions");
                mDialog.getWindow().setAttributes(attrs);
                mDialog.show();
                mDialog.getWindow().getDecorView().setSystemUiVisibility(
                        View.STATUS_BAR_DISABLE_EXPAND);
            }
        }
    }

private ActionsDialog createDialog() {
		...
        mItems = new ArrayList<Action>();
        String[] defaultActions = mContext.getResources().getStringArray(
                com.android.internal.R.array.config_globalActionsList);//设置默认的全局行为选项

        ArraySet<String> addedKeys = new ArraySet<String>();
        for (int i = 0; i < defaultActions.length; i++) {
            String actionKey = defaultActions[i];
            if (addedKeys.contains(actionKey)) {
                // If we already have added this, don't add it again.
                continue;
            }
            if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {//关机模式
                mItems.add(new PowerAction(mContext, mWindowManagerFuncs));
            } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {//飞行模式
                mItems.add(mAirplaneModeOn);
            }
            ...
            addedKeys.add(actionKey);//将默认关机选项加入global action menu
        }
		...
        ActionsDialog dialog = new ActionsDialog(mContext, params);
        dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
        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;
                    }
        });
       ...
        return dialog;
    }

全局行为模式定义如下: frameworks\base\core\res\res\values\config.xml

<!-- 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
         "restart" = restart device
         "emergency" = Launch emergency dialer
         "lockdown" = Lock down device until the user authenticates
         "logout" =  Logout the current user
         -->
    <string-array translatable="false" name="config_globalActionsList">
        <item>power</item> 			//关机
        <item>restart</item>		//重启
        <item>lockdown</item>		//锁屏
        <item>logout</item>			//注销账户
        <item>bugreport</item>		//上报错误
        <item>screenshot</item>		//截屏
        <item>emergency</item>		//紧急
    </string-array>

接着PowerAction.java onLongPress() frameworks\base\services\core\java\com\android\server\policy\PowerAction.java

@Override
    public boolean onLongPress() {
        UserManager um = mContext.getSystemService(UserManager.class);
        if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
            mWindowManagerFuncs.rebootSafeMode(true);
            return true;
        }
        return false;
    }

frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

@Override
    public void rebootSafeMode(boolean confirm) {
        // Pass in the UI context, since ShutdownThread requires it (to show UI).
        ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(),
                confirm);
    }

显示关机进度框

进入关机线程ShutdownThread frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java

public static void rebootSafeMode(final Context context, boolean confirm) {
        ...
        shutdownInner(context, confirm);
    }

private static void shutdownInner(final Context context, boolean confirm) {
		...
        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);
        }
    }

    private static void beginShutdownSequence(Context context) {
		...
        sInstance.mProgressDialog = showShutdownDialog(context);//显示关机进度框
       	...
    }

 private static ProgressDialog showShutdownDialog(Context context) {
        // Throw up a system dialog to indicate the device is rebooting / shutting down.
        ProgressDialog pd = new ProgressDialog(context);

        // Path 1: Reboot to recovery for update
        //   Condition: mReason startswith REBOOT_RECOVERY_UPDATE
        //
        //  Path 1a: uncrypt needed
        //   Condition: if /cache/recovery/uncrypt_file exists but
        //              /cache/recovery/block.map doesn't.
        //   UI: determinate progress bar (mRebootHasProgressBar == True)
        //
        // * Path 1a is expected to be removed once the GmsCore shipped on
        //   device always calls uncrypt prior to reboot.
        //
        //  Path 1b: uncrypt already done
        //   UI: spinning circle only (no progress bar)
        //
        // Path 2: Reboot to recovery for factory reset
        //   Condition: mReason == REBOOT_RECOVERY
        //   UI: spinning circle only (no progress bar)
        //
        // Path 3: Regular reboot / shutdown
        //   Condition: Otherwise
        //   UI: spinning circle only (no progress bar)

        // mReason could be "recovery-update" or "recovery-update,quiescent".
        if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
            // We need the progress bar if uncrypt will be invoked during the
            // reboot, which might be time-consuming.
            mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
                    && !(RecoverySystem.BLOCK_MAP_FILE.exists());
            pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
            if (mRebootHasProgressBar) {
                pd.setMax(100);
                pd.setProgress(0);
                pd.setIndeterminate(false);
                pd.setProgressNumberFormat(null);
                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_update_prepare));
            } else {
                if (showSysuiReboot()) {
                    return null;
                }
                pd.setIndeterminate(true);
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_update_reboot));
            }
        } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
            if (RescueParty.isAttemptingFactoryReset()) {
                // We're not actually doing a factory reset yet; we're rebooting
                // to ask the user if they'd like to reset, so give them a less
                // scary dialog message.
                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);
            } else {
                // Factory reset path. Set the dialog message accordingly.
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_reset_message));
                pd.setIndeterminate(true);
            }
        } else {
            if (showSysuiReboot()) {
                return null;
            }
            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();
        return pd;
    }

pd.show()将各自模式的关机进度对话框显示,直至系统关机。至此,定制化关机界面所需处理的流程到这里就可以结束了,但为了进一步了解关机流程我们继续深入follow。 在beginShutdownSequence()方法最后开启了一个线程,执行了run() frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java

/**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio state if the allotted time has passed.
     */
    public void run() {
        ...
        final IActivityManager am =
                IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);//关机前关闭AMS
            } catch (RemoteException e) {
            }
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
        }
        ...
        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();//关机前关闭PMS
        }
        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);//关机前关闭radios无线信号
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
        }
		...
        // Remaining work will be done by init, including vold shutdown
        rebootOrShutdown(mContext, mReboot, mReason);//进入重启或关机
    }

public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        ...           
        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }

frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java

public static void lowLevelShutdown(String reason) {
        if (reason == null) {
            reason = "";
        }
        SystemProperties.set("sys.powerctl", "shutdown," + reason);//设置系统控制属性sys.powerctl=shutdown
    }

总结以下以上流程:

  • PWM 拦截Power按键,-待机流程在power键up弹起时处理。Power down事件处理power有关的按键事件(截屏、语音助手、通话等)处理。
  • power 键长按,短按,超长按事件处理,根据系统配置的power长按行为决定是否要交互,还是直接关机。
  • ShutdownThread关机Dialog 根据配置文件展示相关的选项,共用户选择。
  • 关机流程最后在PMS 通过设置sys.powerctl系统属性向底层传递,并记录关机原因。

属性的设计流程这里不展开介绍了,具体过程移步Android 系统属性(SystemProperties)介绍

sdm660\system\core\init\property_service.cpp

static void handle_property_set_fd() {
    switch (cmd) {
    case PROP_MSG_SETPROP: {
        ...
        uint32_t result =
            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
        ...
      }
}

// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr, std::string* error) {
	...
	// sys.powerctl is a special property that is used to make the device reboot.  We want to log
    // any process that sets this property to be able to accurately blame the cause of a shutdown.
    if (name == "sys.powerctl") { //记录sys.powerctl 属性的关机进程
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
            // path.
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
    }

    if (name == "selinux.restorecon_recursive") {
        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
    }
	//设置关机属性
    return PropertySet(name, value, error);
}

static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {

    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
    if (pi != nullptr) {
        // ro.* properties are actually "write-once".
        if (StartsWith(name, "ro.")) {
            *error = "Read-only property was already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }

        __system_property_update(pi, value.c_str(), valuelen); //对已存在属性值进行更新
    } else {
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen); //添加新属性键值对
        if (rc < 0) {
            *error = "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }

    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
        WritePersistentProperty(name, value); //写persist属性到rom
    }
    property_changed(name, value); //通知属性值有更新
    return PROP_SUCCESS;
}

system\core\init\init.cpp

void property_changed(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
    // commands to be executed.
    if (name == "sys.powerctl") {
        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
        // because it modifies the contents of the action queue, which can cause the action queue
        // to get into a bad state if this function is called from a command being executed by the
        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
        // command is run in the main init loop.
        // TODO: once property service is removed from init, this will never happen from a builtin,
        // but rather from a callback from the property service socket, in which case this hack can
        // go away.
        shutdown_command = value; //记录关机属性值,准备立即关机
        do_shutdown = true;
    }

    if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
	...
}

int main(int argc, char** argv) {
	...
	    while (true) {//循环检测
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;

        if (do_shutdown && !shutting_down) {//执行关机
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }
}

system\core\init\reboot.cpp

bool HandlePowerctlMessage(const std::string& command) {
    unsigned int cmd = 0;
    std::vector<std::string> cmd_params = Split(command, ",");
    std::string reboot_target = "";
    bool run_fsck = false;
    bool command_invalid = false;

    if (cmd_params.size() > 3) {
        command_invalid = true;
    } else if (cmd_params[0] == "shutdown") {//关机流程,PMS设置的熟悉为shutdown,走此流程。
        cmd = ANDROID_RB_POWEROFF; //设置cmd为ANDROID_RB_POWEROFF,后续流程会用到
        if (cmd_params.size() == 2) {
            if (cmd_params[1] == "userrequested") { //挂机原因为用户触发请求
                // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
                // Run fsck once the file system is remounted in read-only mode.
                run_fsck = true;
            } else if (cmd_params[1] == "thermal") {
                // Turn off sources of heat immediately.
                TurnOffBacklight(); //关闭屏背光
                // run_fsck is false to avoid delay
                cmd = ANDROID_RB_THERMOFF;
            }
        }
    } else if (cmd_params[0] == "reboot") {//重启
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() >= 2) {
            reboot_target = cmd_params[1];
            // When rebooting to the bootloader notify the bootloader writing
            // also the BCB.
            if (reboot_target == "bootloader") {
                std::string err;
                if (!write_reboot_bootloader(&err)) {
                    LOG(ERROR) << "reboot-bootloader: Error writing "
                                  "bootloader_message: "
                               << err;
                }
            }
            // If there is an additional parameter, pass it along
            if ((cmd_params.size() == 3) && cmd_params[2].size()) {
                reboot_target += "," + cmd_params[2];
            }
        }
    } else {
        command_invalid = true;
    }
    if (command_invalid) {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return false;
    }

    LOG(INFO) << "Clear action queue and start shutdown trigger";
    ActionManager::GetInstance().ClearQueue();
    // Queue shutdown trigger first
    ActionManager::GetInstance().QueueEventTrigger("shutdown");
    // Queue built-in shutdown_done
    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
        DoReboot(cmd, command, reboot_target, run_fsck);执行关机后续流程
        return Success();
    };
   ...
}

void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
              bool runFsck) {
    Timer t;
    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;

    // Ensure last reboot reason is reduced to canonical
    // alias reported in bootloader or system boot reason.
    //确保关机原因符合bootloader和system规范
    size_t skip = 0;
    std::vector<std::string> reasons = Split(reason, ",");
    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
         reasons[1] == "hard" || reasons[1] == "warm")) {
        skip = strlen("reboot,");
    }
    //设置persist.sys.boot.reason属性记录关机原因
    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
    sync();

    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;

    auto shutdown_timeout = 0ms;
    if (!SHUTDOWN_ZERO_TIMEOUT) {
        constexpr unsigned int shutdown_timeout_default = 6;
        constexpr unsigned int max_thermal_shutdown_timeout = 3;
        auto shutdown_timeout_final =
            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default);
        if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {
            shutdown_timeout_final = max_thermal_shutdown_timeout;
        }
        shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);
    }
    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";

    // keep debugging tools until non critical ones are all gone.
    const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
    // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
    const std::set<std::string> to_starts{"watchdogd"};
    for (const auto& s : ServiceList::GetInstance()) {
        if (kill_after_apps.count(s->name())) {
            s->SetShutdownCritical();
        } else if (to_starts.count(s->name())) {
            if (auto result = s->Start(); !result) {
                LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
                           << "': " << result.error();
            }
            s->SetShutdownCritical();
        } else if (s->IsShutdownCritical()) {
            // Start shutdown critical service if not started.
            if (auto result = s->Start(); !result) {
                LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
                           << "': " << result.error();
            }
        }
    }

    // remaining operations (specifically fsck) may take a substantial duration
    if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
        TurnOffBacklight();
    }
	//处理关机动画
    Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
    Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
    if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
        // will not check animation class separately
        for (const auto& service : ServiceList::GetInstance()) {
            if (service->classnames().count("animation")) service->SetShutdownCritical();
        }
    }

    // optional shutdown step可选关机步骤
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    //1、除了重要关机服务需要延迟关闭,关闭所有其他服务
    if (shutdown_timeout > 0ms) {
        LOG(INFO) << "terminating init services";

        // Ask all services to terminate except shutdown critical ones.
        for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
            if (!s->IsShutdownCritical()) s->Terminate();
        }

        int service_count = 0;
        // Only wait up to half of timeout here
        auto termination_wait_timeout = shutdown_timeout / 2;
        while (t.duration() < termination_wait_timeout) {
            ReapAnyOutstandingChildren();

            service_count = 0;
            for (const auto& s : ServiceList::GetInstance()) {
                // Count the number of services running except shutdown critical.
                // Exclude the console as it will ignore the SIGTERM signal
                // and not exit.
                // Note: SVC_CONSOLE actually means "requires console" but
                // it is only used by the shell.
                if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
                    service_count++;
                }
            }

            if (service_count == 0) {
                // All terminable services terminated. We can exit early.
                break;
            }

            // Wait a bit before recounting the number or running services.
            std::this_thread::sleep_for(50ms);
        }
        LOG(INFO) << "Terminating running services took " << t
                  << " with remaining services:" << service_count;
    }

    // minimum safety steps before restarting
    // 2. kill all services except ones that are necessary for the shutdown sequence.
    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
        if (!s->IsShutdownCritical()) s->Stop();
    }
    ReapAnyOutstandingChildren();

    // 3. send volume shutdown to vold
    //通知Vold服务关闭
    Service* voldService = ServiceList::GetInstance().FindService("vold");
    if (voldService != nullptr && voldService->IsRunning()) {
        ShutdownVold();
        voldService->Stop();
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }
    // logcat stopped here  关闭logcat
    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
        if (kill_after_apps.count(s->name())) s->Stop();
    }
    // 4. sync, try umount, and optionally run fsck for user shutdown
    sync();
    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
    // Follow what linux shutdown is doing: one more sync with little bit delay
    sync();
    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
    LogShutdownTime(stat, &t);
    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, rebootTarget); //关闭系统
    abort();
}

void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    ...
    switch (cmd) {
        case ANDROID_RB_POWEROFF: //根据前面流程可知走此case
            reboot(RB_POWER_OFF);
            break;
		...
	}
}

bionic\libc\bionic\reboot.cpp

extern "C" int __reboot(int, int, int, void*);
//准备进入kernel reboot
int reboot(int mode) {
  return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
}

kernel\msm-4.4\kernel\reboot.c

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	...
	switch (cmd) {
	...
	case LINUX_REBOOT_CMD_POWER_OFF:
		kernel_power_off();
		do_exit(0);
		break;
	...
	}
}

/**
 *	kernel_power_off - power_off the system
 *
 *	Shutdown everything and perform a clean system power_off.
 */
void kernel_power_off(void)
{
	kernel_shutdown_prepare(SYSTEM_POWER_OFF); //kernel关机准备:更新系统状态,关闭devices
	if (pm_power_off_prepare)
		pm_power_off_prepare(); //pm关机准备
	migrate_to_reboot_cpu();将kernel迁移至cpu0
	syscore_shutdown(); //核心系统关机:执行所有注册的系统核心回调
	pr_emerg("Power down\n");
	kmsg_dump(KMSG_DUMP_POWEROFF);
	machine_power_off(); //平台相关的关机指令,针对不同平台,编译不同的machine_power_off。
}
EXPORT_SYMBOL_GPL(kernel_power_off);

三、调试

3.1、frameworks\base\services

# cd $Android_Root_Path
source build/envsetup.sh
lunch 
mmm frameworks/base/services
# get out\target\product\xxx\system\framework\services.jar
# push out\target\product\xxx\system\framework\services.jar into [devices]/system/framework\services.jar
reboot

3.2、system\core\init

mmm system/core/init
#get out\target\product\xxx\system\bin\init
# push out\target\product\xxx\system\bin\init into [devices]/system/bin\init
reboot

注意: 由于kernel 对log频率做了限制,init log会存在不全情况,需要导入以下修改:

kernel\msm-4.4\kernel\printk\printk.c
static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from)
{
...
/* Ratelimit when not explicitly enabled. */
if (!(devkmsg_log & DEVKMSG_LOG_MASK_ON)) {
- if (!___ratelimit(&user->rs, current->comm))
- return ret;
+ //if (!___ratelimit(&user->rs, current->comm))
+ //return ret;
}
buf = kmalloc(len+1, GFP_KERNEL);
}
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月12日 0

暂无评论

推荐阅读
  R3jekIqJDwNw   2023年11月12日   19   0   0 android关机
R3jekIqJDwNw