手机进入Recovery之 RescueParty
系统版本:Android 12
1. 案例一:三方内置应用频繁Crash导致Recovery
1.1根据现有的log来看:
本次手机重启进入 Recovery
是由于 “com.rjio.slc” 应用频繁发生奔溃导致.
1.2分析线索如下:
W/RescueParty( 1090): Attempting rescue level FACTORY_RESET
D/PackageManager( 1090): Finished rescue level FACTORY_RESET for package com.rjio.slc //导致将会进入FACTORY_RESET模式,将使得手机重启,并让用户选择是否擦除用户数据
I/uncrypt ( 8502): received command: [--prompt_and_wipe_data
I/uncrypt ( 8502): --reason=RescueParty
I/uncrypt ( 8502): --locale=en_IN
I/uncrypt ( 8502): ] (59)
W/uncrypt ( 8502): [libfs_mgr]Warning: unknown flag: optional
D/AAL ( 862): 04-27 01:50:49.329 BL= 853,ESS= 256, 04-27 01:50:49.340 BL= 852,ESS= 256,
I/uncrypt ( 8502): received 0, exiting now
I/RecoverySystemService( 1090): uncrypt setup bcb successfully finished.
V/ShutdownCheckPoints( 1090): Binder shutdown checkpoint recorded with pid=1090
I/MtkSystemServiceFactoryImpl( 1090): Start : MTK Shutdown Thread
V/ShutdownCheckPoints( 1090): System server shutdown checkpoint recorded
//该应用 发生异常 信息如下
E/AndroidRuntime( 8507): FATAL EXCEPTION: Thread-2
E/AndroidRuntime( 8507): Process: com.rjio.slc, PID: 8507
E/AndroidRuntime( 8507): java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
E/AndroidRuntime( 8507): at com.rjio.slc.util.CommonUtility.getSalt(Unknown Source:8)
E/AndroidRuntime( 8507): at com.rjio.slc.util.CommonUtility.getHash(Unknown Source:17)
E/AndroidRuntime( 8507): at com.rjio.slc.util.CommonUtility.getDeviceInfoJsonParams(Unknown Source:96)
E/AndroidRuntime( 8507): at com.rjio.slc.util.CommonUtility.sendDeviceInfo(Unknown Source:2)
E/AndroidRuntime( 8507): at com.rjio.slc.util.NetworkCallThread$InternalThread.run(Unknown Source:228)
I/am_crash( 1090): [8507,0,com.rjio.slc,545799757,java.lang.NullPointerException,Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference,NULL,8]
W/ActivityManager( 1090): Process com.rjio.slc has crashed too many times, killing! Reason: crashed quickly
I/am_process_crashed_too_much( 1090): [0,com.rjio.slc,1000]
I/sysui_multi_action( 1090): [757,316,758,4,759,-1]
I/ShutdownThread( 1090): Shutting down activity manager...
从有限的日志来看确实由于三方应用频繁crash导致系统重启.
1.3进入重启的函数调用栈如下:
RescuePartyObserver.execute(…)
—executeRescueLevel(…) //对应log:Slog.w(TAG, "Attempting rescue level " + levelToString(level));
------executeRescueLevelInternal()
---------case LEVEL_FACTORY_RESET:
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);//这将会导致重启
------------RecoverySystem.rebootPromptAndWipeUserData(…)
--------------- bootCommand(…)// 对应log:–prompt_and_wipe_data
------------------rs.rebootRecoveryWithCommand(command.toString())//这将调用对应服务重启系统
下面我们开始进行探究.
2.应用频繁Crash是如何导致系统重启探究
2.1问题: 我们的系统是如何检测到应用频繁发生crash,并且触发重启机制的?
主要的类为 RescuePartyObserver
类,该类在 RescueParty
中实现 PackageHealthObserver
接口.该类的将会注册到Package的WatchDog
中,用来接收package的失败事件,以及做出相应处理.如何处理?(TODO)
2.1.1RescuePartyObserver类
PackageHealthObserver
接口如下:
/** Register instances of this interface to receive notifications on package failure. */
public interface PackageHealthObserver {
/**
* 当 {@code versionedPackage} 的运行状况检查失败时调用。
*/
@PackageHealthObserverImpact int onHealthCheckFailed(
@Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason,
int mitigationCount);
/**
* 执行 {@link #onHealthCheckFailed} 的缓解措施。
*/
boolean execute(@Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason, int mitigationCount);
/**
*当系统服务器在由 {@link #mBootThreshold} 定义的时间窗口内引导多次时调用
*/
default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
/**
* 执行 {@link #onBootLoop} 的缓解措施
*/
default boolean executeBootLoopMitigation(int mitigationCount) {
return false;
/**
* 观察器的标识符不应在设备更新之间更改,否则看门狗可能会丢弃具有旧名称的观测包。
*/
String getName();
/**
* 如果设置了此项,则不会修剪观察器,即使观察器未显式监视任何包也是如此。
*/
default boolean isPersistent() {
return false;
}
/**
* 如果此观察者希望观察给定的包,则返回 {@code true},否则返回 {@code false}
*/
default boolean mayObservePackage(String packageName) {
return false;
}
}
2.1.2启动RescuePartyObserver流程:
在 SystemServer
启动的startBootstrapServices 阶段 将完成 RescueParty.registerHealthObserver(mSystemContext)
;
//frameworks/base/services/core/java/com/android/server/RescueParty.java
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
RescuePartyObserver.getInstance(context));
}
PackageWatchdog
类的作用主要是监测系统中各个包的运行情况
将 RescuePartyObserver
对象存放到该类中的mAllObservers成员中,可见 PackageWatchdog
类 该成员中保存着不止 RescuePartyObserver
这一种Oberver,既然是Observer,那么后面有某些意外情况会回调它.
//frameworks/base/services/core/java/com/android/server/PackageWatchdog.java
public void registerHealthObserver(PackageHealthObserver observer) {
synchronized (mLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getName());
if (internalObserver != null) {
internalObserver.registeredObserver = observer;
} else {
internalObserver = new ObserverInternal(observer.getName(), new ArrayList<>());
internalObserver.registeredObserver = observer;
mAllObservers.put(observer.getName(), internalObserver);
syncState("added new observer");
}
}
}
在 PackageWatchdog
中主要以下方法会调用到这些 observer
出处理包运行出错的情况
.
1.onPackageFailure //当进程由于崩溃、ANR 或显式运行状况检查而失败时调用。
2.handleFailureImmediately //对于本机崩溃或明显的运行状况检查失败,请直接调用每个观察器以缓解错误,而无需经过故障阈值逻辑。
3.noteBoot //在systemserver启动时调用。如果检测到Systemserver于 boot loop中,请查询每个观察者,并以对用户的影响最小执行缓解操作。
4.writeNow // 这目前为关机线程增加了大约7ms的额外空间 在关机期间将包信息写入文件。调用saveToFile函数将所有observer保持到文件中
5.onHealthCheckPassed //运行情况检查没有问题将调用到这
6.onSupportedPackages //在启动期间调用,以在设备上package准备就绪时通知,以便我们可以开始绑定。TODO 如何检查?
7.getPackagesPendingHealthChecksLocked
8.getNextStateSyncMillisLocked // 返回下一个持续时间(以毫秒为单位)以同步监视程序状态。
9.pruneObserversLocked // 从受监视包上的所有持续时间中删除 {@code elapsedMs,已用掉} 毫秒
10.loadFromFile
11.saveToFile //将所有observer保持到文件中/data/system/package-watchdog.xml中
12.dump
2.1.2onPackageFailure()方法
2.1.2.1onPackageFailure()方法用途1
当 ActivityManagerService
的启动流程走到 onBootPhase(int phase)
的 PHASE_THIRD_PARTY_APPS_CAN_START
阶段,将会调用mService.mPackageWatchdog.onPackagesReady()
进而调用 registerConnectivityModuleHealthListener()
函数
该函数内容如下:
private void registerConnectivityModuleHealthListener() {
// TODO: have an internal method to trigger a rollback by reporting high severity errors,
// and rely on ActivityManager to inform the watchdog of severe network stack crashes
// instead of having this listener in parallel.
mConnectivityModuleConnector.registerHealthListener(
packageName -> {
final VersionedPackage pkg = getVersionedPackage(packageName);
if (pkg == null) {
Slog.wtf(TAG, "NetworkStack failed but could not find its package");
return;
}
final List<VersionedPackage> pkgList = Collections.singletonList(pkg);
onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
});
}
将该 onPackageFailure
函数作为 ConnectivityModuleHealthListener
的一部分注册到 ConnectivityModuleConnector
中,该类和网络相关,注册的目的应该是:和网络相关包运行出现问题将会回调到 onPackageFailure
函数,从而进行相应的处理.
网络相关的我们暂且忽略.
2.1.2.2onPackageFailure()方法用途2
- RollbackPackageHealthObserver
- RollbackManagerServiceImpl
- RollbackManagerService
这几个个类涉及到回滚机制–Rollback
回滚机制:在Android 10.0中,Google新增加了个功能。如果用户对新升级的APP不满意,可以通过“回到过去”,回滚到旧版。当然,如果新安装的apk出现了各种问题无法使用,也可以进行回滚的操作。–我们暂且略过
调用到onPackageFailure函数的流程如下:
SystemServer.startCoreServices)
---mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS)
------RollbackManagerService.onBootPhase(mCurrentPhase)
---------onBootCompleted()
------------PackageHealthObserver.onBootCompletedAsync()
---------------onBootCompleted()
------------------PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes()//进入PackageWatchdog
---------------------mShortTaskHandler.post(()->checkAndMitigateNativeCrashes())//mShortTaskHandler这里的handler应该会将该函数传到SystemServer主线程去执行
代码如下:
private void checkAndMitigateNativeCrashes() {
mNumberOfNativeCrashPollsRemaining--;
// Check if native watchdog reported a crash
if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
// We rollback everything available when crash is unattributable
onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
// we stop polling after an attempt to execute rollback, regardless of whether the
// attempt succeeds or not
} else {
if (mNumberOfNativeCrashPollsRemaining > 0) {
mShortTaskHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
}
}
}
2.2从Thread的线程捕获异常接口开始探究
2.2.1Thread.UncaughtExceptionHandler接口:
该接口说明如下:
Thread由于未捕获的异常而突然终止时调用的处理程序接口。
1.当一个线程要终止由于未捕获到异常的Java虚拟机将使用查询线程其UncaughtExceptionHandler getUncaughtExceptionHandler()
,将调用处理程序的uncaughtException
方法,将线程和异常作为参数。
2.如果一个线程一直没有其UncaughtExceptionHandler
明确设置,那么它ThreadGroup
对象充当其UncaughtExceptionHandler
。 如果ThreadGroup
对象没有特殊要求处理异常,它可以将调用转发给default uncaught exception handler
Thread
中存在两个 UncaughtExceptionHandler
-
Thread.UncaughtExceptionHandler
- 一个是非静态
uncaughtExceptionHandler
:为单个线程设置一个属于线程自己的uncaughtExceptionHandler
,辖范围比较小
- 一个是非静态
-
static Thread.UncaughtExceptionHandler
- 静态的
UncaughtExceptionHandler
。来自所有线程中的Exception
在抛出并且未捕获的情况下,都会从此路过。进程fork的时候设置的就是这个静态的defaultUncaughtExceptionHandler
,管辖范围为整个进程。
- 静态的
线程组类 ThreadGroup也实现了该接口,因为当如果该线程没有明确的设置 UncaughtExceptionHandler
,异常处理将交给 ThreadGroup
进行处理。
我们主要看异常处理方法 uncaughtException
其次,ThreadGroup
可以表示一个线程,也可以表示一个线程组,线程和线程是以树形结构来进行组织的。
如下:
- 如果该线程组还有父线程组,则先执行父线程组的 uncaughtException()
- 如果有设置默认的
UncaughtExceptionHandler
则调用其uncaughtException()
- 如果异常不是
ThreadDeath
实例,则将调用栈打印出来,输出到System.err
。
//ThreadGroup.java,可以
//这个方法将由JVM回调,当出现异常时
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
不同的进程只要设置了 UncaughtExceptionHandler
并且实现相关的 uncaughtException
,那么就有其处理异常的手段.我们可以再源码中搜索 “implements Thread.UncaughtExceptionHandler”
2.2.2我们看系统是如何处理异常的:
/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
两个异常处理类:
-
LoggingHandler
- 主要功能是系统异常信息记录:
-
- 如果是系统进程
system_server
异常,则打印:*** FATAL EXCEPTION IN SYSTEM PROCESS:XXX
日志
- 如果是系统进程
-
- 如果是内存溢出,打印
OutOfMemoryError IN SYSTEM PROCESS: Already dump hprof!
信息
- 如果是内存溢出,打印
-
- 如果其他进行,打印
FATAL EXCEPTION:+进程XXX,以及Pid XXX
等
- 如果其他进行,打印
-
KillApplicationHandler
- 处理导致应用死亡的未捕获异常
-
- 先调用
LoggingHandler(..)
来处理 该异常,也就是说流程以及log信息打印和前面一样
- 先调用
-
- 停止当前进程的trace追踪
-
- 调用AMS的
handleApplicationCrash()
函数和 弹出Crash对话框
- 3.1 判断是否是
System_server
崩溃 - 3.2 调用
handleApplicationCrashInner(...)
处理am管理的生命周期清理的解耦- 3.2.1从PMS中获取该进程,这一步主要的目的是判断是否是在APP增量升级包中发生crash,如果发生,则Slog.e()打印“App crashed on incremental package”这样的log字样
- 3.2.2将Crash信息写入到Event log
EventLogTags.writeAmCrash(...)
- 3.2.3 将错误信息添加到DropBox
addErrorToDropBox(...)
是将crash的信息输出到目录/data/system/dropbox - 3.2.4 调用
AppErrors.crashApplication(...)
出来相关流程- 3.2.4.1
mPackageWatchdog.onPackageFailure()
处理(看如下介绍) - 3.2.4.2
mService.mProcessList.noteAppKill()
处理- 3.2.4.2.1 将该进程放入
mDyingProcesses
中一会杀掉它 - 3.2.4.2.2
mAppExitInfoTracker.scheduleNoteAppKill(..)
函数发送KillHandler.MSG_APP_KILL
记录“杀掉进程”前的一些信息
- 3.2.4.2.1 将该进程放入
- 3.2.4.3 发送
ActivityManagerService.SHOW_ERROR_UI_MSG
消息:主要弹出弹出提示crash的对话框提供用户选择
- 3.2.4.1
- 调用AMS的
-
- 最终调用
Process.killProcess(Process.myPid())
杀掉该进程
- 最终调用
-
- System.exit(10) 退出
代码流程:
//android/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
- System.exit(10) 退出
private static class LoggingHandler implements Thread.UncaughtExceptionHandler{..}//默认捕获异常的异常处理类
//处理因未捕获的异常而导致的应用程序死亡。 framework为主线程捕获这些线程,因此这只对应用程序创建的线程重要。在此方法运行之前,{@link LoggingHandler} 的给定实例应该已经记录了详细信息(如果不是,则首先运行)。
private static class KillApplicationHandler implements
Thread.UncaughtExceptionHandler{...}
3.PackageWatchdog.onPackageFailure函数如何做出反应
3.1处理说明
当通过上面的 3.2.4.1 步骤调用到 PackageWatchdog.onPackageFailure()
,逻辑如下:
-
-
紧急处理–当
failureReason
是FAILURE_REASON_NATIVE_CRASH
和FAILURE_REASON_EXPLICIT_HEALTH_CHECK
这两个 Reason将调用handleFailureImmediately
做立刻处理,
- 原理就是只拿packages中的第一个package,将这个出现问题的package传给各个 PackageHealthObserver 做处理(一般native cash或者explicit health check failures回走这一步)
-
紧急处理–当
-
- 非紧急处理–将逐个遍历packages中的包,逐个调用 PackageHealthObserver 做处理-详细如下:
- 2.1 找到对用户影响最小的观察者(目前有:
RollbackPackageHealthObserver
包回滚机制相关、RescueParty),首先先要获取package的缓解计数(主要统计各个Observer对于package发生的崩溃等其他异常情况的缓解处理次数,这个缓解计数 每当Observer对该package做出缓解action后 就会以当前时间作为值存入一个数组中,缓解计数则就是,这个当这个缓解次数超过一定的值之后,也就是说系统对于APK的发生崩溃的容忍度不能超过一定限度 ,超过限度后,Observers 将会发出回滚、重启等其他较为严重的处理)到此可先看一下下面的<package异常对用户影响程度判断…>再回过来往下看 - 2.2 回过来后,我们此时已经得到了相对 对用户影响程度较小的Observer以及对用户影响程度值,现在就要使用该Observer对于该package 异常做缓解处理了
- 2.2.1 将package的缓解计数加一
- 2.2.2获取最新的缓解计数做缓解处理 可以先继续往下看<缓解处理…>
//frameworks/base/services/core/java/com/android/server/PackageWatchdog.java
public void onPackageFailure(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
//...
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);
// 遍历各个发生异常的package
PackageHealthObserver currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
MonitoredPackage currentMonitoredPackage = null;
// 找到对用户影响最小的Observer,
//即各个Observer对于包异常的处理都不一样,我们要尽量避免出现系统重启或者恢复出厂设置这样的操作
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) {
//得到package的缓解计数
mitigationCount = p.getMitigationCountLocked() + 1;
}
//通过Observer通过onHealthCheckFailed()方法根据mitigationCount缓解计数返回一个package异常影响程度值
int impact = registeredObserver.onHealthCheckFailed(
versionedPackage, failureReason, mitigationCount);//我可以在下面看一下两个Observer的操作
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) {
//将package的缓解计数加一
currentMonitoredPackage.noteMitigationCallLocked();
//得到最新的package的缓解计数
mitigationCount =
currentMonitoredPackage.getMitigationCountLocked();
}
//“缓解处理“
currentObserverToNotify.execute(versionedPackage,
failureReason, mitigationCount);
}
}
}
}
});
}
3.2package异常对用户影响程度判断PackageHealthObserver.onHealthCheckFailed
两个Observer的处理:
- RollbackPackageHealthObserver回滚相关
- RescueParty.RescuePartyObserver
3.2.1. RollbackPackageHealthObserver.java
- 对用户的影响程度中等:
USER_IMPACT_MEDIUM
- 对于
Native crash
则返回的是PackageHealthObserverImpact.USER_IMPACT_MEDIUM
中等程度的影响
- 对于
- 其他异常,
- 有可用的Rollback:
PackageHealthObserverImpact.USER_IMPACT_MEDIUM
- 无可用的Rollback:
PackageHealthObserverImpact.USER_IMPACT_NONE
回滚机制–暂时略过
- 有可用的Rollback:
@Override
public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
// For native crashes, we will roll back any available rollbacks
if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
&& !mContext.getSystemService(RollbackManager.class)
.getAvailableRollbacks().isEmpty()) {
return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
}
if (getAvailableRollback(failedPackage) == null) {
// Don't handle the notification, no rollbacks available for the package
return PackageHealthObserverImpact.USER_IMPACT_NONE;
} else {
// Rollback is available, we may get a callback into #execute
return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
}
}
3.2.2. RescueParty.RescuePartyObserver
- 当原因是
APP CRASH
或者APP 无响应
将调用mapRescueLevelToUserImpact
处理,通过紧急程度RescueLevel
来判断对用户的影响程度UserImpact-
getRescueLevel
通过缓解计数得到紧急程度值紧急程度值如下- mitigationCount == 1:
LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS
- mitigationCount == 2:
LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES
- mitigationCount == 3:
LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS
- mitigationCount == 4:
LEVEL_WARM_REBOOT
和最大紧急程度对比的最小值 - mitigationCount >= 5:
LEVEL_FACTORY_RESET
和最大紧急程度对比的最小值 - 备注:最大紧急程度将由
persist.device_config.configuration.disable_rescue_party_factory_reset
属性决定,- 属性值为true,则最大紧急程度为 LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS
- 属性值为false或null,则最大紧急程度为 LEVEL_FACTORY_RESET
- mitigationCount == 1:
-
mapRescueLevelToUserImpact
方法将会依据刚刚的紧急程度得到一个对应用户影响程度的判断- 影响程度小: USER_IMPACT_LOW
LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS
LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES
- 影响程度高:USER_IMPACT_HIGH
LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS
LEVEL_WARM_REBOOT
LEVEL_FACTORY_RESET
- 影响程度小: USER_IMPACT_LOW
-
@Override
public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
} else {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
}
//-----------
private static int getRescueLevel(int mitigationCount) {
if (mitigationCount == 1) {
return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
} else if (mitigationCount == 2) {
return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
} else if (mitigationCount == 3) {
return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
} else if (mitigationCount == 4) {
return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT);
} else if (mitigationCount >= 5) {
return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET);
} else {
Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
return LEVEL_NONE;
}
}
//-----------
private static int mapRescueLevelToUserImpact(int rescueLevel) {
switch(rescueLevel) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
return PackageHealthObserverImpact.USER_IMPACT_LOW;
case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
case LEVEL_WARM_REBOOT:
case LEVEL_FACTORY_RESET:
return PackageHealthObserverImpact.USER_IMPACT_HIGH;
default:
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
}
主要根据通过两个 Observer
的 onHealthCheckFailed
方法来判断package发生异常对于用户的使用影响程度用多大
-
在回滚机制的判断中,对于用户的使用影响程度最多也只是中等水平
USER_IMPACT_MEDIUM
。 -
在
RescueParty
处理判断中,对于用户的使用影响程度是最大的,易发生USER_IMPACT_HIGH
3.3缓解处理 PackageHealthObserver.execute()
- RollbackPackageHealthObserver
- RescuePartyObserver.execute()
3.3.1RollbackPackageHealthObserver
- 当异常原因是:
Native Crash
将会最终会调用RollbackManagerService
处理,暂时先不岔开研究该服务(TODO) - 其他异常情况将调用可用的rollback进行处理也涉及上述服务,暂时略过
//RollbackPackageHealthObserver.java
@Override
public boolean execute(@Nullable VersionedPackage failedPackage,
@FailureReasons int rollbackReason, int mitigationCount) {
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
mHandler.post(() -> rollbackAll());
return true;
}
RollbackInfo rollback = getAvailableRollback(failedPackage);
//...
mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
// Assume rollback executed successfully
return true;
}
3.3.2. RescuePartyObserver.execute()
- 最终
getRescueLevel
会根据缓解计数 得到紧急程度,最终将会进入executeRescueLevelInternal
,主要分类如下:
execute
—executeRescueLevel
------executeRescueLevelInternal
详细处理流程
@Override
public boolean execute(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
//...
if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
final int level = getRescueLevel(mitigationCount);
executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level);
return true;
} else {
return false;
}
}
//----------
private static void executeRescueLevel(Context context, @Nullable String failedPackage,
int level) {
Slog.w(TAG, "Attempting rescue level " + levelToString(level));
try {
executeRescueLevelInternal(context, level, failedPackage);
//...
}
//---------
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:
//...部分异常处理无关代码已省略
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
level);
resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
break;
case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES,
level);
resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
break;
case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS,
level);
resetDeviceConfig(context, /*isScoped=*/false, failedPackage);
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);
}
//...异常处理省略
};
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() {
//...
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
//..异常处理
};
thread = new Thread(runnable);
thread.start();
break;
//...异常处理省略
}
从上面的代码流程中,我们可以看到,大约两种走向,大致走向我们看3.4和第5点
3.4.executeRescueLevelInternal 对于各种mode的处理
3.4.1.LEVEL_RESET_SETTINGS_XXX三种mode的处理流程
- 1–LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS
- 2–LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES
- 3–LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS
从LEVEL_RESET_SETTINGS_ 这个前缀来看也是和重设Settings相关数据库值相关操作,继续往下看
主要将会调用以下两个流程:
-
resetAllSettingsIfNecessary()
--重设异常package相关的数据库属性值 -
resetDeviceConfig()
–重设设备参数
即:异常package添加的数据库属性值我们要重设,异常package修改的设备参数我们也要重设恢复为默认值(属性表TODO)
3.4.1.1.resetAllSettingsIfNecessary 往下调用的流程:
RescueParty.resetAllSettingsIfNecessary()
—>Settings.Global.resetToDefaultsAsUser(…)
------>SettingsProvider.resetGlobalSetting()和resetSecureSetting()
--------->getResetModeEnforcingPermission,根据紧急程度,
--------->SettingsProvider.mutateSecureSetting()或mutateGlobalSetting()
------------>SettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE,…)
------------>根据三种模式做相应判断处理
--------------->notifyForSettingsChange(key, name)//修改数据库属性值
具体处理如下:
主要根据传入的三种类型
GlobalSetting
最终将会调用到下面的 mutateGlobalSetting() 重设与该package相关的Global数据库属性值。
SecureSetting
最终将会调用到下面的 mutateSecureSetting() 重设与该package相关的Global数据库属性值。
mutateGlobalSetting()
和 mutateSecureSetting()
将最终调用 mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE,..)
完成这一具体操作,不过函数内部根据 LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS..
这三种mode做了相应的区分,不过终究是为了重设与该fail package相关的Global或者Secure属性值。
3.4.1.2.resetDeviceConfig 往下调用的流程:
下面的调用初步研究了一下涉及到 namespace
,相关我们可以具体参看一下 DeviceConfig.java
中对于各个 namespace
的定义,这些 namespace
主要涉及和系统相关的一些数据库中的属性值–也即设备配置参数
代码调用流程:
resetDeviceConfig
—>resetAllAffectedNamespaces(…)或performScopedReset(…)
------>DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace)
流程也较为简单,如上.
3.4.2.LEVEL_WARM_REBOOT 的重启处理流程
- 设置属性值
sys.attempting_factory_reset
属性值为true
- 调用PowerMS.reboot(),进入重启流程
SystemProperties.set(PROP_ATTEMPTING_REBOOT, “true”)
PowerManager pm = context.getSystemService(PowerManager.class);
pm.reboot(TAG);
3.4.3.LEVEL_FACTORY_RESET 重启并擦除用户数据
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
代码流程:
RecoverySystem.rebootPromptAndWipeUserData(…)
代码最终也会走上面pm.reboot()的流程
我们主要关注的是 rebootPromptAndWipeUserData
这个函数是如何导致Factory Reset的
这里将会导致重启,重启流程如下
4.PowerManagerService触发的重启
log字样:
标号:2 5 6 7将对应下面的函数流程<4.2PMS重启函数流程>
2.将会打印Binder shutdown checkpoint recorded with pid=
5.将会打印 ShutdownThread Notifying thread to start shutdown longPressBehavior=
,显示重启dialog
6.做一些初始化相关例如唤醒锁等
7.正真进入重启,
1.将重启原因写入
sys.shutdown.requested
,前面的数字0或1代表是否是reboot
2. 将会dump一些重启相关的log信息 Log:Logging pre-reboot information...
3. 将会发送关机广播Intent.ACTION_SHUTDOWN
4. 广播处理完后将关闭 AMS-am.shutdown(MAX_BROADCAST_TIME), Log:Shutting down activity manager..
5. 关闭其他服务: PMS\radios
- log打印:
Rebooting, reason:
和Performing low-level shutdown...
4.2PMS重启函数流程
函数调用流程如下:
1.PowerManagerService.reboot(boolean confirm/是否弹出重启框/, @Nullable String reason/重启原因/, boolean wait/是否重启完成后再返回/)
—>2.ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason)//检查调用进程,将reason放入ShutdownCheckPoints.mCheckPoints
—>3.shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait)
------>4. ShutdownThread.reboot(getUiContext(), reason, confirm)
--------->5.shutdownInner(context, confirm)
------------>6.beginShutdownSequence()
--------------->7.ShowdownThread.run()
------------------>8.rebootOrShutdown(mContext, mReboot, mReason)
--------------------->9.PowerManagerService.lowLevelReboot(reason)
------------------------>SystemProperties.set("sys.powerctl", "reboot," + reason)
--------------------->10.PowerManagerService.lowLevelShutdown(reason)
------------------------>SystemProperties.set("sys.powerctl", "shutdown," + reason)
具体代码如下:
public void run() {
//...
String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
//...
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
//...
Slog.i(TAG, "Logging pre-reboot information...");
PreRebootLogger.log(mContext);
//...
Log.i(TAG, "Sending shutdown broadcast...");
// 发送广播
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
//...发生的关机广播是否超时处理
//...AMS关闭
am.shutdown(MAX_BROADCAST_TIME);
//... PMS关闭
pm.shutdown();
//...
uncrypt();
//...
// Remaining work will be done by init, including vold shutdown
rebootOrShutdown(mContext, mReboot, mReason);
}
4.2.1属性服务得到sys.powerctl 流程如下:
. handle_property_set_fd() property_service.cpp
.—> HandlePropertySet()
.------> PropertySet()
.---------> PropertyChanged() init.cpp
.------------>ShutdownState.TriggerShutdown()
.--------------->SecondStageMain
------------------>HandlePowerctlMessage()
.--------------------> DoReboot() reboot.cpp
.----------------------->RebootSystem()
- 将会打印重启相关的log:
Reboot start, reason: XXX, reboot_target: XXXX
-
shutdown
超时处理, log:Shutdown timeout: XXms
- 关闭各个服务,部分服务除外
- 关闭背光,清理
activities
- 关机动画相关主要将开机机动画显示出来,可以在
adb shell setprop service.bootanim.exit 0
以及adb shell setprop service.bootanim.process 0
后再设置adb shell setprop ctl.start bootanim
即可以将开机动画启动起来,adb shell setprop ctl.stop bootanim
关闭开机动画,原理类似 -
StopServicesAndLogViolations
停止服务并处理超时
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
bool run_fsck) {
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
//...shutdown 超时处理
//..
// 关闭各个服务
const std::set<std::string> to_starts{"watchdogd"};
std::set<std::string> stop_first;
for (const auto& s : ServiceList::GetInstance()) {
if (kDebuggingServices.count(s->name())) {
// keep debugging tools until non critical ones are all gone.
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
if (auto result = s->Start(); !result.ok()) {
LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
<< "': " << result.error();
}
s->SetShutdownCritical();
} else if (s->IsShutdownCritical()) {
// Start shutdown critical service if not started.
if (auto result = s->Start(); !result.ok()) {
LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
<< "': " << result.error();
}
} else {
stop_first.insert(s->name());
}
}
// 关闭背光并清理activities
if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
TurnOffBacklight();
}
//所谓关机动画相关
Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
if (do_shutdown_animation) {
SetProperty("service.bootanim.exit", "0");
SetProperty("service.bootanim.progress", "0");
// Could be in the middle of animation. Stop and start so that it can pick
// up the right mode.
boot_anim->Stop();
}
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count("animation") == 0) {
continue;
}
// start all animation classes if stopped.
if (do_shutdown_animation) {
service->Start();
}
service->SetShutdownCritical(); // will not check animation class separately
}
if (do_shutdown_animation) {
boot_anim->Start();
surface_flinger->SetShutdownCritical();
boot_anim->SetShutdownCritical();
}
}
// 关机步骤
// 1. terminate all services except shutdown critical ones. wait for delay to finish
if (shutdown_timeout > 0ms) {
StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
}
// Send SIGKILL to ones that didn't terminate cleanly.
StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
SubcontextTerminate();
// Reap subcontext pids.
ReapAnyOutstandingChildren();
// 3. send volume abort_fuse and volume shutdown to vold
Service* vold_service = ServiceList::GetInstance().FindService("vold");
if (vold_service != nullptr && vold_service->IsRunning()) {
// Manually abort FUSE connections, since the FUSE daemon is already dead
// at this point, and unmounting it might hang.
CallVdc("volume", "abort_fuse");
CallVdc("volume", "shutdown");
vold_service->Stop();
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
// logcat stopped here
StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
// 4. sync, try umount, and optionally run fsck for user shutdown
{
Timer sync_timer;
LOG(INFO) << "sync() before umount...";
sync();
LOG(INFO) << "sync() before umount took" << sync_timer;
}
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice();
LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
// 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
if (auto ret = UnmountAllApexes(); !ret.ok()) {
LOG(ERROR) << ret.error();
}
UmountStat stat =
TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
{
Timer sync_timer;
LOG(INFO) << "sync() after umount...";
sync();
LOG(INFO) << "sync() after umount took" << sync_timer;
}
if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
// Send signal to terminate reboot monitor thread.
reboot_monitor_run = false;
sem_post(&reboot_semaphore);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, reboot_target);
abort();
}
5.rebootPromptAndWipeUserData()
这将进入Recovery
模式,代码流程如下:
—RecoverySystem.rebootPromptAndWipeUserData(context, TAG)
------1.vold.abortChanges(“rescueparty”, false)
---------bootCommand(context, null, “–prompt_and_wipe_data”, reasonArg, localeArg)
------------VoldNativeService.abortChanges()
---------------Checkpoint.cp_abortChanges
------------------abort_metadata_file()
------------------android_reboot(ANDROID_RB_RESTART2, 0, message.c_str())
---------------------android_reboot.android_reboot()// 执行 sys.powerctl reboot 完成重启
-----or-2.RecoverySystem.bootCommand(context, null, “–prompt_and_wipe_data”, reasonArg, localeArg);
---------RecoverySystemService.rebootRecoveryWithCommand()
------------setupOrClearBcb(true, command)
---------------mInjector.systemPropertiesSet(“ctl.start”, “setup-bcb”)//执行ctl.start setup-bcb 启动该服务,并通过init进程开启一个socket
---------------socket.sendCommand()/通过socket 将–prompt_and_wipe_data", reasonArg, localeArg组合成的命令发给uncrypt service
//bootable/recovery/uncrypt/uncrypt.rc
service setup-bcb /system/bin/uncrypt --setup-bcb
class main
socket uncrypt stream 600 system system
disabled
oneshot
我们这里即可看到,system_server中的RecoverySystemService 和 uncrypt 进程使用socket通信,通信的内容即发送字符串 prompt_and_wipe_data,以及其他arg参数.交给
setup-bcb该服务进行处理.
5.1 uncrypt.cpp
详细了解 uncrypt.cpp 可参考链接
我们这里只了解它的一部分功能.
uncrypt.cpp中有三个服务: SETUP_BCB,CLEAR_BCB, UNCRYPT,我们这里需要进入Recovery模式即需要进行了解SETUP_BCB.
BCB是什么呢?如下介绍
5.1.1.BootLoader Control Block-BCB
官方注释: 存于flash中一个内容块,用于recovery和bootloader通信
BCB:BootLoader Control Block, 正常启动.,若启动过程中用户没有按下任何组合健,BootLoader 会读取位于misc分区的启动信息控制块,即BCB,他是一个结构体,存放着启动命令command ,根据不同的命令,系统可以进入三种不同的启动模式.
我们可以看一下bootloader_message结构体的注释即可知道
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];
char stage[32];
char reserved[1184];
};
- command 字段中存储的是命令,它有以下几个可能值:
- boot-recovery:系统将启动进入Recovery模式
- update-radia 或者 update-hboot:系统将启动进入更新firmware的模式,这个更新过程由bootloader完成
- NULL:空值,系统将启动进入Main System主系统,正常启动。
- status 字段存储的是更新的结果。更新结束后,由Recovery或者Bootloader将更新结果写入到这个字段中。
- recovery 字段存放的是recovry模块的启动参数,一般包括升级包路径。其存储结构如下:第一行存放字符串“recovery”,第二行存放路径信息“–update_package=/mnt/sdcard/update.zip”等。 因此,参数之间是以“\n”分割的。
这里涉及到系统启动BootLoader相关知识,我们简单理解一下:
当手机没有按下任何键启动时,BootLoader会读取启动控制信息块BCB,BCB中的command命令决定了系统启动进入什么状态,根据command的值,已经command值为null可分为三种模式,
第一种:命令:boot-recovery 进入Recover模式,
第二种:命令:update-radia或者updata-hboot,会进入固件升级,
第三种,命令:null,系统会进入正常启动流程
5.2 SETUP_BCB 流程
uncrypt.cpp
—uncrypt.main()
------setup_bcb(socket_fd)
---------write_bootloader_message(options, &err)
---------write_wipe_package(wipe_package, &err)
最后两条write命令将会拼装的command及recovery信息写入到BCB中,待系统启动到init进程后还会启动Recovery进程(服务),Recovery服务中还会根据get_bootloader_message()得到BCB中相关的信息,做一些例如:是否升级apk的更新包/是否擦除data分区和cache分区,结束后调用finish_recovery() 才继续走后面的系统启动流程.
6.结论如下:
Rescueparty
发起的进入 recovery
,
由以下任意一条件触发:
-
system_server
在 5 分钟内重启 5 次以上。 - 永久性系统应用在 30 秒内崩溃 5 次以上。
该种失败会在Recovery
模式最下方打印REASON:Rescueparty
该种失败需要进recovery
之前的log进行定位,属于Android 上层的问题
6.1 确认方法
log中搜索如下字样即可确认:
am_process_crashed_too_much
PackageManager: Finished rescue level FACTORY_RESET
uncrypt :
ShutdownThread:
7.其他例子
systemui
不断 fatal
导致 recovery
重启
06-10 14:07:20.994 976 1459 W ActivityManager: Process com.android.systemui has crashed too many times, killing! Reason: crashed quickly
06-10 14:07:20.994 976 1459 I am_process_crashed_too_much: [0,com.android.systemui,10143]
06-10 14:07:21.113 976 1043 I rescue_success: 5
06-10 14:07:21.118 976 1043 D PackageManager: Finished rescue level FACTORY_RESET for package com.android.systemui
06-10 14:07:21.119 976 1043 I pm_critical_info: Finished rescue level FACTORY_RESET for package com.android.systemui
06-10 14:07:22.142 5080 5080 I uncrypt : received command: [--prompt_and_wipe_data
06-10 14:07:22.142 5080 5080 I uncrypt : --reason=RescueParty
06-10 14:07:22.142 5080 5080 I uncrypt : --locale=zh_CN_#Hans
06-10 14:07:22.142 5080 5080 I uncrypt : ] (65)
06-10 14:07:22.143 5194 5194 E c.silent.reboo: Not starting debugger since process cannot load the jdwp agent.
06-10 14:07:22.146 5080 5080 I uncrypt : received 0, exiting now
06-10 14:07:22.150 976 5074 I RecoverySystemService: uncrypt setup bcb successfully finished.
06-10 14:07:22.163 976 5074 V ShutdownCheckPoints: Binder shutdown checkpoint recorded with pid=976
06-10 14:07:22.163 976 5074 D PowerManagerService: reboot the device , UID : 1000 , PID : 976 , reason : recovery , confirm = false , wait = true
06-10 14:07:22.169 976 1035 D ShutdownThread: reboot reason : recovery
06-10 14:07:22.171 976 1035 D ShutdownThread: Notifying thread to start shutdown longPressBehavior=1
06-10 14:07:22.176 976 1035 D ShutdownThread: Attempting to use SysUI shutdown UI
06-10 14:07:22.177 976 1035 D ShutdownThread: SysUI is unavailable
06-10 14:07:22.261 976 5218 I ShutdownThread: Logging pre-reboot information...
06-10 14:07:22.263 976 5218 I ShutdownThread: Sending shutdown broadcast...
06-10 14:07:22.853 976 5218 I ShutdownThread: Shutting down Bluetooth
06-10 14:07:22.855 976 5218 I ShutdownThread: Shutting down activity manager...
06-10 14:07:23.468 976 5218 I ShutdownThread: Shutting down package manager...
-------------------往前即可看到fatal信息
06-10 14:07:20.960 4787 4787 D AndroidRuntime: Shutting down VM
06-10 14:07:20.962 4787 4787 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:20.962 4787 4787 E AndroidRuntime: Process: com.android.systemui, PID: 4787
06-10 14:07:20.962 4787 4787 E AndroidRuntime: java.lang.NoClassDefFoundError: Failed resolution of: Lokhttp3/OkHttpClient$Builder;
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.squareup.picasso.OkHttp3Downloader.<init>(OkHttp3Downloader.java:71)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.squareup.picasso.OkHttp3Downloader.<init>(OkHttp3Downloader.java:50)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.squareup.picasso.OkHttp3Downloader.<init>(OkHttp3Downloader.java:40)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.squareup.picasso.Picasso$Builder.build(Picasso.java:848)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.squareup.picasso.Picasso.get(Picasso.java:683)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.statusbar.NotificationMediaManager.finishUpdateMediaMetaData(NotificationMediaManager.java:910)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.statusbar.NotificationMediaManager.updateMediaMetaData(NotificationMediaManager.java:832)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.statusbar.NotificationMediaManager.screenTurnedOff(NotificationMediaManager.java:1395)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.statusbar.phone.StatusBar$16.onReceive(StatusBar.java:3432)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.statusbar.phone.StatusBar.makeStatusBarView(StatusBar.java:1622)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.statusbar.phone.StatusBar.createAndAddWindows(StatusBar.java:3169)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.statusbar.phone.StatusBar.start(StatusBar.java:1099)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.SystemUIApplication.startServicesIfNeeded(SystemUIApplication.java:228)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.SystemUIApplication.startServicesIfNeeded(SystemUIApplication.java:167)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.systemui.keyguard.KeyguardService.onCreate(KeyguardService.java:120)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at android.app.ActivityThread.handleCreateService(ActivityThread.java:4547)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at android.app.ActivityThread.access$1700(ActivityThread.java:273)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2119)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:201)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at android.os.Looper.loop(Looper.java:288)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7981)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:553)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1004)
06-10 14:07:20.962 4787 4787 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: okhttp3.OkHttpClient$Builder
06-10 14:07:20.962 4787 4787 E AndroidRuntime: ... 25 more
com.android.systemui 20s内发生发生十多次 fatal文章来源:https://www.toymoban.com/news/detail-487661.html
06-10 14:07:01.110 1208 1208 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:04.100 3510 3510 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:06.447 3717 3717 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:08.578 3845 3845 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:11.035 3935 3935 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:13.319 4127 4127 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:15.668 4334 4334 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:18.194 4584 4584 E AndroidRuntime: FATAL EXCEPTION: main
06-10 14:07:20.962 4787 4787 E AndroidRuntime: FATAL EXCEPTION: main
参考
BCB
uncrypt流程文章来源地址https://www.toymoban.com/news/detail-487661.html
到了这里,关于手机进入Recovery之 RescueParty的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!