Android Init 系列专题【篇五:reboot & shutdown】
在android系统中,init进程不仅仅作为第一个进程,涉及设备的开机流程,其实android系统的关机和重启流程,也都离不开init。本篇以android系统的关机/重启流程为切入点,重点来介绍一下init进程在这个过程中是如何举足轻重。如上每一行日志的都是非常关键,这个案例是autotesttool自动化测试工具在压测过程中通过设置属性sys.powerctl='reboot,autotes
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方法没有区别
B 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流程。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)