Das Wiederherstellungssystem enthält mehrere Hooks zum Einfügen gerätespezifischen Codes, sodass OTA-Updates auch andere Teile des Geräts als das Android-System aktualisieren können, z.B. den Baseband- oder Funkprozessor.
In den folgenden Abschnitten und Beispielen wird das tardis-Gerät des Anbieters yoyodyne angepasst.
Partitionsübersicht
Ab Android 2.3 unterstützt die Plattform eMMC-Flash-Geräte und das ext4-Dateisystem, das auf diesen Geräten ausgeführt wird. Außerdem werden MTD-Flash-Geräte (Memory Technology Device) und das yaffs2-Dateisystem aus älteren Versionen unterstützt.
Die Datei mit der Partitionstabelle wird durch TARGET_RECOVERY_FSTAB angegeben. Diese Datei wird sowohl vom Recovery-Binärprogramm als auch von den Paket-Build-Tools verwendet. Sie können den Namen der Map-Datei in TARGET_RECOVERY_FSTAB in BoardConfig.mk angeben.
Eine Beispiel-Partitionszuordnungsdatei könnte so aussehen:
device/yoyodyne/tardis/recovery.fstab
# mount point fstype device [device2] [options (3.0+ only)] /sdcard vfat /dev/block/mmcblk0p1 /dev/block/mmcblk0 /cache yaffs2 cache /misc mtd misc /boot mtd boot /recovery emmc /dev/block/platform/s3c-sdhci.0/by-name/recovery /system ext4 /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096 /data ext4 /dev/block/platform/s3c-sdhci.0/by-name/userdata
Mit Ausnahme von /sdcard, das optional ist, müssen alle Bereitstellungspunkte in diesem Beispiel definiert werden. Geräte können auch zusätzliche Partitionen hinzufügen. Es gibt fünf unterstützte Dateisystemtypen:
- yaffs2
-
Ein yaffs2-Dateisystem auf einem MTD-Flash-Gerät. „device“ muss der Name der MTD-Partition sein und in
/proc/mtderscheinen. - MTD
-
Eine MTD-Rohpartition, die für bootfähige Partitionen wie „boot“ und „recovery“ verwendet wird. MTD wird nicht tatsächlich bereitgestellt, aber der Bereitstellungspunkt wird als Schlüssel verwendet, um die Partition zu finden. „device“
muss der Name der MTD-Partition in
/proc/mtdsein. - ext4
- Ein ext4-Dateisystem auf einem eMMC-Flashgerät. „device“ muss der Pfad des Blockgeräts sein.
- eMMC
- Ein rohes eMMC-Blockgerät, das für bootfähige Partitionen wie „boot“ und „recovery“ verwendet wird. Ähnlich wie beim Typ „mtd“ wird „eMMc“ nie tatsächlich bereitgestellt, aber der Bereitstellungspunkt-String wird verwendet, um das Gerät in der Tabelle zu finden.
- vfat
-
Ein FAT-Dateisystem auf einem Blockgerät, in der Regel für externen Speicher wie eine SD-Karte. Das Gerät ist das Blockgerät. „device2“ ist ein zweites Blockgerät, das das System zu mounten versucht, wenn das Mounten des primären Geräts fehlschlägt (zur Kompatibilität mit SD-Karten, die möglicherweise mit einer Partitionstabelle formatiert sind).
Alle Partitionen müssen im Stammverzeichnis gemountet werden. Der Mount-Point-Wert muss also mit einem Schrägstrich beginnen und darf keine weiteren Schrägstriche enthalten. Diese Einschränkung gilt nur für das Bereitstellen von Dateisystemen im Wiederherstellungsmodus. Das Hauptsystem kann sie überall bereitstellen. Die Verzeichnisse
/boot,/recoveryund/miscsollten Rohdatentypen (mtd oder emmc) sein, während die Verzeichnisse/system,/data,/cacheund/sdcard(falls verfügbar) Dateisystemtypen (yaffs2, ext4 oder vfat) sein sollten.
Ab Android 3.0 enthält die Datei „recovery.fstab“ ein zusätzliches optionales Feld: options. Derzeit ist nur die Option length definiert, mit der Sie die Länge der Partition explizit angeben können. Diese Länge wird beim Formatieren der Partition verwendet, z.B. für die Nutzerdatenpartition während eines Vorgangs zum Löschen von Daten/Zurücksetzen auf die Werkseinstellungen oder für die Systempartition während der Installation eines vollständigen OTA-Pakets. Wenn der Längenwert negativ ist, wird die zu formatierende Größe durch Addieren des Längenwerts zur tatsächlichen Partitionsgröße ermittelt. Wenn Sie beispielsweise „length=-16384“ festlegen, werden die letzten 16 KB dieser Partition beim Formatieren dieser Partition nicht überschrieben. Dies unterstützt Funktionen wie die Verschlüsselung der Nutzerdatenpartition, bei der Verschlüsselungsmetadaten am Ende der Partition gespeichert werden, die nicht überschrieben werden sollten.
Hinweis:Die Felder device2 und options sind optional, was beim Parsen zu Unklarheiten führen kann. Wenn der Eintrag im vierten Feld der Zeile mit dem Zeichen „/“ beginnt, wird er als device2-Eintrag betrachtet. Wenn der Eintrag nicht mit dem Zeichen „/“ beginnt, wird er als options-Feld betrachtet.
Startanimation
Gerätehersteller können die Animation anpassen, die beim Booten eines Android-Geräts angezeigt wird. Erstellen Sie dazu eine ZIP-Datei, die gemäß den Spezifikationen im Bootanimationsformat organisiert und platziert ist.
Bei Android Things-Geräten können Sie die gezippte Datei in der Android Things Console hochladen, damit die Bilder in das ausgewählte Produkt aufgenommen werden.
Hinweis:Diese Bilder müssen den Android-Markenrichtlinien entsprechen. Markenrichtlinien finden Sie im Partner Marketing Hub im Bereich „Android“.
Wiederherstellungs-UI
Um Geräte mit unterschiedlicher verfügbarer Hardware (physische Tasten, LEDs, Bildschirme usw.) zu unterstützen, können Sie die Wiederherstellungsoberfläche anpassen, um den Status anzuzeigen und auf die manuell bedienten verborgenen Funktionen für jedes Gerät zuzugreifen.
Ihr Ziel ist es, eine kleine statische Bibliothek mit einigen C++-Objekten zu erstellen, um die gerätespezifischen Funktionen bereitzustellen. Die Datei bootable/recovery/default_device.cpp wird standardmäßig verwendet und ist ein guter Ausgangspunkt zum Kopieren, wenn Sie eine Version dieser Datei für Ihr Gerät schreiben.
Hinweis:Möglicherweise wird hier die Meldung Kein Befehl angezeigt. Wenn Sie Text ein- oder ausblenden möchten, halten Sie die Ein/Aus-Taste gedrückt und drücken Sie die Lautertaste. Wenn Ihr Gerät nicht beide Tasten hat, halten Sie eine beliebige Taste gedrückt, um den Text umzuschalten.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h> #include "common.h" #include "device.h" #include "screen_ui.h"
Funktionen für Kopfzeilen und Elemente
Die Geräteklasse erfordert Funktionen zum Zurückgeben von Headern und Elementen, die im ausgeblendeten Wiederherstellungsmenü angezeigt werden. In den Headern wird beschrieben, wie das Menü bedient wird, z.B. mit welchen Steuerelementen das markierte Element geändert oder ausgewählt wird.
static const char* HEADERS[] = { "Volume up/down to move highlight;", "power button to select.", "", NULL }; static const char* ITEMS[] = {"reboot system now", "apply update from ADB", "wipe data/factory reset", "wipe cache partition", NULL };
Hinweis:Lange Zeilen werden abgeschnitten (nicht umgebrochen). Achten Sie daher auf die Breite des Gerätebildschirms.
CheckKey anpassen
Definieren Sie als Nächstes die RecoveryUI-Implementierung Ihres Geräts. In diesem Beispiel wird davon ausgegangen, dass das Gerät tardis ein Display hat. Sie können also von der integrierten ScreenRecoveryUI-Implementierung erben (siehe Anleitung für Geräte ohne Display). Die einzige Funktion, die in ScreenRecoveryUI angepasst werden kann, ist CheckKey(). Sie übernimmt die erste asynchrone Schlüsselverarbeitung:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
KEY-Konstanten
Die KEY_*-Konstanten sind in linux/input.h definiert. CheckKey() wird unabhängig davon aufgerufen, was im Rest der Wiederherstellung passiert: wenn das Menü deaktiviert ist, wenn es aktiviert ist, während der Paketinstallation, während des Löschens von Nutzerdaten usw. Es kann eine von vier Konstanten zurückgeben:
- UMSCHALTEN Anzeige des Menüs und/oder des Textlogs ein- oder ausschalten
- REBOOT Gerät sofort neu starten
- IGNORIEREN. Diesen Tastendruck ignorieren
- ENQUEUE. Stelle diesen Tastendruck in die Warteschlange, damit er synchron verarbeitet wird (d. h. vom Wiederherstellungsmenüsystem, wenn das Display aktiviert ist).
CheckKey() wird jedes Mal aufgerufen, wenn auf ein Key-Down-Ereignis ein Key-Up-Ereignis für dieselbe Taste folgt. (Die Ereignisabfolge A-down B-down B-up A-up führt nur dazu, dass CheckKey(B) aufgerufen wird.) CheckKey() kann IsKeyPressed() aufrufen, um herauszufinden, ob andere Tasten gedrückt werden. (In der oben genannten Abfolge von Schlüsselereignissen hätte CheckKey(B) bei einem Aufruf von IsKeyPressed(A) „true“ zurückgegeben.)
CheckKey() kann den Status in seiner Klasse beibehalten. Das kann nützlich sein, um Tastenkombinationen zu erkennen. Dieses Beispiel zeigt eine etwas komplexere Einrichtung: Das Display wird durch Gedrückthalten der Ein/Aus-Taste und Drücken der Lauter-Taste umgeschaltet. Das Gerät kann sofort neu gestartet werden, indem die Ein/Aus-Taste fünfmal hintereinander gedrückt wird (ohne dass andere Tasten dazwischen gedrückt werden):
class TardisUI : public ScreenRecoveryUI { private: int consecutive_power_keys; public: TardisUI() : consecutive_power_keys(0) {} virtual KeyAction CheckKey(int key) { if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { return TOGGLE; } if (key == KEY_POWER) { ++consecutive_power_keys; if (consecutive_power_keys >= 5) { return REBOOT; } } else { consecutive_power_keys = 0; } return ENQUEUE; } };
ScreenRecoveryUI
Wenn Sie eigene Bilder (Fehlersymbol, Installationsanimation, Fortschrittsbalken) mit ScreenRecoveryUI verwenden, können Sie die Variable animation_fps festlegen, um die Geschwindigkeit von Animationen in Bildern pro Sekunde (FPS) zu steuern.
Hinweis:Mit dem aktuellen interlace-frames.py-Script können Sie die animation_fps-Informationen im Bild selbst speichern. In früheren Versionen von Android war es erforderlich, animation_fps selbst festzulegen.
Um die Variable animation_fps festzulegen, überschreiben Sie die Funktion ScreenRecoveryUI::Init() in Ihrer Unterklasse. Legen Sie den Wert fest und rufen Sie dann die Funktion parent Init() auf, um die Initialisierung abzuschließen. Der Standardwert (20 FPS) entspricht den Standardbildern für die Wiederherstellung. Wenn Sie diese Bilder verwenden, müssen Sie keine Init()-Funktion angeben. Weitere Informationen zu Bildern finden Sie unter Bilder für die Wiederherstellungs-UI.
Geräteklasse
Nachdem Sie eine RecoveryUI-Implementierung haben, definieren Sie Ihre Geräteklasse (abgeleitet von der integrierten Geräteklasse). Es sollte eine einzelne Instanz Ihrer UI-Klasse erstellt und von der Funktion GetUI() zurückgegeben werden:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
StartRecovery
Die Methode StartRecovery() wird zu Beginn der Wiederherstellung aufgerufen, nachdem die Benutzeroberfläche initialisiert und die Argumente geparst wurden, aber bevor eine Aktion ausgeführt wurde. Die Standardimplementierung führt keine Aktion aus. Sie müssen diese Methode also nicht in Ihrer abgeleiteten Klasse bereitstellen, wenn Sie nichts zu tun haben:
void StartRecovery() {
// ... do something tardis-specific here, if needed ....
}Wiederherstellungsmenü bereitstellen und verwalten
Das System ruft zwei Methoden auf, um die Liste der Kopfzeilen und die Liste der Elemente abzurufen. In dieser Implementierung werden die statischen Arrays zurückgegeben, die oben in der Datei definiert sind:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
HandleMenuKey
Als Nächstes müssen Sie eine HandleMenuKey()-Funktion bereitstellen, die eine Tasteneingabe und die aktuelle Menüsichtbarkeit entgegennimmt und entscheidet, welche Aktion ausgeführt werden soll:
int HandleMenuKey(int key, int visible) { if (visible) { switch (key) { case KEY_VOLUMEDOWN: return kHighlightDown; case KEY_VOLUMEUP: return kHighlightUp; case KEY_POWER: return kInvokeItem; } } return kNoAction; }
Die Methode verwendet einen Tastencode (der zuvor von der CheckKey()-Methode des UI-Objekts verarbeitet und in die Warteschlange gestellt wurde) und den aktuellen Status der Sichtbarkeit des Menüs/Textlogs. Der Rückgabewert ist eine Ganzzahl. Wenn der Wert 0 oder höher ist, wird er als Position eines Menüelements betrachtet, das sofort aufgerufen wird (siehe die Methode InvokeMenuItem() unten). Andernfalls kann es sich um eine der folgenden vordefinierten Konstanten handeln:
- kHighlightUp. Menümarkierung zum vorherigen Element verschieben
- kHighlightDown. Menümarkierung zum nächsten Element verschieben
- kInvokeItem. Aktuell markiertes Element aufrufen
- kNoAction Nichts tun
Wie das sichtbare Argument impliziert, wird HandleMenuKey() auch dann aufgerufen, wenn das Menü nicht sichtbar ist. Im Gegensatz zu CheckKey() wird sie nicht aufgerufen, während die Wiederherstellung etwas ausführt, z. B. Daten löschen oder ein Paket installieren. Sie wird nur aufgerufen, wenn die Wiederherstellung im Leerlauf ist und auf Eingabe wartet.
Trackball-Mechanismen
Wenn Ihr Gerät über einen Trackball-ähnlichen Eingabemechanismus verfügt (der Eingabeereignisse mit dem Typ EV_REL und dem Code REL_Y generiert), werden in der Wiederherstellung KEY_UP- und KEY_DOWN-Tastendrücke synthetisiert, wenn das Trackball-ähnliche Eingabegerät eine Bewegung auf der Y-Achse meldet. Sie müssen lediglich die Ereignisse KEY_UP und KEY_DOWN den Menüaktionen zuordnen. Diese Zuordnung erfolgt nicht für CheckKey(). Daher können Sie Trackball-Bewegungen nicht als Trigger für das Neustarten oder Umschalten der Anzeige verwenden.
Modifikatortasten
Wenn Sie prüfen möchten, ob Tasten als Modifizierer gedrückt werden, rufen Sie die IsKeyPressed() -Methode Ihres eigenen UI-Objekts auf. Auf einigen Geräten wurde beispielsweise durch Drücken von Alt + W im Wiederherstellungsmodus das Löschen von Daten gestartet, unabhängig davon, ob das Menü sichtbar war oder nicht. So könnte die Implementierung aussehen:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
Hinweis:Wenn visible auf „false“ gesetzt ist, ist es nicht sinnvoll, die speziellen Werte zurückzugeben, mit denen das Menü bearbeitet wird (Hervorhebung verschieben, hervorgehobenes Element aufrufen), da der Nutzer die Hervorhebung nicht sehen kann. Sie können die Werte jedoch bei Bedarf zurückgeben.
InvokeMenuItem
Als Nächstes müssen Sie eine InvokeMenuItem()-Methode bereitstellen, die Ganzzahlpositionen im Array der von GetMenuItems() zurückgegebenen Elemente Aktionen zuordnet. Für das Array von Elementen im Beispiel „Tardis“ verwenden Sie:
BuiltinAction InvokeMenuItem(int menu_position) { switch (menu_position) { case 0: return REBOOT; case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; default: return NO_ACTION; } }
Diese Methode kann ein beliebiges Element der BuiltinAction-Enumeration zurückgeben, um dem System mitzuteilen, dass es diese Aktion ausführen soll (oder das NO_ACTION-Element, wenn das System nichts tun soll). Hier können Sie zusätzliche Wiederherstellungsfunktionen bereitstellen, die über die im System enthaltenen Funktionen hinausgehen: Fügen Sie dafür einen Eintrag in Ihr Menü ein, führen Sie ihn hier aus, wenn dieser Menüpunkt aufgerufen wird, und geben Sie NO_ACTION zurück, damit das System nichts anderes tut.
„BuiltinAction“ enthält die folgenden Werte:
- NO_ACTION. Nichts unternehmen.
- REBOOT Beenden Sie den Wiederherstellungsmodus und starten Sie das Gerät normal neu.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Installieren Sie ein Updatepaket von verschiedenen Orten. Weitere Informationen finden Sie unter Sideloading.
- WIPE_CACHE Formatieren Sie nur die Cache-Partition neu. Keine Bestätigung erforderlich, da dies relativ harmlos ist.
- WIPE_DATA Formatieren Sie die Partitionen „userdata“ und „cache“ neu. Dies wird auch als Zurücksetzen auf die Werkseinstellungen bezeichnet. Der Nutzer wird aufgefordert, diese Aktion zu bestätigen, bevor er fortfahren kann.
Die letzte Methode, WipeData(), ist optional und wird immer dann aufgerufen, wenn ein Vorgang zum Löschen von Daten initiiert wird (entweder über die Wiederherstellung über das Menü oder wenn der Nutzer das Zurücksetzen auf die Werkseinstellungen über das Hauptsystem ausgewählt hat). Diese Methode wird aufgerufen, bevor die Partitionen für Nutzerdaten und Cache gelöscht werden. Wenn auf Ihrem Gerät Nutzerdaten an anderen Stellen als auf diesen beiden Partitionen gespeichert sind, sollten Sie sie hier löschen. Sie sollten 0 zurückgeben, um den Erfolg anzuzeigen, und einen anderen Wert für einen Fehler. Der Rückgabewert wird derzeit jedoch ignoriert. Die Partitionen für Nutzerdaten und Cache werden gelöscht, unabhängig davon, ob Sie „Erfolg“ oder „Fehler“ zurückgeben.
int WipeData() {
// ... do something tardis-specific here, if needed ....
return 0;
}Gerät erstellen
Fügen Sie schließlich am Ende der Datei „recovery_ui.cpp“ etwas Boilerplate-Code für die Funktion make_device() ein, mit der eine Instanz Ihrer Geräteklasse erstellt und zurückgegeben wird:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Gerätewiederherstellung erstellen und verknüpfen
Nachdem Sie die Datei „recovery_ui.cpp“ fertiggestellt haben, erstellen Sie sie und verknüpfen Sie sie mit der Wiederherstellung auf Ihrem Gerät. Erstellen Sie in Android.mk eine statische Bibliothek, die nur diese C++-Datei enthält:
device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += bootable/recovery LOCAL_SRC_FILES := recovery_ui.cpp # should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk LOCAL_MODULE := librecovery_ui_tardis include $(BUILD_STATIC_LIBRARY)
Geben Sie dann in der Boardkonfiguration für dieses Gerät Ihre statische Bibliothek als Wert von TARGET_RECOVERY_UI_LIB an.
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
Bilder der Wiederherstellungs-UI
Die Benutzeroberfläche für die Wiederherstellung besteht aus Bildern. Im Idealfall interagieren Nutzer nie mit der Benutzeroberfläche: Bei einem normalen Update wird das Smartphone im Wiederherstellungsmodus gestartet, der Fortschrittsbalken für die Installation wird gefüllt und das Smartphone wird ohne Eingabe des Nutzers wieder im neuen System gestartet. Bei Problemen mit Systemupdates kann der Nutzer nur den Kundenservice anrufen.
Bei einer reinen Bildoberfläche ist keine Lokalisierung erforderlich. Ab Android 5.0 kann mit dem Update jedoch auch ein Textstring (z. B. „Systemupdate wird installiert…“) zusammen mit dem Bild angezeigt werden. Weitere Informationen finden Sie unter Lokalisierter Wiederherstellungstext.
Android 5.0 und höher
Die Wiederherstellungsoberfläche von Android 5.0 und höher verwendet zwei Hauptbilder: das Fehlerbild und die Installationsanimation.
|
Abbildung 1. icon_error.png |
Abbildung 2. icon_installing.png |
Die Installationsanimation wird als einzelnes PNG-Bild mit verschiedenen Frames der Animation dargestellt, die zeilenweise verschachtelt sind. Daher sieht Abbildung 2 zusammengedrückt aus. Für eine 200 × 200 Pixel große Animation mit sieben Frames erstellen Sie beispielsweise ein einzelnes Bild mit 200 × 1.400 Pixeln, wobei der erste Frame die Zeilen 0, 7, 14, 21 usw. umfasst, der zweite Frame die Zeilen 1, 8, 15, 22 usw. und so weiter. Das kombinierte Bild enthält einen Textblock, der die Anzahl der Animationsframes und die Anzahl der Frames pro Sekunde (FPS) angibt. Das Tool bootable/recovery/interlace-frames.py nimmt eine Reihe von Eingabe-Frames entgegen und kombiniert sie zu dem für die Wiederherstellung erforderlichen zusammengesetzten Bild.
Standardbilder sind in verschiedenen Dichten verfügbar und befinden sich in bootable/recovery/res-$DENSITY/images (z. B. bootable/recovery/res-hdpi/images). Wenn Sie während der Installation ein statisches Bild verwenden möchten, müssen Sie nur das Bild „icon_installing.png“ bereitstellen und die Anzahl der Frames in der Animation auf 0 festlegen. Das Fehlersymbol ist nicht animiert, sondern immer ein statisches Bild.
Android 4.x und niedriger
In der Wiederherstellungs-UI von Android 4.x und früher werden das Fehler-Bild (siehe oben) und die Installieren-Animation sowie mehrere Overlay-Bilder verwendet:
|
Abbildung 3. icon_installing.png |
Abbildung 4. icon-installing_overlay01.png |
|
Abbildung 5. icon_installing_overlay07.png |
Während der Installation wird das On-Screen-Display erstellt, indem zuerst das Bild „icon_installing.png“ gezeichnet und dann einer der Overlay-Frames mit dem richtigen Offset darübergelegt wird. Hier ist ein rotes Rechteck eingeblendet, um zu verdeutlichen, wo das Overlay über dem Basisbild platziert wird:
Abbildung 6. Animationsframe 1 wird installiert (icon_installing.png + icon_installing_overlay01.png) |
Abbildung 7. Animationsframe 7 wird installiert (icon_installing.png + icon_installing_overlay07.png) |
Nachfolgende Frames werden angezeigt, indem nur das nächste Overlay-Bild über das bereits vorhandene Bild gezeichnet wird. Das Basisbild wird nicht neu gezeichnet.
Die Anzahl der Frames in der Animation, die gewünschte Geschwindigkeit sowie die X- und Y-Offsets des Overlays relativ zur Basis werden durch Mitgliedsvariablen der Klasse „ScreenRecoveryUI“ festgelegt. Wenn Sie benutzerdefinierte Bilder anstelle von Standardbildern verwenden, überschreiben Sie die Methode Init() in Ihrer abgeleiteten Klasse, um diese Werte für Ihre benutzerdefinierten Bilder zu ändern (weitere Informationen finden Sie unter ScreenRecoveryUI). Das Skript bootable/recovery/make-overlay.py kann beim Konvertieren einer Reihe von Bildframes in das für die Wiederherstellung erforderliche Format „Basisbild + Overlay-Bilder“ helfen, einschließlich der Berechnung der erforderlichen Offsets.
Standardbilder befinden sich in bootable/recovery/res/images. Wenn Sie während der Installation ein statisches Bild verwenden möchten, müssen Sie nur das Bild „icon_installing.png“ bereitstellen und die Anzahl der Frames in der Animation auf 0 festlegen. Das Fehlersymbol ist nicht animiert, sondern immer ein statisches Bild.
Lokalisierter Wiederherstellungstext
Unter Android 5.x wird neben dem Bild ein Textstring angezeigt, z. B. „Systemupdate wird installiert…“. Wenn das Hauptsystem im Wiederherstellungsmodus gestartet wird, wird das aktuelle Gebietsschema des Nutzers als Befehlszeilenoption an die Wiederherstellung übergeben. Für jede anzuzeigende Nachricht enthält die Wiederherstellung ein zweites zusammengesetztes Bild mit vorgerenderten Textstrings für diese Nachricht in jeder Sprache.
Beispielbild für Wiederherstellungstextstrings:
Abbildung 8. Lokalisierter Text für Wiederherstellungsnachrichten
Im Wiederherstellungstext können die folgenden Meldungen angezeigt werden:
- Systemupdate wird installiert…
- Fehler!
- Wird gelöscht… (beim Löschen von Daten/Zurücksetzen auf die Werkseinstellungen)
- Kein Befehl (wenn ein Nutzer das Wiederherstellungsmenü manuell aufruft)
Die Android-App in bootable/recovery/tools/recovery_l10n/ rendert Lokalisierungen einer Nachricht und erstellt das zusammengesetzte Bild. Weitere Informationen zur Verwendung dieser App finden Sie in den Kommentaren in bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java.
Wenn ein Nutzer das Recovery-Menü manuell aufruft, ist das Gebietsschema möglicherweise nicht verfügbar und es wird kein Text angezeigt. Die SMS dürfen nicht entscheidend für den Wiederherstellungsprozess sein.
Hinweis:Die ausgeblendete Benutzeroberfläche, in der Protokollmeldungen angezeigt werden und der Nutzer Aktionen aus dem Menü auswählen kann, ist nur auf Englisch verfügbar.
Fortschrittsanzeigen
Fortschrittsbalken können unter dem Hauptbild (oder der Animation) angezeigt werden. Die Fortschrittsanzeige wird durch die Kombination von zwei Eingabebildern erstellt, die dieselbe Größe haben müssen:
Abbildung 9. progress_empty.png
Abbildung 10. progress_fill.png
Das linke Ende des fill-Bildes wird neben dem rechten Ende des empty-Bildes angezeigt, um die Fortschrittsanzeige zu erstellen. Die Position der Grenze zwischen den beiden Bildern wird geändert, um den Fortschritt anzuzeigen. Bei den oben genannten Paaren von Eingabebildern wird beispielsweise Folgendes angezeigt:
Abbildung 11. Fortschrittsanzeige bei 1%>
Abbildung 12. Fortschrittsanzeige bei 10%
Abbildung 13. Fortschrittsanzeige bei 50%
Sie können gerätespezifische Versionen dieser Bilder bereitstellen, indem Sie sie (in diesem Beispiel) in device/yoyodyne/tardis/recovery/res/images platzieren. Die Dateinamen müssen mit den oben aufgeführten übereinstimmen. Wenn eine Datei in diesem Verzeichnis gefunden wird, verwendet das Build-System sie anstelle des entsprechenden Standardbilds. Es werden nur PNGs im RGB- oder RGBA-Format mit 8‑Bit-Farbtiefe unterstützt.
Hinweis:In Android 5.x wird der Fortschrittsbalken von rechts nach links gefüllt, wenn das Gebietsschema für die Wiederherstellung bekannt ist und es sich um eine linksläufige Sprache (z. B. Arabisch oder Hebräisch) handelt.
Geräte ohne Display
Nicht alle Android-Geräte haben einen Bildschirm. Wenn es sich bei Ihrem Gerät um ein Headless-Gerät oder ein Gerät mit einer reinen Audio-Schnittstelle handelt, müssen Sie die Wiederherstellungs-UI möglicherweise umfangreicher anpassen. Anstatt eine Unterklasse von ScreenRecoveryUI zu erstellen, erstellen Sie direkt eine Unterklasse der übergeordneten Klasse RecoveryUI.
RecoveryUI bietet Methoden zum Verarbeiten von UI-Vorgängen auf niedrigerer Ebene, z. B. „Display umschalten“, „Fortschrittsanzeige aktualisieren“, „Menü anzeigen“ oder „Menüauswahl ändern“. Sie können diese überschreiben, um eine geeignete Benutzeroberfläche für Ihr Gerät bereitzustellen. Vielleicht hat Ihr Gerät LEDs, mit denen Sie den Status durch verschiedene Farben oder Blinkmuster anzeigen können, oder vielleicht können Sie Audio abspielen. Vielleicht möchten Sie ein Menü oder den Modus „Textanzeige“ gar nicht unterstützen. Sie können den Zugriff darauf mit CheckKey()- und HandleMenuKey()-Implementierungen verhindern, die das Display nie ein- oder einen Menüpunkt auswählen. In diesem Fall können viele der RecoveryUI-Methoden, die Sie bereitstellen müssen, einfach leere Stubs sein.)
In der Deklaration von RecoveryUI unter bootable/recovery/ui.h sehen Sie, welche Methoden Sie unterstützen müssen. RecoveryUI ist abstrakt – einige Methoden sind rein virtuell und müssen von Unterklassen bereitgestellt werden –, enthält aber den Code für die Verarbeitung von Tasteneingaben. Sie können das auch überschreiben, wenn Ihr Gerät keine Tasten hat oder Sie sie anders verarbeiten möchten.
Updater
Sie können gerätespezifischen Code in die Installation des Updatepakets einfügen, indem Sie eigene Erweiterungsfunktionen bereitstellen, die aus Ihrem Updater-Script aufgerufen werden können. Hier ist ein Beispiel für eine Funktion für das Tardis-Gerät:
device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h> #include <string.h> #include "edify/expr.h"
Jede Erweiterungsfunktion hat dieselbe Signatur. Die Argumente sind der Name, mit dem die Funktion aufgerufen wurde, ein State*-Cookie, die Anzahl der eingehenden Argumente und ein Array von Expr*-Zeigern, die die Argumente darstellen. Der Rückgabewert ist ein neu zugewiesenes Value*.
Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); }
Ihre Argumente wurden zum Zeitpunkt des Aufrufs Ihrer Funktion noch nicht ausgewertet. Die Logik Ihrer Funktion bestimmt, welche Argumente wie oft ausgewertet werden. So können Sie mit Erweiterungsfunktionen eigene Kontrollstrukturen implementieren. Call Evaluate() zum Auswerten eines Expr* -Arguments, das einen Value* zurückgibt. Wenn Evaluate() NULL zurückgibt, sollten Sie alle Ressourcen freigeben, die Sie verwenden, und sofort NULL zurückgeben. Dadurch werden Abbrüche im Edify-Stack nach oben weitergegeben. Andernfalls übernehmen Sie die Verantwortung für den zurückgegebenen Wert und sind dafür verantwortlich, dass FreeValue() für ihn aufgerufen wird.
Angenommen, die Funktion benötigt zwei Argumente: einen Stringwert key und einen Blobwert image. Sie könnten Argumente so lesen:
Value* key = EvaluateValue(state, argv[0]); if (key == NULL) { return NULL; } if (key->type != VAL_STRING) { ErrorAbort(state, "first arg to %s() must be string", name); FreeValue(key); return NULL; } Value* image = EvaluateValue(state, argv[1]); if (image == NULL) { FreeValue(key); // must always free Value objects return NULL; } if (image->type != VAL_BLOB) { ErrorAbort(state, "second arg to %s() must be blob", name); FreeValue(key); FreeValue(image) return NULL; }
Das Prüfen auf NULL und das Freigeben zuvor ausgewerteter Argumente kann bei mehreren Argumenten mühsam werden. Die Funktion ReadValueArgs() kann dies vereinfachen. Statt des oben stehenden Codes hätten Sie Folgendes schreiben können:
Value* key; Value* image; if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { return NULL; // ReadValueArgs() will have set the error message } if (key->type != VAL_STRING || image->type != VAL_BLOB) { ErrorAbort(state, "arguments to %s() have wrong type", name); FreeValue(key); FreeValue(image) return NULL; }
ReadValueArgs() führt keine Typüberprüfung durch. Das müssen Sie hier tun. Es ist einfacher, dies mit einer if-Anweisung zu erledigen, auch wenn im Fehlerfall eine etwas weniger spezifische Fehlermeldung ausgegeben wird. ReadValueArgs() übernimmt jedoch die Auswertung der einzelnen Argumente und die Freigabe aller zuvor ausgewerteten Argumente (sowie die Festlegung einer nützlichen Fehlermeldung), wenn eine der Auswertungen fehlschlägt. Sie können eine ReadValueVarArgs()-Hilfsfunktion verwenden, um eine variable Anzahl von Argumenten auszuwerten. Sie gibt ein Array von Value* zurück.
Nachdem die Argumente ausgewertet wurden, wird die Funktion ausgeführt:
// key->data is a NUL-terminated string
// image->data and image->size define a block of binary data
//
// ... some device-specific magic here to
// reprogram the tardis using those two values ...
Der Rückgabewert muss ein Value*-Objekt sein. Die Inhaberschaft dieses Objekts wird an den Aufrufer übertragen. Der Aufrufer übernimmt die Verantwortung für alle Daten, auf die durch diese Value* verwiesen wird, insbesondere für das Datenelement.
In diesem Fall möchten Sie einen „true“- oder „false“-Wert zurückgeben, um den Erfolg anzugeben. Denken Sie daran, dass der leere String false und alle anderen Strings true sind. Sie müssen ein Value-Objekt mit einer malloc-Kopie des zurückzugebenden konstanten Strings malloc-en, da der Aufrufer beides free() wird. Vergessen Sie nicht, FreeValue() für die Objekte aufzurufen, die Sie durch die Auswertung Ihrer Argumente erhalten haben.
FreeValue(key); FreeValue(image); Value* result = malloc(sizeof(Value)); result->type = VAL_STRING; result->data = strdup(successful ? "t" : ""); result->size = strlen(result->data); return result; }
Die Convenience-Funktion StringValue() umschließt einen String in einem neuen Value-Objekt.
Verwenden Sie Folgendes, um den oben genannten Code prägnanter zu schreiben:
FreeValue(key);
FreeValue(image);
return StringValue(strdup(successful ? "t" : ""));
}
Um Funktionen in den Edify-Interpreter einzubinden, stellen Sie die Funktion Register_foo bereit, wobei foo der Name der statischen Bibliothek ist, die diesen Code enthält. Rufen Sie RegisterFunction() auf, um jede Erweiterungsfunktion zu registrieren. Konventionsgemäß sollten gerätespezifische Funktionen device.whatever benannt werden, um Konflikte mit zukünftigen integrierten Funktionen zu vermeiden.
void Register_librecovery_updater_tardis() {
RegisterFunction("tardis.reprogram", ReprogramTardisFn);
}Sie können das Makefile jetzt so konfigurieren, dass eine statische Bibliothek mit Ihrem Code erstellt wird. (Dies ist dasselbe Makefile, das im vorherigen Abschnitt zum Anpassen der Wiederherstellungs-UI verwendet wurde. Auf Ihrem Gerät sind möglicherweise beide statischen Bibliotheken definiert.)
device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS) LOCAL_SRC_FILES := recovery_updater.c LOCAL_C_INCLUDES += bootable/recovery
Der Name der statischen Bibliothek muss mit dem Namen der darin enthaltenen Register_libname-Funktion übereinstimmen.
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
Konfigurieren Sie schließlich den Build der Wiederherstellung, um Ihre Bibliothek abzurufen. Fügen Sie Ihre Bibliothek zu TARGET_RECOVERY_UPDATER_LIBS hinzu. Diese Variable kann mehrere Bibliotheken enthalten, die alle registriert werden.
Wenn Ihr Code von anderen statischen Bibliotheken abhängt, die selbst keine Edify-Erweiterungen sind (d.h.
Sie können diese in TARGET_RECOVERY_UPDATER_EXTRA_LIBS auflisten, um sie mit dem Updater zu verknüpfen, ohne ihre (nicht vorhandene) Registrierungsfunktion aufzurufen.Register_libname Wenn Ihr gerätespezifischer Code beispielsweise zlib zum Dekomprimieren von Daten verwenden soll, würden Sie hier libz einfügen.
device/yoyodyne/tardis/BoardConfig.mk
[...] # add device-specific extensions to the updater binary TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis TARGET_RECOVERY_UPDATER_EXTRA_LIBS +=
Die Updater-Skripts in Ihrem OTA-Paket können Ihre Funktion jetzt wie jede andere aufrufen. Um Ihr Tardis-Gerät neu zu programmieren, kann das Aktualisierungsskript Folgendes enthalten:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . Dazu wird die Version der integrierten Funktion package_extract_file() mit einem Argument verwendet, die den Inhalt einer aus dem Update-Paket extrahierten Datei als Blob zurückgibt, um das zweite Argument für die neue Erweiterungsfunktion zu erstellen.
OTA-Paketgenerierung
Die letzte Komponente besteht darin, die Tools zur Generierung von OTA-Paketen über Ihre gerätespezifischen Daten zu informieren und Updater-Skripts auszugeben, die Aufrufe Ihrer Erweiterungsfunktionen enthalten.
Zuerst muss das Build-System über einen gerätespezifischen Blob mit Daten informiert werden. Angenommen, sich Ihre Datendatei in device/yoyodyne/tardis/tardis.dat befindet, deklarieren Sie Folgendes in der AndroidBoard.mk-Datei Ihres Geräts:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
Sie können sie auch in eine Android.mk-Datei einfügen. In diesem Fall muss sie jedoch durch eine Geräteprüfung geschützt werden, da alle Android.mk-Dateien im Baum geladen werden, unabhängig davon, welches Gerät erstellt wird. Wenn Ihr Baum mehrere Geräte enthält, soll die Datei „tardis.dat“ nur beim Erstellen des Tardis-Geräts hinzugefügt werden.
device/yoyodyne/tardis/Android.mk
[...] # an alternative to specifying it in AndroidBoard.mk ifeq (($TARGET_DEVICE),tardis) $(call add-radio-file,tardis.dat) endif
Diese werden aus historischen Gründen als Funkdateien bezeichnet. Sie haben möglicherweise nichts mit dem Funkmodul des Geräts zu tun (falls vorhanden). Es handelt sich lediglich um undurchsichtige Daten-Blobs, die vom Build-System in die ZIP-Datei „target-files“ kopiert werden, die von den OTA-Generierungstools verwendet wird. Wenn Sie einen Build durchführen, wird „tardis.dat“ als RADIO/tardis.dat in „target-files.zip“ gespeichert. Sie können add-radio-file mehrmals aufrufen, um beliebig viele Dateien hinzuzufügen.
Python-Modul
Wenn Sie die Release-Tools erweitern möchten, schreiben Sie ein Python-Modul (muss „releasetools.py“ heißen), das von den Tools aufgerufen werden kann, sofern es vorhanden ist. Beispiel:
device/yoyodyne/tardis/releasetools.py
import common def FullOTA_InstallEnd(info): # copy the data into the package. tardis_dat = info.input_zip.read("RADIO/tardis.dat") common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
Eine separate Funktion verarbeitet das Generieren eines inkrementellen OTA-Pakets. Angenommen, Sie müssen die Tardis in diesem Beispiel nur dann neu programmieren, wenn sich die Datei „tardis.dat“ zwischen zwei Builds geändert hat.
def IncrementalOTA_InstallEnd(info): # copy the data into the package. source_tardis_dat = info.source_zip.read("RADIO/tardis.dat") target_tardis_dat = info.target_zip.read("RADIO/tardis.dat") if source_tardis_dat == target_tardis_dat: # tardis.dat is unchanged from previous build; no # need to reprogram it return # include the new tardis.dat in the OTA package common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
Modulfunktionen
Sie können die folgenden Funktionen im Modul bereitstellen (implementieren Sie nur die, die Sie benötigen).
FullOTA_Assertions()- Wird kurz vor dem Beginn der Generierung eines vollständigen OTA aufgerufen. Hier können Sie Behauptungen zum aktuellen Status des Geräts ausgeben. Geben Sie keine Skriptbefehle aus, die Änderungen am Gerät vornehmen.
FullOTA_InstallBegin()- Wird aufgerufen, nachdem alle Zusicherungen zum Gerätestatus bestanden wurden, aber bevor Änderungen vorgenommen wurden. Sie können Befehle für gerätespezifische Updates ausgeben, die ausgeführt werden müssen, bevor etwas anderes auf dem Gerät geändert wird.
FullOTA_InstallEnd()- Wird am Ende der Skripterstellung aufgerufen, nachdem die Skriptbefehle zum Aktualisieren der Boot- und Systempartitionen ausgegeben wurden. Sie können auch zusätzliche Befehle für gerätespezifische Updates ausgeben.
IncrementalOTA_Assertions()-
Ähnlich wie
FullOTA_Assertions(), wird aber beim Generieren eines inkrementellen Updatepakets aufgerufen. IncrementalOTA_VerifyBegin()- Wird aufgerufen, nachdem alle Zusicherungen zum Gerätestatus bestanden wurden, aber bevor Änderungen vorgenommen wurden. Sie können Befehle für gerätespezifische Updates ausgeben, die ausgeführt werden müssen, bevor etwas anderes auf dem Gerät geändert wird.
IncrementalOTA_VerifyEnd()- Wird am Ende der Bestätigungsphase aufgerufen, wenn das Skript bestätigt hat, dass die Dateien, auf die es zugreifen wird, den erwarteten Startinhalt haben. Bisher wurde auf dem Gerät nichts geändert. Sie können auch Code für zusätzliche gerätespezifische Bestätigungen ausgeben.
IncrementalOTA_InstallBegin()- Wird aufgerufen, nachdem die zu patchenden Dateien als Dateien mit dem erwarteten before-Status bestätigt wurden, aber bevor Änderungen vorgenommen wurden. Sie können Befehle für gerätespezifische Updates ausgeben, die ausgeführt werden müssen, bevor etwas anderes auf dem Gerät geändert wird.
IncrementalOTA_InstallEnd()- Ähnlich wie beim entsprechenden vollständigen OTA-Paket wird diese Funktion am Ende der Skripterstellung aufgerufen, nachdem die Skriptbefehle zum Aktualisieren der Boot- und Systempartitionen ausgegeben wurden. Sie können auch zusätzliche Befehle für gerätespezifische Updates ausgeben.
Hinweis:Wenn die Stromversorgung des Geräts unterbrochen wird, kann die OTA-Installation von vorn beginnen. Sie müssen damit rechnen, dass diese Befehle auf Geräten bereits vollständig oder teilweise ausgeführt wurden.
Funktionen an Info-Objekte übergeben
Übergeben Sie Funktionen an ein einzelnes Info-Objekt, das verschiedene nützliche Elemente enthält:
-
info.input_zip. (Nur vollständige OTAs) Das
zipfile.ZipFile-Objekt für die ZIP-Datei mit den Eingabe-Zieldateien. -
info.source_zip. (Nur inkrementelle OTAs) Das
zipfile.ZipFile-Objekt für die ZIP-Datei mit den Quellziel-Dateien (der Build, der sich bereits auf dem Gerät befindet, wenn das inkrementelle Paket installiert wird). -
info.target_zip. (Nur inkrementelle OTAs) Das
zipfile.ZipFile-Objekt für die ZIP-Datei mit den Ziel-Target-Dateien (der Build, den das inkrementelle Paket auf dem Gerät platziert). -
info.output_zip. Das Paket wird erstellt. Ein
zipfile.ZipFile-Objekt wird zum Schreiben geöffnet. Verwenden Sie common.ZipWriteStr(info.output_zip, filename, data), um dem Paket eine Datei hinzuzufügen. -
info.script. Skriptobjekt, dem Sie Befehle hinzufügen können. Rufen Sie
info.script.AppendExtra(script_text)auf, um Text in das Script auszugeben. Achten Sie darauf, dass der Ausgabetext mit einem Semikolon endet, damit er nicht mit nachfolgenden Befehlen kollidiert.
Weitere Informationen zum Info-Objekt finden Sie in der Dokumentation der Python Software Foundation zu ZIP-Archiven.
Modulspeicherort angeben
Geben Sie den Speicherort des Scripts „releasetools.py“ Ihres Geräts in der Datei „BoardConfig.mk“ an:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
Wenn TARGET_RELEASETOOLS_EXTENSIONS nicht festgelegt ist, wird standardmäßig das Verzeichnis $(TARGET_DEVICE_DIR)/../common verwendet (device/yoyodyne/common in diesem Beispiel). Es ist am besten, den Speicherort des Skripts „releasetools.py“ explizit zu definieren.
Beim Erstellen des Tardis-Geräts ist das Skript „releasetools.py“ in der ZIP-Datei mit den Zieldateien (META/releasetools.py ) enthalten.
Wenn Sie die Release-Tools (entweder img_from_target_files oder ota_from_target_files) ausführen, wird das Script „releasetools.py“ in der ZIP-Datei mit den Zieldateien, sofern vorhanden, dem Script aus dem Android-Quellbaum vorgezogen. Sie können den Pfad zu den gerätespezifischen Erweiterungen auch explizit mit der Option -s (oder --device_specific) angeben. Diese hat die höchste Priorität. So können Sie Fehler korrigieren und Änderungen in den Releasetools-Erweiterungen vornehmen und diese Änderungen auf alte Zieldateien anwenden.
Wenn Sie ota_from_target_files ausführen, wird das gerätespezifische Modul automatisch aus der ZIP-Datei „target_files“ abgerufen und beim Generieren von OTA-Paketen verwendet:
./build/make/tools/releasetools/ota_from_target_files \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
Alternativ können Sie gerätespezifische Erweiterungen angeben, wenn Sie ota_from_target_files ausführen.
./build/make/tools/releasetools/ota_from_target_files \
-s device/yoyodyne/tardis \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
Hinweis:Eine vollständige Liste der Optionen finden Sie in den ota_from_target_files-Kommentaren in build/make/tools/releasetools/ota_from_target_files.
Sideloading-Mechanismus
Die Wiederherstellung hat einen Sideloading-Mechanismus zum manuellen Installieren eines Updatepakets, ohne es Over-the-Air über das Hauptsystem herunterzuladen. Sideloading ist nützlich, um Fehler zu beheben oder Änderungen auf Geräten vorzunehmen, auf denen das Hauptsystem nicht gebootet werden kann.
Bisher erfolgte das Sideloading durch das Laden von Paketen von der SD-Karte des Geräts. Wenn das Gerät nicht bootet, kann das Paket mit einem anderen Computer auf die SD-Karte kopiert und die SD-Karte dann in das Gerät eingelegt werden. Für Android-Geräte ohne herausnehmbaren externen Speicher unterstützt die Wiederherstellung zwei zusätzliche Mechanismen für das Sideloading: das Laden von Paketen von der Cache-Partition und das Laden über USB mit ADB.
Um die einzelnen Sideload-Mechanismen aufzurufen, kann die Device::InvokeMenuItem()-Methode Ihres Geräts die folgenden BuiltinAction-Werte zurückgeben:
-
APPLY_EXT. Sideload eines Updatepakets von einem externen Speicher (
/sdcard-Verzeichnis). In Ihrer Datei „recovery.fstab“ muss der Bereitstellungspunkt/sdcarddefiniert sein. Diese Methode kann nicht auf Geräten verwendet werden, die eine SD-Karte mit einem Symlink zu/data(oder einem ähnlichen Mechanismus) emulieren./dataist in der Regel nicht für die Wiederherstellung verfügbar, da die Datei möglicherweise verschlüsselt ist. In der Wiederherstellungs-UI wird ein Menü mit ZIP-Dateien in/sdcardangezeigt, aus dem der Nutzer eine Datei auswählen kann. -
APPLY_CACHE Ähnlich wie beim Laden eines Pakets aus
/sdcard, nur dass stattdessen das Verzeichnis/cacheverwendet wird, das immer für die Wiederherstellung verfügbar ist. Im regulären System kann/cachenur von privilegierten Nutzern geschrieben werden. Wenn das Gerät nicht bootfähig ist, kann das Verzeichnis/cacheüberhaupt nicht geschrieben werden, was diesen Mechanismus nur begrenzt nutzbar macht. -
APPLY_ADB_SIDELOAD Ermöglicht dem Nutzer, ein Paket über ein USB-Kabel und das ADB-Entwicklungstool an das Gerät zu senden. Wenn dieser Mechanismus aufgerufen wird, startet die Wiederherstellung eine eigene Mini-Version des adbd-Daemons, damit adb auf einem verbundenen Hostcomputer mit ihm kommunizieren kann. Diese Mini-Version unterstützt nur einen Befehl:
adb sideload filename. Die benannte Datei wird vom Hostcomputer an das Gerät gesendet, das sie dann so überprüft und installiert, als wäre sie im lokalen Speicher vorhanden.
Einige Hinweise:
- Es wird nur die USB-Übertragung unterstützt.
-
Wenn die Wiederherstellung „adbd“ normal ausführt (in der Regel bei Userdebug- und Eng-Builds), wird „adbd“ heruntergefahren, während sich das Gerät im ADB-Sideload-Modus befindet, und neu gestartet, wenn „adb sideload“ den Empfang eines Pakets abgeschlossen hat. Im ADB-Sideload-Modus funktionieren keine anderen ADB-Befehle als
sideload(logcat,reboot,push,pull,shellusw. schlagen alle fehl). -
Der ADB-Sideload-Modus kann auf dem Gerät nicht beendet werden. Um den Vorgang abzubrechen, können Sie
/dev/null(oder ein anderes ungültiges Paket) als Paket senden. Das Gerät kann es dann nicht überprüfen und stoppt den Installationsvorgang. DieCheckKey()-Methode der RecoveryUI-Implementierung wird weiterhin für Tastendrücke aufgerufen. So können Sie eine Tastenfolge angeben, mit der das Gerät neu gestartet wird und die im ADB-Sideload-Modus funktioniert.