Gerätespezifischer Code

Das Wiederherstellungssystem umfasst mehrere Hooks zum Einfügen von gerätespezifischem Code, sodass OTA-Updates auch andere Teile des Geräts als das Android-System aktualisieren können (z. B. das Basisband oder den Funkprozessor).

Die folgenden Abschnitte und Beispiele passen das vom Yoyodyne- Anbieter hergestellte Tardis- Gerät an.

Partitionszuordnung

Ab Android 2.3 unterstützt die Plattform eMMc-Flash-Geräte und das auf diesen Geräten ausgeführte ext4-Dateisystem. Es unterstützt auch MTD-Flash-Geräte (Memory Technology Device) und das Dateisystem yaffs2 aus älteren Versionen.

Die Partitionszuordnungsdatei wird durch TARGET_RECOVERY_FSTAB angegeben; Diese Datei wird sowohl von der Wiederherstellungsbinärdatei als auch von den Paketerstellungstools verwendet. Sie können den Namen der Kartendatei in TARGET_RECOVERY_FSTAB in BoardConfig.mk angeben.

Eine Beispiel-Partitionszuordnungsdatei könnte wie folgt 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 Mount-Punkte in diesem Beispiel definiert werden (Geräte können auch zusätzliche Partitionen hinzufügen). Es gibt fünf unterstützte Dateisystemtypen:

yaffs2
Ein yaffs2-Dateisystem auf einem MTD-Flash-Gerät. „Gerät“ muss der Name der MTD-Partition sein und in /proc/mtd erscheinen.
mtd
Eine rohe MTD-Partition, die für bootfähige Partitionen wie Boot und Wiederherstellung verwendet wird. MTD wird nicht tatsächlich gemountet, aber der Mountpunkt wird als Schlüssel zum Auffinden der Partition verwendet. „Gerät“ muss der Name der MTD-Partition in /proc/mtd sein.
ext4
Ein ext4-Dateisystem auf einem eMMc-Flash-Gerät. „Gerät“ muss der Pfad des Blockgeräts sein.
emmc
Ein rohes eMMc-Blockgerät, das für bootfähige Partitionen wie Boot und Wiederherstellung verwendet wird. Ähnlich wie der mtd-Typ wird eMMc nie tatsächlich gemountet, sondern die Mount-Punkt-Zeichenfolge wird verwendet, um das Gerät in der Tabelle zu lokalisieren.
vfett
Ein FAT-Dateisystem auf einem Blockgerät, typischerweise für externen Speicher wie eine SD-Karte. Das Gerät ist das Blockgerät; Gerät2 ist ein zweites Blockgerät, das das System zu mounten versucht, wenn das Mounten des primären Geräts fehlschlägt (aus Gründen der Kompatibilität mit SD-Karten, die möglicherweise mit einer Partitionstabelle formatiert sind oder nicht).

Alle Partitionen müssen im Stammverzeichnis gemountet werden (dh der Mountpunktwert muss mit einem Schrägstrich beginnen und darf keine anderen Schrägstriche enthalten). Diese Einschränkung gilt nur für das Mounten von Dateisystemen bei der Wiederherstellung; Das Hauptsystem ist frei, sie überall zu montieren. Die Verzeichnisse /boot , /recovery und /misc sollten Rohtypen sein (mtd oder emmc), während die Verzeichnisse /system , /data , /cache und /sdcard (falls verfügbar) Dateisystemtypen sein sollten (yaffs2, ext4 oder vfett).

Ab Android 3.0 erhält die Datei „recovery.fstab“ das zusätzliche optionale Feld „ options“ . Derzeit ist die einzige definierte Option length , mit der Sie die Länge der Partition explizit angeben können. Diese Länge wird beim Neuformatieren der Partition verwendet (z. B. für die Benutzerdatenpartition während eines Datenlöschvorgangs/Zurücksetzens 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 ermittelt, indem der Längenwert zur tatsächlichen Partitionsgröße addiert wird. Die Einstellung „Länge=-16384“ bedeutet beispielsweise, dass die letzten 16 KB dieser Partition nicht überschrieben werden, wenn diese Partition neu formatiert wird. Dies unterstützt Funktionen wie die Verschlüsselung der Benutzerdatenpartition (wobei Verschlüsselungsmetadaten am Ende der Partition gespeichert werden, die nicht überschrieben werden sollten).

Hinweis: Die Felder „device2“ und „options“ sind optional und führen zu Mehrdeutigkeiten beim Parsen. Wenn der Eintrag im vierten Feld der Zeile mit einem „/“-Zeichen beginnt, wird er als Geräte2- Eintrag betrachtet; Wenn der Eintrag nicht mit einem „/“-Zeichen beginnt, wird er als Optionsfeld betrachtet.

Boot-Animation

Gerätehersteller haben die Möglichkeit, die beim Booten eines Android-Geräts angezeigte Animation anzupassen. 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-Konsole hochladen, um die Bilder in das ausgewählte Produkt aufzunehmen.

Hinweis: Diese Bilder müssen den Android- Markenrichtlinien entsprechen.

Wiederherstellungs-Benutzeroberfläche

Um Geräte mit unterschiedlicher verfügbarer Hardware (physische Tasten, LEDs, Bildschirme usw.) zu unterstützen, können Sie die Wiederherstellungsschnittstelle anpassen, um den Status anzuzeigen und auf die manuell betriebenen versteckten Funktionen für jedes Gerät zuzugreifen.

Ihr Ziel ist es, eine kleine statische Bibliothek mit einigen C++-Objekten zu erstellen, um die gerätespezifische Funktionalität 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 den Text umzuschalten, halten Sie die Ein-/Aus-Taste gedrückt, während Sie die Lauter-Taste drücken. Wenn Ihre Geräte nicht über beide Tasten verfügen, drücken Sie lange auf eine beliebige Taste, um den Text umzuschalten.

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

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

Kopf- und Artikelfunktionen

Die Device-Klasse erfordert Funktionen zum Zurückgeben von Headern und Elementen, die im ausgeblendeten Wiederherstellungsmenü angezeigt werden. Überschriften beschreiben, wie das Menü bedient wird (z. B. Steuerelemente zum Ändern/Auswählen des hervorgehobenen 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 umbrochen). Berücksichtigen Sie daher die Breite des Bildschirms Ihres Geräts.

Passen Sie CheckKey an

Definieren Sie als Nächstes die RecoveryUI-Implementierung Ihres Geräts. In diesem Beispiel wird davon ausgegangen, dass das Tardis- Gerät über einen Bildschirm verfügt, sodass Sie von der integrierten ScreenRecoveryUI-Implementierung erben können (siehe Anweisungen für Geräte ohne Bildschirm ). Die einzige Funktion, die von ScreenRecoveryUI angepasst werden kann, ist CheckKey() , die die anfängliche asynchrone Schlüsselverarbeitung durchführt:

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 weiteren Verlauf der Wiederherstellung geschieht: wenn das Menü ausgeschaltet ist, wenn es eingeschaltet ist, während der Paketinstallation, während der Benutzerdatenlöschung usw. Es kann eine von vier Konstanten zurückgeben:

  • UMSCHALTEN . Schalten Sie die Anzeige des Menüs und/oder des Textprotokolls ein oder aus
  • NEUSTART . Starten Sie das Gerät sofort neu
  • IGNORIEREN . Ignorieren Sie diesen Tastendruck
  • ENQUEUE . Stellen Sie diesen Tastendruck in die Warteschlange, damit er synchron verarbeitet wird (d. h. vom Wiederherstellungsmenüsystem, wenn die Anzeige aktiviert ist).

CheckKey() wird jedes Mal aufgerufen, wenn auf ein Key-Down-Ereignis ein Key-Up-Ereignis für dieselbe Taste folgt. (Die Abfolge der Ereignisse A-unten B-unten B-oben A-oben führt nur dazu, dass CheckKey(B) aufgerufen wird.) CheckKey() kann IsKeyPressed() aufrufen, um herauszufinden, ob andere Tasten gedrückt gehalten werden. (Wenn CheckKey(B) in der obigen Sequenz von Schlüsselereignissen IsKeyPressed(A) aufgerufen hätte, hätte es „true“ zurückgegeben.)

CheckKey() kann den Status in seiner Klasse beibehalten; Dies kann nützlich sein, um Tastenfolgen zu erkennen. Dieses Beispiel zeigt ein etwas komplexeres Setup: Die Anzeige wird umgeschaltet, indem Sie die Ein-/Aus-Taste gedrückt halten und die Lautstärke erhöhen, und das Gerät kann sofort neu gestartet werden, indem Sie die Ein-/Aus-Taste fünfmal hintereinander drücken (ohne weitere dazwischenliegende Tasten):

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 Ihre eigenen Bilder (Fehlersymbol, Installationsanimation, Fortschrittsbalken) mit ScreenRecoveryUI verwenden, können Sie die Variable animation_fps festlegen, um die Geschwindigkeit in Bildern pro Sekunde (FPS) von Animationen zu steuern.

Hinweis: Mit dem aktuellen Skript interlace-frames.py “ können Sie die Informationen „ animation_fps im Bild selbst speichern. In früheren Android-Versionen war es notwendig, animation_fps selbst festzulegen.

Um die Variable animation_fps festzulegen, überschreiben Sie die Funktion ScreenRecoveryUI::Init() in Ihrer Unterklasse. Legen Sie den Wert fest und rufen Sie dann die 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 bereitstellen. Einzelheiten zu Bildern finden Sie unter Wiederherstellungs-UI-Bilder .

Geräteklasse

Nachdem Sie eine RecoveryUI-Implementierung erstellt haben, definieren Sie Ihre Geräteklasse (Unterklasse der integrierten Geräteklasse). Es sollte eine einzelne Instanz Ihrer UI-Klasse erstellen und diese von der Funktion GetUI() zurückgeben:

class TardisDevice : public Device {
  private:
    TardisUI* ui;

  public:
    TardisDevice() :
        ui(new TardisUI) {
    }

    RecoveryUI* GetUI() { return ui; }

Starte wiederherstellung

Die StartRecovery() Methode wird zu Beginn der Wiederherstellung aufgerufen, nachdem die Benutzeroberfläche initialisiert und die Argumente analysiert wurden, aber bevor eine Aktion ausgeführt wurde. Die Standardimplementierung führt nichts aus, sodass Sie dies nicht in Ihrer Unterklasse bereitstellen müssen, wenn Sie nichts zu tun haben:

   void StartRecovery() {
       // ... do something tardis-specific here, if needed ....
    }

Bereitstellung und Verwaltung des Wiederherstellungsmenüs

Das System ruft zwei Methoden auf, um die Liste der Kopfzeilen und die Liste der Artikel abzurufen. In dieser Implementierung werden die oben in der Datei definierten statischen Arrays zurückgegeben:

const char* const* GetMenuHeaders() { return HEADERS; }
const char* const* GetMenuItems() { return ITEMS; }

HandleMenuKey

Stellen Sie als Nächstes eine HandleMenuKey() Funktion bereit, die einen Tastendruck und die aktuelle Menüsichtbarkeit akzeptiert 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 benötigt 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/Textprotokolls. Der Rückgabewert ist eine Ganzzahl. Wenn der Wert 0 oder höher ist, wird dies als Position eines Menüelements verwendet, das sofort aufgerufen wird (siehe die InvokeMenuItem() Methode unten). Ansonsten kann es sich um eine der folgenden vordefinierten Konstanten handeln:

  • kHighlightUp . Verschieben Sie die Menümarkierung zum vorherigen Element
  • kHighlightDown . Bewegen Sie die Menümarkierung zum nächsten Element
  • kInvokeItem . Rufen Sie das aktuell markierte Element auf
  • kNoAction . Machen Sie mit diesem Tastendruck nichts

Wie durch das sichtbare Argument impliziert, 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, sondern nur, wenn die Wiederherstellung inaktiv ist und auf Eingaben wartet.

Trackball-Mechanismen

Wenn Ihr Gerät über einen Trackball-ähnlichen Eingabemechanismus verfügt (erzeugt Eingabeereignisse mit Typ EV_REL und Code REL_Y), synthetisiert die Wiederherstellung die Tastendrücke KEY_UP und KEY_DOWN, wann immer 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 Auslöser für einen Neustart oder das Umschalten der Anzeige verwenden.

Zusatztasten

Um zu prüfen, ob Tasten als Modifikatoren gedrückt gehalten werden, rufen Sie die IsKeyPressed() Methode Ihres eigenen UI-Objekts auf. Beispielsweise würde das Drücken von Alt-W bei der Wiederherstellung auf einigen Geräten einen Datenlöschvorgang starten, unabhängig davon, ob das Menü sichtbar war oder nicht. Sie könnten es so umsetzen:

   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“ „false“ ist, macht es keinen Sinn, die speziellen Werte zurückzugeben, die das Menü manipulieren (Hervorhebung verschieben, hervorgehobenes Element aufrufen), da der Benutzer die Hervorhebung nicht sehen kann. Sie können die Werte jedoch bei Bedarf zurückgeben.

InvokeMenuItem

Stellen Sie als Nächstes eine InvokeMenuItem() Methode bereit, die ganzzahlige Positionen im Array von Elementen, die von GetMenuItems() zurückgegeben werden, Aktionen zuordnet. Verwenden Sie für das Array von Elementen im Tardis-Beispiel:

   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 jedes Mitglied der BuiltinAction-Enumeration zurückgeben, um das System anzuweisen, diese Aktion auszuführen (oder das NO_ACTION-Mitglied, wenn das System nichts tun soll). Hier können Sie zusätzliche Wiederherstellungsfunktionen bereitstellen, die über das System hinausgehen: Fügen Sie ein Element dafür in Ihr Menü ein, führen Sie es hier aus, wenn dieses Menüelement aufgerufen wird, und geben Sie NO_ACTION zurück, damit das System nichts anderes tut.

BuiltinAction enthält die folgenden Werte:

  • KEINE AKTION . Nichts tun.
  • NEUSTART . Beenden Sie die Wiederherstellung und starten Sie das Gerät normal neu.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD . Installieren Sie ein Update-Paket von verschiedenen Orten. Einzelheiten finden Sie unter Sideloading .
  • WIPE_CACHE . Formatieren Sie nur die Cache-Partition neu. Keine Bestätigung erforderlich, da dies relativ harmlos ist.
  • DATEN LÖSCHEN . Formatieren Sie die Benutzerdaten- und Cache-Partitionen neu. Dies wird auch als „Zurücksetzen der Werksdaten“ bezeichnet. Der Benutzer wird gebeten, diese Aktion zu bestätigen, bevor er fortfährt.

Die letzte Methode, WipeData() , ist optional und wird immer dann aufgerufen, wenn ein Datenlöschvorgang initiiert wird (entweder bei der Wiederherstellung über das Menü oder wenn der Benutzer sich dafür entschieden hat, die Daten vom Hauptsystem auf die Werkseinstellungen zurückzusetzen). Diese Methode wird aufgerufen, bevor die Benutzerdaten und Cache-Partitionen gelöscht werden. Wenn Ihr Gerät Benutzerdaten an einem anderen Ort als diesen beiden Partitionen speichert, sollten Sie sie hier löschen. Sie sollten 0 zurückgeben, um einen Erfolg anzuzeigen, und einen anderen Wert für einen Fehler, obwohl der Rückgabewert derzeit ignoriert wird. Die Benutzerdaten und Cache-Partitionen werden gelöscht, unabhängig davon, ob Sie erfolgreich oder fehlschlagen.

   int WipeData() {
       // ... do something tardis-specific here, if needed ....
       return 0;
    }

Gerät herstellen

Fügen Sie abschließend am Ende der Datei „recovery_ui.cpp“ einige Beispiele 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();
}

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 Platinenkonfiguration 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-Benutzeroberfläche

Die Benutzeroberfläche für die Wiederherstellung besteht aus Bildern. Im Idealfall interagieren Benutzer nie mit der Benutzeroberfläche: Während eines normalen Updates startet das Telefon in die Wiederherstellung, füllt den Installationsfortschrittsbalken und startet ohne Eingabe des Benutzers wieder im neuen System. Im Falle eines Problems mit der Systemaktualisierung besteht die einzige Möglichkeit für den Benutzer darin, den Kundendienst anzurufen.

Eine Nur-Bild-Schnittstelle macht eine Lokalisierung überflüssig. Ab Android 5.0 kann das Update jedoch zusammen mit dem Bild eine Textzeichenfolge (z. B. „Installieren des Systemupdates ...“) anzeigen. Einzelheiten finden Sie unter Lokalisierter Wiederherstellungstext .

Android 5.0 und höher

Die Wiederherstellungs-Benutzeroberfläche von Android 5.0 und höher verwendet zwei Hauptbilder: das Fehlerbild und die Installationsanimation .

Bild wird während eines OTA-Fehlers angezeigt

Abbildung 1. icon_error.png

Bild wird während der OTA-Installation angezeigt

Abbildung 2. icon_installing.png

Die Installationsanimation wird als einzelnes PNG-Bild mit verschiedenen Frames der Animation dargestellt, die zeilenweise verschachtelt sind (weshalb Abbildung 2 gequetscht erscheint). Erstellen Sie beispielsweise für eine 200x200-Animation mit sieben Bildern ein einzelnes 200x1400-Bild, wobei das erste Bild die Zeilen 0, 7, 14, 21, ... ist; der zweite Rahmen besteht aus den Zeilen 1, 8, 15, 22, ...; usw. Das kombinierte Bild enthält einen Textblock, der die Anzahl der Animationsbilder und die Anzahl der Bilder pro Sekunde (FPS) angibt. Das Tool bootable/recovery/interlace-frames.py nimmt eine Reihe von Eingabeframes und kombiniert sie zu dem notwendigen zusammengesetzten Bild, das für die Wiederherstellung verwendet wird.

Standard-Images sind in unterschiedlichen Dichten verfügbar und befinden sich in bootable/recovery/res-$DENSITY/images (z. B. bootable/recovery/res-hdpi/images ). Um während der Installation ein statisches Bild zu verwenden, müssen Sie lediglich das Bild icon_installing.png bereitstellen und die Anzahl der Frames in der Animation auf 0 setzen (das Fehlersymbol ist nicht animiert; es ist immer ein statisches Bild).

Android 4.x und früher

Die Wiederherstellungs-Benutzeroberfläche von Android 4.x und früheren Versionen verwendet das Fehlerbild (siehe oben) und die Installationsanimation sowie mehrere Overlay-Bilder:

Bild wird während der OTA-Installation angezeigt

Abbildung 3. icon_installing.png

Bild wird als erstes Overlay angezeigt

Abbildung 4. icon-installing_overlay01.png

Bild wird als siebtes Overlay angezeigt

Abbildung 5. icon_installing_overlay07.png

Während der Installation wird die Bildschirmanzeige erstellt, indem das Bild „icon_installing.png“ gezeichnet und dann einer der Überlagerungsrahmen mit dem richtigen Versatz darüber gezeichnet wird. Hier wird ein rotes Kästchen eingeblendet, um hervorzuheben, wo die Überlagerung über dem Basisbild platziert wird:

Zusammengesetztes Bild der Installation plus erstes Overlay

Abbildung 6. Animationsbild 1 installieren (icon_installing.png + icon_installing_overlay01.png)

Zusammengesetztes Bild der Installation plus siebtes Overlay

Abbildung 7. Animationsrahmen 7 installieren (icon_installing.png + icon_installing_overlay07.png)

Nachfolgende Frames werden angezeigt, indem nur das nächste Overlay-Bild über das bereits vorhandene gezeichnet wird. Das Basisbild wird nicht neu gezeichnet.

Die Anzahl der Frames in der Animation, die gewünschte Geschwindigkeit sowie die x- und y-Versatze der Überlagerung relativ zur Basis werden durch Mitgliedsvariablen der ScreenRecoveryUI-Klasse festgelegt. Wenn Sie benutzerdefinierte Bilder anstelle von Standardbildern verwenden, überschreiben Sie die Init() Methode in Ihrer Unterklasse, um diese Werte für Ihre benutzerdefinierten Bilder zu ändern (Einzelheiten finden Sie unter ScreenRecoveryUI ). Das Skript bootable/recovery/make-overlay.py kann bei der Konvertierung eines Satzes von Bildrahmen in die für die Wiederherstellung benötigte Form „Basisbild + Overlay-Bilder“ helfen, einschließlich der Berechnung der erforderlichen Offsets.

Standard-Images befinden sich in bootable/recovery/res/images . Um während der Installation ein statisches Bild zu verwenden, müssen Sie lediglich das Bild icon_installing.png bereitstellen und die Anzahl der Frames in der Animation auf 0 setzen (das Fehlersymbol ist nicht animiert; es ist immer ein statisches Bild).

Lokalisierter Wiederherstellungstext

Android 5.x zeigt zusammen mit dem Bild eine Textzeichenfolge an (z. B. „Systemaktualisierung wird installiert ...“). Wenn das Hauptsystem in die Wiederherstellung startet, übergibt es das aktuelle Gebietsschema des Benutzers als Befehlszeilenoption an die Wiederherstellung. Für jede anzuzeigende Nachricht umfasst die Wiederherstellung ein zweites zusammengesetztes Bild mit vorgerenderten Textzeichenfolgen für diese Nachricht in jedem Gebietsschema.

Beispielbild von Wiederherstellungstextzeichenfolgen:

Bild des Wiederherstellungstextes

Abbildung 8. Lokalisierter Text für Wiederherstellungsmeldungen

Der Wiederherstellungstext kann die folgenden Meldungen anzeigen:

  • Systemupdate wird installiert...
  • Fehler!
  • Löschen... (beim Durchführen einer Datenlöschung/Zurücksetzen auf die Werkseinstellungen)
  • Kein Befehl (wenn ein Benutzer die Wiederherstellung manuell startet)

Die Android-App in bootable/recovery/tools/recovery_l10n/ rendert Lokalisierungen einer Nachricht und erstellt das zusammengesetzte Bild. Einzelheiten zur Verwendung dieser App finden Sie in den Kommentaren in bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java .

Wenn ein Benutzer die Wiederherstellung manuell startet, ist das Gebietsschema möglicherweise nicht verfügbar und es wird kein Text angezeigt. Machen Sie die Textnachrichten nicht zu einem kritischen Faktor für den Wiederherstellungsprozess.

Hinweis: Die versteckte Schnittstelle, die Protokollmeldungen anzeigt und es dem Benutzer ermöglicht, Aktionen aus dem Menü auszuwählen, ist nur auf Englisch verfügbar.

Fortschrittsbalken

Unter dem Hauptbild (oder der Animation) können Fortschrittsbalken angezeigt werden. Der Fortschrittsbalken wird durch die Kombination zweier Eingabebilder erstellt, die dieselbe Größe haben müssen:

leerer Fortschrittsbalken

Abbildung 9. progress_empty.png

voller Fortschrittsbalken

Abbildung 10. progress_fill.png

Das linke Ende des Füllbilds wird neben dem rechten Ende des leeren Bilds angezeigt, um den Fortschrittsbalken zu erstellen. Die Position der Grenze zwischen den beiden Bildern wird geändert, um den Fortschritt anzuzeigen. Zeigen Sie beispielsweise mit den oben genannten Paaren von Eingabebildern Folgendes an:

Fortschrittsbalken bei 1 %

Abbildung 11. Fortschrittsbalken bei 1 %>

Fortschrittsbalken bei 10 %

Abbildung 12. Fortschrittsbalken bei 10 %

Fortschrittsbalken bei 50 %

Abbildung 13. Fortschrittsbalken 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. Dateinamen müssen mit den oben aufgeführten übereinstimmen; Wenn in diesem Verzeichnis eine Datei gefunden wird, verwendet das Build-System diese anstelle des entsprechenden Standardbilds. Es werden nur PNGs im RGB- oder RGBA-Format mit 8-Bit-Farbtiefe unterstützt.

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

Geräte ohne Bildschirm

Nicht alle Android-Geräte verfügen über Bildschirme. Wenn Ihr Gerät eine Headless-Appliance ist oder über eine reine Audioschnittstelle verfügt, müssen Sie möglicherweise umfangreichere Anpassungen der Wiederherstellungs-Benutzeroberfläche vornehmen. Anstatt eine Unterklasse von ScreenRecoveryUI zu erstellen, erstellen Sie direkt eine Unterklasse der übergeordneten Klasse RecoveryUI.

RecoveryUI verfügt über Methoden zum Behandeln von UI-Vorgängen auf niedrigerer Ebene, z. B. „Anzeige umschalten“, „Fortschrittsbalken aktualisieren“, „Menü anzeigen“, „Menüauswahl ändern“ usw. Sie können diese überschreiben, um eine geeignete Schnittstelle bereitzustellen für Ihr Gerät. Möglicherweise verfügt Ihr Gerät über LEDs, mit denen Sie verschiedene Farben oder Blinkmuster verwenden können, um den Status anzuzeigen, oder Sie können möglicherweise Audio abspielen. (Vielleicht möchten Sie ein Menü oder den Modus „Textanzeige“ überhaupt nicht unterstützen; Sie können den Zugriff darauf mit CheckKey() und HandleMenuKey() Implementierungen verhindern, die niemals die Anzeige einschalten oder einen Menüpunkt auswählen. In diesem Fall , viele der RecoveryUI-Methoden, die Sie bereitstellen müssen, können nur leere Stubs sein.)

Unter bootable/recovery/ui.h finden Sie die Deklaration von RecoveryUI, um zu sehen, welche Methoden Sie unterstützen müssen. RecoveryUI ist abstrakt – einige Methoden sind rein virtuell und müssen von Unterklassen bereitgestellt werden –, es enthält jedoch den Code für die Verarbeitung von Schlüsseleingaben. Sie können dies auch überschreiben, wenn Ihr Gerät keine Schlüssel hat oder Sie diese anders verarbeiten möchten.

Updater

Sie können bei der Installation des Update-Pakets gerätespezifischen Code verwenden, indem Sie Ihre eigenen Erweiterungsfunktionen bereitstellen, die von Ihrem Updater-Skript aus aufgerufen werden können. Hier ist eine Beispielfunktion 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 die gleiche 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* -Zeigern, die die Argumente darstellen. Der Rückgabewert ist ein neu zugewiesener Value* .

Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) {
    if (argc != 2) {
        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
    }

Ihre Argumente wurden zum Zeitpunkt des Aufrufs Ihrer Funktion noch nicht ausgewertet – die Logik Ihrer Funktion bestimmt, welche davon wie oft ausgewertet werden. Somit können Sie mit Erweiterungsfunktionen eigene Kontrollstrukturen implementieren. Call Evaluate() , um ein Expr* -Argument auszuwerten und einen Value* zurückzugeben. Wenn Evaluate() NULL zurückgibt, sollten Sie alle von Ihnen gehaltenen Ressourcen freigeben und sofort NULL zurückgeben (dies führt zu Abbrüchen im Edify-Stack). Andernfalls übernehmen Sie den Besitz des zurückgegebenen Werts und sind dafür verantwortlich, schließlich FreeValue() dafür aufzurufen.

Angenommen, die Funktion benötigt zwei Argumente: einen Schlüssel mit einem Zeichenfolgenwert und ein Bild mit einem Blobwert. Man könnte Argumente wie diese 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;
    }

Die Überprüfung auf NULL und die Freigabe zuvor ausgewerteter Argumente kann bei mehreren Argumenten mühsam sein. Die Funktion ReadValueArgs() kann dies einfacher machen. Anstelle des obigen Codes 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, daher müssen Sie dies hier tun; Es ist bequemer, dies mit einer if- Anweisung zu tun, auf Kosten einer etwas weniger spezifischen Fehlermeldung, wenn sie fehlschlägt. Aber ReadValueArgs() übernimmt die Auswertung jedes Arguments und gibt alle zuvor ausgewerteten Argumente frei (sowie das Setzen einer nützlichen Fehlermeldung), wenn eine der Auswertungen fehlschlägt. Sie können eine praktische ReadValueVarArgs() Funktion zum Auswerten einer variablen Anzahl von Argumenten verwenden (sie gibt ein Array von Value* zurück).

Nachdem Sie die Argumente ausgewertet haben, führen Sie die Arbeit der Funktion aus:

   // 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 auf den Aufrufer über. Der Aufrufer übernimmt den Besitz aller Daten, auf die dieser Value* verweist – insbesondere das Datenelement.

In diesem Fall möchten Sie einen wahren oder falschen Wert zurückgeben, um den Erfolg anzuzeigen. Denken Sie an die Konvention, dass die leere Zeichenfolge falsch ist und alle anderen Zeichenfolgen wahr sind. Sie müssen ein Value-Objekt mit einer mallocierten Kopie der zurückzugebenden Konstantenzeichenfolge mallocieren, da der Aufrufer beides free() ausführt. Vergessen Sie nicht FreeValue() für die Objekte aufzurufen, die Sie durch die Auswertung Ihrer Argumente erhalten haben!

   FreeValue(key);
    FreeValue(image);

    Value* result = malloc(sizeof(Value));
    result->type = VAL_STRING;
    result->data = strdup(successful ? "t" : "");
    result->size = strlen(result->data);
    return result;
}

Die praktische Funktion StringValue() umschließt einen String in ein neues Value-Objekt. Verwenden Sie, um den obigen 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. Benennen Sie gerätespezifische Funktionen gemäß der Konvention device device . whatever , um Konflikte mit künftig hinzugefügten integrierten Funktionen zu vermeiden.

void Register_librecovery_updater_tardis() {
    RegisterFunction("tardis.reprogram", ReprogramTardisFn);
}

Sie können das Makefile jetzt so konfigurieren, dass mit Ihrem Code eine statische Bibliothek erstellt wird. (Dies ist das gleiche Makefile, das zum Anpassen der Wiederherstellungs-Benutzeroberfläche im vorherigen Abschnitt 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 Funktion Register_ libname übereinstimmen.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Konfigurieren Sie abschließend den Build der Wiederherstellung, um Ihre Bibliothek einzubinden. Fügen Sie Ihre Bibliothek zu TARGET_RECOVERY_UPDATER_LIBS hinzu (die mehrere Bibliotheken enthalten kann; alle werden registriert). Wenn Ihr Code von anderen statischen Bibliotheken abhängt, die selbst keine Edify-Erweiterungen sind (d. h. sie verfügen nicht über die Funktion Register_ libname ), 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 Ihr gerätespezifischer Code beispielsweise zlib zum Dekomprimieren von Daten verwenden möchte, würden Sie libz hier 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-Skripte in Ihrem OTA-Paket können Ihre Funktion jetzt wie jede andere aufrufen. Um Ihr Tardis-Gerät neu zu programmieren, könnte das Update-Skript Folgendes enthalten: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . Dabei wird die Einzelargumentversion der integrierten Funktion package_extract_file() verwendet, die den Inhalt einer aus dem Updatepaket extrahierten Datei als Blob zurückgibt, um das zweite Argument für die neue Erweiterungsfunktion zu erzeugen.

OTA-Paketgenerierung

Die letzte Komponente besteht darin, die OTA-Paketgenerierungstools über Ihre gerätespezifischen Daten zu informieren und Updater-Skripte auszugeben, die Aufrufe Ihrer Erweiterungsfunktionen enthalten.

Informieren Sie zunächst das Build-System ü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önnten es stattdessen auch in eine Android.mk einfügen, aber dann muss es durch eine Geräteprüfung geschützt werden, da alle Android.mk-Dateien im Baum geladen werden, egal welches Gerät erstellt wird. (Wenn Ihr Baum mehrere Geräte umfasst, möchten Sie beim Erstellen des Tardis-Geräts nur die Datei tardis.dat hinzufügen.)

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 Radiodateien genannt; Sie haben möglicherweise nichts mit dem Geräteradio zu tun (falls vorhanden). Es handelt sich lediglich um undurchsichtige Datenblöcke, die das Build-System in die Zieldateien .zip kopiert, die von den OTA-Generierungstools verwendet werden. Wenn Sie einen Build durchführen, wird tardis.dat in der Datei target-files.zip als RADIO/tardis.dat gespeichert. Sie können add-radio-file mehrmals aufrufen, um so viele Dateien hinzuzufügen, wie Sie möchten.

Python-Modul

Um die Release-Tools zu erweitern, schreiben Sie ein Python-Modul (muss „releasetools.py“ heißen), das die Tools aufrufen können, sofern 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 behandelt den Fall der Generierung eines inkrementellen OTA-Pakets. Nehmen wir für dieses Beispiel an, dass Sie die Tardis nur dann neu programmieren müssen, 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 im Modul die folgenden Funktionen bereitstellen (implementieren Sie nur die, die Sie benötigen).

FullOTA_Assertions()
Wird kurz vor Beginn der Generierung eines vollständigen OTA aufgerufen. Dies ist ein guter Ort, um Aussagen über den aktuellen Zustand des Geräts abzugeben. Geben Sie keine Skriptbefehle aus, die Änderungen am Gerät vornehmen.
FullOTA_InstallBegin()
Wird aufgerufen, nachdem alle Aussagen 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 irgendetwas anderes auf dem Gerät geändert wird.
FullOTA_InstallEnd()
Wird am Ende der Skriptgenerierung aufgerufen, nachdem die Skriptbefehle zum Aktualisieren der Start- und Systempartitionen ausgegeben wurden. Sie können auch zusätzliche Befehle für gerätespezifische Updates ausgeben.
IncrementalOTA_Assertions()
Ähnlich wie FullOTA_Assertions() wird jedoch beim Generieren eines inkrementellen Updatepakets aufgerufen.
IncrementalOTA_VerifyBegin()
Wird aufgerufen, nachdem alle Aussagen 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 irgendetwas anderes auf dem Gerät geändert wird.
IncrementalOTA_VerifyEnd()
Wird am Ende der Überprüfungsphase aufgerufen, wenn das Skript die Bestätigung abgeschlossen hat, dass die Dateien, die es berühren wird, den erwarteten Startinhalt haben. Zu diesem Zeitpunkt wurde nichts am Gerät geändert. Sie können auch Code für zusätzliche gerätespezifische Überprüfungen ausgeben.
IncrementalOTA_InstallBegin()
Wird aufgerufen, nachdem überprüft wurde, dass die zu patchenden Dateien den erwarteten Status haben, aber bevor Änderungen vorgenommen wurden. Sie können Befehle für gerätespezifische Updates ausgeben, die ausgeführt werden müssen, bevor irgendetwas anderes auf dem Gerät geändert wird.
IncrementalOTA_InstallEnd()
Ähnlich wie das vollständige Gegenstück zum OTA-Paket wird dies am Ende der Skriptgenerierung aufgerufen, nachdem die Skriptbefehle zum Aktualisieren der Start- und Systempartitionen ausgegeben wurden. Sie können auch zusätzliche Befehle für gerätespezifische Updates ausgeben.

Hinweis: Wenn das Gerät die Stromversorgung verliert, kann die OTA-Installation von vorne beginnen. Seien Sie darauf vorbereitet, mit Geräten zurechtzukommen, auf denen diese Befehle bereits ganz oder teilweise ausgeführt wurden.

Übergeben Sie Funktionen an Infoobjekte

Übergeben Sie Funktionen an ein einzelnes Infoobjekt, das verschiedene nützliche Elemente enthält:

  • info.input_zip . (Nur vollständige OTAs) Das zipfile.ZipFile Objekt für die Eingabezieldateien .zip.
  • info.source_zip . (Nur inkrementelle OTAs) Das zipfile.ZipFile Objekt für die Quellzieldateien .zip (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 Zielzieldateien .zip (der Build, den das inkrementelle Paket auf dem Gerät ablegt).
  • info.output_zip . Paket wird erstellt; Ein zum Schreiben geöffnetes zipfile.ZipFile Objekt. Verwenden Sie common.ZipWriteStr(info.output_zip, filename , data ), um dem Paket eine Datei hinzuzufügen.
  • info.script . Skriptobjekt, an das Sie Befehle anhängen können. Rufen Sie info.script.AppendExtra( script_text ) auf, um Text in das Skript auszugeben. Stellen Sie sicher, dass der Ausgabetext mit einem Semikolon endet, damit er nicht auf später ausgegebene Befehle stößt.

Einzelheiten zum Info-Objekt finden Sie in der Python Software Foundation-Dokumentation für ZIP-Archive .

Geben Sie den Modulstandort an

Geben Sie den Speicherort des releasetools.py-Skripts Ihres Geräts in Ihrer BoardConfig.mk-Datei 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 (in diesem Beispiel device/yoyodyne/common ). Am besten definieren Sie den Speicherort des Skripts „releasetools.py“ explizit. Beim Erstellen des Tardis-Geräts ist das Skript „releasetools.py“ in der ZIP-Datei „target-files“ ( META/releasetools.py ) enthalten.

Wenn Sie die Release-Tools ausführen (entweder img_from_target_files oder ota_from_target_files ), wird das Skript „releasetools.py“ in der ZIP-Datei der Zieldateien, sofern vorhanden, dem Skript 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, die die höchste Priorität hat. Dadurch können Sie Fehler korrigieren, Änderungen an den Releasetools-Erweiterungen vornehmen und diese Änderungen auf alte Zieldateien anwenden.

Wenn Sie nun ota_from_target_files ausführen, wird automatisch das gerätespezifische Modul aus der ZIP-Datei „target_files“ übernommen 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 Kommentaren zu ota_from_target_files in build/make/tools/releasetools/ota_from_target_files .

Seitenlademechanismus

Recovery verfügt über einen Sideloading- Mechanismus für die manuelle Installation eines Update-Pakets, ohne es drahtlos vom Hauptsystem herunterzuladen. Sideloading ist nützlich zum Debuggen oder zum Vornehmen von Änderungen auf Geräten, auf denen das Hauptsystem nicht gestartet werden kann.

In der Vergangenheit erfolgte das Seitenladen durch das Laden von Paketen von der SD-Karte des Geräts. Im Falle eines nicht bootfähigen Geräts kann das Paket mit einem anderen Computer auf die SD-Karte gelegt und dann die SD-Karte in das Gerät eingesetzt werden. Um Android-Geräte ohne austauschbaren externen Speicher zu unterstützen, unterstützt die Wiederherstellung zwei zusätzliche Mechanismen zum Seitenladen: das Laden von Paketen aus der Cache-Partition und das Laden über USB mit adb.

Um jeden Sideload-Mechanismus aufzurufen, kann Device::InvokeMenuItem() Methode Ihres Geräts die folgenden Werte von BuiltinAction zurückgeben:

  • APPLY_EXT . Laden Sie ein Update-Paket von einem externen Speicher (Verzeichnis /sdcard ) quer. Ihre „recovery.fstab“ muss den Mountpunkt /sdcard definieren. Dies ist nicht auf Geräten verwendbar, die eine SD-Karte mit einem symbolischen Link zu /data (oder einem ähnlichen Mechanismus) emulieren. /data steht normalerweise nicht für die Wiederherstellung zur Verfügung, da es möglicherweise verschlüsselt ist. Die Wiederherstellungs-Benutzeroberfläche zeigt ein Menü mit ZIP-Dateien in /sdcard an und ermöglicht dem Benutzer die Auswahl einer davon.
  • APPLY_CACHE . Ähnlich wie beim Laden eines Pakets von /sdcard , außer dass stattdessen das Verzeichnis /cache (das immer für die Wiederherstellung verfügbar ist ) verwendet wird. Auf dem regulären System ist /cache nur für privilegierte Benutzer beschreibbar, und wenn das Gerät nicht bootfähig ist, kann überhaupt nicht in das /cache Verzeichnis geschrieben werden (was diesen Mechanismus von begrenztem Nutzen macht).
  • APPLY_ADB_SIDELOAD . Ermöglicht dem Benutzer, 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 Mini-Version unterstützt nur einen einzigen Befehl: adb sideload filename . Die benannte Datei wird vom Hostcomputer an das Gerät gesendet, das sie dann überprüft und installiert, als ob sie sich im lokalen Speicher befunden hätte.

Ein paar Vorbehalte:

  • Es wird nur der USB-Transport unterstützt.
  • Wenn Ihre Wiederherstellung adbd normal ausführt (normalerweise bei Userdebug- und Eng-Builds), wird das Gerät 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 alle fehl).
  • Sie können den ADB-Sideload-Modus auf dem Gerät nicht verlassen. Um den Vorgang abzubrechen, können Sie /dev/null (oder irgendetwas anderes, das kein gültiges Paket ist) als Paket senden, dann kann das Gerät es nicht überprüfen und den Installationsvorgang stoppen. Die CheckKey() -Methode der RecoveryUI-Implementierung wird weiterhin für Tastendrücke aufgerufen, sodass Sie eine Tastenfolge bereitstellen können, die das Gerät neu startet und im ADB-Sideload-Modus funktioniert.