在实际开发过程中,由于经常维护核心应用程序。核心应用在多次频繁闪退时会进入recovery界面并且提示恢复出厂设置。抓取到的logcat如下:
06-09 09:45:15.994 D/AndroidRuntime( 4197): Shutting down VM
06-09 09:45:15.995 E/AndroidRuntime( 4197): FATAL EXCEPTION: main
06-09 09:45:15.995 E/AndroidRuntime( 4197): Process: net.sunniwell.sanzang, PID: 4197
06-09 09:45:15.995 E/AndroidRuntime( 4197): java.lang.NullPointerException: Attempt to invoke virtual method 'void top.foxhome.demo.adapter.SWVolumeDeviceListViewAdapter.notifyDataSetChanged()' on a null object reference
06-09 09:45:15.995 E/AndroidRuntime( 4197): at top.foxhome.demo.service.FXTaskbarService.refreshSelectionStatus(FXService.java:1267)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at top.foxhome.demo.service.FXService.access$2600(FXService.java:96)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at top.foxhome.demo.service.FXService$SWHandler.handleMessage(FXService.java:1400)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at android.os.Handler.dispatchMessage(Handler.java:106)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at android.os.Looper.loop(Looper.java:223)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at android.app.ActivityThread.main(ActivityThread.java:7664)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at java.lang.reflect.Method.invoke(Native Method)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
06-09 09:45:15.995 E/AndroidRuntime( 4197): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
06-09 09:45:15.998 I/DropBoxManagerService( 447): add tag=system_app_crash isTagEnabled=true flags=0x2
06-09 09:45:15.999 W/ActivityManager( 447): Process top.foxhome.demo has crashed too many times: killing!
06-09 09:45:16.001 I/Process ( 4197): Sending signal. PID: 4197 SIG: 9
06-09 09:45:16.002 W/PackageManager( 447): Incremented rescue level to FACTORY_RESET triggered by UID 1000
06-09 09:45:16.005 W/RescueParty( 447): Attempting rescue level FACTORY_RESET
06-09 09:45:16.010 D/PackageManager( 447): Finished rescue level FACTORY_RESET
该demo为系统应用,预置到系统中进行打包发布。触发异常后该应用会被系统自动唤起。多次唤起后则会进入recovery界面。通过关键词Incremented rescue level to FACTORY_RESET triggered by UID
定位到源码出处:frameworks/base/services/core/java/com/android/server/RescueParty.java#253
private static void executeRescueLevel(Context context, @Nullable String failedPackage) {
final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE);
if (level == LEVEL_NONE) return;
Slog.w(TAG, "Attempting rescue level " + levelToString(level));
try {
executeRescueLevelInternal(context, level, failedPackage);
EventLogTags.writeRescueSuccess(level);
logCriticalInfo(Log.DEBUG,
"Finished rescue level " + levelToString(level));
} catch (Throwable t) {
logRescueException(level, t);
}
}
通过阅读谷歌文档RescueParty为Android系统的救援服务在核心应用多次重启时该机制则会进行启动。官网的原文如下:
救援程序
很多用户严重依赖他们的手机,需要一台始终可以正常工作的设备。然而,有时设备会陷入重新启动循环僵局,这样,用户便需要提交支持服务单据或保修咨询。这个过程会让用户不舒服,设备制造商和运营商则需要支付高昂的费用。
Android 8.0 中纳入了一个功能,当该功能注意到核心系统组件陷入崩溃循环僵局时,就会派出“救援程序”。然后救援程序会通过一系列操作来上报相关情况,以期恢复设备。最后的解决方法是,Rescue Party 使设备重新启动并进入恢复模式,然后提示用户恢复出厂设置。
Android 兼容性定义文档并未要求提供此类 Rescue Party 功能,但此类功能对减少邮件支持记录来说非常有用。
实现
在 Android 8.0 中,救援程序默认处于启用状态,其实现位于 /services/core/java/com/android/server/RescueParty.java 中。在出现以下情况时,救援程序会收到有关启动和崩溃事件的信息,然后即会启动:
system_server 在 5 分钟内重启 5 次以上。
永久性系统应用在 30 秒内崩溃 5 次以上。
当检测到上述某种情况时,救援程序会将其上报给下一救援级别、处理与该级别相关联的任务,并让设备继续运行,看看能否恢复。清除或重置内容的程度随级别而增加。最高级别会提示用户将设备恢复出厂设置。
无需特别的硬件支持即可为救援程序提供支持。实现后,设备的恢复系统必须响应 –prompt_and_wipe_data 命令,且设备必须先提供一种方法,让用户确认用户数据是否有任何损坏,然后再继续运行。此外,恢复系统还应为用户提供有关尝试再次启动设备的选项。
由于每个救援级别都会使设备再次变为可运行状态的时间延后(可能长达 5 分钟),因此设备制造商不应添加自定义救援级别。设备处于不可运行的状态的时间越长,用户发出支持请求或保修咨询,而不是自行恢复其设备的可能性就越大。
验证
当设备具有有效的 USB 数据连接时,系统会停止所有救援事件,因为这是一个较强的信号,表示有人正在调试设备。
如需停止此类抑制行为,请运行以下命令:
adb shell setprop persist.sys.enable_rescue 1
在此处,您可以触发系统或界面崩溃循环。
如需触发低级 system_server 崩溃循环,请运行以下命令:
adb shell setprop debug.crash_system 1
如需触发中级 SystemUI 崩溃循环,请运行以下命令:
adb shell setprop debug.crash_sysui 1
这两个崩溃循环都会启动救援逻辑。所有的救援操作也都会记录到存储在 /data/system/uiderrors.txt 中的永久性 PackageManager 日志中,以供日后进行检查和调试。此外,“软件包警告消息”部分下的每个错误报告中也会包含这些永久性的日志。
救援级别
private static final int LEVEL_NONE = 0;
private static final intLEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1; //主要针对非系统进程的属性设置进行重置
private static final intLEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2;//针对非系统进程属性,来自系统默认的属性重置,其他删除
private static final intLEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;//所有进程系统默认的属性重置,其他删除
private static final intLEVEL_FACTORY_RESET = 4; //尝试恢复出厂设置
触发场景
(1)system_server 在 5 分钟内重启 5 次以上调整一次级别。
(2)永久性系统应用在 30 秒内崩溃 5 次以上调整一次级别。
救援程序的禁用场景
(1)PROP_ENABLE_RESCUE属性值为false,并且PROP_DISABLE_RESCUE为true
(2)eng版本下
(3)手机连接usb模式