MTK平台关机流程和原因(二)

(1)ShutdownThread

从上一篇可以看到,最终会调用此类的shutdown以及reboot等函数,我们来看一下这些函数的实现。

(A)被调用函数

//frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

public static void shutdown(final Context context, String reason, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        mReason = reason;
        shutdownInner(context, confirm);
    }

public static void reboot(final Context context, String reason, boolean confirm) {
        mReboot = true;
        mRebootSafeMode = false;
        mRebootHasProgressBar = false;
        mReason = reason;
        shutdownInner(context, confirm);
    }

public static void rebootSafeMode(final Context context, boolean confirm) {
        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
        if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
            return;
        }

        mReboot = true;
        mRebootSafeMode = true;
        mRebootHasProgressBar = false;
        mReason = null;
        shutdownInner(context, confirm);
    }

public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {

        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
            reason = null;
        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator(context);
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }

            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException unused) {
            }
        }

        ///M: added for shutdown Enhancement@{
        sInstance.mLowLevelShutdownSeq(context);
        /// @}
        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }

(B)shutdownInner函数实现

接下来看一下shutdownInner内部实现。

private static void shutdownInner(final Context context, boolean confirm) {
        // ShutdownThread is called from many places, so best to verify here that the context passed
        // in is themed.
        context.assertRuntimeOverlayThemable();
        //xinghui add.avoid shutdown when monkey test.
        if (ActivityManager.isUserAMonkey()) {
            Log.d(TAG, "Cannot request to shutdown when Monkey is 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();
            }
            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();

            if (mReboot && !mRebootSafeMode) {
                sConfirmDialog.setTitle(com.android.internal.R.string.global_action_restart);
                sConfirmDialog.setMessage(context.getText(com.android.internal.R.string.reboot_confirm_question));
            }

            closer.dialog = sConfirmDialog;
            sConfirmDialog.setOnDismissListener(closer);
            sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
            sConfirmDialog.show();
        } else {
            beginShutdownSequence(context);
        }
    }

由于这段代码融合了phone,TV,tablet等平台,所以字符串有不同的区分。

<string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"您的平板电脑会关闭。"</string>
<string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"您的 Android TV 设备将关闭。"</string>
<string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"您的手表即将关机。"</string>
<string name="shutdown_confirm" product="default" msgid="136816458966692315">"您的手机将会关机。"</string>
<string name="shutdown_confirm_question" msgid="796151167261608447">"您要关机吗?"</string>

而具体是如何区分phone,TV,tablet等平台的,因为会根据系统的如下属性。

PRODUCT_CHARACTERISTICS := nosdcard
PRODUCT_CHARACTERISTICS := tv

ifndef PRODUCT_CHARACTERISTICS
   TARGET_AAPT_CHARACTERISTICS := default
 else
   TARGET_AAPT_CHARACTERISTICS := $(PRODUCT_CHARACTERISTICS)
 endif

ADDITIONAL_PRODUCT_PROPERTIES += ro.build.characteristics=$(TARGET_AAPT_CHARACTERISTICS)

将TARGET_AAPT_CHARACTERISTICS的值赋予ro.build.characteristics,而这个属性在Android编译完成后最终写入到/system目录下的build.prop文件。

(C)MTK重载的部分关机流程函数

这里要提一下,MTK平台有自定义部分关机流程函数,我们可以同步了解一下。

public class ShutdownThread extends Thread {

    protected boolean mIsShowShutdownSysui() {
        return true;
    }

    protected boolean mIsShowShutdownDialog(Context c) {
        return true;
    }
    //add by fenghuan for 开关机动画 end

    protected boolean mStartShutdownSeq(Context c, boolean IsReboot) {
        return true;
    }

    protected void mShutdownSeqFinish(Context c) {
        return;
    }

    protected void mLowLevelShutdownSeq(Context c) {
        return;
    }
}

(D)beginShutdownSequence函数实现

private static void beginShutdownSequence(Context context) {

        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }

        sInstance.mProgressDialog = showShutdownDialog(context);
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

        //...

        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        ///M: added for Shutdown Enhancement @{
        if(sInstance.mStartShutdownSeq(context, mReboot)) {
            sInstance.start();
        }
    }

(2)MtkShutdownThread

//vendor/mediatek/proprietary/frameworks/base/services/core/java/com/mediatek/server/MtkShutdownThread.java

public class MtkShutdownThread extends ShutdownThread {

	@Override
    protected boolean mIsShowShutdownSysui() {
        //...
        return true;
    }

    @Override
    protected boolean mIsShowShutdownDialog(Context context) {
        //...
        return true;
    }

    @Override
    protected boolean mStartShutdownSeq(Context context, boolean isReboot) {
        //...
        return true;
    }

    @Override
    protected void mShutdownSeqFinish(Context context) {
        //...
    }

    @Override
    protected void mLowLevelShutdownSeq(Context context) {
        //...
    }
}

(3)关机Log分析

大致关机Log如下:

03-30 07:48:25.865  1342  1342 D ShutdownThread: Notifying thread to start shutdown longPressBehavior=1
03-30 07:48:27.303  1342  1342 D ShutdownThread: Attempting to use SysUI shutdown UI
03-30 07:48:27.303  1342  1342 D ShutdownThread: SysUI handling shutdown UI

03-30 07:48:27.311  1342  1342 I MtkShutdownThread: hct screen turn off time shutdown_animation_play_time =6000
03-30 07:48:27.312  1342  1342 I MtkShutdownThread: screen turn off time screenTurnOffTime =6000

03-30 07:48:27.321  1342  7625 I ShutdownThread: Sending shutdown broadcast...
03-30 07:48:27.554  1342  7625 I ShutdownThread: Shutting down activity manager...
03-30 07:48:27.842  1342  7625 I ShutdownThread: Shutting down package manager...
03-30 07:48:27.888  1342  7630 W ShutdownThread: Turning off cellular radios...
03-30 07:48:27.902  1342  7630 I ShutdownThread: Waiting for Radio...
03-30 07:48:28.413  1342  7630 I ShutdownThread: Radio turned off.
03-30 07:48:28.414  1342  7630 I ShutdownThread: Radio shutdown complete.

03-30 07:48:28.415  1342  7625 I MtkShutdownThread: mShutOffAnimation: 1

03-30 07:48:28.418  1342  7625 I ShutdownThread: rebootOrShutdown:goToSleep
03-30 07:48:28.918  1342  7625 I ShutdownThread: Rebooting, reason: user requested

在boot_normal中可以看到开机原因。

//adb reboot
bootstat: Canonical boot reason: reboot,shell
bootstat: Last reboot reason read from /metadata/bootstat/persist.sys.boot.reason : reboot,shell. Last reboot reason read from persist.sys.boot.reason : reboot,shell
bootstat: Normalized last reboot reason : reboot,shell

//adb reboot -p
bootstat: Canonical boot reason: cold,powerkey
bootstat: Last reboot reason read from /metadata/bootstat/persist.sys.boot.reason : shutdown,shell. Last reboot reason read from persist.sys.boot.reason : shutdown,shell
bootstat: Normalized last reboot reason : shutdown,shell

//power键关机
bootstat: Canonical boot reason: cold,powerkey
bootstat: Last reboot reason read from /metadata/bootstat/persist.sys.boot.reason : shutdown,userrequested. Last reboot reason read from persist.sys.boot.reason : shutdown,userrequested
bootstat: Normalized last reboot reason : shutdown,userrequested

//power键重启
bootstat: Canonical boot reason: reboot,userrequested
bootstat: Last reboot reason read from /metadata/bootstat/persist.sys.boot.reason : reboot,userrequested. Last reboot reason read from persist.sys.boot.reason : reboot,userrequested
bootstat: Normalized last reboot reason : reboot,userrequested
### Android MTK 平台关机充电流程解析 #### 1. 关机充电背景 在Android设备中,特别是在MTK平台上,当设备处于关机状态并连接到电源适配器时,系统会进入一种特殊的模式——关机充电模式。这种模式允许电池在不启动操作系统的情况下完成快速充电过程[^3]。 #### 2. PowerManagerService 的角色 `PowerManagerService.java` 是 Android 系统框架中的核心服务之一,负责管理设备的电源状态唤醒锁等功能。对于关机充电场景而言,该文件定义了一些关键逻辑用于处理设备从正常运行切换至关机以及后续的充电行为[^1]。 以下是 `PowerManagerService` 中可能涉及的关键方法片段: ```java private void handleShutdown() { // 处理关机请求的具体逻辑 if (mShuttingDown) { nativeHandleShutdown(); // 调用本地接口执行实际操作 } } ``` 上述代码展示了如何通过调用原生函数来触发硬件级别的关机动作。此阶段完成后,设备将完全断电,并等待外部条件满足后再重新激活某些模块以支持关机充电功能。 #### 3. 内核重启机制 一旦设备正式进入关机序列,Linux内核层面也会参与其中。具体来说,`do_kernel_restart()` 函数被用来通知所有注册过的重启处理器准备行动。这些处理器通常由驱动程序提供,它们各自承担不同的职责,比如保存当前上下文或者配置特定寄存器以便于之后恢复工作环境[^2]。 下面是一个简化版的例子展示这一过程的一部分实现细节: ```c void do_kernel_restart(char *cmd) { atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd); } ``` 这里利用了原子通知链表结构 (`atomic_notifier_call_chain`) 来遍历所有的回调函数实例列表(`&restart_handler_list`) ,从而确保每一个必要的组件都能得到适当的通知去响应即将发生的改变 —— 即系统的彻底关闭或者是转换成备用模式如shutdown charging mode . #### 4. 低电量自动关机阈值设定 针对不同型号的产品设计需求,制造商往往希望自定义设置较低水平下的剩余容量百分比作为强制切断供电的标准。例如,在版本号为9.0及以上的新一代安卓智能手机里,默认情况下如果检测到低于某个预定比例(比如说百分之),则立即实施紧急停运措施以防过度耗尽锂电池单元内部储存的能量储备造成永久损害等问题发生[^4]。 为此目的所作的主要改动集中在以下几个方面: - **核心类别位置**: 文件路径位于 `frameworks\base\services\core\java\com\android\server\BatteryService.java`. - **主要功能描述**: 修改现有算法使得能够识别新的临界点数值并且依据它做出相应的决策反应. #### 结论 综上所述,我们可以看到整个 android mtk shutdown charging process 不仅涉及到高层级的应用层面上的服务控制,同时也深入到了中间件乃至最底层的操作系统内核部分。只有全面理解各个层次之间的相互作用关系才能真正掌握其全貌并有效解决可能出现的各种复杂状况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪舞飞影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值