Gerätespezifischer Code

Das Wiederherstellungssystem enthält mehrere Hooks zum Einfügen gerätespezifischen Codes, sodass mit OTA-Updates auch andere Teile des Geräts als das Android-System aktualisiert werden können, z.B. der 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 Tools zum Erstellen von Paketen 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-Flashgerät. „device“ muss der Name der MTD-Partition sein und in /proc/mtd erscheinen.
mtd
Eine MTD-Rohpartition, die für bootfähige Partitionen wie „boot“ und „recovery“ verwendet wird. MTD wird nicht tatsächlich gemountet, aber der Mount-Punkt 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-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 gemountet, aber der Mount-Point-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 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 Wert für den Mount-Point 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, /recovery und /misc sollten Rohdatentypen (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: 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 nicht überschrieben. Dadurch werden Funktionen wie die Verschlüsselung der Nutzerdatenpartition unterstützt. Die Verschlüsselungsmetadaten werden am Ende der Partition gespeichert und sollten nicht überschrieben werden.

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, gilt er als device2-Eintrag. Andernfalls gilt er als options-Feld.

Boot-Animation

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, Sie können 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. Um Text ein- oder auszublenden, 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 ein- oder auszublenden.

device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h>

#include "common.h"
#include "device.h"
#include "screen_ui.h"

Header- und Elementfunktionen

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 hervorgehobene 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
  • NEUSTARTEN Gerät sofort neu starten
  • IGNORIERE. 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 mussten Sie animation_fps selbst festlegen.

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 Standard-Wiederherstellungsbildern. 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 GetUI()-Funktion 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 Aktionen aus. Sie müssen diese Methode also nicht in Ihrer Unterklasse 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 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

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 Bei dieser Tasteneingabe 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 tut, z. B. Daten löschen oder ein Paket installieren. Sie wird nur aufgerufen, wenn die Wiederherstellung im Leerlauf ist und auf Eingaben 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 Methode IsKeyPressed() 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 angeben, die Ganzzahlpositionen im Array der von GetMenuItems() zurückgegebenen Elemente Aktionen zuordnet. Für das Array von Elementen im Tardis-Beispiel 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.
  • NEUSTARTEN 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 aus. Weitere Informationen finden Sie unter Sideloading.
  • WIPE_CACHE Formatieren Sie nur die Cache-Partition neu. Da dies relativ harmlos ist, ist keine Bestätigung erforderlich.
  • 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 Erfolg anzuzeigen, und einen anderen Wert für 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 make_device()-Funktion 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();
}

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 kontaktieren.

Bei einer reinen Bildoberfläche ist keine Lokalisierung erforderlich. Ab Android 5.0 kann bei der Aktualisierung 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 oder höher

Die Wiederherstellungs-UI für Android 5.0 und höher verwendet zwei Hauptbilder: das Fehler-Bild und die Installation-Animation.

Bild, das bei einem OTA-Fehler angezeigt wird

Abbildung 1. icon_error.png

Bild, das während der OTA-Installation angezeigt wird

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. Erstellen Sie für eine 200 × 200 Pixel große Animation mit sieben Frames ein einzelnes Bild mit einer Größe von 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 Animations-Frames 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 zum erforderlichen zusammengesetzten Bild, das für die Wiederherstellung verwendet wird.

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 setzen. 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:

Bild, das während der OTA-Installation angezeigt wird

Abbildung 3: icon_installing.png

Bild als erstes Overlay

Abbildung 4. icon-installing_overlay01.png

Bild als siebtes Overlay

Abbildung 5. icon_installing_overlay07.png

Während der Installation wird das On-Screen-Display erstellt, indem zuerst das Bild „icon_installing.png“ und dann einer der Overlay-Frames mit dem richtigen Offset darüber gezeichnet wird. Hier ist ein rotes Rechteck eingeblendet, um zu verdeutlichen, wo das Overlay über dem Basisbild platziert wird:

Zusammengesetztes Bild aus Installation und erstem Overlay

Abbildung 6 Animationsframe 1 wird installiert (icon_installing.png + icon_installing_overlay01.png)

Zusammengesetztes Bild aus Installation und siebtem Overlay

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 dabei helfen, eine Reihe von Bildframes in das für die Wiederherstellung erforderliche Format „Basisbild + Overlay-Bilder“ zu konvertieren. Dazu werden auch die erforderlichen Offsets berechnet.

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 ein Textstring angezeigt, z.B. „Systemupdate wird installiert…“ zusammen mit dem Bild. 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:

Bild des Wiederherstellungstextes

Abbildung 8. Lokalisierter Text für Mitteilungen zum Ausgleich von Werbeumsatz

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 Gerät manuell im Wiederherstellungsmodus 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 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. 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. Der Fortschrittsbalken wird durch Kombinieren von zwei Eingabebildern erstellt, die dieselbe Größe haben müssen:

Leere Fortschrittsanzeige

Abbildung 9. progress_empty.png

vollständige Fortschrittsanzeige

Abbildung 10. progress_fill.png

Das linke Ende des fill-Bildes wird neben dem rechten Ende des empty-Bildes angezeigt, um den Fortschrittsbalken zu bilden. 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:

Fortschrittsanzeige bei 1%

Abbildung 11. Fortschrittsanzeige bei 1%>

Fortschrittsanzeige bei 10%

Abbildung 12. Fortschrittsanzeige bei 10%

Fortschrittsanzeige bei 50%

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 einer Farbtiefe von 8 Bit unterstützt.

Hinweis:Wenn in Android 5.x das Gebietsschema für die Wiederherstellung bekannt ist und es sich um eine linksläufige Sprache (RTL-Sprache) wie Arabisch oder Hebräisch handelt, wird der Fortschrittsbalken von rechts nach links gefüllt.

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. „Anzeige 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 abgeleiteten Klassen 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-Skript 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 werden nicht zum Zeitpunkt des Funktionsaufrufs ausgewertet. Die Logik Ihrer Funktion bestimmt, welche Argumente wie oft ausgewertet werden. So können Sie Erweiterungsfunktionen verwenden, um eigene Kontrollstrukturen zu 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, FreeValue() für ihn aufzurufen.

Angenommen, die Funktion benötigt zwei Argumente: einen String-Wert key und einen Blob-Wert 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. Außerdem wird eine hilfreiche Fehlermeldung ausgegeben, 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 mallocen, da der Aufrufer beides free() wird. Vergessen Sie nicht, FreeValue() für die Objekte aufzurufen, die Sie durch Auswerten 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 den folgenden Code, 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 haben keine Register_libname-Funktion. Sie können sie in TARGET_RECOVERY_UPDATER_EXTRA_LIBS auflisten, um sie mit dem Updater zu verknüpfen, ohne ihre (nicht vorhandene) Registrierungsfunktion aufzurufen. 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 Updatepaket extrahierten Datei als Blob zurückgibt, um das zweite Argument für die neue Erweiterungsfunktion zu erstellen.

OTA-Paketgenerierung

Im letzten Schritt müssen die Tools zur Generierung von OTA-Paketen über Ihre gerätespezifischen Daten informiert werden, damit sie Updater-Skripts ausgeben, 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 Datei AndroidBoard.mk 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 dabei lediglich um undurchsichtige Daten-Blobs, die das Build-System in die ZIP-Datei „target-files“ kopiert, die von den OTA-Generierungstools verwendet wird. Wenn Sie einen Build durchführen, wird „tardis.dat“ in „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, 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 den Fall, in dem ein inkrementelles OTA-Paket generiert wird. 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 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 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. Seien Sie darauf vorbereitet, 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 Quell-Target-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 Ziel-ZIP-Datei „target-files“ (der Build, den das inkrementelle Paket auf das Gerät legt).
  • 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 für ZIP-Archive.

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 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 zum manuellen Installieren eines Updatepakets, ohne es vom Hauptsystem Over-the-Air herunterzuladen. Sideloading ist nützlich, um Fehler zu beheben oder Änderungen auf Geräten vorzunehmen, auf denen das Hauptsystem nicht gestartet 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 eingesetzt 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 Speichergerät ( /sdcard-Verzeichnis). In Ihrer Datei „recovery.fstab“ muss der Mount-Point /sdcard definiert sein. Diese Methode kann nicht auf Geräten verwendet werden, die eine SD-Karte mit einem Symlink zu /data (oder einem ähnlichen Mechanismus) emulieren. /data ist 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 /sdcard angezeigt, aus dem der Nutzer eine Datei auswählen kann.
  • APPLY_CACHE. Ähnlich wie beim Laden eines Pakets aus /sdcard, nur dass stattdessen das Verzeichnis /cache verwendet wird, das immer für die Wiederherstellung verfügbar ist. Im regulären System kann /cache nur 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 angegebene 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 der Dienst 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, shell usw. schlagen fehl).
  • Der ADB-Sideload-Modus kann auf dem Gerät nicht beendet werden. Wenn Sie den Vorgang abbrechen möchten, 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. Die CheckKey()-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.