文章目录
- 手机进入Recovery之 RescueParty
- 1. 案例一:三方内置应用频繁Crash导致Recovery
- 1.1根据现有的log来看:
- 1.2分析线索如下:
- 1.3进入重启的函数调用栈如下:
- 2.应用频繁Crash是如何导致系统重启探究
- 2.1问题: 我们的系统是如何检测到应用频繁发生crash,并且触发重启机制的?
- 2.1.1RescuePartyObserver类
- 2.1.2启动RescuePartyObserver流程:
- 2.1.2onPackageFailure()方法
- 2.1.2.1onPackageFailure()方法用途1
- 2.1.2.2onPackageFailure()方法用途2
- 2.2从Thread的线程捕获异常接口开始探究
- 2.2.1Thread.UncaughtExceptionHandler接口:
- 2.2.2我们看系统是如何处理异常的:
- 3.PackageWatchdog.onPackageFailure函数如何做出反应
- 3.1处理说明
- 3.2package异常对用户影响程度判断PackageHealthObserver.onHealthCheckFailed
- 3.2.1. RollbackPackageHealthObserver.java
- 3.2.2. RescueParty.RescuePartyObserver
- 3.3缓解处理 PackageHealthObserver.execute()
- 3.3.1RollbackPackageHealthObserver
- 3.3.2. RescuePartyObserver.execute()
- 3.4.executeRescueLevelInternal 对于各种mode的处理
- 3.4.1.LEVEL_RESET_SETTINGS_XXX三种mode的处理流程
- 3.4.1.1.resetAllSettingsIfNecessary 往下调用的流程:
- GlobalSetting
- SecureSetting
- 3.4.1.2.resetDeviceConfig 往下调用的流程:
- 3.4.2.LEVEL_WARM_REBOOT 的重启处理流程
- 3.4.3.LEVEL_FACTORY_RESET 重启并擦除用户数据
- 4.PowerManagerService触发的重启
- 4.2PMS重启函数流程
- 4.2.1属性服务得到sys.powerctl 流程如下:
- 5.rebootPromptAndWipeUserData()
- 5.1 uncrypt.cpp
- 5.1.1.BootLoader Control Block-BCB
- 5.2 SETUP_BCB 流程
- 6.结论如下:
-
- 7.其他例子
- 参考
手机进入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
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
接口如下:
public interface PackageHealthObserver {
@PackageHealthObserverImpact int onHealthCheckFailed(
@Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason,
int mitigationCount);
boolean execute(@Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason, int mitigationCount);
default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
default boolean executeBootLoopMitigation(int mitigationCount) {
return false;
String getName();
default boolean isPersistent() {
return false;
}
default boolean mayObservePackage(String packageName) {
return false;
}
}
2.1.2启动RescuePartyObserver流程:
在 SystemServer
启动的startBootstrapServices 阶段 将完成 RescueParty.registerHealthObserver(mSystemContext)
;
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
RescuePartyObserver.getInstance(context));
}
PackageWatchdog
类的作用主要是监测系统中各个包的运行情况
将 RescuePartyObserver
对象存放到该类中的mAllObservers成员中,可见 PackageWatchdog
类 该成员中保存着不止 RescuePartyObserver
这一种Oberver,既然是Observer,那么后面有某些意外情况会回调它.
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() {
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()
---------------------mShortTaskHandler.post(()->checkAndMitigateNativeCrashes())
代码如下:
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
。
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.3 发送
ActivityManagerService.SHOW_ERROR_UI_MSG
消息:主要弹出弹出提示crash的对话框提供用户选择
-
- 最终调用
Process.killProcess(Process.myPid())
杀掉该进程
-
- System.exit(10) 退出
代码流程:
//android/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
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获取最新的缓解计数做缓解处理 可以先继续往下看<缓解处理…>
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);
PackageHealthObserver currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
MonitoredPackage currentMonitoredPackage = null;
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;
}
}
}
if (currentObserverToNotify != null) {
int mitigationCount = 1;
if (currentMonitoredPackage != null) {
currentMonitoredPackage.noteMitigationCallLocked();
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
回滚机制–暂时略过
@Override
public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
&& !mContext.getSystemService(RollbackManager.class)
.getAvailableRollbacks().isEmpty()) {
return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
}
if (getAvailableRollback(failedPackage) == null) {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
} else {
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
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
@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发生异常对于用户的使用影响程度用多大
3.3缓解处理 PackageHealthObserver.execute()
- RollbackPackageHealthObserver
- RescuePartyObserver.execute()
3.3.1RollbackPackageHealthObserver
- 当异常原因是:
Native Crash
将会最终会调用 RollbackManagerService
处理,暂时先不岔开研究该服务(TODO) - 其他异常情况将调用可用的rollback进行处理也涉及上述服务,暂时略过
@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));
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);
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, true, failedPackage);
break;
case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES,
level);
resetDeviceConfig(context, true, failedPackage);
break;
case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS,
level);
resetDeviceConfig(context, false, failedPackage);
break;
case LEVEL_WARM_REBOOT:
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);
am.shutdown(MAX_BROADCAST_TIME);
pm.shutdown();
uncrypt();
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;
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())) {
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()) {
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());
}
}
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");
boot_anim->Stop();
}
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count("animation") == 0) {
continue;
}
if (do_shutdown_animation) {
service->Start();
}
service->SetShutdownCritical();
}
if (do_shutdown_animation) {
boot_anim->Start();
surface_flinger->SetShutdownCritical();
boot_anim->SetShutdownCritical();
}
}
if (shutdown_timeout > 0ms) {
StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true );
}
StopServicesAndLogViolations(stop_first, 0ms, false );
SubcontextTerminate();
ReapAnyOutstandingChildren();
Service* vold_service = ServiceList::GetInstance().FindService("vold");
if (vold_service != nullptr && vold_service->IsRunning()) {
CallVdc("volume", "abort_fuse");
CallVdc("volume", "shutdown");
vold_service->Stop();
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
StopServices(kDebuggingServices, 0ms, false );
{
Timer sync_timer;
LOG(INFO) << "sync() before umount...";
sync();
LOG(INFO) << "sync() before umount took" << sync_timer;
}
KillZramBackingDevice();
LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
if (auto ret = UnmountAllApexes(); !ret.ok()) {
LOG(ERROR) << ret.error();
}
UmountStat stat =
TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
{
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);
reboot_monitor_run = false;
sem_post(&reboot_semaphore);
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
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
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流程
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)