首页 > 其他分享 >Android 9关机流程

Android 9关机流程

时间:2023-09-23 20:01:41浏览次数:43  
标签:关机 string power ... 流程 reboot shutdown Android

一、整体关机流程

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

标签:关机,string,power,...,流程,reboot,shutdown,Android
From: https://blog.51cto.com/u_15964349/7580684

相关文章

  • 过来Android码农提醒,不要有面试就去,可能会白跑一趟
    前言亲身经历!!面试失败总结(它失败,我也不想要的那种)正值毕业季,毕业生开始走向求职之路,大量求职者随之而来。再加上现在正值金九银十招聘的火热阶段。找工作的都知道,工作成功的最后一步是面试,所以很多求职者都选择有面试就去,这是不可取的。就拿本人最近的面试来说,通勤一个小时,明明之前......
  • Android程序员35岁的职业出路:寻找新的舞台
    前言转眼间已经到了奔四的年纪,岁月匆匆,时光荏苒,转眼间已经在Android行业干了8年,当前项目组也陆陆续续进入了不少00后,80后已经不见踪影,90后正在逐渐淡出,而我,也要开始迎接程序员35岁这个坎,心里还是想要继续做技术这条路,但是也给自己思索了一些转行之路,在此跟大家交流交流。为什么35岁......
  • Android DataBinding——导入以及生成的绑定类
    导入DataBinding库提供了导入、变量和include等功能。导入可以方便在引用布局文件中引用类;变量允许您描述可在绑定表达式中使用的属性;Includes允许您在整个应用程序中重用复杂的布局。ImportsImports允许您轻松地在布局文件中的引用类,就像在代码中一样。data元素内部可以使用零个或......
  • Android Failed to resolve: com.github.PhilJay:MPAndroidChart:v3.1.0
    2022.3.1版本修改settings.gradledependencyResolutionManagement{repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories{maven{url"https://jitpack.io"}//Addthisrepositoryjcenter()//orotherrep......
  • 熬过月余终见offer,一份Android面经
    前言最近我一直在牛客刷帖子看到好多对于现在IT环境的负面消息,自己也是找了一个多月Offer一个都没有,又看到这些感觉面试的勇气又少了…这种状态我根本就不知道任何转变,真的是投简历都不想投!就在这样的状态下,朋友说他那边内推有消息了,说待会HR会和我联系。怎么说了,并没有太多惊喜,因......
  • 干货:《Android 性能优化实战篇》带你杀穿金九银十
    近年来,由于疫情和互联网行业寒冬的影响,Android开发领域的竞争变得更加激烈。各大公司的业务线收缩和裁员成为了常规操作,导致职场环境日益严峻。作为一个螺丝钉,我们想要卷出重围,性能优化必须了解一下!本文将梳理Android性能优化的知识模块,帮助初学者了解应该从哪些方面入手学习性能优......
  • 金九银十Android面试该怎么有效的回答,看完这篇文章就懂了
    今年的严寒使得许多职场人的求职时间变长,以往火热的金三银四不再,金九银十九承载着广大程序员的希望。但是在这个特殊情况下,竞争压力也会增大,各大企业对于求职者的要求也随之增高。很多小伙伴都面临着这样的情况:千辛万苦拿到面试机会,却因各种原因翻车。“在面试的时候不能将自己的真......
  • Android开发笔记[4]-串口控制esp32及使用摄像头
    摘要无需root权限,Android使用串口与esp32通信控制小灯开关;开启Android摄像头预览.平台信息AndroidStudio:ElectricEel|2022.1.1Patch2Gradle:distributionUrl=https://services.gradle.org/distributions/gradle-7.5-bin.zipjvmTarget='1.8'minSdk21targetSdk......
  • android 实现左右滑动和底部菜单切换Demo
    packagecom.tools.ttt;importstaticandroid.content.ContentValues.TAG;importandroid.content.pm.ActivityInfo;importandroid.content.res.Configuration;importandroid.os.Bundle;importandroid.util.Log;importandroid.view.MenuItem;importcom.googl......
  • Android面试必问的6个问题,跟面试官斗智斗勇
    前言在职场中,面试是筛选和评估候选人的重要环节。那么对于各位程序员来讲,在面试的时候,面对HR的“套路”又该如何应对呢?以下是在职场中面试必问的6个问题,以及应对思路。一、请做一下简单的自我介绍面试时,基本上所有求职者听到的第一个问题都是这个,而一般人回答往往也只说姓名、年龄......