救援模式:
目前市场上的手机消费者包括资深用户,当他们的手机出现无限循环启动的异常时,用户没有办法修复异常只能通过设备商售后处理。Google在Android 8.0加入该新功能,称之为rescue party救援程序。主要监控系统核心程序出现循环崩溃的时候,会启动该程序,根据不同的救援级别做出一系列操作,看是否可恢复设备,最严重的时候则是通过进入recovery然后提供用户清空用户数据恢复出厂设置解决。
遇到开机进到recovery模式的情况,其实我们全局搜索下关键字“RescueParty”
就可以看到在init进程收到reboot,recovery指令之前,就可以看到因为救援模式,而发生了wipe-data
以下代码基于Android13:
首先救援分不同的级别,不同的级别执行不同的代码逻辑
//QSSI.13\frameworks\base\services\core\java\com\android\server\RescueParty.java
@VisibleForTesting
static final int LEVEL_NONE = 0;
@VisibleForTesting
static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1;
@VisibleForTesting
static final int LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2;
@VisibleForTesting
static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;
@VisibleForTesting
static final int LEVEL_WARM_REBOOT = 4;
@VisibleForTesting
static final int LEVEL_FACTORY_RESET = 5;
@VisibleForTesting
static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
@VisibleForTesting
static final String TAG = "RescueParty";
....
private static void executeRescueLevelInternal(Context context, int level, @Nullable
String failedPackage) throws Exception {
FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
// Try our best to reset all settings possible, and once finished
// rethrow any exception that we encountered
Exception res = null;
Runnable runnable;
Thread thread;
switch (level) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
try {
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
level);
} catch (Exception e) {
res = e;
}
try {
resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
} catch (Exception e) {
res = e;
}
break;
case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
try {
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES,
level);
} catch (Exception e) {
res = e;
}
try {
resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
} catch (Exception e) {
res = e;
}
break;
case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
try {
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS,
level);
} catch (Exception e) {
res = e;
}
try {
resetDeviceConfig(context, /*isScoped=*/false, failedPackage);
} catch (Exception e) {
res = e;
}
break;
case LEVEL_WARM_REBOOT:
// Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
// when device shutting down.
SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true");
runnable = () -> {
try {
PowerManager pm = context.getSystemService(PowerManager.class);
if (pm != null) {
pm.reboot(TAG);
}
} catch (Throwable t) {
logRescueException(level, failedPackage, t);
}
};
thread = new Thread(runnable);
thread.start();
break;
case LEVEL_FACTORY_RESET:
SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true");
runnable = new Runnable() {
@Override
public void run() {
try {
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
} catch (Throwable t) {
logRescueException(level, failedPackage, t);
}
}
};
thread = new Thread(runnable);
thread.start();
break;
}
if (res != null) {
throw res;
}
}
触发场景:
(1)system_server 在 10 分钟内重启 5 次以上调整一次级别。(Android 12以前 为5分钟内5次)
(2)永久性系统应用在 50 秒内崩溃 5 次以上调整一次级别。(Android 12 以前 默认为30秒内5次)
当检测到上述某种情况时,救援程序会将其上报给下一救援级别、处理与该级别相关联的任务,并让设备继续运行,看看能否恢复。清除或重置内容的程度随级别而增加。最高级别会提示用户将设备恢复出厂设置。
别的逻辑都比较简单,我们看一下恢复出厂的逻辑:
//QSSI.13/frameworks/base/core/java/android/os/RecoverySystem.java
public static void rebootPromptAndWipeUserData(Context context, String reason)
throws IOException {
boolean checkpointing = false;
boolean needReboot = false;
IVold vold = null;
try {
vold = IVold.Stub.asInterface(ServiceManager.checkService("vold"));
if (vold != null) {
checkpointing = vold.needsCheckpoint();
} else {
Log.w(TAG, "Failed to get vold");
}
} catch (Exception e) {
Log.w(TAG, "Failed to check for checkpointing");
}
// If we are running in checkpointing mode, we should not prompt a wipe.
// Checkpointing may save us. If it doesn't, we will wind up here again.
// 检测是否可以通过回滚消除问题,
if (checkpointing) {
try {
vold.abortChanges("rescueparty", false);
Log.i(TAG, "Rescue Party requested wipe. Aborting update");
} catch (Exception e) {
Log.i(TAG, "Rescue Party requested wipe. Rebooting instead.");
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
pm.reboot("rescueparty");
}
return;
}
String reasonArg = null;
if (!TextUtils.isEmpty(reason)) {
reasonArg = "--reason=" + sanitizeArg(reason);
}
final String localeArg = "--locale=" + Locale.getDefault().toString();
//执行恢复出厂命令--prompt_and_wipe_data
bootCommand(context, null, "--prompt_and_wipe_data", reasonArg, localeArg);
}
private static void bootCommand(Context context, String... args) throws IOException {
LOG_FILE.delete();
StringBuilder command = new StringBuilder();
for (String arg : args) {
if (!TextUtils.isEmpty(arg)) {
command.append(arg);
command.append("\n");
}
}
// Write the command into BCB (bootloader control block) and boot from
// there. Will not return unless failed.
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
rs.rebootRecoveryWithCommand(command.toString());
throw new IOException("Reboot failed (no permissions?)");
}
这里通过Biner调用远程的RecoverySystemService
//QSSI.13\frameworks\base\services\core\java\com\android\server\recoverysystem\RecoverySystemService.java
@Override // Binder call
public void rebootRecoveryWithCommand(String command) {
if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
synchronized (sRequestLock) {
if (!setupOrClearBcb(true, command)) {
return;
}
// Having set up the BCB, go ahead and reboot.
//最终还是调用PowerManager,触发重启
PowerManager pm = mInjector.getPowerManager();
pm.reboot(PowerManager.REBOOT_RECOVERY);
}
}
救援程序的禁用场景:
(1)PROP_ENABLE_RESCUE属性值为false,并且PROP_DEVICE_CONFIG_DISABLE_FLAG属性为true
(2)eng版本下
(3)调试版本,并且usb连接电脑
(4)PROP_DISABLE_RESCUE为true
逻辑控制代码:
//QSSI.13\frameworks\base\services\core\java\com\android\server\RescueParty.java
private static boolean isDisabled() {
// Check if we're explicitly enabled for testing
if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
return false;
}
// We're disabled if the DeviceConfig disable flag is set to true.
// This is in case that an emergency rollback of the feature is needed.
if (SystemProperties.getBoolean(PROP_DEVICE_CONFIG_DISABLE_FLAG, false)) {
Slog.v(TAG, "Disabled because of DeviceConfig flag");
return true;
}
// We're disabled on all engineering devices
if (Build.IS_ENG) {
Slog.v(TAG, "Disabled because of eng build");
return true;
}
// We're disabled on userdebug devices connected over USB, since that's
// a decent signal that someone is actively trying to debug the device,
// or that it's in a lab environment.
if (Build.IS_USERDEBUG && isUsbActive()) {
Slog.v(TAG, "Disabled because of active USB connection");
return true;
}
// One last-ditch check
if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) {
Slog.v(TAG, "Disabled because of manual property");
return true;
}
return false;
}
这也就解释了前面的现象,也就是插着usb设备不会进到recovery模式。
系统开机早期,开启RecoverySystemService 服务,然后通过registerHealthObserver注册PackageWatchdog崩溃事件的监听
//QSSI.13\frameworks\base\services\java\com\android\server\SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
t.traceBegin("startBootstrapServices");
...
// Bring up recovery system in case a rescue party needs a reboot
t.traceBegin("StartRecoverySystemService");
mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class);
t.traceEnd();
...
// Now that we have the bare essentials of the OS up and running, take
// note that we just booted, which might send out a rescue party if
// we're stuck in a runtime restart loop.
RescueParty.registerHealthObserver(mSystemContext);
PackageWatchdog.getInstance(mSystemContext).noteBoot();
t.traceEnd();
t.traceEnd(); // startBootstrapServices
}
//QSSI.13\frameworks\base\services\core\java\com\android\server\RescueParty.java
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
RescuePartyObserver.getInstance(context));
}
这里要注意RescuePartyObserver这个内部类实现了PackageHealthObserver接口,比如说当PackageWatchdog检测到应用崩溃就会回调到execute方法,这里的崩溃包括Crash和Anr,最后executeRescueLevel->executeRescueLevelInternal回到上面不同的level等级做处理
public static class RescuePartyObserver implements PackageHealthObserver {
...
@Override
public boolean execute(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
if (isDisabled()) {
return false;
}
if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
//获取当前救援等级
final int level = getRescueLevel(mitigationCount,
mayPerformFactoryReset(failedPackage));
executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level);
return true;
} else {
return false;
}
}
...
}
当设备具有有效的 USB 数据连接时,系统会停止所有救援事件,因为这是一个较强的信号,表示有人正在调试设备。如需停止此类抑制行为,请运行以下命令
adb shell setprop persist.sys.enable_rescue 1
在此处,您可以触发系统或界面崩溃循环。
如需触发低级 system_server 崩溃循环,请运行以下命令:
adb shell setprop debug.crash_system 1
//QSSI.13/frameworks/base/services/java/com/android/server/SystemServer.java
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) {
throw new RuntimeException();
}
现在我们需要看下PackageWatchdog它是怎么知道system_server或者应用出问题了呢?
- 如何判断system_server出现问题
首先我们知道PackageWatchdog是运行在system_server里的,system_server异常会直接导致system_server重启,出问题时根本走不到PackageWatchdog里,那它是如何触发的呢?
我们可以称之为Boot记录法,还记得我们上面说过系统开机的时候会触发一次PackageWatchdog的noteBoot方法吗?这个方法其实用处很简单,只是起到通知作用,告诉PackageWatchdog我这边system_server在进行一次Boot引导了,此时PackageWatchdog会通过内部类BootThreshold进行一次记录,将当前时间写入sys.rescue_boot_start,然后自增sys.rescue_boot_count的值记录system_server重启的次数,后续异常重新noteBoot时这个值就会增加一次,当这些时间和次数都超出预设值时就会触发一个level等级,触发对应RescueParty逻辑,并将当前level等级保存到sys.boot_mitigation_count中,有必要也会保存到mate中,然后重置计数器。
//QSSI.13\frameworks\base\services\core\java\com\android\server\PackageWatchdog.java
@VisibleForTesting
static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5;
static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10);
public void noteBoot() {
synchronized (mLock) {
//自增Boot阈值计数器,并检查是否超出预设值
if (mBootThreshold.incrementAndTest()) {
//重置计数器
mBootThreshold.reset();
//自增当前level等级
int mitigationCount = mBootThreshold.getMitigationCount() + 1;
PackageHealthObserver currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
//处理其他Observers
for (int i = 0; i < mAllObservers.size(); i++) {
final ObserverInternal observer = mAllObservers.valueAt(i);
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null) {
int impact = registeredObserver.onBootLoop(mitigationCount);
if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
&& impact < currentObserverImpact) {
currentObserverToNotify = registeredObserver;
currentObserverImpact = impact;
}
}
}
if (currentObserverToNotify != null) {
//保存当前level等级,有必选会保存到metadata中
mBootThreshold.setMitigationCount(mitigationCount);
mBootThreshold.saveMitigationCountToMetadata();
//回调到RescueParty,执行executeRescueLevel
currentObserverToNotify.executeBootLoopMitigation(mitigationCount);
}
}
}
}
/** Increments the boot counter, and returns whether the device is bootlooping. */
public boolean incrementAndTest() {
//从Meta中读取level等级
readMitigationCountFromMetadataIfNecessary();
final long now = mSystemClock.uptimeMillis();
if (now - getStart() < 0) {
Slog.e(TAG, "Window was less than zero. Resetting start to current time.");
setStart(now);
setMitigationStart(now);
}
//长时间未发生异常,重置level记录
if (now - getMitigationStart() > DEFAULT_DEESCALATION_WINDOW_MS) {
setMitigationCount(0);
setMitigationStart(now);
}
final long window = now - getStart();
//阈值条件内未重新Boot,重置system_server重启计数器
if (window >= mTriggerWindow) {
setCount(1);
setStart(now);
return false;
} else {
//触发阈值条件,计数器+1,当大约mBootTriggerCount时返回true
int count = getCount() + 1;
setCount(count);
EventLogTags.writeRescueNote(Process.ROOT_UID, count, window);
return count >= mBootTriggerCount;
}
}
- 如何检查应用出现问题
应用问题一般分两种Crash和Anr,这些对于当前系统来说都是不正常的情况,都需要记录。
先说Crash,首先我们得知道Android系统是如何获取到异常的。
Java中异常发生时,如果没有一个异常处理器来处理这个异常,程序会被中止。在 JVM 当中有一个预先定义好的异常处理层次结构。结构中的第一层是try catch 块,代码类似:
try {
crashyCode()
} catch (Exception e) {
...
}
如果第一个 catch 块无法处理这个异常,异常便会向此方法的调用方进行传递。如果所有的 catch 块都无法处理某个异常,该异常便会交由当前线程的 UncaughtExceptionHandler 来处理。
setUncaughtExceptionHandler 可以调协在当前线程里,未被 catch 块捕获的异常处理流程会先来到这里;还可以设置在 ThreadGroup 当中,当前线程的 UncaughtExceptionHandler 无法处理的异常会在这里被处理。如果 ThreadGroup 的 UncaughtExceptionHandler 还是无法处理该异常,那么最终将会被交由默认异常处理程序 ( default uncaught exception handler ) 处理,也就是打印出异常栈,并终止程序。当然你也可以覆盖这种行为:
Thread.setDefaultUncaughtExceptionHandler{/*自定义实现的UncaughtExceptionHandler*/}
Android的异常处理也是基于这个进行设计的,首先在RuntimeInit中
//QSSI.13\frameworks\base\core\java\com\android\internal\os\RuntimeInit.java
//注册UncaughtExceptionHandler处理函数,出现未捕获的Crash时调用uncaughtException方法
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
try {
//触发AMS的Crash机制
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
} catch (Throwable t2) {
...
} finally {
// Try everything to make sure this process goes away.
Process.killProcess(Process.myPid());
System.exit(10);
}
}
这边我们将异常捕获到我们的Android代码当中,并由AMS中handleApplicationCrash->handleApplicationCrashInner,此时需要引入AppErrors类,AMS意思调用的其crashApplication进一步处理,
AppErrorscrashApplication->crashApplicationInner
//QSSI.13\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
public void handleApplicationCrash(IBinder app,
ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
/* Native crash reporting uses this inner version because it needs to be somewhat
* decoupled from the AM-managed cleanup lifecycle
*/
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
...
mAppErrors.crashApplication(r, crashInfo);
}
//QSSI.13\frameworks\base\services\core\java\com\android\server\am\AppErrors.java
private void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
int callingPid, int callingUid) {
...
if (r != null) {
mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
PackageWatchdog.FAILURE_REASON_APP_CRASH);
synchronized (mService) {
mService.mProcessList.noteAppKill(r, (crashInfo != null
&& "Native crash".equals(crashInfo.exceptionClassName))
? ApplicationExitInfo.REASON_CRASH_NATIVE
: ApplicationExitInfo.REASON_CRASH,
ApplicationExitInfo.SUBREASON_UNKNOWN,
"crash");
}
}
...
}
这里就调用了PackageWatchdog的onPackageFailure方法,这里逻辑就不细讲了,主要是通过MonitoredPackage做一些crash信息的记录,不同的packageName保存到不同的MonitoredPackage对象中,并通过记录数量以及时间判断是否触发level级别处理
//QSSI.13\frameworks\base\services\core\java\com\android\server\PackageWatchdog.java
public void onPackageFailure(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
if (packages == null) {
Slog.w(TAG, "Could not resolve a list of failing packages");
return;
}
mLongTaskHandler.post(() -> {
synchronized (mLock) {
if (mAllObservers.isEmpty()) {
return;
}
boolean requiresImmediateAction = (failureReason == FAILURE_REASON_NATIVE_CRASH
|| failureReason == FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
if (requiresImmediateAction) {
handleFailureImmediately(packages, failureReason);
} else {
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
VersionedPackage versionedPackage = packages.get(pIndex);
// Observer that will receive failure for versionedPackage
PackageHealthObserver currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
MonitoredPackage currentMonitoredPackage = null;
// Find observer with least user impact
for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
ObserverInternal observer = mAllObservers.valueAt(oIndex);
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null
&& observer.onPackageFailureLocked(
versionedPackage.getPackageName())) {
MonitoredPackage p = observer.getMonitoredPackage(
versionedPackage.getPackageName());
int mitigationCount = 1;
if (p != null) {
mitigationCount = p.getMitigationCountLocked() + 1;
}
int impact = registeredObserver.onHealthCheckFailed(
versionedPackage, failureReason, mitigationCount);
if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
&& impact < currentObserverImpact) {
currentObserverToNotify = registeredObserver;
currentObserverImpact = impact;
currentMonitoredPackage = p;
}
}
}
// Execute action with least user impact
if (currentObserverToNotify != null) {
int mitigationCount = 1;
if (currentMonitoredPackage != null) {
currentMonitoredPackage.noteMitigationCallLocked();
mitigationCount =
currentMonitoredPackage.getMitigationCountLocked();
}
currentObserverToNotify.execute(versionedPackage,
failureReason, mitigationCount);
}
}
}
}
});
}
我们再继续看PowerManager 的reboot操作
//TQSSI.13\frameworks\base\core\java\android\os\PowerManager.java
@RequiresPermission(permission.REBOOT)
public void reboot(@Nullable String reason) {
if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
throw new UnsupportedOperationException(
"Attempted userspace reboot on a device that doesn't support it");
}
try {
mService.reboot(false, reason, true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//QSSI.13\frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
@Override // Binder call
public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
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);
}
}
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
@Nullable final String reason, boolean wait) {
...
if (mHandler == null || !mSystemReady) {
//判断是否为救援模式重启
if (RescueParty.isAttemptingFactoryReset()) {
// 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 {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
}
...
}
public static void lowLevelReboot(String reason) {
if (reason == null) {
reason = "";
}
...
if (reason.equals(PowerManager.REBOOT_RECOVERY)
|| reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
reason = "recovery";
}
....
//这里我们看到设置了属性:sys.powerctl:reboot recovery
SystemProperties.set("sys.powerctl", "reboot," + reason);
try {
Thread.sleep(20 * 1000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
}
此后init进程会收到该属性的变化
//QSSI.13/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.")) {
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") {
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(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;
}
}
...
}
//QSSI.13/system/core/init/init.cpp
void DebugRebootLogging() {
LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
<< " IsShuttingDown: " << IsShuttingDown();
if (shutdown_state.do_shutdown()) {
LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
UnwindMainThreadStack();
}
if (IsShuttingDown()) {
LOG(ERROR) << "sys.powerctl set while init is already shutting down";
UnwindMainThreadStack();
}
}
继而都会出现如下日志:
05-06 09:48:26.674 266 266 I init : sys.powerctl: do_shutdown: 0 IsShuttingDown: 0
05-06 09:48:26.675 1 1 I init : Got shutdown_command 'reboot,recovery' Calling HandlePowerctlMessage()
05-06 09:48:26.687 1 1 I init : Clear action queue and start shutdown trigger
05-06 09:48:26.687 1 1 I init : Entering shutdown mode