Codice specifico del dispositivo

Il sistema di ripristino include diversi ganci per l'inserimento del codice specifico del dispositivo in modo che OTA gli aggiornamenti possono aggiornare anche parti del dispositivo diverse dal sistema Android (ad esempio, la banda di base o un processore radio).

Le sezioni e gli esempi seguenti personalizzano il dispositivo tardis prodotto dalla yoyodyne.

Mappa di partizione

A partire da Android 2.3, la piattaforma supporta i dispositivi flash eMMc e il file system ext4 che esegue su questi dispositivi. Supporta anche i dispositivi flash Memory Technology (MTD) e lo strumento yaffs2 il file system di versioni precedenti.

Il file della mappa di partizione è specificato da TARGET_RECOVERY_FSTAB; questo file è utilizzato sia binario di recupero e gli strumenti per la creazione di pacchetti. Puoi specificare il nome del file della mappa in TARGET_RECOVERY_FSTAB in BoardConfig.mk.

Un file di mappa di partizione di esempio potrebbe avere il seguente aspetto:

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

Ad eccezione di /sdcard, che è facoltativo, tutti i punti di montaggio in è necessario definire un esempio (i dispositivi possono anche aggiungere partizioni aggiuntive). Sono supportate cinque tipi di file system:

yaffs2
Un file system yaffs2 sopra un dispositivo flash MTD. "dispositivo" deve essere il nome della partizione MTD e deve apparire in /proc/mtd.
mtd
Una partizione MTD non elaborata, utilizzata per le partizioni avviabili come avvio e ripristino. MTD non è è stato effettivamente montato, ma il punto di montaggio è utilizzato come chiave per individuare la partizione. "dispositivo" deve essere il nome della partizione MTD in /proc/mtd.
estensione 4
Un file system ext4 sopra un dispositivo flash eMMc. "dispositivo" deve essere il percorso del dispositivo di blocco.
emmc
Un dispositivo a blocchi eMMc non elaborato, utilizzato per le partizioni di avvio, come l'avvio e il ripristino. Simile a il tipo mtd, eMMc non viene mai effettivamente montato, ma la stringa del punto di montaggio viene utilizzata per individuare il dispositivo nella tabella.
vfat
Un file system FAT posizionato sopra un dispositivo a blocchi, in genere per dispositivi di archiviazione esterni come una scheda SD. La dispositivo è il dispositivo a blocchi; device2 è un secondo dispositivo a blocchi che il sistema tenta di montare se il montaggio del dispositivo principale non funziona (per compatibilità con schede SD che potrebbero o meno essere formattata con una tabella di partizione).

Tutte le partizioni devono essere montate nella directory radice (ad esempio, il valore del punto di montaggio deve iniziano con una barra e non hanno altre barre). Questa limitazione si applica solo al montaggio file system in fase di ripristino; il sistema principale può essere montato ovunque. Le directory /boot, /recovery e /misc devono essere tipi non elaborati (mtd o emmc), mentre le directory /system, /data, /cache e /sdcard (se disponibili) devono essere tipi di file system (yaffs2, ext4 o vfat).

A partire da Android 3.0, il file recovery.fstab presenta un campo facoltativo aggiuntivo, opzioni. Al momento l'unica opzione definita è length , che ti consente di specificare specifica la lunghezza della partizione. Questa lunghezza viene utilizzata per la riformattazione della partizione (ad esempio, per la partizione dati utente durante un'operazione di cancellazione dei dati/ripristino dei dati di fabbrica o per la la partizione di sistema durante l'installazione di un pacchetto OTA completo). Se il valore della lunghezza è negativo, la dimensione da formattare viene presa aggiungendo il valore di lunghezza alla dimensione della partizione reale. Per istanza, impostando "length=-16384" significa che gli ultimi 16 kB di quella partizione non saranno sovrascritta quando la partizione viene riformattata. Questo supporta funzionalità come la crittografia la partizione dati utente (dove sono archiviati i metadati di crittografia alla fine della partizione non devono essere sovrascritti).

Nota. I campi device2 e options sono facoltativi e creano ambiguità durante l'analisi. Se il valore nel quarto campo della riga inizia con "/" viene considerata una voce device2 ; se la voce non inizia con "/" viene considerato un campo opzioni.

Animazione di avvio

I produttori di dispositivi hanno la possibilità di personalizzare l'animazione mostrata quando un dispositivo Android è in fase di avvio. Per farlo, crea un file .zip organizzato e posizionato in base alla specifiche in formato bootanimation.

Per dispositivi Android Things, puoi caricare il file compresso nella console Android Things per includere le immagini in il prodotto selezionato.

Nota: queste immagini devono rispettare le linee guida per il brand Android. Per le linee guida per il brand, fai riferimento alla sezione relativa ad Android Marketing partner Hub.

UI di ripristino

Per supportare dispositivi con diversi hardware disponibili (pulsanti fisici, LED, schermi e così via), puoi personalizzare l'interfaccia di ripristino per visualizzare lo stato e accedere all'app funzionalità nascoste per ogni dispositivo.

Il tuo obiettivo è creare una piccola libreria statica con un paio di oggetti C++ per fornire per funzionalità specifiche in base al dispositivo. Il file bootable/recovery/default_device.cpp viene utilizzato per impostazione predefinita e rappresenta una buona punto di partenza da copiare durante la scrittura di una versione di questo file per il tuo dispositivo.

Nota: qui potrebbe essere visualizzato il messaggio Nessun comando. Per attivare/disattivare tieni premuto il tasto di accensione mentre premi il tasto Alza il volume. Se i tuoi dispositivi non hanno entrambi i pulsanti, premi a lungo un pulsante per attivare/disattivare il testo.

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

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

Funzioni intestazione ed elemento

La classe Dispositivo richiede funzioni per restituire intestazioni ed elementi che appaiono nell'elenco menu di ripristino. Le intestazioni descrivono come utilizzare il menu (vale a dire i controlli per modificare/selezionare il elemento evidenziato).

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 };

Nota: le linee lunghe sono troncate (non a capo), quindi mantieni la larghezza schermo del tuo dispositivo.

Personalizza checkKey

A questo punto, definisci l'implementazione RecoveryUI del dispositivo. Questo esempio presuppone che Il dispositivo tardis dispone di uno schermo che ti consente di ereditarlo dalla Implementazione di ScreenRecoveryUI (consulta le istruzioni per dispositivi senza schermo. L'unica funzione per personalizzare da ScreenRecoveryUI è CheckKey(), che esegue la procedura iniziale la gestione asincrona delle chiavi:

class TardisUI : public ScreenRecoveryUI {
  public:
    virtual KeyAction CheckKey(int key) {
        if (key == KEY_HOME) {
            return TOGGLE;
        }
        return ENQUEUE;
    }
};

Costanti KEY

Le costanti KEY_* sono definite in linux/input.h. CheckKey() è chiamata a prescindere da cosa sta succedendo nel resto del processo di ripristino: quando il menu è disattivato, quando sia attivo, durante l'installazione del pacchetto, durante la cancellazione dei dati utente e così via. Può restituire uno dei quattro costanti:

  • ATTIVA/DISATTIVA. Attiva o disattiva la visualizzazione del menu e/o del log di testo
  • RIAVVIA. Riavvia subito il dispositivo
  • IGNORA. Ignora questa pressione di un tasto
  • ENQUEUE. accoda il tasto premuto per utilizzarlo in modo sincrono (ovvero dal prompt sistema di menu se il display è abilitato)

CheckKey() viene chiamato ogni volta che un evento key-down è seguito da un evento key-up per la stessa chiave. (La sequenza di eventi A-down B-down B-up A-up genera solo CheckKey(B) sta chiamando.) CheckKey() può chiamare IsKeyPressed(), per scoprire se gli altri tasti vengono premuti. (Nel precedente sequenza di eventi chiave, se CheckKey(B) ha chiamato IsKeyPressed(A) sarebbe stato vero.

CheckKey() può mantenere lo stato nella sua classe; Questo può essere utile per rilevare sequenze di tasti. Questo esempio mostra una configurazione leggermente più complessa: la visualizzazione viene attivata/disattivata tenendo premuto il tasto di accensione e premendo il tasto Alza il volume e il dispositivo può essere riavviato immediatamente premendo il tasto di accensione cinque volte di seguito (senza altri tasti intermedi):

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;
    }
};

Interfaccia utente per il ripristino dello schermo

Quando utilizzi immagini personalizzate (icona di errore, animazione di installazione, barre di avanzamento) con ScreenRecoveryUI puoi impostare la variabile animation_fps per controllare la velocità in fotogrammi al secondo (f/s) delle animazioni.

Nota: lo script interlace-frames.py corrente ti consente di archiviare le informazioni animation_fps nell'immagine stessa. Nelle versioni precedenti di Android era necessario impostare animation_fps personalmente.

Per impostare la variabile animation_fps, sostituisci il valore ScreenRecoveryUI::Init() nella tua sottoclasse. Imposta il valore, quindi richiama il metodo parent Init() funzione per completare l'inizializzazione. Il valore predefinito (20 FPS) corrisponde alle immagini di ripristino predefinite; quando utilizzi queste immagini, non devi fornire una funzione Init(). Per maggiori dettagli sulle immagini, vedi Immagini UI di ripristino.

Classe dispositivo

Dopo aver implementato RecoveryUI, definisci la classe del dispositivo (sottoclasse del integrata Dispositivo). Deve creare una singola istanza della classe UI e restituire che dalla funzione GetUI():

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

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

    RecoveryUI* GetUI() { return ui; }

Avvia ripristino

Il metodo StartRecovery() viene chiamato all'inizio del ripristino, dopo che l'interfaccia utente sono stati inizializzati e dopo che gli argomenti sono stati analizzati, ma prima che sia stata eseguita qualsiasi azione prese. L'implementazione predefinita non fa nulla, quindi non è necessario specificarlo nel se non devi fare nulla:

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

Fornire e gestire il menu di recupero

Il sistema chiama due metodi per ottenere l'elenco di righe di intestazione e l'elenco di elementi. In questo vengono restituiti gli array statici definiti all'inizio del file:

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

Tasto Menu Handle

Successivamente, fornisci una funzione HandleMenuKey(), che preveda la pressione di un tasto e la visibilità del menu e decide quale azione intraprendere:

   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;
    }

Il metodo prende un codice chiave (che è stato precedentemente elaborato e accodato dal CheckKey() dell'oggetto UI) e lo stato attuale del menu/log di testo visibilità. Il valore restituito è un numero intero. Se il valore è 0 o superiore, viene considerato come posizione di una voce di menu, che viene richiamata immediatamente (vedi InvokeMenuItem() di seguito). In caso contrario, può essere uno dei seguenti: costanti predefinite:

  • kHighlightUp. Sposta l'evidenziazione del menu all'elemento precedente
  • KHighlightdown. Sposta l'evidenziazione del menu all'elemento successivo
  • kInvokeItem. Richiama l'elemento attualmente evidenziato
  • kNoAction. Non fare nulla con la pressione dei tasti

Come sottinteso dall'argomento visibile, HandleMenuKey() viene chiamato anche se il menu è non visibile. A differenza di CheckKey(), non viene chiamato durante il ripristino ad esempio la cancellazione dei dati o l'installazione di un pacchetto, ma viene chiamato solo quando il ripristino è inattivo e in attesa di input.

Meccanismi trackball

Se il dispositivo ha un meccanismo di input simile a una trackball (genera eventi di input di tipo EV_REL e il codice REL_Y), il ripristino sintetizza i tasti KEY_UP e KEY_DOWN ogni volta che Il dispositivo di input simile a una trackball segnala il movimento sull'asse Y. Devi solo mappare KEY_UP e KEY_DOWN eventi nelle azioni del menu. Questa mappatura non viene eseguita per CheckKey(), quindi non puoi utilizzare i movimenti della trackball come attivatori per il riavvio o attivare/disattivare il display.

Tasti di modifica

Per verificare se i tasti vengono premuti come modificatori, chiama il metodo IsKeyPressed() del tuo oggetto UI. Ad esempio, su alcuni dispositivi premendo Alt-W durante il ripristino, la cancellazione dei dati, indipendentemente dal fatto che il menu fosse visibile o meno. Potresti implementare questo approccio:

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

Nota: se visible è false, non ha senso restituire l'elemento speciale valori che manipolano il menu (sposta l'evidenziazione, richiama l'elemento evidenziato) perché l'utente non può per vedere i momenti salienti. Tuttavia, se vuoi, puoi restituire i valori.

RichiamaMenuItem

Successivamente, fornisci un metodo InvokeMenuItem() che mappa posizioni intere nell'array di elementi restituiti da GetMenuItems() alle azioni. Per l'array di elementi nella esempio di tardis, utilizza:

   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;
        }
    }

Questo metodo può restituire qualsiasi membro dell'enum builtinAction per indicare al sistema di prenderlo (o il membro NO_ACTION se desideri che il sistema non faccia nulla). Questo è il posto in cui fornisci funzionalità di ripristino aggiuntive oltre a quelle presenti nel sistema: aggiungi un elemento nella il tuo menu, eseguilo qui quando tale voce di menu viene richiamata e restituisce NO_ACTION affinché il sistema non fa nient'altro.

builtinAction contiene i seguenti valori:

  • NESSUNA AZIONE. Non fare niente.
  • RIAVVIA. Esci dal ripristino e riavvia normalmente il dispositivo.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Installa un pacchetto di aggiornamento da varie luoghi. Per maggiori dettagli, vedi Sideload.
  • WIPE_CACHE. Riformatta solo la partizione cache. Non è richiesta la conferma perché relativamente innocua.
  • WIPE_DATA. Riformattare i dati utente e le partizioni della cache, noto anche come dati di fabbrica resettare. All'utente viene chiesto di confermare questa azione prima di procedere.

L'ultimo metodo, WipeData(), è facoltativo e viene chiamato ogni volta che viene eseguita la cancellazione dei dati (dal recupero tramite il menu o quando l'utente ha scelto di eseguire ripristino dei dati di fabbrica dal sistema principale). Questo metodo viene chiamato prima che i dati utente vengono cancellati definitivamente. Se il tuo dispositivo memorizza i dati utente in un luogo diverso da questi due partizioni, devi resettarlo qui. Devi restituire 0 per indicare l'esito positivo e un'altra per l'errore, sebbene al momento il valore restituito venga ignorato. I dati utente e la cache vengono cancellati indipendentemente dal fatto che tu restituisca un errore o un errore.

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

Marca dispositivo

Infine, includi del boilerplate alla fine del file recovery_ui.cpp per Funzione make_device() che crea e restituisce un'istanza della classe Dispositivo:

class TardisDevice : public Device {
   // ... all the above methods ...
};

Device* make_device() {
    return new TardisDevice();
}

Dopo aver completato il file recovery_ui.cpp, crealo e collegalo al ripristino sul dispositivo. Nella Android.mk, crea una libreria statica che contenga solo questo file C++:

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)

Quindi, nella configurazione della scheda di questo dispositivo, specifica la tua libreria statica come valore TARGET_RECOVERY_UI_LIB.

device/yoyodyne/tardis/BoardConfig.mk
 [...]

# device-specific extensions to the recovery UI
TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis

Immagini UI di ripristino

L'interfaccia utente di ripristino è composta da immagini. Idealmente, gli utenti non interagiscono mai con la UI: Durante un normale aggiornamento, lo smartphone avvia il ripristino, riempie la barra di avanzamento dell'installazione e si avvia di nuovo nel nuovo sistema senza alcun input dell'utente. Nel caso in cui un sistema di aggiornamento, l'unica azione dell'utente che può essere intrapresa è chiamare l'assistenza clienti.

Un'interfaccia con solo immagini elimina la necessità di localizzazione. Tuttavia, a partire da Android 5.0 aggiornamento può visualizzare una stringa di testo (ad es. "Installazione dell'aggiornamento di sistema...") insieme all'immagine. Per maggiori dettagli, vedi Testo di recupero localizzato.

Android 5.0 e versioni successive

L'interfaccia utente di ripristino di Android 5.0 e versioni successive utilizza due immagini principali: l'immagine dell'errore e l'animazione di installazione.

immagine mostrata durante l&#39;errore OTA

Figura 1. icon_error.png

immagine mostrata durante l&#39;installazione dell&#39;OTA

Figura 2. icon_installing.png

L'animazione di installazione è rappresentata come una singola immagine PNG con diversi frame dei animazione interlacciata per riga (motivo per cui la Figura 2 appare schiacciata). Ad esempio, per un Animazione 200 x 200 a sette frame, crea una singola immagine 200 x 1400 in cui il primo fotogramma è le righe 0, 7 14, 21, ...; il secondo frame è le righe 1, 8, 15, 22, ...; e così via. L'immagine combinata include un blocco di testo che indica il numero di frame dell'animazione e di frame al secondo (f/s). Lo strumento bootable/recovery/interlace-frames.py accetta un insieme di frame di input e le combina nell'immagine composita necessaria utilizzata dal ripristino.

Le immagini predefinite sono disponibili con densità diverse e si trovano in bootable/recovery/res-$DENSITY/images (ad es. bootable/recovery/res-hdpi/images). Per utilizzare un'immagine statica durante l'installazione, devi fornire solo l'immagine icon_installing.png e impostare il numero di frame nella a 0 (l'icona di errore non è animata; è sempre un'immagine statica).

Android 4.x e versioni precedenti

L'interfaccia utente di ripristino per Android 4.x e versioni precedenti utilizza l'immagine error (mostrata sopra) e animazione in corso in fase di installazione più diverse immagini overlay:

immagine mostrata durante l&#39;installazione dell&#39;OTA

Figura 3. icon_installing.png

immagine mostrata come prima
sovrapposizione

Figura 4. icon-installing_overlay01.png

immagine mostrata come settima
sovrapposizione

Figura 5. icon_installing_overlay07.png

Durante l'installazione, la visualizzazione sullo schermo viene creata disegnando il file icon_installing.png e poi disegnando sopra uno dei frame di overlay nell'offset corretto. In questo caso, viene sovrapposto per evidenziare il punto in cui l'overlay è posizionato sull'immagine di base:

immagine composita di
overlay installazione più primo

Figura 6. Installazione del frame dell'animazione 1 (icon_installing.png + icon_installing_overlay01.png)

immagine composita di
Settimo overlay installazione più

Figura 7. Installazione del frame dell'animazione 7 (icon_installing.png + icon_installing_overlay07.png)

I frame successivi vengono mostrati disegnando solo l'immagine di overlay successiva sopra il sono già presenti. l'immagine di base non viene ridisegnata.

Il numero di frame nell'animazione, la velocità desiderata e gli offset x e y dell'overlay rispetto alla base sono impostate dalle variabili membro della classe ScreenRecoveryUI. Quando si utilizza immagini personalizzate anziché immagini predefinite, sostituisci il metodo Init() in per modificare questi valori per le immagini personalizzate (per dettagli, vedi ScreenRecoveryUI). Lo script bootable/recovery/make-overlay.py può aiutarti a convertire un insieme di cornici immagine a "immagine di base + immagini overlay" richiesta per il recupero, compresa l'elaborazione dei dati le compensazioni necessarie.

Le immagini predefinite si trovano in bootable/recovery/res/images. Per usare un'immagine statica durante l'installazione è sufficiente fornire l'immagine icon_installing.png e impostare il numero di fotogrammi dell'animazione a 0 (l'icona di errore non è animata; è sempre un'immagine statica).

Testo di recupero localizzato

Android 5.x mostra una stringa di testo (ad es. "Installazione dell'aggiornamento di sistema...") e dell'immagine. Quando il sistema principale si avvia nel ripristino, passa alle impostazioni internazionali correnti dell'utente come l'opzione a riga di comando per il ripristino. Per ogni messaggio da visualizzare, il recupero include un secondo immagine composita con stringhe di testo prerenderizzate per il messaggio in ogni lingua.

Immagine di esempio di stringhe di testo per il ripristino:

immagine del testo di ripristino

Figura 8. Testo localizzato per i messaggi di recupero

Il testo di recupero può mostrare i seguenti messaggi:

  • Installazione aggiornamento di sistema...
  • Errore!
  • Cancellazione... (durante la cancellazione dei dati o il ripristino dei dati di fabbrica)
  • Nessun comando (quando un utente avvia manualmente il ripristino)

L'app per Android in bootable/recovery/tools/recovery_l10n/ esegue il rendering delle localizzazioni di un messaggio e crea l'immagine composita. Per maggiori dettagli sull'utilizzo di questa app, consulta commenti in bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java.

Quando un utente avvia manualmente il ripristino, le impostazioni internazionali potrebbero non essere disponibili e non è presente alcun testo visualizzati. Non rendere i messaggi fondamentali per il processo di ripristino.

Nota:l'interfaccia nascosta che visualizza i messaggi di log e consente all'utente di seleziona le azioni dal menu è disponibile solo in inglese.

Barre di avanzamento

Le barre di avanzamento possono essere visualizzate sotto l'immagine principale (o l'animazione). La barra di avanzamento è combinando due immagini di input, che devono avere le stesse dimensioni:

barra di avanzamento vuota

Figura 9. progress_empty.png

barra di avanzamento completa

Figura 10. progress_fill.png

L'estremità sinistra dell'immagine di riempimento viene visualizzata accanto all'estremità destra della vuota per fare la barra di avanzamento. La posizione del confine tra i due le immagini vengono modificate per indicare l'avanzamento. Ad esempio, con le coppie di immagini di input indicate sopra, Rete Display:

barra di avanzamento all&#39;1%

Figura 11. Barra di avanzamento all'1%>

barra di avanzamento al 10%

Figura 12. Barra di avanzamento al 10%

barra di avanzamento al 50%

Figura 13. Barra di avanzamento al 50%

Puoi fornire versioni specifiche di queste immagini inserendole in (in esempio) device/yoyodyne/tardis/recovery/res/images di Google. I nomi file devono corrispondere a quelli elencati sopra. quando un file viene trovato in quella directory, il sistema di compilazione della build lo utilizza preferito rispetto all'immagine predefinita corrispondente. Soltanto i file PNG in RGB o Sono supportati i formati RGBA con profondità di colore a 8 bit.

Nota. In Android 5.x, se la lingua è nota per il ripristino ed è un lingua da destra a sinistra (RTL) (arabo, ebraico e così via), la barra di avanzamento si riempie da destra a rimanenti.

Dispositivi senza schermo

Non tutti i dispositivi Android dispongono di uno schermo. Se il dispositivo è un elettrodomestico headless o dispone di interfaccia solo audio, potrebbe essere necessario eseguire una personalizzazione più completa della UI di ripristino. Invece della creazione di una sottoclasse di ScreenRecoveryUI, direttamente la sottoclasse principale RecoveryUI.

RecoveryUI dispone di metodi per gestire le operazioni di livello inferiore dell'interfaccia utente, ad esempio "attiva/disattiva la visualizzazione", "aggiorna la barra di avanzamento" "mostra il menu" "cambia la selezione del menu" e così via. Puoi eseguire l'override per fornire un'interfaccia appropriata per il tuo dispositivo. Forse il tuo dispositivo ha dei LED dove puoi usare colori o sequenze di lampeggiamenti diversi per indicare lo stato, o magari riprodurre audio. (Forse non vuoi supportare affatto un menu o la modalità di "visualizzazione del testo"; puoi non è possibile accedervi con CheckKey() e HandleMenuKey() implementazioni che non attivano mai la visualizzazione o selezionano un menu molto utile. In questo caso, molti dei metodi RecoveryUI che devi fornire possono essere vuoti. stub.)

Consulta bootable/recovery/ui.h per la dichiarazione di RecoveryUI per conoscere i metodi che devi supportare. RecoveryUI è astratto: alcuni metodi sono puri e virtuali e devono essere forniti ma contiene il codice per eseguire l'elaborazione degli input chiave. Puoi sostituirlo se il dispositivo non ha chiavi o vuoi elaborarle in modo diverso.

Google Updater

Puoi utilizzare un codice specifico per il dispositivo nell'installazione del pacchetto di aggiornamento fornendo il tuo le proprie funzioni di estensione che possono essere richiamate dallo script di aggiornamento. Ecco un esempio funzione per il dispositivo tardis:

device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h>
#include <string.h>

#include "edify/expr.h"

Ogni funzione di estensione ha la stessa firma. Gli argomenti sono il nome con cui è stata richiamata, un cookie State*, il numero di argomenti in arrivo array di puntatori Expr* che rappresentano gli argomenti. Il valore restituito è Value* assegnato di recente.

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);
    }

Gli argomenti non sono stati valutati nel momento in cui viene chiamata la funzione, ovvero della logica determina quali vengono valutate e quante volte. Di conseguenza, puoi utilizzare l'estensione per implementare le tue strutture di controllo. Call Evaluate() da valutare un argomento Expr* , restituendo un Value*. Se Evaluate() restituisce NULL, devi liberare tutte le risorse che tieni e restituire immediatamente NULL (questo propaga e interrompe lo stack edify). In caso contrario, acquisirai la proprietà del Valore restituito responsabile delle chiamate FreeValue().

Supponiamo che la funzione abbia bisogno di due argomenti: una chiave con valore di stringa e una con valore BLOB image. Potresti leggere argomenti come questo:

   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;
    }

Verificare la presenza di NULL e liberare gli argomenti valutati in precedenza può essere noioso per più argomenti. La funzione ReadValueArgs() può semplificare l'operazione. Al posto del codice in alto, potresti aver scritto questo:

   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() non esegue il controllo del tipo, quindi devi farlo qui; è più facile utile per farlo con un'istruzione if al costo di produrre un po' meno in caso di errore. Ma ReadValueArgs() gestisce la valutazione ogni argomento e liberando tutti quelli valutati in precedenza (oltre a impostare un un messaggio di errore) se una delle valutazioni ha esito negativo. Puoi utilizzare uno dei seguenti ReadValueVarArgs() funzione di convenienza per valutare un numero variabile di (restituisce un array di Value*).

Dopo aver valutato gli argomenti, esegui il lavoro della funzione:

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

Il valore restituito deve essere un oggetto Value*. la proprietà di questo oggetto passerà l'utente. Il chiamante acquisisce la proprietà di tutti i dati a cui punta Value*: nello specifico il membro dei dati.

In questo caso, lo scopo è restituire un valore true o false per indicare che l'operazione è andata a buon fine. Ricorda che convenzione secondo cui la stringa vuota è false e tutte le altre stringhe siano true. Tu deve Malloc un oggetto Value con una copia Malloc della stringa costante da restituire, dato che il chiamante free() . Non dimenticare di chiamare il numero FreeValue() che hai ottenuto valutando i tuoi argomenti.

   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;
}

La funzione di convenienza StringValue() aggrega una stringa in un nuovo oggetto Value. Utilizzalo per scrivere il codice riportato sopra in modo più breve:

   FreeValue(key);
    FreeValue(image);

    return StringValue(strdup(successful ? "t" : ""));
}

Per collegare funzioni all'interprete edify, fornisci la funzione Register_foo dove foo è il nome della libreria statica che contiene questo codice. Chiama RegisterFunction() per registrare ogni funzione di estensione. Di convenzione, nomina le funzioni specifiche del dispositivo device.whatever da evitare aggiunti in conflitto con le funzioni integrate future.

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

Ora puoi configurare il makefile per creare una libreria statica con il tuo codice. (È lo stesso makefile utilizzato per personalizzare l'UI di recupero nella sezione precedente; sul dispositivo potrebbero essere presenti librerie statiche definite qui.)

device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := recovery_updater.c
LOCAL_C_INCLUDES += bootable/recovery

Il nome della libreria statica deve corrispondere al nome della libreria Funzione Register_libname contenuta al suo interno.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Infine, configura la build di ripristino per eseguire il pull della libreria. Aggiungi la tua raccolta a TARGET_RECOVERY_UPDATER_LIBS (che può contenere più librerie; vengono tutte registrate). Se il codice dipende da altre librerie statiche che non sono a loro volta edificate estensioni (ad es. non hanno una funzione Register_libname), puoi elencarli TARGET_RECOVERY_UPDATER_EXTRA_LIBS per collegarli al programma di aggiornamento senza chiamare il loro funzione di registrazione (inesistente). Ad esempio, se il codice specifico del tuo dispositivo volesse utilizzare zlib per decomprimere i dati, includerai libz qui.

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 +=

Gli script del programma di aggiornamento nel tuo pacchetto OTA possono ora chiamare la tua funzione come qualsiasi altro. Per riprogrammare tuo dispositivo tardis, lo script di aggiornamento potrebbe contenere: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . Questa utilizza la versione a argomento singolo della funzione integrata package_extract_file(), che restituisce i contenuti di un file estratto dal pacchetto di aggiornamento sotto forma di blob da produrre il secondo argomento alla nuova funzione di estensione.

Generazione di pacchetti OTA

Il componente finale è far conoscere gli strumenti di generazione dei pacchetti OTA dati specifici del dispositivo ed emettono script di aggiornamento che includono chiamate alle funzioni dell'estensione.

Innanzitutto, fai in modo che il sistema di compilazione venga a conoscenza di un BLOB di dati specifico per un dispositivo. Presupporre i dati file è in device/yoyodyne/tardis/tardis.dat, dichiara quanto segue nel AndroidBoard.mk del dispositivo:

device/yoyodyne/tardis/AndroidBoard.mk
  [...]

$(call add-radio-file,tardis.dat)

Potresti anche inserirlo in un file Android.mk, ma poi deve essere protetto da un dispositivo. verifica, poiché tutti i file Android.mk nell'albero vengono caricati indipendentemente dal dispositivo l'IA generativa. Se la struttura ad albero include più dispositivi, vuoi che il file tardis.dat venga aggiunto solo quando che costruisce il dispositivo tardis.)

device/yoyodyne/tardis/Android.mk
  [...]

# an alternative to specifying it in AndroidBoard.mk
ifeq (($TARGET_DEVICE),tardis)
  $(call add-radio-file,tardis.dat)
endif

Questi vengono chiamati file radio per motivi storici; potrebbero non avere nulla a che fare con alla radio del dispositivo (se presente). Si tratta semplicemente di blob opachi di dati in cui il sistema di compilazione copia il file .zip dei file di destinazione utilizzato dagli strumenti di generazione OTA. Quando crei una build, tardis.dat è archiviato nel file target-files.zip come RADIO/tardis.dat. Puoi chiamare add-radio-file più volte per aggiungere tutti i file che vuoi.

Modulo Python

Per estendere gli strumenti di rilascio, scrivi un modulo Python (deve essere denominato releasetools.py). Gli strumenti che possiamo chiamare, se presente. Esempio:

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"));""")

Il caso di generazione di un pacchetto OTA incrementale viene gestito da una funzione separata. Per questo Ad esempio, supponiamo che tu debba riprogrammare i tardis solo quando il file tardis.dat è stato modificato. tra due build.

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"));""")

Funzioni del modulo

Nel modulo puoi fornire le seguenti funzioni (implementa solo quelle di cui hai bisogno).

FullOTA_Assertions()
È stato chiamato poco prima dell'inizio della generazione di un'agenzia di viaggi online completa. È un buon posto per emettere dichiarazioni sullo stato attuale del dispositivo. Non emettere comandi di script che apportano modifiche alla dispositivo.
FullOTA_InstallBegin()
Richiamato dopo che sono trascorse tutte le affermazioni sullo stato del dispositivo, ma prima di qualsiasi modifica sono state apportate. Puoi emettere comandi per aggiornamenti specifici del dispositivo che devono essere eseguiti prima tutte le altre funzionalità sul dispositivo sono state modificate.
FullOTA_InstallEnd()
Viene chiamato alla fine della generazione dello script, dopo i comandi per aggiornare boot e sono state emesse partizioni di sistema. Puoi anche emettere altri comandi per dispositivi mobili.
IncrementalOTA_Assertions()
Simile a FullOTA_Assertions(), ma viene chiamato durante la generazione di una di aggiornamento del pacchetto.
IncrementalOTA_VerifyBegin()
Richiamato dopo che sono trascorse tutte le asserzioni sullo stato del dispositivo ma prima che un'applicazione. Puoi emettere comandi per gli aggiornamenti specifici del dispositivo che devono essere eseguiti prima di qualsiasi operazione sul dispositivo è stata modificata.
IncrementalOTA_VerifyEnd()
Viene chiamato al termine della fase di verifica, quando lo script ha completato la conferma del i file che toccherà avranno i contenuti iniziali previsti. A questo punto, sulla dispositivo è stato modificato. Puoi anche emettere codice per altre richieste verifiche.
IncrementalOTA_InstallBegin()
Richiamato dopo che è stato verificato che i file a cui applicare la patch hanno il livello previsto stato before ma prima che siano state apportate modifiche. Puoi emettere comandi per di aggiornamenti specifici del dispositivo, che devono essere eseguiti prima che qualsiasi altro dispositivo venga modificato.
IncrementalOTA_InstallEnd()
Analogamente alla controparte del pacchetto OTA completo, viene indicata alla fine dello script generata, dopo che i comandi di script per aggiornare le partizioni di avvio e di sistema sono stati vengono emesse. Puoi anche inviare comandi aggiuntivi per aggiornamenti specifici del dispositivo.

Nota: se il dispositivo si spegne, l'installazione OTA potrebbe riavviarsi dalla partendo da zero. Preparati ad affrontare i dispositivi su cui sono già stati eseguiti questi comandi, completamente o parzialmente.

Passare funzioni agli oggetti informazioni

Passa le funzioni a un singolo oggetto informazioni che contiene vari elementi utili:

  • info.input_zip. (Solo OTA completi) L'oggetto zipfile.ZipFile per l'attributo file di destinazione di input .zip.
  • info.source_zip. (Solo OTA incrementali) L'oggetto zipfile.ZipFile per il file ZIP di origine target-files (la build già presente sul dispositivo quando è in fase di installazione).
  • info.target_zip. (Solo OTA incrementali) L'oggetto zipfile.ZipFile per il file ZIP di destinazione dei file di destinazione (la build che il pacchetto incrementale inserisce sul dispositivo).
  • info.output_zip. Creazione del pacchetto in corso. un oggetto zipfile.ZipFile aperto per la scrittura. Usa common.ZipWriteStr(info.output_zip, filename, data) per aggiungere un al pacchetto.
  • info.script. Oggetto script a cui è possibile aggiungere comandi. Chiama info.script.AppendExtra(script_text) per inserire testo nello script. Assicurati che il testo dell'output termini con un punto e virgola in modo che non rientri nei comandi emessi in seguito.

Per maggiori dettagli sull'oggetto info, consulta documentazione di Python Software Foundation per gli archivi ZIP.

Specifica la posizione del modulo

Specifica la posizione dello script releasetools.py del dispositivo nel file BoardConfig.mk:

device/yoyodyne/tardis/BoardConfig.mk
 [...]

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

Se TARGET_RELEASETOOLS_EXTENSIONS non è impostata, il valore predefinito è il Directory $(TARGET_DEVICE_DIR)/../common (device/yoyodyne/common in questo esempio). È preferibile definire esplicitamente la posizione dello script releasetools.py. Durante la creazione del dispositivo tardis, lo script releasetools.py è incluso nei file di destinazione File .zip (META/releasetools.py ).

Quando esegui gli strumenti di rilascio (img_from_target_files o ota_from_target_files), lo script releasetools.py nel file ZIP di destinazione-files, se presente è preferita rispetto a quella dell'albero delle origini Android. Puoi anche indicare esplicitamente specificare il percorso delle estensioni specifiche per dispositivo con il -s (oppure --device_specific), che ha la massima priorità. Questo consente di correggere gli errori, apportare modifiche nelle estensioni releasetools e applicare queste modifiche ai vecchi file di destinazione.

Ora, quando esegui ota_from_target_files, la funzionalità acquisisce automaticamente specifico del dispositivo dal file .zip target_files e lo utilizza durante la generazione di OTA di spedizione:

./build/make/tools/releasetools/ota_from_target_files \
    -i PREVIOUS-tardis-target_files.zip \
    dist_output/tardis-target_files.zip \
    incremental_ota_update.zip

In alternativa, puoi specificare estensioni specifiche per dispositivo quando esegui ota_from_target_files.

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

Nota: per un elenco completo delle opzioni, fai riferimento alle ota_from_target_files commenti in build/make/tools/releasetools/ota_from_target_files.

Meccanismo di sideload

Il ripristino ha un meccanismo di sideload per installare manualmente un pacchetto di aggiornamento senza scaricandolo over-the-air dal sistema principale. Il sideload è utile per eseguire il debug o a tutti i dispositivi su cui non è possibile avviare il sistema principale.

In passato, il sideload avveniva tramite il caricamento dei pacchetti dalla scheda SD del dispositivo. nel nel caso di un dispositivo che non si avvia, il pacchetto può essere inserito sulla scheda SD utilizzando computer, quindi sulla scheda SD inserita nel dispositivo. Per supportare i dispositivi Android senza rimovibile, il ripristino supporta due meccanismi aggiuntivi per il sideload: caricare i pacchetti dalla partizione cache e caricarli tramite USB utilizzando adb.

Per richiamare ogni meccanismo di sideload, viene usato il metodo Device::InvokeMenuItem() del dispositivo può restituire i seguenti valori di builtinAction:

  • APPLY_EXT. Esegui il sideload di un pacchetto di aggiornamento dalla memoria esterna ( /sdcard) ). Il file recovery.fstab deve definire il punto di montaggio /sdcard . Questo è non utilizzabile su dispositivi che emulano una scheda SD con un collegamento simbolico a /data (o alcune meccanismo simile). /data in genere non è disponibile per il recupero perché possono essere crittografati. La UI di recupero mostra un menu di file ZIP in /sdcard e permette all'utente di selezionarne una.
  • APPLY_CACHE Simile al caricamento di un pacchetto da /sdcard, ad eccezione del fatto che Viene utilizzata la directory /cache (che è sempre disponibile per il recupero) . Dal sistema normale, solo gli utenti con privilegi possono scrivere in /cache , e se il dispositivo non è avviabile, non è possibile scrivere la directory /cache (il che rende questo meccanismo di utilità limitata).
  • APPLY_ADB_SIDELOAD. Consente all'utente di inviare un pacco al dispositivo tramite cavo USB e lo strumento di sviluppo ADB. Quando questo meccanismo viene richiamato, il ripristino avvia il proprio del daemon adbd per consentire ad adb su un computer host connesso di comunicare con il daemon. Questo mini supporta un solo comando: adb sideload filename. Il file denominato viene inviato dalla macchina host al dispositivo, che quindi verifica lo installa come se fosse stato occupato nello spazio di archiviazione locale.

Alcune avvertenze:

  • È supportato solo il trasporto USB.
  • Se il ripristino esegue normalmente adbd (solitamente true per userdebug ed eng build), verrà arrestato mentre il dispositivo è in modalità sideload adb e verrà riavviato quando adb il sideload ha terminato di ricevere un pacchetto. In modalità sideload adb, nessun comando adb ne esegue altri. di sideload lavoro ( logcat, reboot, push, pull, shell e così via non hanno avuto esito positivo).
  • Non puoi uscire dalla modalità sideload adb sul dispositivo. Per interrompere, puoi inviare /dev/null (o qualsiasi altro pacchetto non valido) come pacchetto e il dispositivo non riuscirà a verificarla e la procedura di installazione verrà interrotta. RecoveryUI il metodo CheckKey() dell'implementazione continuerà a essere chiamato per la pressione dei tasti, in modo che tu possa fornire una sequenza di tasti che riavvii il dispositivo e funzioni in modalità sideload adb.