Einrichtungsprozess für die native Android-Wiederherstellung

Vorwort

In diesem Artikel wird die Android-Funktion zur Wiederherstellung der Einstellungen vorgestellt. Vor einiger Zeit haben Kollegen relevante Wissenspunkte zu Android-Wiederherstellungseinstellungen und zum Löschen von Daten ausgetauscht. Jetzt werde ich die relevanten Wissenspunkte für zukünftige Studien speichern. Vielen Dank @Kollege für Ihre sorgfältige Zusammenfassung~~~

Text

1. Übersicht

Lassen Sie uns zunächst kurz über den Master-Reset-Prozess sprechen oder darüber, welche Teile dieser nativen Funktion am Ausführungsprozess beteiligt sind.

Ausgehend von der Auslösung haben die Android-Einstellungen grundsätzlich die Funktion, Feldeinstellungen wiederherzustellen/Daten zu löschen, und verwandte Funktionen können über die Einstellungen ausgelöst werden. Die eigentliche Funktionsimplementierung wird jedoch nicht durch die Einstellungen implementiert. Es gibt einen MasterClearReceiver-Rundfunkempfänger im System Die Aufgabe der Einstellungen besteht darin, dass, wenn der Benutzer die entsprechende Funktionstaste drückt, die Einstellungen die entsprechende Sendung senden und die Sendung während des Flusses vom MasterClearReceiver im System empfangen wird, wodurch die spezifische Funktion fortgesetzt wird. Wenn MasterClearReceiver die Funktionsbefehlsübertragung empfängt, kommuniziert er mit RecoverySystem. Nach einer Reihe von Konfigurationen in RecoverySystemService wird die gesamte Maschine über die Neustartfunktion von PowerManager neu gestartet. Nach dem Neustart wechselt sie in den Wiederherstellungsmodus, um bestimmte Werkseinstellungen/Löschen durchzuführen Daten Nach Abschluss der Arbeiten starten Sie das System neu und wechseln wieder zum Hauptsystem.

Das Obige ist eine kurze Beschreibung des Prozesses zum Wiederherstellen der Werkseinstellungen. Es ist ersichtlich, dass die eigentliche Arbeit zum Wiederherstellen der Werkseinstellungen/Löschen von Daten tatsächlich im Wiederherstellungsmodus abgeschlossen ist. Das Hauptsystem speichert relevante Anweisungen, bevor es in die Wiederherstellung eintritt, und kommuniziert mit der Wiederherstellung beim Start und führen Sie die entsprechenden Anweisungen aus. Im Folgenden stellen wir kurz die relevanten Kenntnisse des Wiederherstellungsmodus vor und stellen dann den gesamten Funktionsablauf und die spezifischen Vorgänge vor, die ab dem Wiederherstellungsprozess der Werkseinstellungen ausgeführt werden.

2. Wiederherstellungsmodus

Bei der Wiederherstellung handelt es sich um die Backup-Funktion des Android-Telefons. Dabei handelt es sich um einen Modus, der die Daten oder das System im Android-Telefon ändern kann. In diesem Modus können Sie das bestehende System sichern oder aktualisieren und hier auch die Werkseinstellungen wiederherstellen.

2.1 Funktion des Wiederherstellungsmoduls

Die beiden Hauptfunktionen des Wiederherstellungsmodus sind wie folgt:

Werkseinstellungen wiederherstellen

升级

2.2 Funktionsweise des Wiederherstellungsmodus

2.2.1 Wiederherstellungsstart

Die folgende Abbildung zeigt das Flussdiagramm des Systems, das in die Wiederherstellung eintritt:
Fügen Sie hier eine Bildbeschreibung ein

Wie in der Abbildung oben dargestellt, werden drei Wege zum Aufrufen des Wiederherstellungsmodus dargestellt:

  • Lesen Sie das Wiederherstellungsflag im Register

  • Tastenkombination

  • Schreiben Sie Anweisungen in die sonstige Partition und die Cache-Partition

2.2.1.1 Lesen Sie das Wiederherstellungsflag im Register

Es gibt zwei Möglichkeiten, diese Situation auszulösen: ADB Reboot Recovery und Powermanager.reboot(…, „recovery“,…)

Schauen wir uns zunächst die Eingabe der Wiederherstellung über Powermanager an. Powermanager ruft die Funktion reboot() auf und schreibt schließlich ein Wiederherstellungsflag in das Register. Wenn die Maschine neu startet, wird das Flag im BootLoader gelesen und wechselt dann in den Wiederherstellungsmodus, der dem ähnelt ADB-Neustart-Wiederherstellungsmethode. Beide erreichen ihre Ziele, indem sie den Wert von sys.powerctl ändern

2.2.1.2 Tastenkombination

Die Tastenkombinationsmethode ist relativ einfach. Wenn die Maschine neu startet und BootLoader aufruft, überprüft sie zunächst das Wiederherstellungsflag im Register. Wenn kein Flag vorhanden ist, erkennt es, ob die Kombinationstaste gedrückt wird. Wenn sie gedrückt wird, wird dies ausgewählt Wechseln Sie in den Wiederherstellungsmodus.

2.2.1.3 MSIC- und CACHE-Partitionsbefehlseintrag

Mit dieser Methode können Sie im Allgemeinen die CACHE-Partition im Root-Modus manuell ändern. Sie können mit dieser Methode auch die Werkseinstellungen in den Telefoneinstellungen wiederherstellen.

2.2.2 Einführung in den Wiederherstellungsmodus

Wenn der Benutzer während des Startvorgangs keine Tastenkombination drückt. Der Bootloader liest den Boot-Control-Informationsblock BCB (Bootloader Control Block), der sich in der MISC-Partition befindet. Es ist eine Struktur. Speichert den Startbefehl. Nach verschiedenen Befehlen. Das System kann in drei verschiedene Startmodi wechseln: MainSystem, Recovery und Bootloader.

Der spezifische Prozess ist wie folgt:

  1. Beim Einschalten des Geräts führt der Bootloader zunächst eine Hardware-Initialisierung durch.
  2. Erfolgt keine Tasteneingabe seitens des Benutzers, setzt der Bootloader den normalen Startvorgang fort.
  3. Der Bootloader liest die BCB-Blöcke in der MISC-Partition. BCB enthält Bootloader-Steuerungsinformationen, wie Wiederherstellungsmeldungen, OTA-Update-Status usw.
  4. Basierend auf den Informationen im BCB bestimmt der Bootloader den nächsten Vorgang. Aktivieren Sie beispielsweise den Wiederherstellungsmodus oder starten Sie direkt vom Hauptsystem.
  5. Schließlich setzt der Bootloader den Bootvorgang gemäß der vom BCB festgelegten Operation fort.

Die Arbeit von Recovery erfordert aus Sicht der Kommunikationsarchitektur die Zusammenarbeit der gesamten Softwareplattform. Es gibt drei Hauptteile.

MainSystem: „Main System“ ist der normale Arbeitsmodus von Android, entsprechend boot.img.

  • Während des normalen Startvorgangs (keine anderen Befehle in BCB) verwendet der Bootloader boot.img, um das Hauptsystem zu booten.
  • Im Hauptsystem werden Systemaktualisierungen über OTA oder das Paket update.zip durchgeführt.
  • Vor dem Neustart des Updates schreibt das Hauptsystem Befehle in den BCB der MISC-Partition.
  • Dieser Befehl wird verwendet, um den Bootloader darüber zu informieren, dass beim nächsten Start in den Wiederherstellungsmodus statt in den Hauptsystemmodus gewechselt werden muss.
  • Auf diese Weise kann der Bootloader nach dem Neustart gemäß dem Befehl im BCB in den Wiederherstellungsmodus starten, um den Aktualisierungsvorgang abzuschließen.

Wiederherstellung: Die Wiederherstellungspartition wird bereitgestellt, nachdem das System in den Wiederherstellungsmodus wechselt. Diese Partition enthält „recovery.img“ (identisch mit boot.img. Enthält Standard-Kernel- und Root-Dateisysteme). Nach dem Aufrufen dieses Modus wird der Wiederherstellungsdienst (/sbin/recovery) hauptsächlich ausgeführt, um entsprechende Vorgänge auszuführen (Neustart, Aktualisierung von update.zip, Cache-Partition löschen usw.).

Bootloader: Zusätzlich zum normalen Lade- und Startsystem. Nachrichten vom Hauptsystem und der Wiederherstellung werden auch durch Lesen der MISC-Partition (BCB) abgerufen.

  • Nachrichten von der Wiederherstellung: Statusmeldungen für Vorgänge wie Systemaktualisierungen und Wiederherstellung der Werkseinstellungen.
  • Nachrichten vom Hauptsystem: wie OTA-Update-Ergebnisse, Ergebnisse des Zurücksetzens auf die Werkseinstellungen usw.
    Der Bootloader liest die Meldungen in der MISC-Partition vor dem Start und führt entsprechende Vorgänge basierend auf den Meldungen aus, wie z. B. das Springen in den Wiederherstellungsmodus usw.

2.2.3 Wie kommunizieren die drei Teile miteinander?

2.2.3.1 Kommunikation zwischen dem Hauptsystem und Recovery über /cache/recovery

Recovery kommuniziert mit dem Hauptsystem über drei Dateien im Ordner /cache/recovery/:

Im Ordner /cache/recovery befinden sich drei Funktionsdateien:

1.  /cache/recovery/command 用于传递主系统的指令
2.  /cache/recovery/log 保存 recovery 模式运行时产生的 log
3.  /cache/recovery/last_log 保存上次 Recovery 会话的日志,供参考

Die von /cache/recovery/command unterstützten Anweisungen sind (eine Anweisung pro Zeile, die mit „\n“ endet):

--update_package=path  验证安装 OTA 包,OTA 升级使用
--wipe_data    清除用户数据(cache分区),然后重启
--prompt_and_wipe_data 提示用户数据已损坏,征得用户同意后擦除用户数据(和缓存),然后重新启动
--wipe_cache  清除 cache 分区(但是不包含用户数据),然后重启
--set_encrypted_filesystem=on|off   启用/禁用加密的文件系统
--just_exit  不做操作,直接退出,重启
2.2.3.2. BootLoader und Wiederherstellung kommunizieren über BCB (BootLoader Control Block)

BCB ist nicht nur die Kommunikationsbrücke zwischen BootLoader und Wiederherstellung, sondern auch die Kommunikationsbrücke zwischen BootLoader und dem Hauptsystem und wird somit zur Kommunikationsmethode zwischen Wiederherstellung und Hauptsystem. Derzeit wird /cache/recovery im neuesten Android-System verwendet. Dort Es gibt nur sehr wenige, die hauptsächlich BCB verwenden, um den Informationsaustausch zwischen Wiederherstellung und Hauptsystem zu realisieren. MISC-Partition im Flash gespeichert. Es handelt sich selbst um eine struct bootloader_message-Struktur. Der von der MISC-Partition verwendete Speicherplatz wird wie folgt zugewiesen:

0k - 2k 用于 bootloader_message
2k - 16k 用于 BootLoader(2K-4K范围可以选择性地用作 bootloader_message_ab结构体)
16K - 64K 由 uncrypt 和 recovery 用于存储 A/B 设备的 wipe_package

Die Struktur bootloader_message ist wie folgt definiert:

struct bootloader_message {
    char command[32];
    char status[32];
    char recovery[768];
    char stage[32];
    char reserved[1184];
};

Befehl: Linux aktualisiert diesen Wert, wenn ein Neustart im Wiederherstellungsmodus oder eine Aktualisierung der Bootloader-Firmware erforderlich ist. Wenn die Firmware aktualisiert wird, aktualisiert der Bootloader auch diesen Wert. Auch beim Beenden der Wiederherstellung nach einem erfolgreichen Update. Der Wert dieses Mitglieds wird gelöscht, um zu verhindern, dass beim Neustart erneut in den Wiederherstellungsmodus gewechselt wird.

Status: Nach Abschluss des entsprechenden Updates. Der Bootloader schreibt die laufenden Ergebnisse in dieses Feld.

Recovery: Kann vom Hauptsystem- oder Wiederherstellungsdienstprogramm geschrieben werden. Das Inhaltsformat dieser Datei ist:

        “recovery\n
         <recovery command>\n
         <recovery command>”

ist eine Zeichenfolge, die mit „recovery\n“ beginnen muss, andernfalls werden alle Inhaltsfelder dieses Felds ignoriert. Gleichzeitig endet jede Anweisung mit „\n“ und jede Anweisung belegt eine Zeile.
Der Teil nach „recovery\n“ ist der von /cache/recovery/command unterstützte Befehl. Es kann als Sicherung der Befehlsoperation während der Wiederherstellungsoperation verstanden werden.
Der Prozess der Wiederherstellungsoperation ist: Zuerst BCB lesen und dann /cache/recovery/command lesen und dann beides zurück in BCB schreiben, um sicherzustellen, dass die Operation ausgeführt wird, bevor sie in das Hauptsystem gelangt.
Bevor nach dem Vorgang das Hauptsystem aufgerufen wird, löscht Recovery das Befehlsfeld und das Wiederherstellungsfeld von BCB, um sicherzustellen, dass es nach dem Neustart nicht mehr in den Wiederherstellungsmodus wechselt.

2.3 Wiederherstellungsprozess

Im Vergleich zum Hauptsystem handelt es sich beim Wiederherstellungsmodus tatsächlich um ein Mikrosystem. Es ist nicht so komplex wie das Hauptsystem, es ist nur für einfache Funktionen und das Zeichnen einer einfachen Schnittstelle verantwortlich, und die Codelogik ist relativ einfach und leicht zu verstehen. Die Hauptlogik ist in „recovery.cpp“ implementiert. Schauen wir uns an, was sie hauptsächlich tut:

  • Initialisieren und laden Sie die Umgebungsvariable RECOVERY.
  • Laden Sie die Konfigurationsdatei des Wiederherstellungsbefehls „recovery.fstab“.
  • Schnittstellenzeichnung, Eingabeereignisse usw. initialisieren. - Mounten Sie die Partition gemäß der Befehlskonfigurationsdatei.
  • Analysieren Sie Wiederherstellungsbefehle und führen Sie sie aus, z. B. Daten löschen, Update-Pakete installieren usw.
  • Protokollausgabe anzeigen.
  • Starten Sie das System neu, wenn Sie fertig sind.

Das allgemeine Code-Flussdiagramm lautet wie folgt

Bitte fügen Sie eine Bildbeschreibung hinzu

Die beiden wichtigeren Funktionen sind get_args() und getopt_long()

get_args() dient als externe Schnittstelle und ruft intern getopt_long() auf, um die Befehlszeilenanalyse zu implementieren.

getopt_long():

  • Gemäß den vordefinierten Optionsregeln werden die Befehlszeilenparameter analysiert und der Schlüssel der entsprechenden Option zurückgegeben.
  • Die Optionsparameter werden nicht direkt verarbeitet, es wird nur der Optionsschlüssel zurückgegeben.

get_args():

  • Rufen Sie getopt_long() auf, um Befehlszeilenoptionen zu analysieren.
  • Verarbeiten Sie die Optionsparameter basierend auf dem von getopt_long() zurückgegebenen Optionsschlüssel weiter.
  • Rufen Sie beispielsweise für die Option „-update_package“ den Pfadparameter des Aktualisierungspakets ab.
  • Speichern Sie die analysierten Parameter in globalen Variablen.

Vorgang zum Zurücksetzen auf die Werkseinstellungen:

  1. Wählen Sie „Einstellungen“ -> „System“ -> „Systemwiederherstellung“.

  2. Das Hauptsystem schreibt „–wipe_data“ in BCB;

  3. Neustart des Hauptsystems („Recovery“), um in den Wiederherstellungsmodus zu gelangen;

  4. Die Funktion get_args() liest BCB und schreibt „boot-recovery“ und „--wipe_data“, um zu erfahren, dass die Daten gelöscht werden müssen.

  5. erase_volume() führt die Formatierung/Datenpartitionierung durch

  6. Formatieren Sie auch die /cache-Partition

  7. finish_recovery() löscht BCB. Starten Sie dann das Hauptsystem neu.

OTA-Recovery-Upgrade-Prozess:

  1. Das Hauptsystem lädt das Update-Paket nach /cache/some-filename.zip herunter

  2. Das Hauptsystem schreibt „–update_package=/cache/some-filename.zip“ in BCB

  3. Starten Sie das Hauptsystem in der Wiederherstellung neu

  4. get_args() schreibt „boot-recovery“ und „–update_package=…“ in BCB
    – Auf diese Weise gelangen Sie nach dem nächsten abnormalen Neustart erneut in die Wiederherstellung Updates installieren – a>

  5. nstall_package() installiert Updates

  6. finish_recovery() schließt die Installation ab, schließt die Wiederherstellung ab und löscht BCB
    – Danach wird das System neu gestartet und gelangt in das Hauptsystem –

  7. Wenn die Installation fehlschlägt

    7a. prompt_and_wait() zeigt einen Fehler an und wartet auf Benutzeroperation
    7b. Der Benutzer startet neu im Hauptsystem

3. Wiederherstellen der Werkseinstellungen

1. Reagieren Sie auf die Funktion „Werkseinstellungen wiederherstellen“ in den nativen Einstellungen und senden Sie dann einen Reset-Broadcast (Intent.ACTION_FACTORY_RESET).

2. Die FrameWork-Schicht empfängt diese Übertragung und führt die Reset-Einstellung der Android-Schicht gemäß den von der Übertragung übertragenen Parametern aus.

3. Wenn die Android-Ebene vollständig ausgeführt ist, werden die Konfigurationsinformationen zum Zurücksetzen in BCB geschrieben und schließlich in die Wiederherstellung übernommen.

Zeitdiagramm des Master-Resets:

Fügen Sie hier eine Bildbeschreibung ein

Die Analyse des Master-Reset-Prozesses ist wie folgt

(1) Codeanalyse der Java-Ebene

1.Framework empfängt Reset-Broadcast

Deklarieren Sie MasterClearReceiver .java in „framework/base/core/res/AndroidManifest.xml“, um den Reset-Broadcast statisch zu empfangen

1.	 <receiver android:name="com.android.server.MasterClearReceiver"
2.	            android:permission="android.permission.MASTER_CLEAR">
3.	            <intent-filter
4.	                    android:priority="100" >
5.	                <!-- For Checkin, Settings, etc.: action=FACTORY_RESET -->
                   //这条广播表示 恢复出厂设置
6.	                <action android:name="android.intent.action.FACTORY_RESET" />
7.	                <!-- As above until all the references to the deprecated MASTER_CLEAR                         get updated to FACTORY_RESET. -->
9.	                <action android:name="android.intent.action.MASTER_CLEAR" />
10.	
11.	                <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
12.	                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
13.	                <category android:name="android.intent.category.MASTER_CLEAR" />
14.	            </intent-filter>
15.	        </receiver>

2.MasterClearReceiver .onReceive()

Führen Sie die Methode onReceive() in MasterClearReceiver.java aus

Framework/base/services/core/java/com/android/server/MasterClearReceiver .java

1. public void onReceive(final Context context, final Intent intent) {
    
    
2.	        if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
    
    
3.	            if (!"google.com".equals(intent.getStringExtra("from"))) {
    
    
4.	                Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
5.	                return;
6.	            }
7.	        }
8.	        if (Intent.ACTION_MASTER_CLEAR.equals(intent.getAction())) {
    
    
9.	            Slog.w(TAG, "The request uses the deprecated Intent#ACTION_MASTER_CLEAR, "
10.	                    + "Intent#ACTION_FACTORY_RESET should be used instead.");
11.	        }
12.	        if (intent.hasExtra(Intent.EXTRA_FORCE_MASTER_CLEAR)) {
    
    
13.	            Slog.w(TAG, "The request uses the deprecated Intent#EXTRA_FORCE_MASTER_CLEAR, "
14.	                    + "Intent#EXTRA_FORCE_FACTORY_RESET should be used instead.");
15.	        }
16.	
17.	        final String factoryResetPackage = context
18.	                .getString(com.android.internal.R.string.config_factoryResetPackage);
           //判断如果广播是Intent.ACTION_FACTORY_RESET,且factoryResetPackage不为空
19.	        if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction())
20.	                && !TextUtils.isEmpty(factoryResetPackage)) {
    
    
21.	            intent.setPackage(factoryResetPackage).setComponent(null);
            //重新将广播发出去
22.	            context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
23.	            return;
24.	        }
         
25.	        // 从广播中获取shutdown、reason、forceWipe、mWipeEsims等参数的值
           //mWipeExternalStorage代表是否需要擦除外部储存的标志
          // mWipeEsims代表是否需要擦除eSIM的标志
26.	        final boolean shutdown = intent.getBooleanExtra("shutdown", false);
27.	        final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
28.	        mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
29.	        mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
30.	        final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
31.	                || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
32.	
33.	        Slog.w(TAG, "!!! FACTORY RESET !!!");
34.	        // The reboot call is blocking, so we need to do it on another thread.
            // 创建一个新线程thr
35.	        Thread thr = new Thread("Reboot") {
    
    
36.	            @Override
37.	            public void run() {
    
    
38.	                try {
    
    
39.	                    RecoverySystem
40.	                            .rebootWipeUserData(context, shutdown, reason, forceWipe,       mWipeEsims);
41.	                    Log.wtf(TAG, "Still running after master clear?!");
42.	                } catch (IOException e) {
    
    
43.	                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
44.	                } catch (SecurityException e) {
    
    
45.	                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
46.	                }
47.	            }
48.	        };
49.	
50.	        if (mWipeExternalStorage) {
    
    
51.	            // thr will be started at the end of this task.
52.	            new WipeDataTask(context, thr).execute();
53.	        } else {
    
    
54.	            thr.start();
55.	        }
56.	    }

Überprüfen Sie den MasterClearReceiver-Code. Wenn onReceive den Broadcast empfängt, stellt es fest, ob der Broadcast Intent.ACTION_FACTORY_RESET ist und das FactoryResetPackage nicht leer ist, sendet dann den Broadcast erneut und kehrt zum Beenden zurück. Anschließend wird ein neuer Thread thr erstellt, der neu startet und Benutzerdaten intern löscht. Der thr-Thread wird jedoch nicht sofort ausgeführt, sondern ermittelt, ob die Daten auf der externen Speicherkarte oder SIM-Karte gelöscht werden müssen. Bei Bedarf wird ein WipeDataTask-Objekt erstellt, das intern die WipeAdoptableDisks-Methode von StorageManager aufruft, um die Daten auf der externen Speicherkarte zu löschen.
Nach dem Aufruf von WipeAdoptableDisks von StorageManager zum Löschen der Daten auf der externen Speicherkarte wird die Startmethode des Thr-Threads ausgeführt, die Run-Methode wird ausgelöst und die rebootWipeUserData-Methode von RecoverySystem wird ausgeführt aufgerufen.

3.RecoverySystem.rebootWipeUserData()

Frameworks/base/core/java/android/os/RecoverySystem.java

Die rebootWipeUserData-Methode von RecoverySystem ist unten dargestellt.

1.	public static void rebootWipeUserData(Context context, boolean shutdown, String 				reason, boolean force, boolean wipeEuicc) throws IOException {
    
    
    // 检查是否允许执行工厂重置,如果不允许则抛出SecurityException
3.	        UserManager um = (UserManager) 															context.getSystemService(Context.USER_SERVICE);
4.	        if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
    
    
5.	            throw new SecurityException("Wiping data is not allowed for this user.");
6.	        }
7.	        final ConditionVariable condition = new ConditionVariable();
8.	//发送Intent.ACTION_MASTER_CLEAR_NOTIFICATION广播,并等待其完成
9.	        Intent intent = new  																		Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
10.	        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
11.	                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
12.	        context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
13.	                android.Manifest.permission.MASTER_CLEAR,
14.	                new BroadcastReceiver() {
    
    
15.	                    @Override
16.	                    public void onReceive(Context context, Intent intent) {
    
    
17.	                        condition.open();
18.	                    }
19.	                }, null, 0, null, null);
20.	
21.	        // Block until the ordered broadcast has completed.
22.	        condition.block();
23.	//如果需要擦除eUICC数据,则调用wipeEuiccData()执行擦除操作。否则调用removeEuiccInvisibleSubs()
24.	        EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
25.	        if (wipeEuicc) {
    
    
26.	            wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
27.	        } else {
    
    
28.	            removeEuiccInvisibleSubs(context, euiccManager);
29.	        }
30.	
31.	        String shutdownArg = null;
32.	        if (shutdown) {
    
    
33.	            shutdownArg = "--shutdown_after";
34.	        }
35.	
36.	        String reasonArg = null;
37.	        if (!TextUtils.isEmpty(reason)) {
    
    
38.	            String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", 							System.currentTimeMillis()).toString();
39.	            reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
40.	        }
41.	
42.	        final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
43.	        Log.i(TAG," RecoverySystem.java rebootWipeUserData");
44.	        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
45.	    }

Während der Ausführung der rebootWipeUserData-Methode werden die gekapselten Parameter --wipe_data, --locale gesendet und anschließend die bootCommand-Methode aufgerufen.

4.RecoverySystem.bootCommand()

Die BootCommand-Methode lautet wie folgt:

1.	private static void bootCommand(Context context, String... args) throws IOException {
    
    
2.	        LOG_FILE.delete();
3.	//构建command字符串,将args中的每个参数拼接并换行
4.	        StringBuilder command = new StringBuilder();
5.	        for (String arg : args) {
    
    
6.	            if (!TextUtils.isEmpty(arg)) {
    
    
7.	                command.append(arg);
8.	                command.append("\n");
9.	            }
10.	        }
11.	
12.	        // Write the command into BCB (bootloader control block) and boot from
13.	        // there. Will not return unless failed.
14.	        RecoverySystem rs = (RecoverySystem)                                                     context.getSystemService(Context.RECOVERY_SERVICE);
15.	        Log.i(TAG," RecoverySystem.java bootcommand");
16.	        rs.rebootRecoveryWithCommand(command.toString());
17.	
18.	        throw new IOException("Reboot failed (no permissions?)");
19.	    }

Diese Methode schreibt die eingehenden Befehlsparameter in bootCommand. Die bootCommand-Methode ruft außerdem die rebootRecoveryWithCommand-Methode von RecoverySystemService auf.

5.RecoverySystemService.rebootRecoveryWithCommand()

Frameworks/base/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java

Die rebootRecoveryWithCommand-Methode von RecoverySystemService lautet wie folgt:

1.	public void rebootRecoveryWithCommand(String command) {
    
    
2.	        if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
3.	        synchronized (sRequestLock) {
    
    
             //setupOrClearBcb将之前传递过来的参数写入BCB中
4.	            if (!setupOrClearBcb(true, command)) {
    
    
5.	                return;
6.	            }
7.	
8.	          //   Having set up the BCB, go ahead and reboot.
9.	            PowerManager pm = mInjector.getPowerManager();           
10.	            pm.reboot(PowerManager.REBOOT_RECOVERY);     
11.	        }
12.	    }
private boolean setupOrClearBcb(boolean isSetup, String command) {
    
    
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
       //检查解密服务是否可用,如果不可用直接返回false
        final boolean available = checkAndWaitForUncryptService();
        if (!available) {
    
    
            Slog.e(TAG, "uncrypt service is unavailable.");
            return false;
        }
		// 根据isSetup参数决定是设置BCB(setup-bcb)还是清除BCB(clear-bcb)
        if (isSetup) {
    
    
            mInjector.systemPropertiesSet("ctl.start", "setup-bcb");
        } else {
    
    
            mInjector.systemPropertiesSet("ctl.start", "clear-bcb");
        }
        //通过socket与 uncrypt通信,将之前的参数发送给 uncrypt
        // Connect to the uncrypt service socket.
        UncryptSocket socket = mInjector.connectService();
        if (socket == null) {
    
    
            Slog.e(TAG, "Failed to connect to uncrypt socket");
            return false;
        }

        try {
    
    
            // Send the BCB commands if it's to setup BCB.
            if (isSetup) {
    
    
                socket.sendCommand(command);
            }

            // Read the status from the socket.
            int status = socket.getPercentageUncrypted();

            // Ack receipt of the status code. uncrypt waits for the ack so
            // the socket won't be destroyed before we receive the code.
            socket.sendAck();
           //根据状态码判断设置/清除BCB是否成功,成功则返回true,失败返回false
            if (status == 100) {
    
    
                Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear")
                        + " bcb successfully finished.");
            } else {
    
    
                // Error in /system/bin/uncrypt.
                Slog.e(TAG, "uncrypt failed with status: " + status);
                return false;
            }
        } catch (IOException e) {
    
    
            Slog.e(TAG, "IOException when communicating with uncrypt:", e);
            return false;
        } finally {
    
    
            socket.close();
        }

        return true;
    }

uncrypt.cpp verarbeitet die Parameter des Programmstarts separat. Wenn der Parameter „setup-bcb“ ist, wird die Methode setup_bcb() aufgerufen.

bootable/recovery/uncrypt/uncrypt.cpp

static bool setup_bcb(const int socket) {
    
    
    // c5. receive message length
    int length;
    if (!android::base::ReadFully(socket, &length, 4)) {
    
    
        PLOG(ERROR) << "failed to read the length";
        return false;
    }
    length = ntohl(length);

    // c7. receive message
    std::string content;
    content.resize(length);
    if (!android::base::ReadFully(socket, &content[0], length)) {
    
    
        PLOG(ERROR) << "failed to read the message";
        return false;
    }
    LOG(INFO) << "  received command: [" << content << "] (" << content.size() << ")";
    std::vector<std::string> options = android::base::Split(content, "\n");
    std::string wipe_package;
    for (auto& option : options) {
    
    
        if (android::base::StartsWith(option, "--wipe_package=")) {
    
    
            std::string path = option.substr(strlen("--wipe_package="));
            if (!android::base::ReadFileToString(path, &wipe_package)) {
    
    
                PLOG(ERROR) << "failed to read " << path;
                return false;
            }
            option = android::base::StringPrintf("--wipe_package_size=%zu", wipe_package.size());
        }
    }

    // c8. setup the bcb command
    //write_bootloader_message()方法将之前传递过来的参数写入BCB
    std::string err;
    if (!write_bootloader_message(options, &err)) {
    
    
        LOG(ERROR) << "failed to set bootloader message: " << err;
        //成功写入参数后,调用这个函数,返回状态值100
        write_status_to_socket(-1, socket);
        return false;
    }
    if (!wipe_package.empty() && !write_wipe_package(wipe_package, &err)) {
    
    
        PLOG(ERROR) << "failed to set wipe package: " << err;
        write_status_to_socket(-1, socket);
        return false;
    }
    // c10. send "100" status
    write_status_to_socket(100, socket);
    return true;
}

Das Schreiben von Parametern erfolgt hier:

Die Methode write_bootloader_message() schreibt die zuvor übergebenen Parameter in BCB. Implementiert durch die Methode write_misc_partition() in bootloader_message.cpp

bootable/recovery/bootloader_message/bootloader_message.cpp

Diese Methode implementiert die Funktion des Schreibens von Daten in die Blockgerätedatei der Misc-Partition.

bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
                          size_t offset, std::string* err) {
    
    
  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
  if (fd == -1) {
    
    
    *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
    //通过lseek()将文件指针移动到offset偏移位置
  if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
    
    
    *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
    //通过WriteFully()将p指向的缓冲区的数据写入fd文件描述符,大小为size个字节
  if (!android::base::WriteFully(fd, p, size)) {
    
    
    *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
    //通过fsync()刷新文件缓冲,将数据写入物理设备
  if (fsync(fd) == -1) {
    
    
    *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
                                       strerror(errno));
    return false;
  }
  return true;
}

Nachdem Sie die Parameter in die sonstige Partition geschrieben haben, geben Sie den Statuswert Schritt für Schritt an setupOrClearBcb() zurück und führen Sie dann den Neustartvorgang durch

pm.reboot(PowerManager.REBOOT_RECOVERY) startet im Wiederherstellungsmodus neu

Diese Methode ruft die Neustartmethode von PowerManager auf, um das System neu zu starten.

6.PowerManager.reboot()

Frameworks/base/core/java/android/os/PowerManager.java

1.	 public void reboot(@Nullable String reason) {
    
    
2.	        if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
    
    
3.	            throw new UnsupportedOperationException(
4.	              "Attempted userspace reboot on a device that doesn't support it");
5.	        }
6.	        try {
    
    
7.	            mService.reboot(false, reason, true);            
8.	        } catch (RemoteException e) {
    
    
9.	            throw e.rethrowFromSystemServer();
10.	        }
11.	    }

Der Neustart von PowerManager ruft außerdem den Neustart von PowerManagerService auf. Wenn der erste Parameter wahr ist, wird ein Bestätigungs-Popup-Fenster angezeigt. Wenn Sie ihn hier auf „false“ setzen, bedeutet dies, dass kein Bestätigungs-Popup-Fenster erforderlich ist.

7.PowerManagerService.reboot()

Frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

Die Neustartmethode von PowerManagerService lautet wie folgt:

1.	public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
    
    
    //检查调用方是否有REBOOT和RECOVERY权限
2.	    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, 				null);
3.	            if (PowerManager.REBOOT_RECOVERY.equals(reason)
4.	                    || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
    
    
5.	mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
6.	            }
7.	
8.	            final long ident = Binder.clearCallingIdentity();
9.	            try {
    
    
10.	                Log.i(TAG," powermanagerservice.java reboot start");
11.	                (HALT_MODE_REBOOT, confirm, reason, wait);
12.	                Log.i(TAG," powermanagerservice.java reboot---                                                                    >shutdownOrRebootInternal");
13.	            } finally {
    
    
14.	                Binder.restoreCallingIdentity(ident);
15.	            }
16.	        }
17.	    private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean               confirm,@Nullable final String reason, boolean wait) {
    
    
19.	        if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
    
    
20.	            if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
    
    
21.	                throw new UnsupportedOperationException(
22.	                 "Attempted userspace reboot on a device that doesn't support it");
23.	            }
24.	            UserspaceRebootLogger.noteUserspaceRebootWasRequested();
25.	        }
26.	        if (mHandler == null || !mSystemReady) {
    
    
27.	            if (RescueParty.isAttemptingFactoryReset()) {
    
    
28.	                // If we're stuck in a really low-level reboot loop, and a
29.	                // rescue party is trying to prompt the user for a factory data
30.	                // reset, we must GET TO DA CHOPPA!
31.	                PowerManagerService.lowLevelReboot(reason);           
32.	            } else {
    
    
33.	                throw new IllegalStateException("Too early to call shutdown() or                                                                               reboot()");
34.	            }
35.	        }
36. 	//创建一个Runnable,在run()方法中根据haltMode的值来调用ShutdownThread的reboot()、     rebootSafeMode()或shutdown()方法执行重启或关机操作
37.	        Runnable runnable = new Runnable() {
    
    
38.	            @Override
39.	            public void run() {
    
    
40.	                synchronized (this) {
    
    
41.	                    if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
    
    
42.	                        ShutdownThread.rebootSafeMode(getUiContext(), confirm);
43.	                    } else if (haltMode == HALT_MODE_REBOOT) {
    
    
44.	                         //走到这里
45.	                        ShutdownThread.reboot(getUiContext(), reason, confirm);
46.	                    } else {
    
    
47.	                        ShutdownThread.shutdown(getUiContext(), reason, confirm);
48.	                    }
49.	                }
50.	            }
51.	        };
52.	
53.	        // ShutdownThread must run on a looper capable of displaying the UI.
54.	        Message msg = Message.obtain(UiThread.getHandler(), runnable);
55.	        msg.setAsynchronous(true);
56.	        UiThread.getHandler().sendMessage(msg);
57.	
58.	        // PowerManager.reboot() is documented not to return so just wait for the                   inevitable.
           //PowerManager.reboot()被记录为不会返回,所以只需等待不可避免的结果。
59.	        if (wait) {
    
    
60.	            synchronized (runnable) {
    
    
61.	                while (true) {
    
    
62.	                    try {
    
    
63.	                        runnable.wait();
64.	                    } catch (InterruptedException e) {
    
    
65.	                    }
66.	                }
67.	            }
68.	        }
69.	    }

Die Neustartmethode von PowerManagerService prüft zunächst, ob REBOOT- und RECOVERY-Berechtigungen vorhanden sind, ruft dann die Methode „shutdownOrRebootInternal()“ auf und löst schließlich die Neustartmethode von ShutdownThread aus.

8.ShutdownThread.reboot()

Frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

Die Neustartmethode von ShutdownThread lautet wie folgt

1.	public static void reboot(final Context context, String reason, boolean confirm) {
    
    
2.	        mReboot = true;
3.	        mRebootSafeMode = false;
4.	        mRebootHasProgressBar = false;
5.	        mReason = reason;
6.	         //走到这里
7.	        shutdownInner(context, confirm);
8.	    }
9.	    private static void shutdownInner(final Context context, boolean confirm) {
    
    
10.	        // ShutdownThread is called from many places, so best to verify here that the                context passed
11.	        // in is themed.
12.	        context.assertRuntimeOverlayThemable();
13.	
14.	        // ensure that only one thread is trying to power down.
15.	        // any additional calls are just returned
           // 检查sIsStarted标志位,确保重启操作没有已经启动,防止重复调用
16.	        synchronized (sIsStartedGuard) {
    
    
17.	            if (sIsStarted) {
    
    
18.	                Log.d(TAG, "Request to shutdown already running, returning.");
19.	                return;
20.	            }
21.	        }
22.	//根据longPressBehavior和mRebootSafeMode的值决定显示哪个确认对话框
23.	        final int longPressBehavior = context.getResources().getInteger(
24.	        com.android.internal.R.integer.config_longPressOnPowerBehavior);
25.	        final int resourceId = mRebootSafeMode
26.	                ? com.android.internal.R.string.reboot_safemode_confirm
27.	                : (longPressBehavior == 2
28.	                        ? com.android.internal.R.string.shutdown_confirm_question
29.	                        : com.android.internal.R.string.shutdown_confirm);
30.	
31.	        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" +                            longPressBehavior);
32.	   //如果confirm为true,则显示确认对话框
33.	        if (confirm) {
    
    
34.	            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
35.	            if (sConfirmDialog != null) {
    
    
36.	                sConfirmDialog.dismiss();
37.	            }
38.	            sConfirmDialog = new AlertDialog.Builder(context)
39.	                    .setTitle(mRebootSafeMode
40.	                            ? com.android.internal.R.string.reboot_safemode_title
41.	                            : com.android.internal.R.string.power_off)
42.	                    .setMessage(resourceId)
43.	                    .setPositiveButton(com.android.internal.R.string.yes, new                                    DialogInterface.OnClickListener() {
    
    
44.	                        public void onClick(DialogInterface dialog, int which) {
    
    
45.	                            beginShutdownSequence(context);
46.	                        }
47.	                    })
48.	                    .setNegativeButton(com.android.internal.R.string.no, null)
49.	                    .create();
50.	            closer.dialog = sConfirmDialog;
51.	            sConfirmDialog.setOnDismissListener(closer);
52.	            sConfirmDialog.getWindow().
                           setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
53.	            sConfirmDialog.show();
54.	        } else {
    
    // 如果confirm为false,则直接调用beginShutdownSequence启动关机序列
55.	            beginShutdownSequence(context);
56.	        }
57.	    }

Die Reboot-Methode ruft außerdem die ShutdownInner-Methode auf. ShutdownInner ermittelt zunächst, ob ein Bestätigungs-Popup-Fenster angezeigt werden muss. Da die Werkseinstellungen wiederhergestellt werden, bedeutet false hier, dass das Bestätigungs-Popup-Fenster nicht angezeigt werden muss Die Methode beginShutdownSequence wird direkt aufgerufen.

9.ShutdownThread.beginShutdownSequence()

Die beginShutdownSequence-Methode von ShutdownThread lautet wie folgt.

1. private static void beginShutdownSequence(Context context) {
    
    
2.	        synchronized (sIsStartedGuard) {
    
    
    //检查sIsStarted标志位,确保关机序列没有已经启动,防止重复启动
3.	            if (sIsStarted) {
    
    
4.	                Log.d(TAG, "Shutdown sequence already running, returning.");
5.	                return;
6.	            }
7.	            sIsStarted = true;
8.	        }
9.	//显示shutdownDialog对话框并获取Context
10.	        sInstance.mProgressDialog = showShutdownDialog(context);
11.	        sInstance.mContext = context;
    //获取PowerManager实例,用于后续获取WakeLock
12.	        sInstance.mPowerManager =                                                                     (PowerManager)context.getSystemService(Context.POWER_SERVICE);
13.	
14.	        // make sure we never fall asleep again
15.	        sInstance.mCpuWakeLock = null;
16.	        try {
    
    
17.	            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
18.	                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
19.	            sInstance.mCpuWakeLock.setReferenceCounted(false);
20.	            sInstance.mCpuWakeLock.acquire();
21.	        } catch (SecurityException e) {
    
    
22.	            Log.w(TAG, "No permission to acquire wake lock", e);
23.	            sInstance.mCpuWakeLock = null;
24.	        }
25.	
26.	        // also make sure the screen stays on for better user experience
        //检查屏幕是否已开启,如果是则获取一个FULL_WAKE_LOCK权限的ScreenWakeLock,保持屏幕常亮
27.	        sInstance.mScreenWakeLock = null;
28.	        if (sInstance.mPowerManager.isScreenOn()) {
    
    
29.	            try {
    
    
30.	                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
31.	                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
32.	                sInstance.mScreenWakeLock.setReferenceCounted(false);
33.	                sInstance.mScreenWakeLock.acquire();
34.	            } catch (SecurityException e) {
    
    
35.	                Log.w(TAG, "No permission to acquire wake lock", e);
36.	                sInstance.mScreenWakeLock = null;
37.	            }
38.	        }
39.	
40.	        if (SecurityLog.isLoggingEnabled()) {
    
    
41.	            SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
42.	        }
43.	
44.	        // start the thread that initiates shutdown
           //创建Handler并启动关机线程
45.	        sInstance.mHandler = new Handler() {
    
    
46.	        };
47.	      // 启动线程
48.	        sInstance.start();
49.	    }

Diese Methode führt einige Verarbeitungsvorgänge durch:

Zeigen Sie einen Systemfortschrittsdialog an, um anzuzeigen, dass das aktuelle Gerät heruntergefahren/neu gestartet wird.
Behalten Sie die CPU- und Bildschirmsperre bei, halten Sie das Gerät wach und den Bildschirm eingeschaltet.
Starten Sie abschließend den Thread und führen Sie die Run-Methode aus. Dieser Thread bezieht sich auf ShutdownThread.java, und die ausgeführte run()-Methode ist auch die run()-Methode dieser Klasse. Die Methode ist zu lang. Sie dient hauptsächlich dazu, vor dem Neustart einige Vorbereitungsarbeiten durchzuführen. Sie können sich direkt die zuletzt aufgerufene Funktion ansehen

1. public void run() {
    
    
2.	        TimingsTraceLog shutdownTimingLog = newTimingsLog();
3.	        shutdownTimingLog.traceBegin("SystemServerShutdown");
4.	        metricShutdownStart();
5.	        metricStarted(METRIC_SYSTEM_SERVER);
6.	
7.	        BroadcastReceiver br = new BroadcastReceiver() {
    
    
8.	            @Override public void onReceive(Context context, Intent intent) {
    
    
9.	                // We don't allow apps to cancel this, so ignore the result.
10.	                actionDone();
11.	            }
12.	        };
13.	 ……
14.	        shutdownTimingLog.traceEnd(); // SystemServerShutdown
15.	        metricEnded(METRIC_SYSTEM_SERVER);
16.	        saveMetrics(mReboot, mReason);
17.	        // Remaining work will be done by init, including vold shutdown
            // 重启前的准备工作结束, 开始重启
18.	        rebootOrShutdown(mContext, mReboot, mReason);
19.	    }

Abschließend wird die Methode rebootOrShutdown() von ShutdownThread.java aufgerufen.

10.shutdownThread.rebootOrShutdown()

1.	    public static void rebootOrShutdown(final Context context, boolean reboot, String 				reason) {
    
    
    // 如果reboot为true,则先调用PowerManagerService.lowLevelReboot()尝试重启。如果重启失败,则将reason设置为null,并进行关机操作
2.	        if (reboot) {
    
    
3.	            Log.i(TAG, "Rebooting, reason: " + reason);
4.	            PowerManagerService.lowLevelReboot(reason);
5.	            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
6.	            reason = null;
7.	        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
    
    
8.	            // vibrate before shutting down(关闭前振动)
9.	            Vibrator vibrator = new SystemVibrator(context);
10.	            try {
    
    
11.	                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
12.	            } catch (Exception e) {
    
    
13.	                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                    //振动故障不应中断停机。只需记录即可。
14.	                Log.w(TAG, "Failed to vibrate during shutdown.", e);
15.	            }
16.	
17.	            // vibrator is asynchronous so we need to wait to avoid shutting down too                    soon.(可控震源是异步的,所以我们需要等待以避免过早关闭。)
18.	            try {
    
    
19.	                Thread.sleep(SHUTDOWN_VIBRATE_MS);
20.	            } catch (InterruptedException unused) {
    
    
21.	            }
22.	        }
23.	        // Shutdown power
24.	        Log.i(TAG, "Performing low-level shutdown...");
25.	        PowerManagerService.lowLevelShutdown(reason);
26.	    }

Stellen Sie fest, ob es sich um einen Neustart handelt. Wenn es sich um einen Neustart handelt, rufen Sie Folgendes auf: PowerManagerService.lowLevelReboot(reason);

11.PowerManagerService.lowLevelReboot()

Die lowLevelReboot()-Methode von PowerManagerService.java lautet wie folgt

 public static void lowLevelReboot(String reason) {
    
    
     //检查reason是否为空,如果是则设置为空字符串
2.	        if (reason == null) {
    
    
3.	            reason = "";
4.	        }
5.	
6.	        // If the reason is "quiescent", it means that the boot process should 						proceed
7.	        // without turning on the screen/lights.
8.	        // The "quiescent" property is sticky, meaning that any number
9.	        // of subsequent reboots should honor the property until it is reset.
    
    //如果reason为"quiescent"或以",quiescent"结尾,则设置sQuiescent标志位,并截取reason
10.	        if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
    
    
11.	            sQuiescent = true;
12.	            reason = "";
13.	        } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
    
    
14.	            sQuiescent = true;
15.	            reason = reason.substring(0,
16.	                    reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
17.	        }
18.	// 如果reason为REBOOT_RECOVERY或REBOOT_RECOVERY_UPDATE,则将reason设置为"recovery"
19.	        if (reason.equals(PowerManager.REBOOT_RECOVERY)
20.	                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
    
    
21.	         // 走的这里
22.	            reason = "recovery";
23.	        }    
24.	// 如果sQuiescent被设置,则将",quiescent"追加到reason的末尾
25.	        if (sQuiescent) {
    
    
28.	            reason = reason + ",quiescent";
29.	        }
30.	        // 设置这个prop触发init进程重启进入recovery模式
31.	        SystemProperties.set("sys.powerctl", "reboot," + reason);
32.	        try {
    
    
33.	            Thread.sleep(20 * 1000L);
34.	        } catch (InterruptedException e) {
    
    
35.	            Thread.currentThread().interrupt();
36.	        }
37.	        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
38.	    }

Bestimmen Sie zunächst den Grund für den Neustart. Übergeben Sie je nach Grund unterschiedliche Befehlsparameter, um die native Ebene aufzurufen (der Grund ist hier die Wiederherstellung) und starten Sie den Neustartvorgang.

(2) Analyse der nativen Schicht

Nach dem Neustart des Geräts wechselt es automatisch in den Wiederherstellungsmodus, liest die BCB-Informationen und beginnt mit dem Löschen der Daten- und Cache-Partitionen. Nach erfolgreicher Löschung wird das System neu gestartet, startet dann den normalen Startvorgang und verwendet den Inhalt von wieder die Systempartition, um die Startinitialisierung abzuschließen.

Nach dem Neustart können Sie sehen, dass der Wiederherstellungsdienst über die Datei init.rc im Wiederherstellungsmodus gestartet wird. Einzelheiten finden Sie in der Datei bootable/recovery/etc/init.rc. Der Codeausschnitt lautet wie folgt:

Die Hauptfunktion des Wiederherstellungsdienstes befindet sich in der Datei bootable/recovery/recovery.c. Die Hauptprozessanalyse der Hauptfunktion lautet wie folgt:

1. Recovery_main.cpp

Geben Sie zunächst „recovery_main.cpp“ ein

1.	int main(int argc, char** argv) {
2.	……
3.	  load_volume_table();  //加载挂载分区
4.	
5.	  std::string stage;
6.	//从/misc分区读取之前写入到BCB中的信息,赋值给变量args
7.	  std::vector<std::string> args = get_args(argc, argv, &stage);
8.	  auto args_to_parse = StringVectorToNullTerminatedArray(args);
9.	
10.	 ……
11.	  int arg;
12.	  int option_index;
13.	  //对args信息进行一些处理
14.	  while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", 				OPTIONS, &option_index)) != -1) {
15.	    switch (arg) {
16.	      case 't':
17.	        show_text = true;
18.	        break;
19.	      case 0: {
20.	        std::string option = OPTIONS[option_index].name;
21.	        if (option == "locale") {
22.	          locale = optarg;
23.	        } else if (option == "reason") {
24.	          reason = optarg;
25.	          LOG(INFO) << "reason is [" << reason << "]";
26.	        } else if (option == "fastboot" &&
27.	                   android::base::GetBoolProperty("ro.boot.dynamic_partitions", 					false)) {
28.	          fastboot = true;
29.	        }
30.	        break;
31.	      }
32.	    }
33.	  }
34.	 ……
35.	//while(true)循环体中判断start_recovery()返回参数
36.	  while (true) {
37.	   ……
38.	    auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);
39.	
40.	    if (ret == Device::KEY_INTERRUPTED) {
41.	      ret = action.exchange(ret);
42.	      if (ret == Device::NO_ACTION) {
43.	        continue;
44.	      }
45.	    }
46.	    switch (ret) {
47.	      case Device::SHUTDOWN:
48.	        ui->Print("Shutting down...\n");
49.	        Shutdown("userrequested,recovery");
50.	        break;
51.	……
52.	
53.	      case Device::REBOOT:
54.	        ui->Print("Rebooting...\n");
55.	        Reboot("userrequested,recovery");
56.	        break;
57.	……
58.	    }
59.	  }
60.	
61.	  // Should be unreachable.
62.	  return EXIT_SUCCESS;
63.	}

Bestimmen Sie nach einiger Verarbeitung der Argumentinformationen, ob es sich um StartFastboot() oder start_recovery() handelt. Die Auswahl hier ist start_recovery(). Die Rückgabeparameter von start_recovery() werden im while(true)-Schleifenkörper bestimmt, um den nächsten Schritt auszuführen .

2. Wiederherstellung.cpp

Die start_recovery()-Methode von Recovery.cpp lautet wie folgt:

1.	Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& 		args) {
2.	  static constexpr struct option OPTIONS[] = {
3.	    { "fastboot", no_argument, nullptr, 0 },
4.	 ……
5.	    { "wipe_cache", no_argument, nullptr, 0 },
6.	    { "wipe_data", no_argument, nullptr, 0 },
7.	    { "wipe_package_size", required_argument, nullptr, 0 },
8.	    { nullptr, 0, nullptr, 0 },
9.	  };
10.	  ……
11.	// args参数被处理赋值给args_to_parse变量
12.	  auto args_to_parse = StringVectorToNullTerminatedArray(args);
13.	  int arg;
14.	  int option_index;
15.	//通过getopt_long()对参数进行判断选择对应的操作
16.	  while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", 				OPTIONS, &option_index)) != -1) {
18.	    switch (arg) {
19.	      case 't':
20.	        // Handled in recovery_main.cpp
21.	        break;
22.	      case 'x':
23.	        just_exit = true;
24.	        break;
25.	      case 0: {
26.	        std::string option = OPTIONS[option_index].name;
27.	        if (option == "install_with_fuse") {
28.	          install_with_fuse = true;
29.	        } else if (option == "locale" || option == "fastboot" || option == "reason") {
30.	          // Handled in recovery_main.cpp
31.	  ……
           // 根据传参的内容为 wipe_data 走到这里
32.	        } else if (option == "wipe_data") {
33.	          should_wipe_data = true;
34.	        } else if (option == "wipe_package_size") {
35.	          android::base::ParseUint(optarg, &wipe_package_size);
36.	        }
37.	        break;
38.	      }
39.	      case '?':
40.	        LOG(ERROR) << "Invalid command argument";
41.	        continue;
42.	    }
43.	  }
44.	  optind = 1;
45.	
46.	  // next_action indicates the next target to reboot into upon finishing the install. 			It could be
47.	  // overridden to a different reboot target per user request.
48.	  Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : 						Device::REBOOT;
49.	  ……
50.	
51.	  if (update_package != nullptr) {
52.	    ……
    //走到这里
53.	  } else if (should_wipe_data) {
54.	    save_current_log = true;
55.	    CHECK(device->GetReason().has_value());
56.	    bool convert_fbe = device->GetReason().value() == "convert_fbe";
    // 调用 wipeData函数
57.	    if (!WipeData(device, convert_fbe)) {
58.	      status = INSTALL_ERROR;
59.	    }
60.	  } 
61.	  ……
62.	
63.	error:
64.	  if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
65.	    ui->SetBackground(RecoveryUI::ERROR);
66.	    if (!ui->IsTextVisible()) {
67.	      sleep(5);
68.	    }
69.	  }
70.	……

Wie bei der oben genannten Methode main() wird der Parameter args verarbeitet und der Variablen args_to_parse zugewiesen. Die Parameter werden über getopt_long() beurteilt, um die entsprechende Operation auszuwählen. Wenn option == „wipe_data“, sollte_wipe_data = true sein und dann durch aufgerufen werden Die Beurteilungsvariable Should_wipe_data Die WipeData(device, Convert_fbe)-Methode löscht Benutzerdaten

3. Wipe_data.cpp

Die WipeData()-Methode von Wipe_data.cpp lautet wie folgt:

1.	bool WipeData(Device* device, bool convert_fbe) {
2.	  RecoveryUI* ui = device->GetUI();
3.	  ui->Print("\n-- Wiping data...\n");
4.	//检查更新状态和是否完成合并
5.	  if (!FinishPendingSnapshotMerges(device)) {
6.	    ui->Print("Unable to check update status or complete merge, cannot wipe 				partitions.\n");
7.	    return false;
8.	  }
9.	//调用PreWipeData()做擦除前准备,结果保存到success
10.	  bool success = device->PreWipeData();
11.	  if (success) {
    // 擦除/data分区,按convert_fbe决定是否转换格式
12.	    success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
13.	    bool has_cache = volume_for_mount_point("/cache") != nullptr;
14.	    if (has_cache) {
15.	      success &= EraseVolume(CACHE_ROOT, ui, false);
16.	    }
17.	    if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
18.	      success &= EraseVolume(METADATA_ROOT, ui, false);
19.	    }
20.	  }
21.	  if (success) {
22.	    success &= device->PostWipeData();
23.	  }
24.	  ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
25.	  return success;
26.
27.	}

Die WipeData()-Methode führt Formatierungsvorgänge für die Partitionen /data, /cache und /metadata durch. Hier können Sie die zu formatierende Partition anpassen. Danach gibt start_recovery() Device::REBOOT zurück und das Gerät startet neu.

4. Zusammenfassung

1. Die Anwendung kann die Funktion zur Einstellung des nativen Erscheinungsbilds der Wiederherstellung aktivieren, indem sie den Broadcast android.intent.action.FACTORY_RESET sendet. Natürlich müssen Sie eine Erlaubnis beantragen (android:permission="android.permission.MASTER_CLEAR")
2. Die Klassenübertragung ist in /android/frameworks/base/core/res/AndroidManifest definiert. xml im System Der Empfänger ist MasterClearReceiver
3. Stellen Sie die Werkseinstellungen wieder her. Der Empfänger MasterClearReceiver ist unter /android/frameworks/base/services/core/java/com/android/server definiert. Systemdienste im selben Verzeichnis
4. MasterClearReceiver erstellt während der Broadcast-Empfangsverarbeitung von onReceiver() einen Thread, und dieser Thread ruft die Funktion RecoverySystem.rebootWipeUserData() auf, um Benutzerdaten zu löschen. Der Thr-Thread wird jedoch nicht sofort fortgesetzt. Das System bestimmt anhand des Werts im übergebenen Intent, ob die Daten auf der externen Speicherkarte oder SIM-Karte gelöscht werden müssen. Bei Bedarf wird die Funktion StorageManager.wipeAdoptableDisks() ausgeführt wird aufgerufen, um die externe Speicherkarte oder SIM-Karte zu löschen. SIM-Karten-Datenlöschung
5. RecoverySystem ist in /android/frameworks/base/core/java/android/os/RecoverySystem.java definiert und ist eine funktionale Schnittstelle, die von Android bereitgestellt wird.
6. Die Funktion RecoverySystem.rebootWipeUserData() ruft die Funktion bootCommand() für den nächsten Verarbeitungsschritt auf.
7. Die Funktion RecoverySystem.bootCommand() ruft die Funktion RecoverySystem.rebootRecoveryWithCommand() für die AIDL-Kommunikation auf, um mit dem RecoverySystemService.java-Server zu kommunizieren. Die Hilfsdatei ist /android/frameworks/base/core/java/android/os/IRecoverySystem.aidl und die Serverdatei ist /android/frameworks/base/services/core/java/com/android/server/RecoverySystemService.java 10 gesteuert werden. Die PowerManager.reboot()-Funktion befindet sich in /android/frameworks/base/core/java/android/os / PowerManager.java ist die von Android bereitgestellte Schnittstelle, die Funktion reboot() unterstützt die Kommunikation, der Server ist PowerManagerService.java und der Pfad lautet /android/frameworks/base/services/core/java/com/android/server/ power/PowerManagerService .java. 9. Die Funktion setupOrCleanBcb() Die Eigenschaft ctl.start (setup-bcb oder clear-bcb) wird festgelegt und der Uncrypt-Dienst wird über Socket (/system/bin/uncrypt) kommuniziert. Verschiedene in init.rc definierte Dienste können über das ctl.start-Attribut
8. Die Funktion RecoverySystemService.rebootRecoveryWithCommand() ruft zuerst die Funktion setupOrCleanBcb() und dann die Funktion PowerManager.reboot() auf, um im Wiederherstellungsmodus neu zu starten

11. Wechseln Sie nach dem Neustart in den Wiederherstellungsmodus. Die Wiederherstellung liest den BCB, führt die ausgegebenen Anweisungen aus und führt Vorgänge wie das Wiederherstellen der Werkseinstellungen und das Aktualisieren durch.

12. Nachdem die Ausführung im Wiederherstellungsmodus abgeschlossen ist, wechseln Sie zum Starten zurück zum Hauptsystem.

Supongo que te gusta

Origin blog.csdn.net/Yang_Mao_Shan/article/details/133939560
Recomendado
Clasificación