Das Wiederherstellungssystem enthält mehrere Hooks zum Einfügen von gerätespezifischem Code, damit bei Over-the-air-Updates auch andere Teile des Geräts als das Android-System aktualisiert werden können (z.B. das Basisband oder der Funkprozessor).
In den folgenden Abschnitten und Beispielen wird das Gerät tardis des Anbieters yoyodyne angepasst.
Partitionskarte
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 Dateisystem yaffs2 aus älteren Releases unterstützt.
Die Partitionszuordnungsdatei wird durch TARGET_RECOVERY_FSTAB angegeben. Diese Datei wird sowohl vom Wiederherstellungs-Binary als auch von den Tools zum Erstellen von Paketen verwendet. Sie können den Namen der Map-Datei in TARGET_RECOVERY_FSTAB in BoardConfig.mk angeben.
Eine Beispieldatei für die Partitionstabelle 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 werden fünf Dateisystemtypen unterstützt:
- yaffs2
-
Ein Yaffs2-Dateisystem auf einem MTD-Flash-Gerät. „device“ muss der Name der MTD-Partition sein und in
/proc/mtd
erscheinen. - mtd
-
Eine RAW-MTD-Partition, die für bootfähige Partitionen wie Boot- und Wiederherstellungspartitionen 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/mtd
sein. - ext4
- Ein ext4-Dateisystem auf einem eMMC-Flash-Gerät. „device“ muss der Pfad des Blockgeräts sein.
- eMMC
- Ein Roh-eMMc-Blockgerät, das für bootfähige Partitionen wie Boot- und Wiederherstellungspartitionen verwendet wird. Ähnlich wie beim mtd-Typ wird eMMc nie tatsächlich bereitgestellt, sondern 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. device ist das Blockgerät. device2 ist ein zweites Blockgerät, das das System bereitstellen möchte, wenn das Bereitstellen des primären Geräts fehlschlägt. Dies dient der Kompatibilität mit SD-Karten, die mit oder ohne Partitionstabelle formatiert sein können.
Alle Partitionen müssen im Stammverzeichnis bereitgestellt werden. Das bedeutet, dass der Wert des Bereitstellungspunkts mit einem Schrägstrich beginnen und keine weiteren Schrägstriche enthalten darf. Diese Einschränkung gilt nur für das Bereitstellen von Dateisystemen im Wiederherstellungsmodus. Im Hauptsystem können sie überall bereitgestellt werden. Die Verzeichnisse
/boot
,/recovery
und/misc
sollten Raw-Typen (mtd oder emmc) sein, während die Verzeichnisse/system
,/data
,/cache
und/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 namens options. Derzeit ist nur die Option length (Länge) definiert. Damit können Sie die Länge der Partition explizit angeben. Diese Länge wird beim Neuformatieren der Partition verwendet (z.B. für die userdata-Partition bei einem Datenlöschvorgang/Zurücksetzen auf die Werkseinstellungen oder für die Systempartition bei der Installation eines vollständigen OTA-Pakets). Wenn der Längenwert negativ ist, wird die Größe zum Formatieren berechnet, indem der Längenwert zur tatsächlichen Partitionsgröße addiert wird. Wenn Sie beispielsweise „length=-16384“ festlegen, werden die letzten 16 KiB dieser Partition nicht überschrieben, wenn die Partition neu formatiert wird. Dies unterstützt Funktionen wie die Verschlüsselung der Benutzerdatenpartition, bei der Verschlüsselungsmetadaten am Ende der Partition gespeichert werden, die nicht überschrieben werden sollten.
Hinweis:Die Felder device2 und options sind optional und können zu Unklarheiten beim Parsen führen. Wenn der Eintrag im vierten Feld der Zeile mit einem „/“ beginnt, wird er als device2-Eintrag betrachtet. Wenn der Eintrag nicht mit einem „/“ beginnt, wird er als options-Feld betrachtet.
Boot animation
Gerätehersteller können die Animation anpassen, die beim Starten eines Android-Geräts angezeigt wird. Erstellen Sie dazu eine ZIP-Datei, die gemäß den Spezifikationen im Bootanimation-Format organisiert und gespeichert ist.
Bei Android Things-Geräten können Sie die ZIP-Datei in die Android Things Console hochladen, damit die Bilder in das ausgewählte Produkt aufgenommen werden.
Hinweis:Diese Bilder müssen den Markenrichtlinien von Android entsprechen. Markenrichtlinien finden Sie im Bereich „Android“ des Partner Marketing Hub.
Wiederherstellungs-UI
Unterstützung für Geräte mit unterschiedlicher verfügbarer Hardware (physische Tasten, LEDs, Bildschirme usw.) Sie können die Wiederherstellungsoberfläche anpassen, um den Status anzuzeigen und auf die manuell bedienbaren ausgeblendeten 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 den Text ein- oder ausblenden möchten, halten Sie die Ein-/Aus-Taste gedrückt und drücken Sie gleichzeitig die Lautertaste. Wenn Ihr Gerät nicht über beide Tasten verfügt, halten Sie eine beliebige Taste gedrückt, um den Text zu aktivieren oder zu deaktivieren.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h> #include "common.h" #include "device.h" #include "screen_ui.h"
Kopf- und Artikelfunktionen
Die Geräteklasse erfordert Funktionen zum Zurückgeben von Überschriften und Elementen, die im ausgeblendeten Wiederherstellungsmenü angezeigt werden. Überschriften beschreiben, wie das Menü funktioniert (d.h. Steuerelemente zum Ändern/Auswählen des markierten Elements).
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). Berücksichtigen Sie daher die Breite des Displays Ihres Geräts.
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 die integrierte ScreenRecoveryUI-Implementierung übernehmen (siehe Anleitung für Geräte ohne Display). Die einzige Funktion, die über ScreenRecoveryUI angepasst werden kann, ist CheckKey()
, die die anfängliche asynchrone Schlüsselverwaltung übernimmt:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
KEY-Konstanten
Die Konstanten KEY_* sind in linux/input.h
definiert. CheckKey()
wird unabhängig davon aufgerufen, was im Rest der Wiederherstellung passiert: wenn das Menü deaktiviert oder aktiviert ist, während der Paketinstallation oder beim Löschen von Nutzerdaten usw. Es kann eine von vier Konstanten zurückgeben:
- SCHALTEN Menü und/oder Textprotokoll ein- oder ausblenden
- STARTEN. Starten Sie das Gerät sofort neu.
- IGNORE. Diese Tastenaktivierung ignorieren
- ENQUEUE Diese Tasteneingabe wird synchron verarbeitet, d. h. vom Wiederherstellungsmenü, wenn das Display aktiviert ist.
CheckKey()
wird jedes Mal aufgerufen, wenn auf ein Tasten-Nieder-Ereignis ein Tasten-Hoch-Ereignis für dieselbe Taste folgt. (Die Ereignisabfolge A-nach-unten B-nach-unten B-nach-oben A-nach-oben führt nur dazu, dass CheckKey(B)
aufgerufen wird.) CheckKey()
kann IsKeyPressed()
aufrufen, um herauszufinden, ob andere Tasten gedrückt werden. In der obigen Abfolge von Schlüsselereignissen würde IsKeyPressed(A)
bei einem Aufruf von IsKeyPressed(A)
„wahr“ zurückgeben.CheckKey(B)
CheckKey()
kann den Status in seiner Klasse beibehalten. Das kann nützlich sein, um Tastenfolgen zu erkennen. Dieses Beispiel zeigt eine etwas komplexere Einrichtung: Das Display wird durch gleichzeitiges Drücken der Ein‑/Aus-Taste und der Lautstärketaste ein- und ausgeschaltet. Das Gerät kann sofort neu gestartet werden, indem die Ein‑/Aus-Taste fünfmal hintereinander gedrückt wird, ohne dass andere Tasten 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 der Animationen in Frames 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 Android-Versionen musste animation_fps
manuell festgelegt werden.
Wenn Sie die Variable animation_fps
festlegen möchten, ü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 Standardwiederherstellungsbildern. Wenn Sie diese Bilder verwenden, müssen Sie keine Init()
-Funktion angeben. Weitere Informationen zu Bildern finden Sie unter Bilder für die Wiederherstellungsoberfläche.
Geräteklasse
Nachdem Sie eine RecoveryUI-Implementierung haben, definieren Sie Ihre Geräteklasse (untergeordnete Klasse der integrierten Geräteklasse). Sie sollte eine einzelne Instanz Ihrer UI-Klasse erstellen und diese über die GetUI()
-Funktion zurückgeben:
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 nichts aus. Wenn Sie also nichts tun möchten, müssen Sie dies in Ihrer Unterklasse nicht angeben:
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 Artikel 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
Geben Sie als Nächstes eine HandleMenuKey()
-Funktion an, die eine Tastenpresse und die aktuelle Menüsichtbarkeit berücksichtigt 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 nimmt einen Schlüsselcode (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 an. Der Rückgabewert ist eine Ganzzahl. Wenn der Wert 0 oder höher ist, wird dies als Position eines Menüpunkts interpretiert, der sofort aufgerufen wird (siehe Methode InvokeMenuItem()
unten). Andernfalls kann es eine der folgenden vordefinierten Konstanten sein:
- kHighlightUp. Menüpunkt zum vorherigen Element verschieben
- kHighlightDown. Menüpunkt zum nächsten Element verschieben
- kInvokeItem. Aktuell markiertes Element aufrufen
- kNoAction. Bei dieser Tastenpresse nichts tun
Wie das Argument „visible“ andeutet, wird HandleMenuKey()
auch dann aufgerufen, wenn das Menü nicht sichtbar ist. Im Gegensatz zu CheckKey()
wird es nicht aufgerufen, während die Wiederherstellung beispielsweise Daten löscht oder ein Paket installiert. Es wird nur aufgerufen, wenn die Wiederherstellung inaktiv ist und auf Eingabe wartet.
Trackball-Mechanismen
Wenn Ihr Gerät einen Trackball-ähnlichen Eingabemechanismus hat (Eingabeereignisse vom Typ EV_REL und Code REL_Y generiert), werden bei der Wiederherstellung Tastendrücke für KEY_UP und KEY_DOWN simuliert, wenn das Trackball-ähnliche Eingabegerät eine Bewegung auf der Y-Achse meldet. Sie müssen lediglich die Ereignisse KEY_UP und KEY_DOWN auf Menüaktionen abbilden. Bei CheckKey()
ist dies nicht der Fall. Sie können also keine Trackballbewegungen als Auslöser für das Neustarten oder Ein-/Ausschalten des Displays verwenden.
Modifikatortasten
Wenn Sie prüfen möchten, ob Tasten als Modifikatoren gedrückt werden, rufen Sie die IsKeyPressed()
-Methode Ihres eigenen UI-Objekts auf. Auf einigen Geräten wird beispielsweise durch Drücken von Alt-W im Wiederherstellungsmodus ein Datenlöschvorgang gestartet, unabhängig davon, ob das Menü sichtbar ist oder nicht. Sie könnten das so implementieren:
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“ (falsch) gesetzt ist, ist es nicht sinnvoll, die speziellen Werte zurückzugeben, mit denen das Menü manipuliert wird (Hervorhebung verschieben, hervorgehobenes Element aufrufen), da der Nutzer die Markierung nicht sehen kann. Sie können die Werte jedoch bei Bedarf zurückgeben.
InvokeMenuItem
Geben Sie als Nächstes eine InvokeMenuItem()
-Methode an, die Ganzzahlpositionen im Array der von GetMenuItems()
zurückgegebenen Elemente Aktionen zuordnet. Für das Array mit Elementen im Beispiel „Tardis“ verwenden Sie Folgendes:
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; } }
Mit dieser Methode kann jedes Mitglied des BuiltinAction-Eintrags zurückgegeben werden, um dem System zu sagen, dass es diese Aktion ausführen soll (oder das Mitglied NO_ACTION, wenn das System nichts tun soll). Hier können Sie zusätzliche Wiederherstellungsfunktionen bereitstellen, die über die im System vorhandenen hinausgehen: Fügen Sie ein Element in Ihr Menü ein, führen Sie es hier aus, wenn dieser Menüpunkt aufgerufen wird, und geben Sie „NO_ACTION“ zurück, damit das System nichts weiter tut.
BuiltinAction enthält die folgenden Werte:
- NO_ACTION. Nichts unternehmen.
- STARTEN. Beenden Sie die Wiederherstellung und starten Sie das Gerät normal neu.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD Update-Pakete von verschiedenen Orten aus installieren. Weitere Informationen finden Sie unter Sideloading.
- WIPE_CACHE. Formatieren Sie nur die Cache-Partition neu. Es ist keine Bestätigung erforderlich, da dies relativ harmlos ist.
- WIPE_DATA. Die Partitionen „userdata“ und „cache“ neu formatieren, auch als Zurücksetzen auf die Werkseinstellungen bezeichnet Der Nutzer wird aufgefordert, diese Aktion zu bestätigen, bevor er fortfährt.
Die letzte Methode, WipeData()
, ist optional und wird immer dann aufgerufen, wenn ein Datenlöschvorgang gestartet wird (entweder über die Wiederherstellung im Menü oder wenn der Nutzer über das Hauptsystem ein Zurücksetzen auf die Werkseinstellungen ausgewählt hat). Diese Methode wird aufgerufen, bevor die Nutzerdaten und Cachepartitionen gelöscht werden. Wenn auf Ihrem Gerät Nutzerdaten an anderer Stelle als in diesen beiden Partitionen gespeichert werden, sollten Sie sie hier löschen. Sie sollten 0 für einen Erfolg und einen anderen Wert für einen Fehler zurückgeben. Derzeit wird der Rückgabewert jedoch ignoriert. Die Partitionen für Nutzerdaten und Cache werden gelöscht, unabhängig davon, ob Sie „Erfolg“ oder „Fehlschlag“ zurückgeben.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
Hersteller des Geräts
Fügen Sie am Ende der Datei „recovery_ui.cpp“ einige Boilerplate-Code für die Funktion make_device()
ein, die eine Instanz Ihrer Device-Klasse erstellt und zurückgibt:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Wiederherstellungsfunktion für Geräte 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 für 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 der Wiederherstellung besteht aus Bildern. Idealerweise interagieren Nutzer nie mit der Benutzeroberfläche: Bei einem normalen Update wird das Smartphone in den Wiederherstellungsmodus gestartet, die Fortschrittsanzeige der Installation wird gefüllt und das Smartphone wird ohne Eingabe des Nutzers wieder in das neue System gestartet. Bei einem Systemaktualisierungsproblem kann der Nutzer nur den Kundenservice anrufen.
Bei einer reinen Bildoberfläche ist keine Lokalisierung erforderlich. Ab Android 5.0 kann beim Update jedoch neben dem Bild ein Textstring (z. B. „Systemupdate wird installiert…“) angezeigt werden. Weitere Informationen finden Sie unter Lokalisierter Wiederherstellungstext.
Android 5.0 und höher
Die Wiederherstellungs-UI 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 dargestellt, bei dem die verschiedenen Frames der Animation nach Zeile verschachtelt sind. Deshalb wirkt Abbildung 2 gestaucht. Erstellen Sie beispielsweise für eine 200 × 200 Pixel große Animation mit sieben Frames ein einzelnes Bild mit 200 × 1.400 Pixeln, wobei der erste Frame die Zeilen 0, 7, 14, 21 usw. enthält, der zweite Frame die Zeilen 1, 8, 15, 22 usw. usw. Das kombinierte Bild enthält einen Text-Chunk, der die Anzahl der Frames in der Animation und die Anzahl der Frames pro Sekunde (FPS) angibt. Das Tool bootable/recovery/interlace-frames.py
nimmt eine Reihe von Eingabeframes und kombiniert sie zu dem erforderlichen zusammengesetzten Bild, das für die Wiederherstellung verwendet wird.
Standardbilder sind in verschiedenen Dichten verfügbar und befinden sich unter 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“ angeben und die Anzahl der Frames in der Animation auf „0“ setzen. Das Fehlersymbol ist nicht animiert, sondern immer ein statisches Bild.
Android 4.x und niedriger
Die Wiederherstellungsoberfläche von Android 4.x und niedriger verwendet das Bild Fehler (siehe oben), die Installationsanimation sowie mehrere Overlay-Bilder:
![]() Abbildung 3: icon_installing.png |
![]() Abbildung 4: icon-installing_overlay01.png |
![]() Abbildung 5: icon_installing_overlay07.png |
Während der Installation wird das Displaybild erstellt, indem das Bild „icon_installing.png“ gezeichnet und dann einer der Overlay-Frames mit dem richtigen Offset darüber gezeichnet wird. Hier wird ein rotes Feld eingefügt, um zu verdeutlichen, wo das Overlay auf dem Basisbild platziert wird:
![]() Abbildung 6 Installation von Animationsframe 1 (icon_installing.png + icon_installing_overlay01.png) |
![]() Abbildung 7. Installation des Animationsframes 7 (icon_installing.png + icon_installing_overlay07.png) |
Bei nachfolgenden Frames wird nur das nächste Overlay-Bild über dem vorhandenen Bild gezeichnet. Das Basisbild wird nicht neu gezeichnet.
Die Anzahl der Frames in der Animation, die gewünschte Geschwindigkeit sowie die X- und Y-Offset 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 untergeordneten Klasse, um diese Werte für Ihre benutzerdefinierten Bilder zu ändern. Weitere Informationen finden Sie unter ScreenRecoveryUI. Das Script bootable/recovery/make-overlay.py
kann bei der Umwandlung einer Reihe von Bildframes in das für die Wiederherstellung erforderliche Format „Basisbild + Overlay-Bilder“ helfen, einschließlich der Berechnung der erforderlichen Offsets.
Die Standardbilder befinden sich unter 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“ angeben und die Anzahl der Frames in der Animation auf „0“ setzen. Das Fehlersymbol ist nicht animiert, sondern immer ein statisches Bild.
Lokalisierter Wiederherstellungstext
Unter Android 5.x wird ein Textstring angezeigt (z.B. „Systemupdate wird installiert…“) zusammen mit dem Image. Wenn das Hauptsystem in die Wiederherstellungsoption startet, wird die aktuelle Sprache des Nutzers als Befehlszeilenoption an die Wiederherstellung übergeben. Für jede anzuzeigende Nachricht enthält die Wiederherstellung ein zweites zusammengesetztes Bild mit vorab gerenderten Textstrings für diese Nachricht in jeder Sprache.
Beispielbild für Wiederherstellungstextstrings:

Abbildung 8. Lokalisierter Text für Mitteilungen zur Wiederherstellung
Im Wiederherstellungstext können die folgenden Meldungen angezeigt werden:
- Systemupdate wird installiert…
- Fehler!
- Wird gelöscht… (beim Löschen aller Daten/Zurücksetzen auf die Werkseinstellungen)
- Kein Befehl (wenn ein Nutzer das Gerät manuell in die Wiederherstellung startet)
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 unter bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
.
Wenn ein Nutzer das Wiederherstellungssystem manuell startet, ist die Sprache möglicherweise nicht verfügbar und es wird kein Text angezeigt. Die SMS dürfen nicht für den Wiederherstellungsprozess entscheidend sein.
Hinweis:Die ausgeblendete Benutzeroberfläche, in der Protokollmeldungen angezeigt und Aktionen aus dem Menü ausgewählt werden können, ist nur auf Englisch verfügbar.
Fortschrittsbalken
Fortschrittsbalken können unter dem Hauptbild (oder der Hauptanimation) 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 gefüllten Bildes wird neben dem rechten Ende des leeren Bildes angezeigt, um den Fortschrittsbalken zu bilden. Die Position der Grenze zwischen den beiden Bildern wird geändert, um den Fortschritt anzuzeigen. Bei den obigen Eingabebildpaaren 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 device/yoyodyne/tardis/recovery/res/images
ablegen (in diesem Beispiel). Die Dateinamen müssen mit den oben aufgeführten übereinstimmen. Wenn in diesem Verzeichnis eine Datei gefunden wird, wird sie vom Build-System anstelle des entsprechenden Standard-Images verwendet. Es werden nur PNGs im RGB- oder RGBA-Format mit 8-Bit-Farbtiefe unterstützt.
Hinweis:Wenn die Sprache der Wiederherstellung in Android 5.x bekannt ist und eine linksläufige Sprache (z. B. Arabisch oder Hebräisch) ist, wird die Fortschrittsanzeige von rechts nach links gefüllt.
Geräte ohne Display
Nicht alle Android-Geräte haben ein Display. Wenn es sich bei Ihrem Gerät um ein headless-Gerät handelt oder es nur eine Audiooberfläche hat, müssen Sie die Benutzeroberfläche für die Wiederherstellung möglicherweise umfangreicher anpassen. Anstatt eine Unterklasse von ScreenRecoveryUI zu erstellen, erstellen Sie eine Unterklasse der übergeordneten Klasse RecoveryUI.
RecoveryUI bietet Methoden zur Verarbeitung von UI-Vorgängen auf niedrigerer Ebene, z. B. „Display ein-/ausschalten“, „Fortschrittsanzeige aktualisieren“, „Menü anzeigen“ und „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 unterschiedliche Farben oder Blinkmuster anzeigen können, oder Sie können Audio abspielen. (Möglicherweise möchten Sie kein Menü oder den Modus „Textanzeige“ unterstützen. Sie können den Zugriff mit CheckKey()
- und HandleMenuKey()
-Implementierungen verhindern, die das Display nie einschalten oder ein Menüelement auswählen. In diesem Fall können viele der von Ihnen anzugebenden RecoveryUI-Methoden einfach leere Stubs sein.
In bootable/recovery/ui.h
finden Sie die Erklärung für RecoveryUI, in der steht, welche Methoden Sie unterstützen müssen. RecoveryUI ist abstrakt – einige Methoden sind rein virtuell und müssen von untergeordneten Klassen bereitgestellt werden. Sie enthält jedoch den Code zur Verarbeitung wichtiger Eingaben. Sie können diese Einstellung auch überschreiben, wenn Ihr Gerät keine Schlüssel hat oder Sie sie anders verarbeiten möchten.
Updater
Sie können bei der Installation des Update-Pakets gerätespezifischen Code verwenden, indem Sie eigene Erweiterungsfunktionen angeben, die von Ihrem Update-Script aufgerufen werden können. Hier ist eine Beispielfunktion für das Gerät „Tardis“:
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, unter dem die Funktion aufgerufen wurde, ein State*
-Cookie, die Anzahl der eingehenden Argumente und ein Array von Expr*
-Pointern, die die Argumente darstellen. Der Rückgabewert ist eine neu zugewiesene 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); }
Die Argumente werden nicht zum Zeitpunkt des Aufrufs der Funktion ausgewertet. Die Logik der Funktion bestimmt, welche Argumente und wie oft sie ausgewertet werden. So können Sie mithilfe von Erweiterungsfunktionen eigene Kontrollstrukturen implementieren. Call Evaluate()
, um ein Expr*
-Argument zu bewerten und ein Value*
zurückzugeben. Wenn Evaluate()
NULL zurückgibt, solltest du alle von dir gehaltenen Ressourcen freigeben und sofort NULL zurückgeben. Dadurch werden Abbruchsmeldungen an den Edify-Stack weitergeleitet. Andernfalls übernehmen Sie die Verantwortung für den zurückgegebenen Wert und müssen FreeValue()
später darauf anwenden.
Angenommen, die Funktion benötigt zwei Argumente: einen Stringwert key und einen Blobwert image. Sie könnten Argumente so vorlesen:
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; }
Bei mehreren Argumenten kann es mühsam werden, nach NULL zu suchen und zuvor ausgewertete Argumente freizugeben. Die Funktion ReadValueArgs()
kann Ihnen dabei helfen. Anstelle des Codes oben hätten Sie auch 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 Typprüfung durch. Sie müssen das hier selbst tun. Es ist einfacher, dies mit einer if-Anweisung zu tun, allerdings wird bei einem Fehler eine etwas weniger spezifische Fehlermeldung ausgegeben. ReadValueArgs()
wertet jedoch jedes Argument aus und gibt alle zuvor ausgewerteten Argumente frei. Außerdem wird eine nützliche Fehlermeldung ausgegeben, wenn eine der Auswertungen fehlschlägt. Sie können die ReadValueVarArgs()
-Funktion verwenden, um eine variable Anzahl von Argumenten zu bewerten. Dabei wird ein Array von Value*
zurückgegeben.
Nach der Auswertung der Argumente 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. Das Eigentum an diesem Objekt geht an den Aufrufer über. Der Aufrufer übernimmt die Inhaberschaft aller Daten, auf die diese Value*
verweist, insbesondere des Datenelements.
In diesem Fall möchten Sie den Wert „true“ oder „false“ zurückgeben, um einen Erfolg anzuzeigen. Denken Sie daran, dass der leere String false und alle anderen Strings true sind. Sie müssen ein Value-Objekt mit einer malloc'd-Kopie des konstanten Strings zuweisen, der zurückgegeben werden soll, da der Aufrufer beide free()
. Vergiss nicht, FreeValue()
auf die Objekte anzuwenden, die du durch die Auswertung deiner Argumente erhalten hast.
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 praktische Funktion StringValue()
umschließt einen String in ein neues „Value“-Objekt.
So können Sie den Code oben prägnanter schreiben:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
Wenn Sie Funktionen in den edify-Interpreter einbinden möchten, geben Sie die Funktion Register_foo
an, wobei foo der Name der statischen Bibliothek ist, die diesen Code enthält. Rufen Sie RegisterFunction()
auf, um jede Erweiterungsfunktion zu registrieren. Gerätespezifische Funktionen sollten gemäß der Konvention 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 Wiederherstellungsoberfläche verwendet wurde. Auf Ihrem Gerät sind möglicherweise beide statischen Bibliotheken hier 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 abschließend den Wiederherstellungs-Build so, dass Ihre Bibliothek einbezogen wird. 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 keine edify-Erweiterungen sind (d.h.,
keine Register_libname
-Funktion haben), können Sie diese in TARGET_RECOVERY_UPDATER_EXTRA_LIBS auflisten, um sie mit dem Updater zu verknüpfen, ohne ihre (nicht vorhandene) Registrierungsfunktion aufzurufen. Wenn Sie beispielsweise in Ihrem gerätespezifischen Code zlib zum Dekomprimieren von Daten verwenden möchten, fügen Sie hier libz hinzu.
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 Update-Scripts in Ihrem OTA-Paket können Ihre Funktion jetzt wie jede andere aufrufen. Um Ihr TARDIS-Gerät neu zu programmieren, kann das Update-Script Folgendes enthalten:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. Dabei wird die Version der integrierten Funktion package_extract_file()
mit nur einem Argument verwendet, die den Inhalt einer Datei zurückgibt, die aus dem Update-Paket extrahiert wurde, 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 Update-Scripts auszugeben, die Aufrufe Ihrer Erweiterungsfunktionen enthalten.
Informieren Sie das Build-System zuerst über einen gerätespezifischen Datenblob. Angenommen, Ihre Datendatei befindet sich in device/yoyodyne/tardis/tardis.dat
, deklarieren Sie Folgendes in AndroidBoard.mk Ihres Geräts:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
Sie können sie stattdessen auch in eine Android.mk-Datei einfügen. Sie muss dann jedoch durch eine Geräteprüfung geschützt werden, da alle Android.mk-Dateien im Verzeichnis unabhängig vom zu erstellenden Gerät geladen werden. Wenn Ihr Stammbaum mehrere Geräte enthält, sollte 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
Sie werden aus historischen Gründen als Radiodateien bezeichnet, haben aber möglicherweise nichts mit dem Funkschnittstellen des Geräts (falls vorhanden) zu tun. Es sind einfach undurchsichtige Datenblöcke, die vom Build-System in die ZIP-Zieldateien kopiert werden, die von den OTA-Generierungstools verwendet werden. Bei einem Build wird „tardis.dat“ in der Datei „target-files.zip“ als RADIO/tardis.dat
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, falls vorhanden. 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 den Fall, dass ein inkrementelles OTA-Paket generiert wird. Angenommen, Sie müssen die TARDIS 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 angeben (implementieren Sie nur die, die Sie benötigen).
FullOTA_Assertions()
- Wird zu Beginn der Generierung einer vollständigen OTA aufgerufen. Hier können Sie auch Behauptungen zum aktuellen Status des Geräts formulieren. Senden Sie keine Scriptbefehle, die Änderungen am Gerät vornehmen.
FullOTA_InstallBegin()
- Wird aufgerufen, nachdem alle Prüfungen des Gerätestatus bestanden haben, aber bevor Änderungen vorgenommen wurden. Sie können Befehle für gerätespezifische Updates senden, die ausgeführt werden müssen, bevor andere Änderungen auf dem Gerät vorgenommen werden.
FullOTA_InstallEnd()
- Wird am Ende der Scriptgenerierung aufgerufen, nachdem die Scriptbefehle zum Aktualisieren der Boot- und Systempartitionen ausgegeben wurden. Sie können auch zusätzliche Befehle für gerätespezifische Updates senden.
IncrementalOTA_Assertions()
-
Ähnlich wie
FullOTA_Assertions()
, wird aber beim Generieren eines inkrementellen Update-Pakets aufgerufen. IncrementalOTA_VerifyBegin()
- Wird aufgerufen, nachdem alle Prüfungen des Gerätestatus bestanden haben, aber bevor Änderungen vorgenommen wurden. Sie können Befehle für gerätespezifische Updates senden, die ausgeführt werden müssen, bevor etwas anderes auf dem Gerät geändert wird.
IncrementalOTA_VerifyEnd()
- Wird am Ende der Überprüfungsphase aufgerufen, wenn das Script bestätigt hat, dass die Dateien, die es bearbeiten wird, den erwarteten Anfangsinhalt haben. Bisher wurde nichts auf dem Gerät geändert. Sie können auch Code für zusätzliche gerätespezifische Bestätigungen ausgeben.
IncrementalOTA_InstallBegin()
- Wird aufgerufen, nachdem bestätigt wurde, dass die zu patchenden Dateien den erwarteten Vorher-Zustand haben, aber bevor Änderungen vorgenommen wurden. Sie können Befehle für gerätespezifische Updates senden, die ausgeführt werden müssen, bevor andere Änderungen auf dem Gerät vorgenommen werden.
IncrementalOTA_InstallEnd()
- Ähnlich wie sein Pendant für das vollständige OTA-Paket wird dieser Befehl am Ende der Scriptgenerierung aufgerufen, nachdem die Scriptbefehle zum Aktualisieren der Boot- und Systempartitionen ausgegeben wurden. Sie können auch zusätzliche Befehle für gerätespezifische Updates senden.
Hinweis:Wenn die Stromversorgung des Geräts unterbrochen wird, wird die OTA-Installation möglicherweise von vorn gestartet. Seien Sie darauf vorbereitet, mit Geräten umzugehen, auf denen diese Befehle bereits vollständig oder teilweise ausgeführt wurden.
Funktionen an Infoobjekte übergeben
Funktionen an ein einzelnes Infoobjekt übergeben, 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 Eingabezieldateien. -
info.source_zip. (Nur inkrementelle OTAs) Das
zipfile.ZipFile
-Objekt für die .zip-Datei der Quellzieldateien (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 Ziel-ZIP-Dateien (die Builds, die das inkrementelle Paket auf das Gerät überträgt). -
info.output_zip. Paket wird erstellt; ein
zipfile.ZipFile
-Objekt wurde zum Schreiben geöffnet. Verwenden Sie common.ZipWriteStr(info.output_zip, filename, data), um dem Paket eine Datei hinzuzufügen. -
info.script. Scriptobjekt, dem Sie Befehle anhängen können. Rufen Sie
info.script.AppendExtra(script_text)
auf, um Text in das Script auszugeben. Der Ausgabetext muss mit einem Semikolon enden, damit er nicht mit Befehlen überschrieben wird, die danach ausgegeben werden.
Weitere Informationen zum Info-Objekt finden Sie in der Dokumentation der Python Software Foundation zu ZIP-Archiven.
Speicherort des Moduls angeben
Geben Sie in der BoardConfig.mk-Datei den Speicherort des releasetools.py-Scripts Ihres Geräts 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
(device/yoyodyne/common
in diesem Beispiel) verwendet. Es empfiehlt sich, den Speicherort des Scripts „releasetools.py“ explizit anzugeben.
Beim Erstellen des Tardis-Geräts ist das Script „releasetools.py“ in der ZIP-Datei der Zieldateien (META/releasetools.py
) enthalten.
Wenn Sie die Release-Tools (img_from_target_files
oder ota_from_target_files
) ausführen, wird das Script „releasetools.py“ in der ZIP-Datei der Zieldateien, falls vorhanden, dem Script aus dem Android-Quellcodebaum vorgezogen. Sie können den Pfad zu den gerätespezifischen Erweiterungen auch explizit mit der Option -s
(oder --device_specific
) angeben, die die höchste Priorität hat. So können Sie Fehler korrigieren und Änderungen an den Releasetools-Erweiterungen vornehmen und diese Änderungen auf alte Zieldateien anwenden.
Wenn Sie jetzt 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 bietet einen Sideloading-Mechanismus, mit dem ein Update-Paket manuell installiert werden kann, ohne dass es über das Hauptsystem per WLAN heruntergeladen werden muss. Sideloading ist nützlich, um auf Geräten, auf denen das Hauptsystem nicht gestartet werden kann, Fehler zu beheben oder Änderungen vorzunehmen.
Bisher wurde das Sideloading durch das Laden von Paketen von der SD-Karte des Geräts durchgeführt. Bei einem Gerät, das nicht startet, kann das Paket mit einem anderen Computer auf die SD-Karte kopiert und dann in das Gerät eingesetzt werden. Für Android-Geräte ohne externen Wechselspeicher werden bei der Wiederherstellung zwei zusätzliche Mechanismen für das Sideloading unterstützt: das Laden von Paketen aus der Cache-Partition und das Laden über USB mit ADB.
Um jeden Sideload-Mechanismus aufzurufen, kann die Device::InvokeMenuItem()
-Methode Ihres Geräts die folgenden Werte von „BuiltinAction“ zurückgeben:
-
APPLY_EXT. Sie können ein Update-Paket per Sideload aus dem externen Speicher (
/sdcard
-Verzeichnis) übertragen. In recovery.fstab muss der Bereitstellungspunkt/sdcard
definiert sein. Diese Funktion ist nicht auf Geräten verfügbar, die eine SD-Karte mit einem Symlink zu/data
oder einem ähnlichen Mechanismus emulieren./data
kann in der Regel nicht wiederhergestellt werden, da es möglicherweise verschlüsselt ist. Die Wiederherstellungs-Benutzeroberfläche zeigt ein Menü mit ZIP-Dateien in/sdcard
an und ermöglicht es dem Nutzer, eine auszuwählen. -
APPLY_CACHE. Ähnlich wie das Laden eines Pakets von
/sdcard
, mit der Ausnahme, dass stattdessen das Verzeichnis/cache
verwendet wird, das für die Wiederherstellung immer verfügbar ist. Im regulären System kann/cache
nur von Nutzern mit Berechtigungen beschrieben werden. Wenn das Gerät nicht bootfähig ist, kann das Verzeichnis/cache
überhaupt nicht beschrieben werden. Dies schränkt die Nützlichkeit dieses Mechanismus ein. -
APPLY_ADB_SIDELOAD. Ermöglicht es Nutzern, 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 Miniversion des adbd-Daemons, damit adb auf einem verbundenen Hostcomputer mit ihm kommunizieren kann. Diese Miniversion unterstützt nur einen Befehl:
adb sideload filename
. Die benannte Datei wird vom Hostcomputer an das Gerät gesendet, das sie dann überprüft und installiert, als wäre sie im lokalen Speicher vorhanden.
Einige Hinweise:
- Nur USB-Transport wird unterstützt.
-
Wenn in der Wiederherstellung adbd normal ausgeführt wird (in der Regel bei userdebug- und eng-Builds), wird es heruntergefahren, während sich das Gerät im adb-Sideload-Modus befindet. Es wird dann neu gestartet, wenn der Empfang eines Pakets über adb Sideload abgeschlossen ist. Im Sideload-Modus von ADB funktionieren nur der Befehl
sideload
und keine anderen ADB-Befehle (logcat
,reboot
,push
,pull
,shell
usw.). -
Sie können den ADB-Sideload-Modus auf dem Gerät nicht beenden. Wenn Sie den Vorgang abbrechen möchten, können Sie
/dev/null
(oder ein anderes nicht gültiges Paket) als Paket senden. Das Gerät kann es dann nicht überprüfen und der Installationsvorgang wird beendet. Die MethodeCheckKey()
der RecoveryUI-Implementierung wird weiterhin bei Tastendrücken aufgerufen. Sie können also eine Tastenfolge angeben, mit der das Gerät neu gestartet wird und die im adb-Sideload-Modus funktioniert.