手机进入Recovery之 RescueParty

2023-05-16

文章目录

  • 手机进入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.结论如下:
      • 6.1 确认方法
    • 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   //导致将会进入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 可以表示一个线程,也可以表示一个线程组,线程和线程是以树形结构来进行组织的。
如下:

  1. 如果该线程组还有父线程组,则先执行父线程组的 uncaughtException()
  2. 如果有设置默认的 UncaughtExceptionHandler 则调用其 uncaughtException()
  3. 如果异常不是 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
    • 主要功能是系统异常信息记录:
      1. 如果是系统进程system_server异常,则打印: *** FATAL EXCEPTION IN SYSTEM PROCESS:XXX 日志
      1. 如果是内存溢出,打印 OutOfMemoryError IN SYSTEM PROCESS: Already dump hprof! 信息
      1. 如果其他进行,打印 FATAL EXCEPTION:+进程XXX,以及Pid XXX
  • KillApplicationHandler
    • 处理导致应用死亡的未捕获异常
      1. 先调用 LoggingHandler(..) 来处理 该异常,也就是说流程以及log信息打印和前面一样
      1. 停止当前进程的trace追踪
      1. 调用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的对话框提供用户选择
      1. 最终调用 Process.killProcess(Process.myPid()) 杀掉该进程
      1. 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(),逻辑如下:

    1. 紧急处理–当 failureReasonFAILURE_REASON_NATIVE_CRASHFAILURE_REASON_EXPLICIT_HEALTH_CHECK 这两个 Reason将调用 handleFailureImmediately 做立刻处理,
    • 原理就是只拿packages中的第一个package,将这个出现问题的package传给各个 PackageHealthObserver 做处理(一般native cash或者explicit health check failures回走这一步)
    1. 非紧急处理–将逐个遍历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

  1. 对用户的影响程度中等:USER_IMPACT_MEDIUM
    1. 对于 Native crash 则返回的是 PackageHealthObserverImpact.USER_IMPACT_MEDIUM 中等程度的影响
  2. 其他异常,
    1. 有可用的Rollback:PackageHealthObserverImpact.USER_IMPACT_MEDIUM
    2. 无可用的Rollback:PackageHealthObserverImpact.USER_IMPACT_NONE
      回滚机制–暂时略过
@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

  1. 当原因是APP CRASH 或者APP 无响应 将调用 mapRescueLevelToUserImpact 处理,通过紧急程度 RescueLevel 来判断对用户的影响程度UserImpact
    1. getRescueLevel 通过缓解计数得到紧急程度值紧急程度值如下
      1. mitigationCount == 1: LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS
      2. mitigationCount == 2:LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES
      3. mitigationCount == 3:LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS
      4. mitigationCount == 4:LEVEL_WARM_REBOOT 和最大紧急程度对比的最小值
      5. mitigationCount >= 5:LEVEL_FACTORY_RESET 和最大紧急程度对比的最小值
      6. 备注:最大紧急程度将由 persist.device_config.configuration.disable_rescue_party_factory_reset 属性决定,
        1. 属性值为true,则最大紧急程度为 LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS
        2. 属性值为false或null,则最大紧急程度为 LEVEL_FACTORY_RESET
    2. mapRescueLevelToUserImpact 方法将会依据刚刚的紧急程度得到一个对应用户影响程度的判断
      1. 影响程度小: USER_IMPACT_LOW
        1. LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS
        2. LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES
      2. 影响程度高:USER_IMPACT_HIGH
        1. LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS
        2. LEVEL_WARM_REBOOT
        3. 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;
    }
}

主要根据通过两个 ObserveronHealthCheckFailed 方法来判断package发生异常对于用户的使用影响程度用多大

  • 在回滚机制的判断中,对于用户的使用影响程度最多也只是中等水平 USER_IMPACT_MEDIUM

  • RescueParty 处理判断中,对于用户的使用影响程度是最大的,易发生 USER_IMPACT_HIGH

3.3缓解处理 PackageHealthObserver.execute()

  • RollbackPackageHealthObserver
  • RescuePartyObserver.execute()

3.3.1RollbackPackageHealthObserver

  1. 当异常原因是:Native Crash 将会最终会调用 RollbackManagerService 处理,暂时先不岔开研究该服务(TODO)
  2. 其他异常情况将调用可用的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()

  1. 最终 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. 1–LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS
  2. 2–LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES
  3. 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

  1. 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()

  1. 将会打印重启相关的log: Reboot start, reason: XXX, reboot_target: XXXX
  2. shutdown 超时处理, log:Shutdown timeout: XXms
  3. 关闭各个服务,部分服务除外
  4. 关闭背光,清理activities
  5. 关机动画相关主要将开机机动画显示出来,可以在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 关闭开机动画,原理类似
  6. 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
由以下任意一条件触发:

  1. system_server 在 5 分钟内重启 5 次以上。
  2. 永久性系统应用在 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(使用前将#替换为@)

手机进入Recovery之 RescueParty 的相关文章

  • Ubuntu的recovery模式进入及退出

    如果root密码忘记 xff0c 可以进入recovery模式进行重置 或者当sudo命令出现错误时 xff0c 进入recovery模式可以修复 etc sudoers文件 recovery模式 1 启动Ubuntu系统 xff0c 过程
  • Windows使用cmd刷入recovery.img

    Windows使用cmd刷入recovery img Windows键 43 R回车后进入cmd命令终端 进入fastboot 手机进入fastboot模式有2种方法 第一种进入方法是 xff0c 如果你的手机能用adb识别到 xff0c
  • 手机进入Recovery之 RescueParty

    文章目录 手机进入Recovery之 RescueParty1 案例一 三方内置应用频繁Crash导致Recovery1 1根据现有的log来看 1 2分析线索如下 1 3进入重启的函数调用栈如下 2 应用频繁Crash是如何导致系统重启探
  • 时钟恢复(CDR:Clock and Data Recovery)和PLL/DLL

    CDR的作用及应用场景 CDR的主要有两大作用 xff0c 第一是为接收器端各电路提供时钟信号 xff1b 第二是对接收到的信号进行判决 xff0c 便于数据信号的恢复与后续处理 CDR在各种高速PHY RX的应用 xff1a 比如高速ph
  • OMF和flash_recovery_area的关系!

    在OMF出现之前 9i R1 oracle db的文件 xff0c 主要指dbf xff0c redo xff0c ctl是通过os管理的 xff0c 为了简化对数据库文件的管理 xff0c oracle引入了OMF 通过omf创建的dbf
  • 移植Android recovery升级(1)介绍

    Android OTA升级大家都有接触过 xff0c 网上也有很多说明文章 xff0c 我这边把Android OTA升级的架构移植到嵌入式设备上 对Android OTA说明文章 xff0c 我感觉比较好的是 xff1a https bl
  • Oracle:ORA-00283: recovery session canceled due to errors

    我做了一个这样的实验 xff1a 归档模式下进行手工热备并进行还原恢复 xff0c 结果将数据文件还原后 xff0c 却报出了如下错误 xff1a ORA 00283 recovery session canceled due to err
  • WARMING! ! ! BIOS Recovery mode has been detected. Please put the file “ASUS. CAp“ into HDD or a rem

    文章目录 问题场景 xff1a 解决方案 xff1a 步骤1 xff1a 下载适当的BIOS文件步骤2 xff1a 将BIOS文件复制到可移动设备或硬盘驱动器中步骤3 xff1a 进入BIOS恢复模式步骤4 xff1a 恢复BIOS步骤5
  • ES recovery、主副分片复制会有一段时间block写入?

    先说结论 1 ES在主副本分片复制时候不会block写入 version gt 2 x 2 ES在recovery主分片时候会有一段时间block写入 全文 ES recovery 主副分片复制会有一段时间block写入 阿里云开发者社区E
  • Android 9.0修改recovery 菜单项字体大小

    1 前言 在Android9 0rom定制化开发中 在原生系统中 对于recovery模块这部分也是相当重要的部分 所以当进入recovery模式后 界面会g menu actions 菜单选项和 提示文字 而这些文字的 大小不像上层一样是
  • 从 CVS *,v 文件的存档中恢复文件

    我试图从一堆过去用 CVS 管理的文件中恢复一些旧的源代码 纯文本 我有一个目录 其中包含 CVS 的 v 文件格式的所有源代码文件 这看起来大部分像原始文件 但其中有相当多的 CVS 特定标头和修订信息 有没有一种简单的方法可以删除所有
  • Git 恢复:“目标文件为空”。如何重新创建树木?

    注意 我没有此存储库的任何损坏前克隆 我相信我的情况与这里描述的其他人不同 因为我缺少一棵树 而不是一个斑点 发生了什么 当我尝试通过 LAN 通过 SSH 克隆存储库时 Git 返回一条错误 指出存储库已损坏 remote error o
  • 撤消对存储过程的更改

    我更改了一个存储过程 并在不知不觉中覆盖了其他开发人员对其所做的一些更改 有没有办法撤消更改并恢复旧脚本 不幸的是 我没有该数据库的备份 因此排除了该选项 答案是YES you can拿回来 但这并不容易 所有数据库日志every对其进行的
  • matlab 中的 .asv 文件

    当我保存一个 m文件位于文件夹中时 MATLAB 自动保存 asv文件位于同一文件夹中 我打开这个文件 发现它从头开始就包含了我的代码 忽略了我删除的内容 这个文件有什么用呢 如果我删除该文件会有什么风险 asv 文件就是您所说的 自动保存
  • 如何使用 SVN 热复制恢复存储库?

    好的 我使用svn的hotcopy进行增量备份 现在如何测试hotcopy是否正常工作 我在这里搜索了有关热复制的帖子 他们中的大多数似乎只是鼓励使用 svn hotcopy 但没有谈论如何使用 hotcopy 进行恢复 关于如何使用我制作
  • MySQL:InnoDb:信号量等待已持续 > 600 秒。我们故意让服务器崩溃

    Windows Server 2012R2 上具有 32GB RAM 的 MySQL 5 7 16 服务器现在每 18 分钟左右重新启动一次 错误日志中显示以下内容 InnoDB Diagnostic info printed to the
  • 如何进行选择性 Mongo 恢复?

    假设我有一个Mongo具有两个数据库的副本集 一个主数据库和几个辅助数据库 db1 and db2 中学一所Mongo崩溃并丢失数据 现在当这个Mongo重新启动就会recover并复制both db1 and db2从初级开始 由于这样的
  • 恢复发送至自部署合约的BNB

    我正在尝试创建 BOT 因此在 BSC 中从 eatamask 创建并部署了一个合约 我向该合约发送了一些 BNB 来检查 但没有成功 我怎样才能拿回BNB 感谢帮助 因为我对此很陌生 除非您在合约中具有允许您提取资金的自定义功能 否则它们
  • 在代码管理菜单中恢复 Visual Studio Code 中删除的文件

    我真的很沮丧 因为我不小心删除了 Visual Studio Code 中的 3 个文件 我是通过左侧的 源代码管理 菜单完成的 现在我的问题 是否有可能恢复我这样删除的 3 个文件 我希望得到任何答案 也许您想知道 我使用 Windows
  • Cassandra 备份,包括架构

    我感兴趣的备份技术有两种 a SCHEMA 备份 恢复数据库模式 添加或删除列 更改列类型 添加表等 b 数据备份 恢复数据 更新 从一个表读取到另一个表 让我通过例子来解释一下 首先 我创建实体 客户 Customer 编号 名称 11

随机推荐