android 关机重启流程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 关机重启流程相关的知识,希望对你有一定的参考价值。

参考技术A https://developer.android.com/intl/zh-CN/reference/android/os/PowerManager.html

在PowerManager的API文档中,给出了一个关机/重启接口:
public void reboot (String reason)
对于这个接口的描述很简单,就是几句话。
接口的作用就是重启设备,而且,就算重启成功了也没有返回值。
需要包含REBOOT权限,也就是android.permission.REBOOT
唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。

1.frameworks/base/core/java/android/os/PowerManager.java
2.frameworks/base/core/java/android/os/IPowerManager.aidl
3.frameworks/base/services/java/com/android/server/PowerManagerService.java
4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
---------------------》
6.system/core/libcutils/android_reboot.c
7.bionic/libc/unistd/reboot.c

8.__reboot通过syscall来到内核
9.kernel/sys.c

frameworks/base/core/java/android/os/PowerManager.java

mService为IPowerManager Binder接口服务。

frameworks/base/core/java/android/os/IPowerManager.aidl

frameworks/base/services/java/com/android/server/PowerManagerService.java

frameworks/base/services/java/com/android/server/pm/ShutdownThread.java

这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。

重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行。
frameworks/base/services/java/com/android/server/pm/ShutdownThread.java

在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。

如果确认重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。

frameworks/base/services/java/com/android/server/PowerManagerService.java

frameworks/base/services/jni/com_android_server_PowerManagerService.cpp

可以看到无论是关机还是重启,都是调用android_reboot来实现的,只是参数不一样而已。

system/core/libcutils/android_reboot.c

以reboot recovery为例,arg即为recovery,所在在第五步的时候会传入ANDROID_RB_RESTART2。到了android_reboot函数中,会看到这样的定义#ifdef RECOVERY_PRE_COMMAND,即属于重启前会执行的命令,如果定义了就会执行。

下面也是做了一些关机重启前的预处理工作,sync()作用是将缓存中的信息写入磁盘,以免程序异常结束导致文件被损坏,linux系统关机前会做几次这样的动作;而remount_ro()作用是通过调用emergency_remount()强制将文件系统挂载为只读,不再允许任何写入操作,同时会通过检查/proc/mounts的设备状态来确认是否当前的所有写入工作已经完成,这个检查过程是阻塞操作。

接下来才是对参数的解析处理:

1)普通重启 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;

2)关机 ANDROID_RB_POWEROFF, 无需reason,直接调用reboot进行关机;

3)带参数的特殊重启 ANDROID_RB_RESTART2, reason 将为默认值 -1

这里又出现一个#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定义了它,则无论上层传下来的参数是什么样的,最终都只是普通重启而已。定义它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,应该有厂商会喜欢这么做的,毕竟除了普通重启,都可能带给用户一定的风险。

最后会对reason进行一个检测,那么通过上边的分析,其实只有带参数的特殊重启才会为-1,而不等于-1的情况中有普通重启和关机,而关机已经自行解决了……所以,不等于-1的情况到了这里也只有普通重启了。最终这里就是区分普通重启与特殊重启的地方了。这里再插入一个问题,其他的几个cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:

reboot(reason) -> reboot(RB_AUTOBOOT) -> __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL )
__reboot通过syscall来到内核bionic/libc/arch-arm/syscalls/__reboot.S

其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。

已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,在include/asm-generic/unistd.h中可以找到内核对__NR_reboot的syscall函数映射,即

同时,能够发现如此温馨的一幕,内核已经指引我们下一步该去哪里寻找sys_reboot,即kernel/sys.c。

include/linux/syscalls.h

与__reboot的调用参数一致。

进入sys.c文件后,并没有找到名为sys_reboot的函数,而通过仔细查找,发现一个很有趣的函数,其定义为SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),对比__reboot的参数,能够符合。究竟是不是这个函数?

同样在include/linux/syscalls.h文件中,能够找到这样几个定义:

而pm_power_off为空的话,就把用户的关机命令转换为挂起:
arch/arm/kernel/process.c

pm_power_off = msm_pm_power_off;

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)
这个过程是用reboot_mutex互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。

bionic/libc/include/sys/reboot.h 中可以看到android定义的启动方式
RESTART
POWER_OFF
RESTART2

对框架进行赋值,qcom 平台 845上已经不是这函数,自己查找
arm_pm_restart = msm_pm_restart;

下面是qcom 实现,每个平台不同

可以在跟踪这个流程的过程中会发现,确实是有存在关机的相关接口的。那么关机该怎么用呢?
frameworks/base/services/java/com/android/serverBatteryService.java

重启方式: 最后就是设定寄存器,Uboot 解析不同寄存器的值进入不同的启动模式

recovery 如果传下来的字符串是recovery那么,就在RTC寄存器里设置某个特定值,当uboot里读取RTC寄存器的时候如果获取了这个特定值,那就可以起recovery这个动作了。

Ref: https://blog.csdn.net/leerobin83/article/details/7162751

上面主要讲到流程,在实际开发中, 主动调用系统开机关机如何做
(Ref: https://blog.csdn.net/luzhenrong45/article/details/42092007 )
一. 发送系统广播方式
二. 通过init.rc启动系统服务来运行sh文件
三. Runtime调用Linux-shell
四 . PowerManager reboot以及反射调用PowerManagerService shutdown
五.使用ShutdownThread (尝试不成功,但想法觉得可行)

Intent.java位于源码/frameworks/base/core/java/android/content/Intent.java下面

脚本方式,实际都是基于指令的

使用PowerManager 或ShutdownThread 都是基于关机流程

Android 12 关机重启流程

文章托管在gitee上 Android Notes , 同步csdn
本文基于Android12 分析

关机流程

Android上层触发关机的入口很多,但最终几乎都是调用ShutdownThread.shutdown来实现。如下是一些常见的调用关机的点:

  • StatusBarManagerService#shutdown, 这个主要是对接SystemUI
  • WindowManagerService#shutdown, 以WindowManagerFuncs接口提供给系统其他模块使用,诸如GlobalActions、PhoneWindowManager。
  • PowerManager#shutdown, 以binder服务形式提供给客户端调用,需要持有android.permission.REBOOT权限。
  • 通过action启动ShutdownActivity请求关机重启,需要权限 android.permission.SHUTDOWN
    ACTION_REQUEST_SHUTDOWN = “com.android.internal.intent.action.REQUEST_SHUTDOWN”;

以PowerManager#shutdown调用为例,来分析一下关机流程

PowerManager#shutdown

mService实际上是PowerManagerService binder服务的bp端, 执行mService.shutdown实际上是向 PMS binder服务发起调用

/**
 * Turn off the device.
 *
 * @param confirm If true, shows a shutdown confirmation dialog.
 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
 * @param wait If true, this call waits for the shutdown to complete and does not return.
 *
 * @hide
 */
public void shutdown(boolean confirm, String reason, boolean wait) 
    try  // 向 PowerManagerService 发起binder调用
        mService.shutdown(confirm, reason, wait);
     catch (RemoteException e) 
        throw e.rethrowFromSystemServer();
    

PowerManagerService$BinderService#shutdown

上面的调用将会调用以下代码

/**
 * Shuts down the device.
 *
 * @param confirm If true, shows a shutdown confirmation dialog.
 * @param wait If true, this call waits for the shutdown to complete and does not return.
 */
@Override // Binder call
public void shutdown(boolean confirm, String reason, boolean wait) 
    // 检查执行是否有
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    // 记录 shutdown 事件,会在log中进行打印
    ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
    final long ident = Binder.clearCallingIdentity();
    try  // 直接给 PowerManagerService 处理
        shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
     finally 
        Binder.restoreCallingIdentity(ident);
    

ShutdownCheckPoints#recordCheckPoint

ShutdownCheckPoints 类是一个专门用来记录关机重启详细信息,如调用者pid,原因

/** Records the pid of the caller process as a shutdown check point. */
public static void recordCheckPoint(int callerProcessId, @Nullable String reason) 
    INSTANCE.recordCheckPointInternal(callerProcessId, reason);


@VisibleForTesting
void recordCheckPointInternal(int callerProcessId, @Nullable String reason) 
    recordCheckPointInternal(callerProcessId == Process.myPid()
            ? new SystemServerCheckPoint(mInjector, reason)
            : new BinderCheckPoint(mInjector, callerProcessId, reason));
    // systme log 中打印 shutdown 原因
    Slog.v(TAG, "Binder shutdown checkpoint recorded with pid=" + callerProcessId);


PowerManagerService#shutdownOrRebootInternal

这个函数处理包含了关机和重启的处理,通过调用ShutdownThread.shutdown/reboot 实现。

private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
        @Nullable final String reason, boolean wait) 
    if (PowerManager.REBOOT_USERSPACE.equals(reason)) 
        if (!PowerManager.isRebootingUserspaceSupportedImpl())  // 检查是否支持userspace reboot
            throw new UnsupportedOperationException(
                    "Attempted userspace reboot on a device that doesn't support it");
        
        UserspaceRebootLogger.noteUserspaceRebootWasRequested();
    
    if (mHandler == null || !mSystemReady)  // 系统还没准备好
        if (RescueParty.isAttemptingFactoryReset())  // RescueParty 正执行恢复出厂
            // If we're stuck in a really low-level reboot loop, and a
            // rescue party is trying to prompt the user for a factory data
            // reset, we must GET TO DA CHOPPA!
            // No check point from ShutdownCheckPoints will be dumped at this state.
            PowerManagerService.lowLevelReboot(reason);
         else  // 给caller 抛出一个异常
            throw new IllegalStateException("Too early to call shutdown() or reboot()");
        
    

    Runnable runnable = new Runnable() 
        @Override
        public void run() 
            synchronized (this) 
                if (haltMode == HALT_MODE_REBOOT_SAFE_MODE)  // 重启到安全模式
                    ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                 else if (haltMode == HALT_MODE_REBOOT)  // 重启
                    ShutdownThread.reboot(getUiContext(), reason, confirm);
                 else  // 关机
                    ShutdownThread.shutdown(getUiContext(), reason, confirm);
                
            
        
    ;

    // ShutdownThread must run on a looper capable of displaying the UI.
    Message msg = Message.obtain(UiThread.getHandler(), runnable);
    msg.setAsynchronous(true); // 设置异步防止被阻塞
    UiThread.getHandler().sendMessage(msg); // 通过发送消息处理

    // PowerManager.reboot() is documented not to return so just wait for the inevitable.
    if (wait)   // 阻塞到完成关机重启
        synchronized (runnable) 
            while (true) 
                try 
                    runnable.wait();
                 catch (InterruptedException e) 
                
            
        
    

ShutdownThread#shutdown

confirm 参数表示是否需要确认,若需要确认会弹出一个对话框。

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

ShutdownThread#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();

    // ensure that only one thread is trying to power down.
    // any additional calls are just returned
    synchronized (sIsStartedGuard) 
        if (sIsStarted)  // 进行中
            Log.d(TAG, "Request to shutdown already running, returning.");
            return;
        
    

    // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but
    // this point preserves the system trace of the trigger point of the ShutdownThread.
    ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);

    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();
        closer.dialog = sConfirmDialog;
        sConfirmDialog.setOnDismissListener(closer);
        sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        sConfirmDialog.show();
     else  // 直接进行关机流程
        beginShutdownSequence(context);
    

ShutdownThread#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);

    // make sure we never fall asleep again
    sInstance.mCpuWakeLock = null;
    try  // 防止机器休眠
        sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
        sInstance.mCpuWakeLock.setReferenceCounted(false);
        sInstance.mCpuWakeLock.acquire();
     catch (SecurityException e) 
        Log.w(TAG, "No permission to acquire wake lock", e);
        sInstance.mCpuWakeLock = null;
    

    // also make sure the screen stays on for better user experience
    sInstance.mScreenWakeLock = null;
    if (sInstance.mPowerManager.isScreenOn())  // 保持屏幕长亮
        try 
            sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
            sInstance.mScreenWakeLock.setReferenceCounted(false);
            sInstance.mScreenWakeLock.acquire();
         catch (SecurityException e) 
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mScreenWakeLock = null;
        
    

    if (SecurityLog.isLoggingEnabled()) 
        SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
    

    // start the thread that initiates shutdown
    sInstance.mHandler = new Handler() 
    ;
    sInstance.start(); // sInstance是ShutdownThread对象 其继承自Thread,开启线程执行关机流程

ShutdownThread#run

/**
 * Makes sure we handle the shutdown gracefully.
 * Shuts off power regardless of radio state if the allotted time has passed.
 */
public void run() 
    TimingsTraceLog shutdownTimingLog = newTimingsLog();
    shutdownTimingLog.traceBegin("SystemServerShutdown");
    metricShutdownStart();
    metricStarted(METRIC_SYSTEM_SERVER);

    // Start dumping check points for this shutdown in a separate thread.
    Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(
            new File(CHECK_POINTS_FILE_BASENAME));// 将记录写入/data/system/shutdown-checkpoints/checkpoints
    dumpCheckPointsThread.start();

    BroadcastReceiver br = new BroadcastReceiver() 
        @Override public void onReceive(Context context, Intent intent) 
            // We don't allow apps to cancel this, so ignore the result.
            actionDone(); // 结束关机广播处理
        
    ;

    /*
     * Write a system property in case the system_server reboots before we
     * get to the actual hardware restart. If that happens, we'll retry at
     * the beginning of the SystemServer startup.
     */
    
        String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
        SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
    

    /*
     * If we are rebooting into safe mode, write a system property
     * indicating so.
     */
    if (mRebootSafeMode) 
        SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
    

    shutdownTimingLog.traceBegin("DumpPreRebootInfo");
    try 
        Slog.i(TAG, "Logging pre-reboot information...");
        PreRebootLogger.log(mContext);
     catch (Exception e) 
        Slog.e(TAG, "Failed to log pre-reboot information", e);
    
    shutdownTimingLog.traceEnd(); // DumpPreRebootInfo

    metricStarted(METRIC_SEND_BROADCAST);
    shutdownTimingLog.traceBegin("SendShutdownBroadcast");
    Log.i(TAG, "Sending shutdown broadcast...");

    // First send the high-level shut down broadcast.
    mActionDone = false;
    Intent intent = new Intent(Intent.ACTION_SHUTDOWN); // 发送关机广播
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    mContext.sendOrderedBroadcastAsUser(intent,
            UserHandle.ALL, null, br, mHandler, 0, null, null);

    final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; // 10s 超时
    synchronized (mActionDoneSync)  // 等待关机广播处理完成,上面调用通知actionDone
        while (!mActionDone) 
            long delay = endTime - SystemClock.elapsedRealtime();
            if (delay <= 0) 
                Log.w(TAG, "Shutdown broadcast timed out");
                break;
             else if (mRebootHasProgressBar) 
                int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                        BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                sInstance.setRebootProgress(status, null);
            
            try 
                mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
             catch (InterruptedException e) 
            
        
    
    if (mRebootHasProgressBar) 
        sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
    
    shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
    metricEnded(METRIC_SEND_BROADCAST);

    Log.i(TAG, "Shutting down activity manager...");
    shutdownTimingLog.traceBegin("ShutdownActivityManager");
    metricStarted(METRIC_AM);

    final IActivityManager am =
            IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
    if (am != null)  // ams 处理shutdown
        try 
            am.shutdown(MAX_BROADCAST_TIME);
         catch (RemoteException e) 
        
    
    if (mRebootHasProgressBar) 
        sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
    
    shutdownTimingLog.traceEnd();// ShutdownActivityManager
    metricEnded(METRIC_AM);

    Log.i(TAG, "Shutting down package manager...");
    shutdownTimingLog.traceBegin("ShutdownPackageManager");
    metricStarted(METRIC_PM);

    final PackageManagerService pm = (PackageManagerService)
        ServiceManager.getService("package");
    if (pm != null)  // pms 处理shutdown
        pm.shutdown();
    
    if (mRebootHasProgressBar) 
        sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
    
    shutdownTimingLog.traceEnd(); // ShutdownPackageManager
    metricEnded(METRIC_PM);

    // Shutdown radios.
    shutdownTimingLog.traceBegin("ShutdownRadios");
    metricStarted(METRIC_RADIOS);
    shutdownRadios(MAX_RADIO_WAIT_TIME);
    if (mRebootHasProgressBar) 
        sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
    
    shutdownTimingLog.traceEnd(); // ShutdownRadios
    metricEnded(METRIC_RADIOS);

    if (mRebootHasProgressBar) 
        sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

        // If it's to reboot to install an update and uncrypt hasn't been
        // done yet, trigger it now.
        uncrypt();
    

    // Wait for the check points dump thread to finish, or kill it if not finished in time.
    shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
    try 
        dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
     catch (InterruptedException ex)  // 等待记录写入完成
    
    shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait

    shutdownTimingLog.traceEnd(); // SystemServerShutdown
    metricEnded(METRIC_SYSTEM_SERVER);
    saveMetrics(mReboot, mReason);
    // Remaining work will be done by init, including vold shutdown
    rebootOrShutdown(mContext, mReboot, mReason); // 继续执行

ShutdownThread#rebootOrShutdown

通过PowerManagerService来实现 shutdown/reboot

/**
 * Do not call this directly. Use @link #reboot(Context, String, boolean)
 * or @link #shutdown(Context, String, boolean) instead.
 *
 * @param context Context used to vibrate or null without vibration
 * @param reboot true to reboot or false to shutdown
 * @param reason reason for reboot/shutdown
 */
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) 
        
    
    // Shutdown power
    Log.i(TAG, "Performing low-level shutdown...");
    PowerManagerService.lowLevelShutdown(reason); // 关机

PowerManagerService#lowLevelShutdown

此方法通过设置属性sys.powerctl来通知init处理shutdown

/**
 * Low-level function turn the device off immediately, without trying
 * to be clean.  Most people should use @link ShutdownThread for a clean shutdown.
 *
 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
 */
public static void lowLevelShutdown(String reason) 
    if (reason == null) 
        reason = "";
    
    SystemProperties.set("sys.powerctl", "shutdown," + reason);

设置属性sys.powerctl值,init的property service将会接受设置的值并进行处理。

HandlePropertySet

init处理设置属性

// @system/core/init/property_service.cpp
// 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,
                           SocketConnection* socket, std::string* error) 
    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) 
        return ret;
    

    if (StartsWith(name, "ctl."))  // 处理 ctl.xxx
        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, 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());
        
        //  打印log
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                  << process_log_string;
        if (!value.empty()) 
            DebugRebootLogging();
        
        if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) 
            *error = "Userspace reboot is not supported by this device";
            return PROP_ERROR_INVALID_VALUE;
        
    

    // If a process other than init is writing a non-empty value, it means that process is
    // requesting that init performs a restorecon operation on the path specified by 'value'.
    // We use a thread to do this restorecon operation to prevent holding up init, as it may take
    // a long time to complete.
    if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) 
        static AsyncRestorecon async_restorecon;
        async_restorecon.TriggerRestorecon(value);
        return PROP_SUCCESS;
    

    return PropertySet(name, value, error); // 设置属性

PropertyChanged

当设置成功后,会调用PropertyChanged

// @system/core/init/init.cpp
void PropertyChanged(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")  // 处理sys.powerctl属性变化
        trigger_shutdown(value); // 触发关机
    

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

    prop_waiter_state.CheckAndResetWait(name, value);

trigger_shutdown在SecondStageMain中定义,是一个lambda表达式

int SecondStageMain(int argc, char** argv) 
    ...
    trigger_shutdown = [](const std::string& command)  shutdown_state.TriggerShutdown(command); ;
    ...

因此接下来调用 ShutdownState#TriggerShutdown

// @system/core/init/init.cpp
void TriggerShutdown(const std::string& command) 
    // We can't call HandlePowerctlMessage() directly 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.
    auto lock = std::lock_guardshutdown_command_lock_;
    shutdown_command_ = command;
    do_shutdown_ = true;
    WakeMainInitThread(); // 唤醒主线程处理

在主线程的循环中处理 shutdown command

// @system/core/init/init.cpp
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) 
    LOG(INFO) << "Got shutdown_command '" << *shutdown_command
              << "' Calling HandlePowerctlMessage()";
    HandlePowerctlMessage(*shutdown_command);
    shutdown_state.set_do_shutdown(false);

HandlePowerctlMessage

// @system/core/init/reboot.cpp
void 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;
    bool userspace_reboot = false;

    if (cmd_params[0] == "shutdown")  // 处理关机
        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];
            if (reboot_target == "userspace") 
                LOG(INFO) << "Userspace reboot requested";
                userspace_reboot = true;
            
            // adb reboot fastboot should boot into bootloader for devices not
            // supporting logical partitions.
            if (reboot_target == "fastboot" &&
                !android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) 
                reboot_target = "bootloader";
            
            // 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;
                
             else if (reboot_target == "recovery") 
                bootloader_message boot = ;
                if (std::string err; !read_bootloader_message(&boot, &err)) 
                    LOG(ERROR) << "Failed to read bootloader message: " << err;
                
                // Update the boot command field if it's empty, and preserve
                // the other arguments in the bootloader message.
                if (!CommandIsPresent(&boot)) 
                    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
                    if (std::string err; !write_bootloader_message(boot, &err)) 
                        LOG(ERROR) << "Failed to set bootloader message: " << err;
                        return;
                    
                
             else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
                       reboot_target == "fastboot") 
                std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
                                                                          : reboot_target;
                const std::vector<std::string> options = 
                        "--" + arg,
                ;
                std::string err;
                if (!write_bootloader_message(options, &err)) 
                    LOG(ERROR) << "Failed to set bootloader message: " << err;
                    return;
                
                reboot_target = "recovery";
            

            // If there are additional parameter, pass them along
            for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) 
                reboot_target += "," + cmd_params[i];
            
        
     else 
        command_invalid = true;
    
    if (command_invalid) 
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return;
    

    // We do not want to process any messages (queue'ing triggers, shutdown messages, control
    // messages, etc) from properties during reboot.
    StopSendingMessages();

    if (userspace_reboot) 
        HandleUserspaceReboot();
        return;
    

    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 Result<void>;
    ;
    // 将shutdown封装为<Builtin Action>并添加到队列,之后在主循环被处理,接着DoReboot将被调用
    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");

    EnterShutdown();

cmd 定义如下

// @system/core/libcutils/include/cutils/android_reboot.h
/* Commands */
#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
#define ANDROID_RB_THERMOFF 0xDEAD0004

EnterShutdown 用以清除wait prop和 pending exec flag

static void EnterShutdown() 
    LOG(INFO) << "Entering shutdown mode";
    shutting_down = true;
    // Skip wait for prop if it is in progress
    ResetWaitForProp();
    // Clear EXEC flag if there is one pending
    for (const auto& s : ServiceList::GetInstance()) 
        s->UnSetExec();
    

DoReboot

接下来,在shutdown action被处理后,DoReboot被调用

// @system/core/init/reboot.cpp
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
// reboot_target Reboot target string like "bootloader". Otherwise, it should be an empty string.
// run_fsck Whether to run fsck after umount is done.
//
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
                     bool run_fsck) 
    Timer t;
    LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;

    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;

    auto shutdown_timeout = 0ms;
    if (!SHUTDOWN_ZERO_TIMEOUT)  // 计算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";

    sem_t reboot_semaphore;
    if (sem_init(&reboot_semaphore, false, 0) == -1) 
        // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
        LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
        RebootSystem(cmd, reboot_target);
    

    // Start a thread to monitor init shutdown process
    LOG(INFO) << "Create reboot monitor thread.";
    bool reboot_monitor_run = true;
    // 创建 monitor 线程
    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,
                                      shutdown_timeout, &reboot_monitor_run);
    reboot_monitor_thread.detach();

    // Start reboot monitor thread
    sem_post(&reboot_semaphore);

    // Ensure last reboot reason is reduced to canonical
    // alias reported in bootloader or system boot reason.
    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,");
    
    // 写重启原因到属性和文件
    // /* Android reboot reason stored in this property */
    // #define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
    // #define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY
    PersistRebootReason(reason.c_str() + skip, true);

    // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
    // worry about unmounting it.
    if (!IsDataMounted())  // data没有挂载,直接调用RebootSystem
        sync();
        RebootSystem(cmd, reboot_target);
        abort();
    

    // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
    const std::set<std::string> to_starts"watchdogd";
    std::set<std::string> stop_first;
    for (const auto& s : ServiceList::GetInstance()) 
        if (kDebuggingServices.count(s->name())) 
            // keep debugging tools until non critical ones are all gone.
            s->SetShutdownCritical();
         else if (to_starts.count(s->name())) 
            if (auto result = s->Start(); !result.ok()) 
                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.ok()) 
                LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
                           << "': " << result.error();
            
         else 
            stop_first.insert(s->name());
        
    

    // remaining operations (specifically fsck) may take a substantial duration
    if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) 
        TurnOffBacklight();
    

    // 处理关机动画
    Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
    Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
    if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) 
        bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);

        if (do_shutdown_animation) 
            SetProperty("service.bootanim.exit", "0");
            SetProperty("service.bootanim.progress", "0");
            // Could be in the middle of animation. Stop and start so that it can pick
            // up the right mode.
            boot_anim->Stop();
        

        for (const auto& service : ServiceList::GetInstance()) 
            if (service->classnames().count("animation") == 0) 
                continue;
            

            // start all animation classes if stopped.
            if (do_shutdown_animation) 
                service->Start();
            
            service->SetShutdownCritical();  // will not check animation class separately
        

        if (do_shutdown_animation) 
            boot_anim->Start();
            surface_flinger->SetShutdownCritical();
            boot_anim->SetShutdownCritical();
        
    

    // optional shutdown step
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    if (shutdown_timeout > 0ms) 
        StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
    
    // Send SIGKILL to ones that didn't terminate cleanly.
    StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
    SubcontextTerminate();
    // Reap subcontext pids.
    ReapAnyOutstandingChildren();

    // 3. send volume abort_fuse and volume shutdown to vold
    Service* vold_service = ServiceList::GetInstance().FindService("vold");
    if (vold_service != nullptr && vold_service->IsRunning()) 
        // Manually abort FUSE connections, since the FUSE daemon is already dead
        // at this point, and unmounting it might hang.
        CallVdc("volume", "abort_fuse");
        CallVdc("volume", "shutdown");
        vold_service->Stop();
     else 
        LOG(INFO) << "vold not running, skipping vold shutdown";
    
    // logcat stopped here 停止 logcat
    StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
    // 4. sync, try umount, and optionally run fsck for user shutdown
    
        Timer sync_timer;
        LOG(INFO) << "sync() before umount...";
        sync();
        LOG(INFO) << "sync() before umount took" << sync_timer;
    
    // 5. drop caches and disable zram backing device, if exist
    KillZramBackingDevice();

    LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
    // 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
    if (auto ret = UnmountAllApexes(); !ret.ok()) 
        LOG(ERROR) << ret.error();
    
    UmountStat stat =
            TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
    // Follow what linux shutdown is doing: one more sync with little bit delay
    
        Timer sync_timer;
        LOG(INFO) << "sync() after umount...";
        sync();
        LOG(INFO) << "sync() after umount took" << sync_timer;
    
    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
    LogShutdownTime(stat, &t);

    // Send signal to terminate reboot monitor thread.
    reboot_monitor_run = false;
    sem_post(&reboot_semaphore);

    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, reboot_target); /// 执行关机重启
    abort();

RebootSystem

// @system/core/init/reboot_utils.cpp
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) 
    LOG(INFO) << "Reboot ending, jumping to kernel";

    if (!IsRebootCapable()) 
        // On systems where init does not have the capability of rebooting the
        // device, just exit cleanly.
        exit(0);
    

    switch (cmd) 
        case ANDROID_RB_POWEROFF: // 关机
            reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2: // 重启
            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
            break;

        case ANDROID_RB_THERMOFF:
            if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) 
                LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
                static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
                syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                        LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
             else 
                reboot(RB_POWER_OFF);
            
            break;
    
    // In normal case, reboot should not return.
    PLOG(ERROR) << "reboot call returned";
    abort();


接着调用bionic 的 reboot 函数:

// @bionic/libc/bionic/reboot.cpp
#include <unistd.h>
#include <sys/reboot.h>

extern "C" int __reboot(int, int, int, void*);

int reboot(int mode) 
  // 调用 linux reboot 函数
  return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, nullptr);

C++层关机入口

在C++代码里面可以直接调用 android_reboot 来实现关机重启

// @system/core/libcutils/include/cutils/android_reboot.h
/* Commands */
#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
#define ANDROID_RB_THERMOFF 0xDEAD0004

/* Properties */
#define ANDROID_RB_PROPERTY "sys.powerctl"

/* Android reboot reason stored in this property */
#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
#define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY

/* Reboot or shutdown the system.
 * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
 * Due to that, process calling this should have proper selinux permission
 * to write to the property or the call will fail.
 */
int android_reboot(unsigned cmd, int flags, const char* arg);

android_reboot

// @system/core/libcutils/android_reboot.cpp
int android_reboot(unsigned cmd, int /*flags*/, const char* arg) 
    int ret;
    const char* restart_cmd = NULL;
    char* prop_value;

    switch (cmd) 
        case ANDROID_RB_RESTART:  // deprecated
        case ANDROID_RB_RESTART2:
            restart_cmd = "reboot";
            break;
        case ANDROID_RB_POWEROFF:
            restart_cmd = "shutdown";
            break;
        case ANDROID_RB_THERMOFF:
            restart_cmd = "shutdown,thermal";
            break;
    
    if (!restart_cmd) return -1;
    if (arg && arg[0]) 
        ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
     else 
        ret = asprintf(&prop_value, "%s", restart_cmd);
    
    if (ret < 0) return -1;
    // 设置属性sys.powerctl, 和上层一样都会触发init执行关机
    ret = property_set(ANDROID_RB_PROPERTY, prop_value);
    free(prop_value);
    return ret;

关机流程总结

Java层:

ShutdownThread#shutdown -> PowerManagerService#lowLevelShutdown -> 设置属性"sys.powerctl" -> init处理关机 -> bionic reboot -> linux __reboot

C++层

android_reboot -> 设置属性"sys.powerctl" -> init处理关机 -> bionic reboot -> linux __reboot

重启流程

Android上层重启的入口与关机流程类似,只是调用的方法从shutdown变成了 reboot,也是调用 ShutdownThread.reboot 来实现。如下是一些常见的调用关机的点:

  • StatusBarManagerService#reboot, 这个主要是对接SystemUI
  • WindowManagerService#reboot, 以WindowManagerFuncs接口提供给系统其他模块使用,诸如GlobalActions、PhoneWindowManager。
  • PowerManager#reboot, 以binder服务形式提供给客户端调用,需要持有android.permission.REBOOT权限。
  • 通过action启动ShutdownActivity请求关机重启,需要权限 android.permission.SHUTDOWN
    ACTION_REQUEST_SHUTDOWN = “com.android.internal.intent.action.REQUEST_SHUTDOWN”;

ShutdownThread#reboot

/**
 * Request a clean shutdown, waiting for subsystems to clean up their
 * state etc.  Must be called from a Looper thread in which its UI
 * is shown.
 *
 * @param context Context used to display the shutdown progress dialog. This must be a context
 *                suitable for displaying UI (aka Themable).
 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
 * @param confirm true if user confirmation is needed before shutting down.
 */
public static void reboot(final Context context, String reason, boolean confirm) 
    mReboot = true;
    mRebootSafeMode = false;
    mRebootHasProgressBar = false;
    mReason = reason;
    shutdownInner(context, confirm); // 和关机类似,最终调用rebootOrShutdown

rebootOrShutdown

重启情况是通过PowerManagerService.lowLevelReboot进行重启

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;
     ...

PowerManagerService#lowLevelReboot

/**
 * Low-level function to reboot the device. On success, this
 * function doesn't return. If more than 20 seconds passes from
 * the time a reboot is requested, this method returns.
 *
 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
 */
public static void lowLevelReboot(String reason) 
    if (reason == null) 
        reason = "";
    

    // If the reason is "quiescent", it means that the boot process should proceed
    // without turning on the screen/lights.
    // The "quiescent" property is sticky, meaning that any number
    // of subsequent reboots should honor the property until it is reset.
    if (reason.equals(PowerManager.REBOOT_QUIESCENT))  // 息屏重启
        sQuiescent = true;
        reason = "";
     else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) 
        sQuiescent = true;
        reason = reason.substring(0,
                reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
    

    if (reason.equals(PowerManager.REBOOT_RECOVERY)
            || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE))  // 进入 recovery
        reason = "recovery";
    

    if (sQuiescent) 
        // Pass the optional "quiescent" argument to the bootloader to let it know
        // that it should not turn the screen/lights on.
        reason = reason + ",quiescent";
    
    // 设置sys.powerctl
    SystemProperties.set("sys.powerctl", "reboot," + reason);
    try 
        Thread.sleep(20 * 1000L);
     catch (InterruptedException e) 
        Thread.currentThread().interrupt();
    
    Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");

如同关机流程,设置sys.powerctl然后init会执行重启。

如何分析关机重启问题

了解了上面的流程,分析问题应该不难,查看关键的log即可。下面以关机为例来分析,对于关机的机器来说,通常插上充电器是会进入关机充电状态,有关机充电动画。而某些情况实际是发生了死机,插上充电器没有充电动画,一般需要长按10+s开机。

查看main log

主要看下ShutdownThread log

01-22 15:54:05.117339  2174  2174 D ShutdownThread: Notifying thread to start shutdown longPressBehavior=1
01-22 15:54:05.181086  2174 25190 I ShutdownThread: Sending shutdown broadcast...
01-22 15:54:06.122875  2174 25190 I ShutdownThread: Shutting down activity manager...
01-22 15:54:06.233849  2174 25190 I ShutdownThread: Shutting down package manager...
01-22 15:54:06.242261  2174 25296 W ShutdownThread: Turning off cellular radios...
01-22 15:54:06.256316  2174 25296 I ShutdownThread: Waiting for Radio...
01-22 15:54:06.558647  2174 25296 I ShutdownThread: Radio turned off.
01-22 15:54:06.558735  2174 25296 I ShutdownThread: Radio shutdown complete.
01-22 15:54:07.118474  2174 25190 I ShutdownThread: Shutdown critical subsyslist is :modem :
01-22 15:54:07.118492  2174 25190 I ShutdownThread: Waiting for a maximum of 10000ms
01-22 15:54:07.608521  2174 25190 I ShutdownThread: Vendor subsystem(s) shutdown successful
01-22 15:54:07.608561  2174 25190 I ShutdownThread: Performing low-level shutdown...

查看system log

可以查看ShutdownCheckPoints,看看是那个进程发起的,比如这里的 pid=3393 就是 systemui 触发的关机。

01-22 15:54:05.114927  2174 12696 V ShutdownCheckPoints: Binder shutdown checkpoint recorded with pid=3393
01-22 15:54:05.117219  2174  2174 V ShutdownCheckPoints: System server shutdown checkpoint recorded

查看 sys.boot.reason

结合开机后的开机原因,可以获取更多信息,比如下面的一些原因

// 通常是用户操作
[sys.boot.reason]: [shutdown,userrequested]
// 这种通常是低电导致,可以看下kernel的healthd 查看下电量上报信息
[sys.boot.reason]: [shutdown,battery]

重启关机的原因有很多,如下定义在PowerManager

/// frameworks/base/core/java/android/os/PowerManager.java
/**
 * The value to pass as the 'reason' argument to reboot() to reboot into
 * recovery mode for tasks other than applying system updates, such as
 * doing factory resets.
 * <p>
 * Requires the @link android.Manifest.permission#RECOVERY
 * permission (in addition to
 * @link android.Manifest.permission#REBOOT).
 * </p>
 * @hide
 */
public static final String REBOOT_RECOVERY = "recovery";

/**
 * The value to pass as the 'reason' argument to reboot() to reboot into
 * recovery mode for applying system updates.
 * <p>
 * Requires the @link android.Manifest.permission#RECOVERY
 * permission (in addition to
 * @link android.Manifest.permission#REBOOT).
 * </p>
 * @hide
 */
public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";

/**
 * The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on
 * the device.
 * @hide
 */
public static final String REBOOT_REQUESTED_BY_DEVICE_OWNER = "deviceowner";

/**
 * The 'reason' value used when rebooting in safe mode
 * @hide
 */
public static final String REBOOT_SAFE_MODE = "safemode";

/**
 * The 'reason' value used for rebooting userspace.
 * @hide
 */
@SystemApi
public static final String REBOOT_USERSPACE = "userspace";

/**
 * The 'reason' value used when rebooting the device without turning on the screen.
 * @hide
 */
public static final String REBOOT_QUIESCENT = "quiescent";

/**
 * The value to pass as the 'reason' argument to android_reboot().
 * @hide
 */
public static final String SHUTDOWN_USER_REQUESTED = "userrequested";

/**
 * The value to pass as the 'reason' argument to android_reboot() when battery temperature
 * is too high.
 * @hide
 */
public static final String SHUTDOWN_BATTERY_THERMAL_STATE = "thermal,battery";

/**
 * The value to pass as the 'reason' argument to android_reboot() when device temperature
 * is too high.
 * @hide
 */
public static final String SHUTDOWN_THERMAL_STATE = "thermal";

/**
 * The value to pass as the 'reason' argument to android_reboot() when device is running
 * critically low on battery.
 * @hide
 */
public static final String SHUTDOWN_LOW_BATTERY = "battery";

除了上面的原因,重启原因还包括如下

rescueparty / RescueParty  --- RescueParty机制重启的原因
rollback_staged_install    --- crashes回滚机制重启

healthd 信息

查看kernel log查看healthd的电量信息

// l=0 电量值
// t=25.0  电池温度℃
// chg=  充电状态,为空是没有进行充电
05-19 17:49:59.253565  1200  1200 W healthd : battery l=0 v=7892 t=25.0 h=2 st=3 c=320 fc=13320000 cc=5 chg=

healthd log 打印

/// system/core/healthd/BatteryMonitor.cpp
void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
                               const struct healthd_config& healthd_config) 
    char dmesgline[256];
    size_t len;
    const HealthInfo_1_0& props = health_info.legacy.legacy;
    if (props.batteryPresent) 
        snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
                 props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
                 abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
                 props.batteryHealth, props.batteryStatus);

        len = strlen(dmesgline);
        if (!healthd_config.batteryCurrentNowPath.isEmpty()) 
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
                            props.batteryCurrent);
        

        if (!healthd_config.batteryFullChargePath.isEmpty()) 
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
                            props.batteryFullCharge);
        

        if (!healthd_config.batteryCycleCountPath.isEmpty()) 
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
                            props.batteryCycleCount);
        
     else 
        len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
    

    snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
             props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
             props.chargerWirelessOnline ? "w" : "");

    KLOG_WARNING(LOG_TAG, "%s\\n", dmesgline);

查看 PMIC PON log

而有些情况是看不到上面的log,通常可能是异常关机,可以查看开机后的 kernel 的 PMIC PON log

01-01 08:00:21.958147   418   418 I         : PMIC PON log: Fundamental Reset: PON_PBL_STATUS=XVDD, DVDD
01-01 08:00:21.958160   418   418 I         : PMIC PON log: FAULT_REASON2=RESTART_PON
01-01 08:00:21.958212   418   418 I         : PMIC PON log: PON Trigger: USB_CHARGER
01-01 08:00:21.958262   418   418 I         : PMIC PON log: Reset Trigger: PS_HOLD
01-01 08:00:21.958273   418   418 I         : PMIC PON log: Reset Type: HARD_RESET
01-01 08:00:21.958304   418   418 I         : PMIC PON log: PON Trigger: KPDPWR_N
01-01 08:00:21.958315   418   418 I         : PMIC PON log: PON Trigger: HARD_RESET

一些常见场景

如下包含一些关机重启的场景的触发点

低电

/// @frameworks/base/services/core/java/com/android/server/BatteryService.java
private void shutdownIfNoPowerLocked() 
    // shut down gracefully if our battery is critically low and we are not powered.
    // wait until the system has booted before attempting to display the shutdown dialog.
    if (shouldShutdownLocked()) 
        mHandler.post(new Runnable() 
            @Override
            public void run() 
                if (mActivityManagerInternal.isSystemReady()) 
                    // 使用intent启动ShutdownActivity来执行关机
                    Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                    intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                    intent.putExtra(Intent.EXTRA_REASON,
                            PowerManager.SHUTDOWN_LOW_BATTERY);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                
            
        );
    


高温

/// @frameworks/base/services/core/java/com/android/server/BatteryService.java
private void shutdownIfOverTempLocked() 
    // shut down gracefully if temperature is too high (> 68.0C by default)
    // wait until the system has booted before attempting to display the
    // shutdown dialog.
    if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature)  // 温度超标关机, 默认临界68度
        mHandler.post(new Runnable() 
            @Override
            public void run() 
                if (mActivityManagerInternal.isSystemReady()) 
                    Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                    intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                    intent.putExtra(Intent.EXTRA_REASON,
                            PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                
            
        );
    


Thermal

/// @frameworks/base/services/core/java/com/android/server/power/ThermalManagerService.java
private void shutdownIfNeeded(Temperature temperature) 
    if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) 
        return;
    
    final PowerManager powerManager = getContext().getSystemService(PowerManager.class);
    switch (temperature.getType()) 
        case Temperature.TYPE_CPU:
            // Fall through
        case Temperature.TYPE_GPU:
            // Fall through
        case Temperature.TYPE_NPU:
            // Fall through
        case Temperature.TYPE_SKIN: // 设置硬件高温
            powerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
            break;
        case Temperature.TYPE_BATTERY: // 电池高温
            powerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
            break;
    

status bar关机重启

这个通常是从systemui发起

/// frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
/**
 * Allows the status bar to shutdown the device.
 */
@Override
public void shutdown() 
    enforceStatusBarService();
    String reason = PowerManager.SHUTDOWN_USER_REQUESTED;
    ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
    final long identity = Binder.clearCallingIdentity();
    try 
        mNotificationDelegate.prepareForPossibleShutdown();
        // ShutdownThread displays UI, so give it a UI context.
        mHandler.post(() ->
                ShutdownThread.shutdown(getUiContext(), reason, false));
     finally 
        Binder.restoreCallingIdentity(identity);
    


/**
 * Allows the status bar to reboot the device.
 */
@Override
public void reboot(boolean safeMode) 
    enforceStatusBarService();
    String reason = safeMode
            ? PowerManager.REBOOT_SAFE_MODE
            : PowerManager.SHUTDOWN_USER_REQUESTED;
    ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
    final long identity = Binder.clearCallingIdentity();
    try 
        mNotificationDelegate.prepareForPossibleShutdown();
        mHandler.post(() -> 
            // ShutdownThread displays UI, so give it a UI context.
            if (safeMode) 
                ShutdownThread.rebootSafeMode(getUiContext(), true);
             else 
                ShutdownThread.reboot(getUiContext(), reason, false);
            
        );
     finally 
        Binder.restoreCallingIdentity(identity);
    


Rollback重启

@WorkerThread
private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
        BroadcastReceiver listener, @Nullable VersionedPackage logPackage) 
    assertInWorkerThread();
    PackageInstaller packageInstaller =
            mContext.getPackageManager().getPackageInstaller();
    List<RollbackInfo> recentRollbacks =
            rollbackManager.getRecentlyCommittedRollbacks();
    for (int i = 0; i < recentRollbacks.size(); i++) 
        RollbackInfo recentRollback = recentRollbacks.get(i);
        int sessionId = recentRollback.getCommittedSessionId();
        if ((rollbackId == recentRollback.getRollbackId())
                && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) 
            PackageInstaller.SessionInfo sessionInfo =
                    packageInstaller.getSessionInfo(sessionId);
            if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) 
                mContext.unregisterReceiver(listener);
                saveStagedRollbackId(rollbackId, logPackage);
                WatchdogRollbackLogger.logEvent(logPackage,
                        FrameworkStatsLog
                        .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
                        "");
             else if (sessionInfo.isStagedSessionFailed()
                    && markStagedSessionHandled(rollbackId)) 
                WatchdogRollbackLogger.logEvent(logPackage,
                        FrameworkStatsLog
                

以上是关于android 关机重启流程的主要内容,如果未能解决你的问题,请参考以下文章

Android 12 关机重启流程

Android 重启与关机的实现源码分析

Android系统关机或重启的几种实现方式

Android系统关机或重启的几种实现方式

Android系统关机或重启的几种实现方式

Android系统关机或重启的几种实现方式