Android Init 系列专题【总篇:深入浅出】https://blog.csdn.net/qq_27672101/article/details/144153376

Android Init 系列专题【篇一:fstab分区表挂载】https://blog.csdn.net/qq_27672101/article/details/146104979

Android Init 系列专题【篇二:Selinux启动流程】https://blog.csdn.net/qq_27672101/article/details/149076909

Android Init 系列专题【篇三:property_service】https://blog.csdn.net/qq_27672101/article/details/148702336

Android Init 系列专题【篇四:service & action】https://blog.csdn.net/qq_27672101/article/details/154580471

Android Init 系列专题【篇五:reboot & shutdown】https://blog.csdn.net/qq_27672101/article/details/154455341Android Init 系列专题【篇六:native进程自定义】https://blog.csdn.net/qq_27672101/article/details/147935703


在android系统中,init进程不仅仅作为第一个进程,涉及设备的开机流程,其实android系统的关机和重启流程,也都离不开init。本篇以android系统的关机/重启流程为切入点,重点来介绍一下init进程在这个过程中是如何举足轻重。

在android系统中,让系统重启或者关机的方式有多种,这里介绍一下常用的手段:

  • 长按电源键
  • 调用PowerManager的reboot或者shutdown方法
  • 发送Intent.ACTION_REQUEST_SHUTDOWN系统广播
  • 执行reboot或者poweroff命令
  • 设置sys.powerctl属性

先附上一份自制流程图:

一、Init进程关机重启流程

虽然有如上众多方式让系统重启或者关机,但大部分方式在native init层其实是通过sys.powerctl属性的方式来完成系统的重启或者关机,这里先以一个案例的日志来进行说明:

[  166.920846] <0>.(0)[311:init]init 27: [166912][13]Received sys.powerctl='reboot,autotesttool' from pid: 1259 (system_server)
[  166.926731] <2>.(2)[1:init]init 18: [166921][3]Got shutdown_command 'reboot,autotesttool' Calling HandlePowerctlMessage()
[  166.940250] <1>.(1)[1:init]init 25: [166921][3]Reboot start, reason: reboot,autotesttool, reboot_target: autotesttool
[  166.941877] <1>.(1)[1:init]hang_detect: init set reboot command.
[  166.942734] <1>.(1)[1:init]init 25: [166921][3][HANG_DETECT] set hang detect reboot flag.
[  166.945487] <1>.(1)[1:init]init 25: [166921][3]Create reboot monitor thread.
[  167.024221] <3>.(3)[311:init]init 27: [167012][5]ReapLogC PropSet [sys.powerctl]=[reboot,autotesttool]166917 [init.svc.vendor.volte_ua]=[stopping]166918 [init.svc.vendor.volte_ua]=[stopped]166921 [init.svc_debug_pid.vendor.volte_ua]=[]166921 [persist.sys.boot.reason]=[reboot,autotesttool]166966 [vendor.volte_md_status]=[stop]166967 [vendor.mdl.reset_md]=[1]166967 [init.svc.blank_screen]=[running]166975 [ro.boottime.blank_screen]=[166972756317]166975 [init.svc_debug_pid.blank_screen]=[2914]166977 [vendor.mtk.md1.status]=[flightmode]166978 [init.svc.nvram-hidl-1-1]=[stopping]166997 [init.svc.vendor.wifi_hal_legacy]=[stopping]167000 [init.svc.vendor.vibrator-default]=[stopping]167006 [init.svc.vendor.ril-daemon-mtk]=[stopping]167012 [init.svc.vendor.volte_stack]=[stopping]167019 Done
[  171.072287] <1>.(1)[1:init]init 25: [166921][3]Service vold has 'reboot_on_failure' option and failed, shutting down system.
[  174.747922] <0>.(0)[1:init]init 18: [166921][3]Reboot ending, jumping to kernel
[  175.062840] <0>-(0)[1:init][<c015da6c>] (kernel_restart) from [<c015df0c>] (sys_reboot+0x194/0x218)
[  175.064855] <0>-(0)[1:init][<c015df0c>] (sys_reboot) from [<c0101000>] (ret_fast_syscall+0x0/0x54)
[  175.102453] <0>-(0)[1:init][<c015da6c>] (kernel_restart) from [<c015df0c>] (sys_reboot+0x194/0x218)
[  175.104467] <0>-(0)[1:init][<c015df0c>] (sys_reboot) from [<c0101000>] (ret_fast_syscall+0x0/0x54)
[  175.159437] <0>-(0)[1:init][<c015da6c>] (kernel_restart) from [<c015df0c>] (sys_reboot+0x194/0x218)
[  175.161453] <0>-(0)[1:init][<c015df0c>] (sys_reboot) from [<c0101000>] (ret_fast_syscall+0x0/0x54)
[  175.228705] <0>.(0)[1:init]reboot: Restarting system with command 'autotesttool'

如上每一行日志的都是非常关键,这个日志抓取的是MTK last_kmsg日志,这个案例是autotesttool自动化测试工具在压测过程中通过设置属性sys.powerctl='reboot,autotesttool'方式来对系统进行重启。主要关键日志如下:

  • init 27: [166912][13]Received sys.powerctl='reboot,autotesttool' from pid: 1259 (system_server)                    --->  init进程接收到sys.powerctl属性的改变
  • init 25: [166921][3]Reboot start, reason: reboot,autotesttool, reboot_target: autotesttool      ---> init进程开始执行DoReboot函数
  • init 18: [166921][3]Reboot ending, jumping to kernel    ---> init进程执行RebootSystem函数进入内核态   
  • reboot: Restarting system with command 'autotesttool'   --->kernel进行restaring重启操作?

如上面总结到,init进程就是通过sys.powerctl属性来进行控制,那么如何进行控制了呢?首先就需要看看系统属性的设置,和属性被设置之后,init进程是如何进行监听。

1、sys.powerctl的设置和监听

无论哪一个进程设置属性,init进程最后都会在HandlePropertySet接收到,并将属性值通过PropertySet方法设置到属性服务里面。

在HandlePropertySet函数中判断如何属性的名字以sys.powerctl开头,那么就一定是要重启或者关机了,这里进行了日志打印:Received sys.powerctl='reboot,autotesttool' from pid: 1259 (system_server)

在init进程的loop循环中,如果检测到重启或者关机的命令,执行HandlePowerctlMessage去过滤shutdown和reboot。这里的日志为:Got shutdown_command 'reboot,autotesttool' Calling HandlePowerctlMessage

2、HandlePowerctlMessage

init进程在main loop循环中针对关机和重启的操作,交给了HandlePowerctlMessage来进行处理,此函数具体解析了shutdown和reboot参数,如下代码:

//system/core/init/reboot.cpp 
//command参数解析,以setprop sys.powerctl reboot,recovery属性为准
//cmd_params[0]解析出来就是reboot,cmd_params[1]解析出来就是recovery
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;
    //核心逻辑1:参数解析
    //如果是shutdown流程
    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.
                //如果原因是userrequested设置run_fsck为true
                run_fsck = true; 
            } else if (cmd_params[1] == "thermal") {
                //如果原因是thermal通过TurnOffBacklight函数关闭背光
                TurnOffBacklight();
                cmd = ANDROID_RB_THERMOFF; // run_fsck is false to avoid delay
            }
        }
    //如果是reboot流程
    } else if (cmd_params[0] == "reboot") {
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() >= 2) {
            //把cmd_params[1]赋值给reboot_target 
            reboot_target = cmd_params[1];
            if (reboot_target == "userspace") {
                //reboot,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") {
                //reboot,fastboot和reboot,bootloader处理流程
                std::string err;
                if (!write_reboot_bootloader(&err)) {
                    LOG(ERROR) << "reboot-bootloader: Error writing " "bootloader_message: " << err;
                }
            } else if (reboot_target == "recovery") {
                //reboot,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 == "quiescent") {
                //reboot,quiescent处理流程
                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-quiescent", 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") {
                //reboot,sideload和reboot,sideload-auto-reboot和reboot,fastboot最终都变成了reboot,recovery
                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.
    //核心逻辑2:关机/重启前natvie层需要做的事情
    StopSendingMessages();
    //核心逻辑2:第一件事情如果reboot,userspace执行HandleUserspaceReboot,然后退出流程
    if (userspace_reboot) {
        HandleUserspaceReboot();
        return;
    }
    //核心逻辑2:第二件事情清理action队列并触发shutdown,该操作会执行rc配置的shutdown事项
    LOG(INFO) << "Clear action queue and start shutdown trigger";
    ActionManager::GetInstance().ClearQueue();
    ActionManager::GetInstance().QueueEventTrigger("shutdown");
    //核心逻辑2:第三件事情rc配置shutdown事项完毕执行DoReboot
    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) { //定义了shutdown_handler并指定DoReboot函数,在shutdown_done之后被触发
        DoReboot(cmd, command, reboot_target, run_fsck);
        return Result<void>{};
    };
    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
    //核心逻辑2:第四件事情调用EnterShutdown进入shutdown
    EnterShutdown();
}

如上代码逻辑,此函数的逻辑主要分为两部分,先进行参数解析,然后做关机前的一些预备工作,接下来我们看看做了那些关机前的预备工作:

  • 触发shutdown事件
  • 调用DoReboot函数
  • 调用EnterShutdown函数

3、DoReboot常规方式关机重启

接着HandlePowerctlMessage函数,我们先看看非reboot,userspace方式的下电流程。首先触发了shutdwon事件,然后调用了DoReboot,最后调用了EnterShutdown。

on shutdown触发器在rc中并没有搜到太多的配置,可以暂时忽略不计,后面的EnterShutdown函数日志基本上没有打印,估计基本上没有调用,为什么没有调用,我们先看看DoReboot流程,此函数的标志性日志:Reboot start, reason: reboot,autotesttool, reboot_target: autotesttool

1)播放关机动画

在DoReboot函数中,还实现了要不要播放关机动画的逻辑,如下代码,如果属性ro.init.shutdown_animation被设置为true,那么就会启动bootanim进程播放关机动画。

2)如何kill其他native进程?

3)文件系统卸载

在干掉其他所有native层进程之后,就开始umount文件系统,如下代码逻辑

4)RebootSystem & abort终止进程

在native层服务都被干掉之后,文件系统卸载掉之后,就开始调用RebootSystem来进入kernel层关机了,这里有标志性日志:Reboot ending, jumping to kernel,但因为这个时候日志工具已经被干掉,所以jumping to kernel的打印在普通日志中几乎没有,但是我在mtk 工具抓取的last_kmsg文件中找到过这行打印

如上代码逻辑,可以看到RebootSystem函数直接调用了kernel层的reboot指令。这里总结有如下几种方式调用到内核层:

  • 如果是shutdown的方式,直接执行reboot(RB_POWER_OFF)进行关机
  • 如果是reboot的方式,直接通过syscall访问内核,LINUX_REBOOT_CMD_RESTART2表示重启内核并支持传递参数,rebootTarget.c_str()指定重启参数,例如bootloader、recovery
  • RebootSystem函数被__attribute__修饰,表示改函数没有返回值,且最后调用abort,表示直接终止当前进程

4、DoUserspaceReboot用户空间重启

接着上面的逻辑,如果reboot,userspace的方式重启,那么就调用HandleUserspaceReboot函数。那么userspace到底什么意思呢?原来是只重启用户空间,这种方式kernel层保持运行。

在此函数主要逻辑就是通过触发器机制,触发了userspace-reboot-requested事件,调用DoUserspaceReboot函数,在此触发userspace-reboot事件。如下代码:

1)用户空间重启核心流程

我们先跳过rc事件,先看看DoUserSpaceReboot,因为在此函数中也触发了一批相关rc事件。该函数的标志性日志:Userspace reboot initiated。之后依次触发如下流程:

  • 遍历干掉native进程
  • 干掉ZRAM
  • 重置vold进程

  • debug进程依旧运行
  • apex服务终止

  • 重启所有native层服务
  • 退出关机标记
  • 触发userspace-reboot-resume表示用户空间重启完毕,做一些系统恢复动作

    2)action事件触发流程

    从前文模糊的感受到,整个过程根本没有去调用kernel层去让系统下电,native层的服务被终止掉之后,又赶紧被重启,这些流程也体现在aciton事件中,梳理如上总共如下几个action事件:

    • on userspace-reboot-requested
    • 停止native进程和apex服务/重置vold进程/重启native进程
    • on userspace-reboot-resume
    • on userspace-reboot-fs-remount
    • on post-fs-data
    • on zygote-start
    • on early-boot
    • on boot
    • on userspace-reboot

    二、FW层关机重启流程

    FW层完成关机或者重启的服务必须得是PowerManagerService。我们可以直接拿到它对应的客户端代理对象PowerManager,调用shutdown/reboot/reboot/rebootSafeMode这三个接口。

    //frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java 
            /**
             * Reboots the device.
             * @param confirm If true, shows a reboot confirmation dialog.
             * @param reason The reason for the reboot, or null if none.
             * @param wait If true, this call waits for the reboot to complete and does not return.
             */
            @Override // Binder call
            public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
                //先检查调用者是否存在REBOOT权限
                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
                //如果关机原因是REBOOT_RECOVERY,需要检查调用者是否存在对应RECOVERY权限
                if (PowerManager.REBOOT_RECOVERY.equals(reason) || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
                    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
                }
                //检测系统重启操作和重启原因,便于日志分析
                ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
                final long ident = Binder.clearCallingIdentity();
                try {
                    shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
            /**
             * Reboots the device into safe mode
             * @param confirm If true, shows a reboot confirmation dialog.
             * @param wait If true, this call waits for the reboot to complete and does not return.
             */
            @Override // Binder call
            public void rebootSafeMode(boolean confirm, boolean wait) {
                //先检查调用者是否存在REBOOT权限
                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
                //设置重启原因为REBOOT_SAFE_MODE
                String reason = PowerManager.REBOOT_SAFE_MODE;
                ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
                final long ident = Binder.clearCallingIdentity();
                try {
                    shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm, reason, wait);
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
            /**
             * 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) {
                //检查调用者是否具有REBOOT权限
                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
                ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
                final long ident = Binder.clearCallingIdentity();
                try {
                    shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
    

    PMS关于这三个接口大致逻辑基本一致,先检查是否拥有reboot权限,最后调用shutdownOrRebootInternal方法,其中三个参数的意义如下:

    • boolean confirm:是否弹出确认关机对话框,如果为true弹出对话框
    • String reason:重启或者关机原因,如果是rebootSafeMode接口固定原因为REBOOT_SAFE_MODE
    • boolean wait:是否等待接口调用完成,如果为true者改方法不会返回,因为系统已经关机或者重启

    1、shutdownOrRebootInternal

    //frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
        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()) {
                    throw new UnsupportedOperationException( "Attempted userspace reboot on a device that doesn't support it");
                }
                UserspaceRebootLogger.noteUserspaceRebootWasRequested();
            }
            //如果系统还没有重启成功进入判断
            if (mHandler == null || !mSystemReady) {
                if (RescueParty.isAttemptingFactoryReset()) {
                    PowerManagerService.lowLevelReboot(reason);
                } else {
                    throw new IllegalStateException("Too early to call shutdown() or reboot()");
                }
            }
            //调用ShutdownThread线程去进行真正的关机处理
            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线程运行在UI线程
            Message msg = Message.obtain(UiThread.getHandler(), runnable);
            msg.setAsynchronous(true);
            UiThread.getHandler().sendMessage(msg);
            //如果需要等待,那么在这里制造一个死循环,让该函数无法返回
            if (wait) {
                synchronized (runnable) {
                    while (true) {
                        try { runnable.wait(); } catch (InterruptedException e) { }
                    }
                }
            }
        }

    2、ShutdownThread

    接着上面pms.shutdownOrRebootInternal方法,通过UI线程ShutdownThread进一步调用shutdown和reboot,该线程实现reboot和shutdown的方法如下:

    //frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java 
    public final class ShutdownThread extends Thread {
        private static final String TAG = "ShutdownThread";
        private static boolean mReboot;
        private static boolean mRebootSafeMode;
        private static boolean mRebootHasProgressBar;
        private static String mReason;
        private static final ShutdownThread sInstance = new ShutdownThread(); // static instance of this thread
        private ShutdownThread() {
        }
        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);
        }
    }

    如上三个接口,最后其实走的shutdownInner方法,这里只带上了confirm参数,该参数为true在重启前弹出对话框给用户进行确认,这也是为什么该线程的上下文必须传递一个UI线程上下文:

    beginShutdownSequence方法前面的逻辑其实也是针对UI对话框的处理:最终还是调用的静态实例对象,该对象是一个线程,因此启动线程的run方法,

    3、如何通知SystemServer执行下电流程?

    如上调用的ShutdownThread线程,接下来看看ShutdownThread.run方法,在该方法通知重要系统服务执行下电操作,并默认设置了10秒钟进行等待。

    //frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
    public final class ShutdownThread extends Thread { 
       public void run() {
            //流程1:打印日志SystemServerShutdown表示systemserver开始shutdown
            TimingsTraceLog shutdownTimingLog = newTimingsLog();
            shutdownTimingLog.traceBegin("SystemServerShutdown");
            metricShutdownStart();
            metricStarted(METRIC_SYSTEM_SERVER);
            Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(new File(CHECK_POINTS_FILE_BASENAME));
            dumpCheckPointsThread.start();
            //流程1:设置sys.shutdown.requested来表示systemserver进程要准备下电了.
            {  // 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);
            }
            //流程1:设置persist.sys.safemode属性表示安全下电
            if (mRebootSafeMode) {
                SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
            }
            //流程2:抓取重启日志信息
            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
            //流程3:发送广播Intent.ACTION_SHUTDOWN告知系统所有进程准备下电
            metricStarted(METRIC_SEND_BROADCAST);
            shutdownTimingLog.traceBegin("SendShutdownBroadcast");
            mActionDone = false;
            Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
            final ActivityManagerInternal activityManagerInternal = LocalServices.getService( ActivityManagerInternal.class);
            activityManagerInternal.broadcastIntentWithCallback(intent,
                    new IIntentReceiver.Stub() {
                        @Override
                        public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                            mHandler.post(ShutdownThread.this::actionDone);
                        }
                    }, null, UserHandle.USER_ALL, null, null, null);
            //流程3:发送广播之后循环等待MAX_BROADCAST_TIME默认是10秒
            final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
            synchronized (mActionDoneSync) {
                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);
            //流程4:通知AMS进行下电流程
            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) {
                try {
                    am.shutdown(MAX_BROADCAST_TIME);
                } catch (RemoteException e) {
                }
            }
            if (mRebootHasProgressBar) {
                sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
            }
            shutdownTimingLog.traceEnd();// ShutdownActivityManager
            metricEnded(METRIC_AM);
            //流程5:通知PMS进行下电流程
            Log.i(TAG, "Shutting down package manager...");
            shutdownTimingLog.traceBegin("ShutdownPackageManager");
            metricStarted(METRIC_PM);
            final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
            if (pm != null) {
                pm.shutdown();
            }
            if (mRebootHasProgressBar) {
                sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
            }
            shutdownTimingLog.traceEnd(); // ShutdownPackageManager
            metricEnded(METRIC_PM);
            //流程6:通知通信相关服务进行下电流程
            shutdownTimingLog.traceBegin("ShutdownRadios");
            metricStarted(METRIC_RADIOS);
            //流程6:shutdownRadios主要通知TelephonyManager服务进行下电流程
            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);
                uncrypt();
            }
            shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
            try {
                dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
            } catch (InterruptedException ex) {
            }
            //流程7:打印日志SystemServerShutdown表示systemserver结束shutdown
            shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait
            shutdownTimingLog.traceEnd(); // SystemServerShutdown
            metricEnded(METRIC_SYSTEM_SERVER);
            saveMetrics(mReboot, mReason);
            //流程8:rebootOrShutdown真正开始执行下电流程
            rebootOrShutdown(mContext, mReboot, mReason);
        }
    }

    如上代码逻辑,在ShutdownThread线程中主动调用了AMS/PMS/TMS几个重要服务的下电流程,另外发送了Intent.ACTION_SHUTDOWN广播通知其他系统服务执行下电流程,并且还默认等待了10秒。整个下电流程的日志可以参考如下:

    	Line 580: 11-03 17:09:03.528  1486  2416 V ShutdownCheckPoints: Binder shutdown checkpoint recorded with pid=2712
    	Line 581: 11-03 17:09:03.529  1486  1486 V ShutdownCheckPoints: System server shutdown checkpoint recorded
    	Line 675: 11-03 17:09:03.553  1486  7327 I ShutdownThread: Logging pre-reboot information...
    	Line 677: 11-03 17:09:03.554  1486  7327 V ShutdownTiming: DumpPreRebootInfo took to complete: 1ms
    	Line 678: 11-03 17:09:03.554  1486  7327 I ShutdownThread: Sending shutdown broadcast...
    	Line 1122: 11-03 17:09:03.764  1486  7327 V ShutdownTiming: SendShutdownBroadcast took to complete: 210ms
    	Line 1123: 11-03 17:09:03.764  1486  7327 I ShutdownThread: Shutting down activity manager...
    	Line 1932: 11-03 17:09:04.440  1486  7327 V ShutdownTiming: ShutdownActivityManager took to complete: 676ms
    	Line 1933: 11-03 17:09:04.440  1486  7327 I ShutdownThread: Shutting down package manager...
    	Line 1943: 11-03 17:09:04.452  1486  7327 V ShutdownTiming: ShutdownPackageManager took to complete: 11ms
    	Line 1945: 11-03 17:09:04.460  1486  7356 W ShutdownThread: Turning off cellular radios...
    	Line 2079: 11-03 17:09:04.473  1486  7356 I ShutdownThread: Waiting for Radio...
    	Line 2266: 11-03 17:09:04.796  1486  7356 I ShutdownThread: Radio turned off.
    	Line 2267: 11-03 17:09:04.796  1486  7356 V ShutdownTiming: ShutdownRadio took to complete: 336ms
    	Line 2268: 11-03 17:09:04.796  1486  7356 I ShutdownThread: Radio shutdown complete.
    	Line 2269: 11-03 17:09:04.797  1486  7327 V ShutdownTiming: ShutdownRadios took to complete: 345ms
    	Line 2272: 11-03 17:09:04.798  1486  7327 V ShutdownTiming: ShutdownCheckPointsDumpWait took to complete: 0ms
    	Line 2274: 11-03 17:09:04.798  1486  7327 V ShutdownTiming: SystemServerShutdown took to complete: 1258ms
    	Line 2278: 11-03 17:09:04.800  1486  7327 I ShutdownThread: Shutdown critical subsyslist is :modem : 
    	Line 2279: 11-03 17:09:04.800  1486  7327 I ShutdownThread: Waiting for a maximum of 10000ms
    	Line 3383: 11-03 17:09:05.533  1486  7327 I ShutdownThread: Vendor subsystem(s) shutdown successful
    	Line 3651: 11-03 17:09:06.054  1486  7327 I ShutdownThread: Performing low-level shutdown...

    最后需要说明一点的是,android fw监听ACTION_SHUTDOWN广播,实现fw层下电流程,如下图:

    4、设置sys.powerctl属性进行关机或重启

    接着上面rebootOrShutdown的流程,主要逻辑如下:

    最后无论是重启还是关机,最后都离不开设置sys.powerctl属性的方式,如下代码:

    5、ShutdownCheckPoints

    最后讲解一下ShutdownCheckPoints机制,翻译过来就是fw用来检测关机/重启的点,即fw提供了一套机制,在关机或者重启之前,通过ShutdownCheckPoints记录一下调用的进程和原因,方便我们在跟踪重启或者关机的原因。

    1)如何记录关机信息?

    //os/la.qssi16/frameworks/base/services/core/java/com/android/server/power/ShutdownCheckPoints.java
    public final class ShutdownCheckPoints {
        //日志TAG
        private static final String TAG = "ShutdownCheckPoints";
        //单例设计模式
        private static final ShutdownCheckPoints INSTANCE = new ShutdownCheckPoints();
        @VisibleForTesting
        void recordCheckPointInternal(@Nullable String reason) {
            //核心逻辑:如果只有原因没有传PID或者包名,那么就认为是systemserver进程发起
            recordCheckPointInternal(new SystemServerCheckPoint(mInjector.currentTimeMillis(), reason));
            Slog.v(TAG, "System server shutdown checkpoint recorded");
        }
        @VisibleForTesting
        void recordCheckPointInternal(int callerProcessId, @Nullable String reason) {
            long timestamp = mInjector.currentTimeMillis();
            //核心逻辑:如果进程PID等于当前PID,那么说明是systemserver进程的子线程
            recordCheckPointInternal(callerProcessId == Process.myPid()
                    ? new SystemServerCheckPoint(timestamp, reason)
                    : new BinderCheckPoint(timestamp, callerProcessId, reason));
            Slog.v(TAG, "Binder shutdown checkpoint recorded with pid=" + callerProcessId);
        }
        @VisibleForTesting
        void recordCheckPointInternal(String intentName, String packageName, @Nullable String reason) {
            long timestamp = mInjector.currentTimeMillis();
            //核心逻辑:如果进程包名是android,那么说明是systemserver进程的子线程
            recordCheckPointInternal("android".equals(packageName)
                    ? new SystemServerCheckPoint(timestamp, reason)
                    : new IntentCheckPoint(timestamp, intentName, packageName, reason));
            Slog.v(TAG, String.format("Shutdown intent checkpoint recorded intent=%s from package=%s", intentName, packageName));
        }
        //核心逻辑,把每一次记录添加到list列表中,在dump的时候可以直接读出来,也可以直接pull出来
        private void recordCheckPointInternal(CheckPoint checkPoint) {
            synchronized (mCheckPoints) {
                mCheckPoints.add(checkPoint);
                if (mCheckPoints.size() > mInjector.maxCheckPoints()) mCheckPoints.remove(0);
            }
        }

    如上定义可以对ShutdownCheckPoints进行如下总结:

    • ShutdownCheckPoints采用了单例模式
    • ShutdownCheckPoints维护了一个列表,在每次记录关机节点的时候就会实例化一个CheckPoint添加到这个列表,这个列表可以设置大小等参数

    关机节点有如下几类:

    • SystemServerCheckPoint表示systemserver进程包括子线程发起的重启/关机指令

    • BinderCheckPoint表示跨进程通信的进程发起的重启/关机指令

    • IntentCheckPoint表示普通应用进程发起的重启/关机指令,关键信息有包名和Intent

    2)何时记录关机信息?

    如上搜索出来的代码,在FW中有四个地方会调用ShutdownCheckPoints进行记录关机信息:

    A PMS执行shutdown和reboot的时候

    因为PMS作为跨进程通信的服务端,所以这里getCallingPid获取的是对端的PID,如果判断是对端PID和当前进程ID一致,那就说明是systemserver进程发起的:

    B ActivityStarter拦截SHUTDOWN和REBOOT广播的时候

    广播机制其实是由ActivityStarter强相关,因此fw在此类中直接拦截这几个关机广播,并进行记录,记录的时候传递intent和包名,如果这个包名是android的话,那就是systemserver的子线程,否则的话就实例化了IntentCheckPoint,表示其他进程通过广播的方式发起关机流程:

    C SystemUI进程调用shutdown和reboot的时候

    D ShutdownThread线程触发最后的记录

    实际上这里可以不用的,但是根据注释来看是为了最后的一个保险记录,即防止前面的流程没有记录到,这里直接传递的null对象,因此会直接实例化SystemServer检查点,其实该线程本身也就是systemserver进程的子线程。

    3)关机信息被存储在哪里?

    先给一个结论:这些关机信息被存储在/data/system/shutdown-checkpoints/。

    我们接下来在看看是如何存在关机节点信息的呢?

    //os/la.qssi16/frameworks/base/services/core/java/com/android/server/power/ShutdownCheckPoints.java
    public final class ShutdownCheckPoints {
        private static final String TAG = "ShutdownCheckPoints";
        private final Injector mInjector;
        /**
         * Creates a {@link Thread} that calls {@link #dump(PrintWriter)} on a rotating file created
         * from given {@code baseFile} and a timestamp suffix. Older dump files are also deleted by this
         * thread.
         */
        public static Thread newDumpThread(File baseFile) {
            return INSTANCE.newDumpThreadInternal(baseFile);
        }
        @VisibleForTesting
        Thread newDumpThreadInternal(File baseFile) {
            return new FileDumperThread(this, baseFile, mInjector.maxDumpFiles());
        }
        //Thread that writes {@link ShutdownCheckPoints#dumpInternal(PrintWriter)} to a new file and  deletes old ones to keep the total number of files down to a given limit.
        private static final class FileDumperThread extends Thread {
            FileDumperThread(ShutdownCheckPoints instance, File baseFile, int fileCountLimit) {
                mInstance = instance;
                mBaseFile = baseFile;
                mBaseDir = baseFile.getParentFile();
                mFileCountLimit = fileCountLimit;
            }
            @Override
            public void run() {
                if (!mBaseDir.exists()) {
                    mBaseDir.mkdirs();
                    mBaseDir.setExecutable(true, false);
                    mBaseDir.setReadable(true, false);
                }
                File[] checkPointFiles = listCheckPointsFiles();
                int filesToDelete = checkPointFiles.length - mFileCountLimit + 1;
                for (int i = 0; i < filesToDelete; i++) {
                    checkPointFiles[i].delete();
                }
                //保存到文件中
                File nextCheckPointsFile = new File(String.format("%s-%d",
                        mBaseFile.getAbsolutePath(), System.currentTimeMillis()));
                writeCheckpoints(nextCheckPointsFile);
            }
        }
    }

    例如低电量关机的日志信息,BatteryService也是通过关机广播来发起,执行如下命令得到如下日志:

    adb pull /data/system/shutdown-checkpoints/  .

    Shutdown request from SYSTEM at 2026-04-10 11:48:23.589 GMT (epoch=1775821703589)
    com.android.server.wm.ActivityStarter.execute
     at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1327)
     at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1271)
     at android.app.ContextImpl.startActivityAsUser(ContextImpl.java:1170)
     at android.app.ContextImpl.startActivityAsUser(ContextImpl.java:1137)
     at com.android.server.BatteryService.startShutdownActivity(BatteryService.java:1459)
     at com.android.server.BatteryService.lambda$shutdownIfNoPowerLocked$3(BatteryService.java:1392)
     at com.android.server.BatteryService.$r8$lambda$stMNXwR2tj9OB8nt5pxcwYi9ChE(BatteryService.java:0)
     at com.android.server.BatteryService$$ExternalSyntheticLambda1.run(R8$$SyntheticClass:0)
     at android.os.Handler.handleCallback(Handler.java:995)
     at android.os.Handler.dispatchMessage(Handler.java:103)
     at android.os.Looper.loopOnce(Looper.java:248)
     at android.os.Looper.loop(Looper.java:338)
     at com.android.server.SystemServer.run(SystemServer.java:1052)
     at com.android.server.SystemServer.main(SystemServer.java:705)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:918)

    普通logcat日志如下:

    04-10 11:48:27.461  2292  2292 V ShutdownCheckPoints: Shutdown intent checkpoint recorded intent=com.android.internal.intent.action.REQUEST_SHUTDOWN from package=android
    04-10 11:48:27.531  2292  2292 V ShutdownCheckPoints: Shutdown intent checkpoint recorded intent=com.android.internal.intent.action.REQUEST_SHUTDOWN from package=android
    04-10 11:48:27.552  2292  2292 V ShutdownCheckPoints: Shutdown intent checkpoint recorded intent=com.android.internal.intent.action.REQUEST_SHUTDOWN from package=android
    04-10 11:48:27.572  2292  2292 V ShutdownCheckPoints: Shutdown intent checkpoint recorded intent=com.android.internal.intent.action.REQUEST_SHUTDOWN from package=android
    04-10 11:48:27.678  2292  2292 V ShutdownCheckPoints: Shutdown intent checkpoint recorded intent=com.android.internal.intent.action.REQUEST_SHUTDOWN from package=android
    04-10 11:48:27.710  2292  2292 V ShutdownCheckPoints: Shutdown intent checkpoint recorded intent=com.android.internal.intent.action.REQUEST_SHUTDOWN from package=android
    04-10 11:48:27.981  2292  2292 V ShutdownCheckPoints: Shutdown intent checkpoint recorded intent=com.android.internal.intent.action.REQUEST_SHUTDOWN from package=android
    04-10 11:48:28.282  2292  3939 V ShutdownCheckPoints: Binder shutdown checkpoint recorded with pid=2292
    04-10 11:48:28.285  2292  2379 V ShutdownCheckPoints: System server shutdown checkpoint recorded
    04-10 11:48:30.813  2292  3968 V ShutdownTiming: ShutdownCheckPointsDumpWait took to complete: 0ms

    三、常用关机重启流程

    1、SystemUI关机和重启

    2、Settings恢复出厂设置

    3、应用发送ACTION_REQUEST_SHUTDOWN

    除了SystemUI和Settings内置了一些关机/重启方式,这里介绍一下通过发送广播的方式进行关机或者重启。如下示例代码:

    //实例化ACTION_REQUEST_SHUTDOWN广播
    Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
    //设置boolean confirm参数
    intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
    //设置String reason参数
    intent.putExtra(Intent.EXTRA_REASON,PowerManager.SHUTDOWN_LOW_BATTERY);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);

    如上代码是截取的BatteryService在低电量检查的时候关机代码,三方应用也可以按照这个模式来实现对设备进行关机或者重启,但是需要添加如下权限:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.SHUTDOWN" />

    我们来看看这个广播是如何实现系统关机或者重启的呢?先看看此广播的定义:

    在继续看看com.android.internal.intent.action.REQUEST_SHUTDOWN使用的地方,在A14代码中就找到如下一处:

    A ShutdownActivity

    原来是直接启动fw的ShutdownActivity,该界面在定义的时候也申明了需要android.permission.SHUTDOWN权限。那么ShutdownActivity到底干了撒?

    //frameworks/base/core/java/com/android/internal/app/ShutdownActivity.java
    public class ShutdownActivity extends Activity {
        private static final String TAG = "ShutdownActivity";
        private boolean mReboot;
        private boolean mConfirm;
        private boolean mUserRequested;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Intent intent = getIntent();
            //mReboot判断是重启还是关机,如果是Intent.ACTION_REBOOT设置mReboot为true
            mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
            //mConfirm从EXTRA_KEY_CONFIRM获取,设置是否需要和用户确认
            mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
            //mUserRequested从EXTRA_USER_REQUESTED_SHUTDOWN获取,设置重启关机原因
            mUserRequested = intent.getBooleanExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, false);
            //如果Intent存在EXTRA_USER_REQUESTED_SHUTDOWN为true就设置userrequested
            //否则就从EXTRA_REASON获取设置原因
            final String reason = mUserRequested
                    ? PowerManager.SHUTDOWN_USER_REQUESTED
                    : intent.getStringExtra(Intent.EXTRA_REASON);
            Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
            Thread thr = new Thread("ShutdownActivity") {
                @Override
                public void run() {
                    IPowerManager pm = IPowerManager.Stub.asInterface( ServiceManager.getService(Context.POWER_SERVICE));
                    try {
                        //最后还是调用了PMS的reboot和shutdown方法
                        if (mReboot) {
                            pm.reboot(mConfirm, null, false);
                        } else {
                            pm.shutdown(mConfirm, reason, false);
                        }
                    } catch (RemoteException e) {
                    }
                }
            };
            thr.start();
            finish();
            // Wait for us to tell the power manager to shutdown.
            try {
                thr.join();
            } catch (InterruptedException e) {
            }
        }
    }

    根据如上逻辑,进行如下总结:

    • 关机使用广播Intent.ACTION_REQUEST_SHUTDOWN
    • 重启使用广播Intent.ACTION_REBOOT
    • 通过Intent.EXTRA_KEY_CONFIRM设置是否需要人机交互
    • 如果Intent.EXTRA_USER_REQUESTED_SHUTDOWN为true,那么原因就是userrequested
    • 如果Intent.EXTRA_USER_REQUESTED_SHUTDOWN为true,那么原因就以Intent.EXTRA_REASON为准
    • 通过广播的方式进行重启/下电,本质上与直接调用pms的shutdown和reboot方法没有区别

    BatteryService发送关机广播

    在FW层BatteryService就使用了这种方式去让系统关机,BatteryService持续检测当前电池电量和当前温度,如果电量低于阀值,当前温度高于阀值,就会优雅的进行关机,关机的方式就是通过发送ACTION_REQUEST_SHUTDOWN广播。如下代码:

    4、终端执行adb reboot

    除了上述介绍到的通过应用发起关机或重启流程,我们还可以直接去命令终端执行adb reboot或者adb shell reboot等命令实现关机或重启。但其实两者还是有些区别:

    A adb shell reboot

    adb shell reboot命令首先通过ADB协议启动一个shell环境,然后执行reboot命令,reboot命令被内置在/system/bin/目录下,所以adb shell reboot命令的本质是直接调用reboot.bin二进制执行文件

    那么reboot.bin是如何实现的?如上代码路径,被定义在/system/core/reboot/目录下

    //system/core/reboot/Android.bp 
    //cc_binary指定生成二进制可执行文件,名字叫做reboot
    cc_binary {
        name: "reboot", 
        srcs: ["reboot.c"],
        shared_libs: ["libcutils"],
        cflags: ["-Werror"],
        recovery_available: true,
    }
    //system/core/reboot/reboot.c 
    int main(int argc, char* argv[]) {
        int ret;
        size_t prop_len;
        char property_val[PROPERTY_VALUE_MAX];
        static const char reboot[] = "reboot";
        const char* cmd = reboot;
        char* optarg = "";
        opterr = 0;
        do {
            int c;
            c = getopt(argc, argv, "p");
            if (c == -1)  break;
            switch (c) {
            case 'p':
                //核心逻辑:adb shell reboot -p表示关机,这里命令改成了shutdown
                cmd = "shutdown";
                break;
            case '?':
                fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]);
                exit(EXIT_FAILURE);
            }
        } while (1);
        if(argc > optind + 1) {
            fprintf(stderr, "%s: too many arguments\n", argv[0]);
            exit(EXIT_FAILURE);
        }
        if (argc > optind) optarg = argv[optind];
        if (!optarg || !optarg[0]) optarg = "shell";
        prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
        if (prop_len >= sizeof(property_val)) {
            fprintf(stderr, "%s command too long: %s\n", cmd, optarg);
            exit(EXIT_FAILURE);
        }
        //#define ANDROID_RB_PROPERTY "sys.powerctl"
        //核心逻辑:设置sys.powerctl属性值
        ret = property_set(ANDROID_RB_PROPERTY, property_val);
        if (ret < 0) {
            perror(cmd);
            exit(EXIT_FAILURE);
        }
        // Don't return early. Give the reboot command time to take effect to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
        if (cmd == reboot) {
            while (1)   pause();
        }
        fprintf(stderr, "Done\n");
        return 0;
    }
    

    代码总结:

    • reboot.cpp主函数最终还是通过设置sys.powerctl属性的方式来实现关机和重启
    • adb shell reboot -p 表示关机

    B adb reboot

    adb reboot命令同上面的命令的区别在于,通过ADB协议直接向设备上的adbd守护进程,adbd守护进程解析到不是shell、root等这样的参数,解析出来是reboot参数,因而主动调用了/system/bin/reboot可执行文件,来实现系统重启或关机。

    详情可以参考:https://blog.csdn.net/qq_27672101/article/details/148332643

    5、reboot_on_failure reboot引起系统重启

    这里介绍一下安卓中一种比较特殊的机制,即rc文件中reboot_on_failure reboot语句,该语句广泛应用于android native层,如下A14代码搜索:

    我之前遇到了一个案例,是reboot_on_failure  reboot,netbpfload-failed触发。但这里我们以vold进程的配置举例:

    //system/vold/vold.rc
    service vold /system/bin/vold  --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0  --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
        class core
        ioprio be 2
        task_profiles ProcessCapacityHigh
        shutdown critical
        group root reserved_disk
        user root
        reboot_on_failure reboot,vold-failed

    如上service的最后一行配置,意思即为如果vold进程意外终止,那么触发reboot指令,并传递reason为vold-failed表示这次重启的原因是vold进程挂掉导致。

    A reboot_on_failure字段解析

    如上代码逻辑,init进程在解析service的时候,如果存在reboot_on_failure字段,就会把该字段后续的字符串赋值到成员变量on_failure_reboot_target_中,例如这个案例就把reboot,vold-failed字符串赋给此变量。

    B service异常触发trigger_shutdown

    现在已经把命令reboot,vold-failed配置到了service的成员变量on_failure_reboot_target_中,那么service何时才用上她呢?

    如上代码,这里的触发条件主要有如下两种场景:

    • 在启动Service的时候,通过make_scope_guard指定了回调函数,即在start函数启动异常的时候执行{}里面的代码块,如果on_failure_reboot_target_非空就通过trigger_shutdown去触发系统重启。
    • 在清理Service的时候,通过Reap去进行回收操作,此时直接判断on_failure_reboot_target_非空就触发trigger_shutdown去触发系统重启。
    • trigger_shutdown触发系统重启,虽然和设置sys.powerctl方式不一样,但是回到本篇《sys.powerctl的设置和监听》小节,可以发现sys.powerctl也是通过此方式进行触发。

    C trigger_shutdown触发流程

    trigger_shutdown的触发流程前文只是提到,并没有深入讲解到她的设计机制,其实我们只要理解trigger_shutdown("reboot,xxx")就是像init进程申请重启/关机命令,init main loop循环中就会检测到并调用HandlePowerctlMessage流程。

    这里补充一下这块的机制,如上代码,先看看trigger_shutdown是什么?从util.cpp的定义来他是一盒函数指针:

    void (*trigger_shutdown)(const std::string& command) = nullptr;

    针对C语言的函数指针变量这里不作具体介绍,表现的理解可以把他当成函数进行调用,但前提是对函数指针进行了赋值,赋值通常为函数的首地址,否则就会发生未知错误,我们来看看在哪里进行赋值的?

    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); }; 

    上面这段代码在init main loop的SecondStageMain()函数中,即调用函数指针trigger_shutdown,其实就是调用ShutdownState.TriggerShutdown方法。这个静态类实现了对shutdown命令的单例封装,在main loop循环中通过CheckShutdown来检测是否存在shutdown命令,进而触发HandlePowerctlMessage流程。

    Logo

    火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

    更多推荐