Il sistema di ripristino include diversi hook per l'inserimento di codice specifico del dispositivo, in modo che gli aggiornamenti OTA possano aggiornare anche parti del dispositivo diverse dal sistema Android (ad es. la baseband o il processore radio).
Le seguenti sezioni ed esempi personalizzano il dispositivo tardis prodotto dal fornitore yoyodyne.
Mappa delle partizioni
A partire da Android 2.3, la piattaforma supporta i dispositivi flash eMMC e il file system ext4 che viene eseguito su questi dispositivi. Supporta anche i dispositivi flash Memory Technology Device (MTD) e il file system yaffs2 delle versioni precedenti.
Il file della mappa delle partizioni è specificato da TARGET_RECOVERY_FSTAB; questo file viene utilizzato sia dal binario di ripristino sia dagli strumenti di creazione dei pacchetti. Puoi specificare il nome del file della mappa in TARGET_RECOVERY_FSTAB in BoardConfig.mk.
Un file di mappatura delle partizioni 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 questo
esempio devono essere definiti (i dispositivi possono anche aggiungere partizioni aggiuntive). Esistono cinque tipi di file system supportati:
- yaffs2
-
Un file system yaffs2 su un dispositivo flash MTD. "device" deve essere il nome della partizione MTD
e deve essere visualizzato in
/proc/mtd
. - mtd
-
Una partizione MTD non elaborata, utilizzata per partizioni avviabili come boot e recovery. MTD non è
effettivamente montato, ma il punto di montaggio viene utilizzato come chiave per individuare la partizione. "device"
deve essere il nome della partizione MTD in
/proc/mtd
. - ext4
- Un file system ext4 su un dispositivo flash eMMc. "device" deve essere il percorso del dispositivo a blocchi.
- emmc
- Un dispositivo a blocchi eMMC non elaborato, utilizzato per partizioni avviabili come boot e recovery. Simile al tipo mtd, eMMc non viene mai montato, ma la stringa del punto di montaggio viene utilizzata per individuare il dispositivo nella tabella.
- vfat
-
Un file system FAT su un dispositivo a blocchi, in genere per l'archiviazione esterna, ad esempio una scheda SD. Il
dispositivo è il dispositivo a blocchi; device2 è un secondo dispositivo a blocchi che il sistema tenta di montare se
il montaggio del dispositivo principale non riesce (per la compatibilità con le schede SD che potrebbero o meno
essere formattate con una tabella delle partizioni).
Tutte le partizioni devono essere montate nella directory principale (ovvero il valore del punto di montaggio deve iniziare con una barra e non avere altre barre). Questa limitazione si applica solo al montaggio dei filesystem nel ripristino; il sistema principale è libero di montarli ovunque. Le directory
/boot
,/recovery
e/misc
devono essere di tipo non elaborato (mtd o emmc), mentre le directory/system
,/data
,/cache
e/sdcard
(se disponibili) devono essere di tipo file system (yaffs2, ext4 o vfat).
A partire da Android 3.0, il file recovery.fstab acquisisce un campo facoltativo aggiuntivo, options. Al momento l'unica opzione definita è length , che ti consente di specificare in modo esplicito la lunghezza della partizione. Questa lunghezza viene utilizzata durante la riformattazione della partizione (ad es. per la partizione userdata durante un'operazione di cancellazione dei dati/ripristino dei dati di fabbrica o per la partizione di sistema durante l'installazione di un pacchetto OTA completo). Se il valore della lunghezza è negativo, la dimensione da formattare viene ottenuta aggiungendo il valore della lunghezza alla dimensione effettiva della partizione. Ad esempio, l'impostazione "length=-16384" indica che gli ultimi 16.000 byte della partizione non verranno sovrascritti quando la partizione viene riformattata. Ciò supporta funzionalità come la crittografia della partizione userdata (in cui i metadati di crittografia sono archiviati alla fine della partizione che non deve essere sovrascritta).
Nota: i campi device2 e options sono facoltativi e creano ambiguità nell'analisi. Se la voce nel quarto campo della riga inizia con il carattere "/", viene considerata una voce device2; se la voce non inizia con il carattere "/", viene considerato un campo options.
Animazione di avvio
I produttori di dispositivi hanno la possibilità di personalizzare l'animazione mostrata all'avvio di un dispositivo Android. A tale scopo, crea un file .zip organizzato e posizionato in base alle specifiche del formato bootanimation.
Per i dispositivi Android Things, puoi caricare il file compresso nella console Android Things per includere le immagini nel 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 Android del Partner Marketing Hub.
UI di ripristino
Per supportare dispositivi con hardware disponibile diverso (pulsanti fisici, LED, schermi e così via), puoi personalizzare l'interfaccia di ripristino per visualizzare lo stato e accedere alle funzionalità nascoste azionate manualmente per ogni dispositivo.
Il tuo obiettivo è creare una piccola libreria statica con un paio di oggetti C++ per fornire la
funzionalità specifica del dispositivo. Il file
bootable/recovery/default_device.cpp
viene utilizzato per impostazione predefinita e rappresenta un buon
punto di partenza da copiare quando scrivi una versione di questo file per il tuo dispositivo.
Nota:qui potresti visualizzare il messaggio Nessun comando. Per attivare/disattivare il testo, 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 qualsiasi 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 di intestazione e articolo
La classe Device richiede funzioni per restituire intestazioni ed elementi visualizzati nel menu di ripristino nascosto. Le intestazioni descrivono come utilizzare il menu (ad es. i controlli per modificare/selezionare l'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 righe lunghe vengono troncate (non mandate a capo), quindi tieni presente la larghezza dello schermo del dispositivo.
Personalizza CheckKey
Dopodiché, definisci l'implementazione di RecoveryUI del dispositivo. Questo esempio presuppone che il dispositivo
tardis abbia uno schermo, quindi puoi ereditare dall'implementazione
ScreenRecoveryUI integrata (vedi le istruzioni per i
dispositivi senza schermo). L'unica funzione da
personalizzare da ScreenRecoveryUI è CheckKey()
, che gestisce
inizialmente le chiavi in modo asincrono:
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()
viene
chiamato indipendentemente da ciò che accade nel resto del ripristino: quando il menu è disattivato, quando
è attivo, durante l'installazione del pacchetto, durante la cancellazione dei dati utente e così via. Può restituire una delle quattro
costanti:
- ATTIVA/DISATTIVA. Attiva o disattiva la visualizzazione del menu e/o del log di testo
- RIAVVIA. Riavvia immediatamente il dispositivo
- IGNORA. Ignora questa pressione di tasti
- ENQUEUE. Metti in coda questa pressione dei tasti da utilizzare in modo sincrono (ovvero dal sistema di menu di ripristino 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 comporta solo la chiamata di
CheckKey(B)
.) CheckKey()
può chiamare
IsKeyPressed()
per scoprire se altri tasti sono premuti. (Nella sequenza
di eventi chiave riportata sopra, se CheckKey(B)
avesse chiamato IsKeyPressed(A)
, avrebbe
restituito true.)
CheckKey()
può mantenere lo stato nella sua classe; ciò può essere utile per rilevare
sequenze di tasti. Questo esempio mostra una configurazione leggermente più complessa: il display viene attivato
tenendo premuto il tasto di accensione e premendo il tasto del volume su 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; } };
ScreenRecoveryUI
Quando utilizzi le tue immagini (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 (FPS) delle animazioni.
Nota:lo script interlace-frames.py
attuale consente di
memorizzare le informazioni animation_fps
nell'immagine stessa. Nelle versioni precedenti di
Android era necessario impostare animation_fps
manualmente.
Per impostare la variabile animation_fps
, esegui l'override della
funzione ScreenRecoveryUI::Init()
nella sottoclasse. Imposta il valore, quindi chiama la
funzione parent Init()
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 informazioni dettagliate sulle immagini, vedi
Immagini dell'interfaccia utente di ripristino.
Classe del dispositivo
Dopo aver implementato RecoveryUI, definisci la classe del dispositivo (sottoclasse della
classe Device integrata). Deve creare una singola istanza della classe UI e restituirla
dalla funzione GetUI()
:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
StartRecovery
Il metodo StartRecovery()
viene chiamato all'inizio del recupero, dopo l'inizializzazione dell'UI e l'analisi degli argomenti, ma prima che venga intrapresa qualsiasi azione. L'implementazione predefinita non fa nulla, quindi non è necessario fornirla nella sottoclasse se non hai nulla da fare:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
Fornire e gestire il menu di ripristino
Il sistema chiama due metodi per ottenere l'elenco delle righe di intestazione e l'elenco degli elementi. In questa implementazione, restituisce gli array statici definiti nella parte superiore del file:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
HandleMenuKey
Successivamente, fornisci una funzione HandleMenuKey()
che accetta la pressione di un tasto e la visibilità attuale 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 accetta un codice chiave (precedentemente elaborato e messo in coda dal metodo CheckKey()
dell'oggetto UI) e lo stato attuale della visibilità del menu/registro di testo. Il valore restituito è un numero intero. Se il valore è 0 o superiore, viene considerato come la
posizione di una voce di menu, che viene richiamata immediatamente (vedi il
metodo InvokeMenuItem()
di seguito). In caso contrario, può essere una delle seguenti
costanti predefinite:
- kHighlightUp. Sposta l'evidenziazione del menu sull'elemento precedente
- kHighlightDown. Sposta l'evidenziazione del menu sull'elemento successivo
- kInvokeItem. Richiama l'elemento attualmente evidenziato
- kNoAction. Non fare nulla con questa pressione dei tasti
Come suggerito dall'argomento visibile, HandleMenuKey()
viene chiamato anche se il menu non è
visibile. A differenza di CheckKey()
, non viene chiamato mentre il ripristino esegue
un'operazione come la cancellazione dei dati o l'installazione di un pacchetto. Viene chiamato solo quando il ripristino è inattivo
e in attesa di input.
Meccanismi di trackball
Se il tuo dispositivo ha un meccanismo di input simile a una trackball (genera eventi di input di tipo EV_REL
e codice REL_Y), il ripristino sintetizza le pressioni dei tasti KEY_UP e KEY_DOWN ogni volta che
il dispositivo di input simile a una trackball segnala un movimento sull'asse Y. Tutto ciò che devi fare è mappare gli eventi KEY_UP e
KEY_DOWN sulle azioni del menu. Questa mappatura non avviene per
CheckKey()
, quindi non puoi utilizzare i movimenti della trackball come trigger per il riavvio o
l'attivazione/disattivazione del display.
Tasti di modifica
Per verificare che i tasti vengano tenuti premuti come modificatori, chiama il metodo IsKeyPressed()
del tuo oggetto UI. Ad esempio, su alcuni dispositivi la pressione di Alt+W nel ripristino avvia una
cancellazione dei dati indipendentemente dalla visibilità del menu. Puoi implementare in questo modo:
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 i valori speciali che manipolano il menu (spostano l'evidenziazione, richiamano l'elemento evidenziato) poiché l'utente non può vedere l'evidenziazione. Tuttavia, se vuoi, puoi ripristinare i valori.
InvokeMenuItem
Successivamente, fornisci un metodo InvokeMenuItem()
che mappa le posizioni intere nell'array
di elementi restituiti da GetMenuItems()
alle azioni. Per l'array di elementi nell'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'enumerazione BuiltinAction per indicare al sistema di eseguire l'azione (o il membro NO_ACTION se vuoi che il sistema non faccia nulla). Questo è il punto in cui fornire funzionalità di recupero aggiuntive oltre a quelle presenti nel sistema: aggiungi un elemento nel menu, eseguilo qui quando viene richiamato l'elemento di menu e restituisci NO_ACTION in modo che il sistema non faccia altro.
BuiltinAction contiene i seguenti valori:
- NO_ACTION. Non fare niente.
- RIAVVIA. Esci dal ripristino e riavvia normalmente il dispositivo.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Installare un pacchetto di aggiornamento da varie posizioni. Per maggiori dettagli, vedi Caricamento laterale.
- WIPE_CACHE. Formatta solo la partizione della cache. Nessuna conferma richiesta in quanto si tratta di un'azione relativamente innocua.
- WIPE_DATA. Riformattare le partizioni userdata e cache, operazione nota anche come ripristino dati di fabbrica. All'utente viene chiesto di confermare questa azione prima di procedere.
L'ultimo metodo, WipeData()
, è facoltativo e viene chiamato ogni volta che viene avviata un'operazione di cancellazione dei dati (dal ripristino tramite il menu o quando l'utente ha scelto di eseguire un
ripristino dei dati di fabbrica dal sistema principale). Questo metodo viene chiamato prima che le partizioni dei dati utente e della cache
vengano cancellate. Se il dispositivo memorizza dati utente in una posizione diversa da queste due
partizioni, devi cancellarli qui. Devi restituire 0 per indicare la riuscita e un altro
valore per l'errore, anche se attualmente il valore restituito viene ignorato. Le partizioni dei dati utente e della cache
vengono cancellate indipendentemente dal fatto che venga restituito un esito positivo o negativo.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
Marca del dispositivo
Infine, includi un testo standard alla fine del file recovery_ui.cpp per la
funzione make_device()
che crea e restituisce un'istanza della classe Device:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Creare un link al recupero del dispositivo
Dopo aver completato il file recovery_ui.cpp, compilalo e collegalo al ripristino sul dispositivo. In 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 per questo dispositivo, specifica la tua libreria statica come valore di TARGET_RECOVERY_UI_LIB.
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
Immagini della UI di ripristino
L'interfaccia utente di ripristino è costituita da immagini. Idealmente, gli utenti non interagiscono mai con la UI: Durante un aggiornamento normale, lo smartphone si avvia in modalità di ripristino, riempie la barra di avanzamento dell'installazione e si avvia di nuovo nel nuovo sistema senza input da parte dell'utente. In caso di problemi di aggiornamento del sistema, l'unica azione che l'utente può intraprendere è chiamare l'assistenza clienti.
Un'interfaccia solo con immagini elimina la necessità di localizzazione. Tuttavia, a partire da Android 5.0, l'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 errore e l'animazione installazione.
![]() Figura 1. icon_error.png |
![]() Figura 2. icon_installing.png |
L'animazione di installazione è rappresentata come una singola immagine PNG con diversi frame dell'animazione interlacciati per riga (motivo per cui la Figura 2 appare schiacciata). Ad esempio, per un'animazione
200x200 di sette fotogrammi, crea una singola immagine 200x1400 in cui il primo fotogramma è costituito dalle righe 0, 7,
14, 21 e così via; il secondo fotogramma è costituito dalle righe 1, 8, 15, 22 e così via. L'immagine combinata include un
blocco di testo che indica il numero di fotogrammi dell'animazione e il numero di fotogrammi al secondo
(FPS). Lo strumento bootable/recovery/interlace-frames.py
prende un insieme di frame di input
e li combina nell'immagine composita necessaria per il recupero.
Le immagini predefinite sono disponibili in diverse densità 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 solo fornire l'immagine icon_installing.png e impostare il numero di frame nell'animazione su 0 (l'icona di errore non è animata, è sempre un'immagine statica).
Android 4.x e versioni precedenti
L'UI di ripristino di Android 4.x e versioni precedenti utilizza l'immagine errore (mostrata sopra) e l'animazione installazione, oltre a diverse immagini di overlay:
![]() Figura 3. icon_installing.png |
![]() Figura 4. icon-installing_overlay01.png |
![]() Figura 5. icon_installing_overlay07.png |
Durante l'installazione, la visualizzazione sullo schermo viene creata disegnando l'immagine icon_installing.png e poi uno dei frame di overlay sopra, con l'offset corretto. Qui, una casella rossa è sovrapposta per evidenziare il punto in cui l'overlay viene posizionato sopra l'immagine di base:
![]() Figura 6. Installazione del frame dell'animazione 1 (icon_installing.png + icon_installing_overlay01.png) |
![]() Figura 7. Installazione del frame dell'animazione 7 (icon_installing.png + icon_installing_overlay07.png) |
I frame successivi vengono visualizzati disegnando solo l'immagine di overlay successiva sopra ciò che è già presente; l'immagine di base non viene ridisegnata.
Il numero di frame nell'animazione, la velocità desiderata e gli offset X e Y della sovrapposizione
rispetto alla base sono impostati dalle variabili membro della classe ScreenRecoveryUI. Quando utilizzi
immagini personalizzate anziché immagini predefinite, esegui l'override del metodo Init()
nella
sottoclasse per modificare questi valori per le immagini personalizzate (per maggiori dettagli, vedi
ScreenRecoveryUI). Lo script
bootable/recovery/make-overlay.py
può aiutarti a convertire un insieme di frame di immagini
nel formato "immagine di base + immagini di overlay" necessario per il recupero, incluso il calcolo degli
offset necessari.
Le immagini predefinite si trovano in bootable/recovery/res/images
. Per utilizzare un'immagine statica
durante l'installazione, devi fornire solo l'immagine icon_installing.png e impostare il numero di
fotogrammi nell'animazione su 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 in corso…") insieme all'immagine. Quando il sistema principale si avvia in modalità di ripristino, passa le impostazioni internazionali correnti dell'utente come opzione della riga di comando per il ripristino. Per ogni messaggio da visualizzare, il recupero include una seconda immagine composita con stringhe di testo pre-renderizzate per quel messaggio in ogni lingua.
Immagine di esempio delle stringhe di testo di recupero:

Figura 8. Testo localizzato per i messaggi di recupero
Il messaggio di recupero può visualizzare i seguenti messaggi:
- Installazione dell'aggiornamento di sistema in corso…
- Errore!
- Cancellazione in corso… (durante la cancellazione dei dati/il ripristino dei dati di fabbrica)
- Nessun comando (quando un utente esegue l'avvio in modalità di ripristino manualmente)
L'app per Android in bootable/recovery/tools/recovery_l10n/
esegue il rendering delle localizzazioni
di un messaggio e crea l'immagine composita. Per informazioni dettagliate sull'utilizzo di questa app, consulta i commenti in bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
.
Quando un utente esegue l'avvio in modalità di ripristino manualmente, le impostazioni internazionali potrebbero non essere disponibili e non viene visualizzato alcun testo. Non rendere i messaggi critici per la procedura di recupero.
Nota:l'interfaccia nascosta che mostra i messaggi di log e consente all'utente di selezionare le azioni dal menu è disponibile solo in inglese.
Barre di avanzamento
Le barre di avanzamento possono essere visualizzate sotto l'immagine (o l'animazione) principale. La barra di avanzamento è creata combinando due immagini di input, che devono avere le stesse dimensioni:

Figura 9. progress_empty.png

Figura 10. progress_fill.png
L'estremità sinistra dell'immagine piena viene visualizzata accanto all'estremità destra dell'immagine vuota per formare la barra di avanzamento. La posizione del confine tra le due immagini viene modificata per indicare l'avanzamento. Ad esempio, con le coppie di immagini di input riportate sopra, visualizza:

Figura 11. Barra di avanzamento all'1%>

Figura 12. Barra di avanzamento al 10%

Figura 13. Barra di avanzamento al 50%
Puoi fornire versioni specifiche per dispositivo di queste immagini inserendole in (in questo
esempio) device/yoyodyne/tardis/recovery/res/images
. I nomi file devono corrispondere a quelli elencati sopra. Quando viene trovato un file in questa directory, il
sistema di compilazione lo utilizza preferibilmente all'immagine predefinita corrispondente. Sono supportati solo i PNG in formato RGB o
RGBA con profondità di colore a 8 bit.
Nota:in Android 5.x, se le impostazioni internazionali sono note al ripristino e sono in una lingua con scrittura da destra a sinistra (arabo, ebraico e così via), la barra di avanzamento si riempie da destra a sinistra.
Dispositivi senza schermo
Non tutti i dispositivi Android hanno schermi. Se il tuo dispositivo è un elettrodomestico headless o ha un'interfaccia solo audio, potrebbe essere necessario personalizzare in modo più approfondito la UI di ripristino. Invece di creare una sottoclasse di ScreenRecoveryUI, crea una sottoclasse direttamente della classe padre RecoveryUI.
RecoveryUI dispone di metodi per la gestione di operazioni dell'interfaccia utente di livello inferiore, come "attivare/disattivare la visualizzazione",
"aggiornare la barra di avanzamento", "mostrare il menu", "modificare la selezione del menu" e così via. Puoi eseguire l'override
di questi metodi per fornire un'interfaccia appropriata per il tuo dispositivo. Forse il tuo dispositivo ha dei LED che
puoi usare con diversi colori o sequenze di lampeggio per indicare lo stato, oppure puoi riprodurre
audio. Forse non vuoi supportare un menu o la modalità "visualizzazione testo "; puoi
impedirne l'accesso con implementazioni di CheckKey()
e
HandleMenuKey()
che non attivano mai la visualizzazione o selezionano una voce di menu. In questo caso, molti dei metodi RecoveryUI che devi fornire possono essere solo stub vuoti.)
Consulta bootable/recovery/ui.h
per la dichiarazione di RecoveryUI per vedere quali metodi
devi supportare. RecoveryUI è astratta: alcuni metodi sono virtuali puri e devono essere forniti dalle
sottoclassi, ma contiene il codice per l'elaborazione degli input della tastiera. Puoi anche ignorare
questa impostazione se il tuo dispositivo non ha tasti o se vuoi elaborarli in modo diverso.
Google Updater
Puoi utilizzare codice specifico per il dispositivo nell'installazione del pacchetto di aggiornamento fornendo le tue funzioni di estensione che possono essere chiamate dall'interno dello script di aggiornamento. Ecco una funzione di esempio 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 chiamata la funzione, un cookie State*
, il numero di argomenti in entrata e un array di puntatori Expr*
che rappresentano gli argomenti. Il valore restituito è un
Value*
appena allocato.
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); }
I tuoi argomenti non sono stati valutati al momento della chiamata della funzione. La logica della funzione determina quali vengono valutati e quante volte. Pertanto, puoi utilizzare le funzioni di estensione per implementare le tue strutture di controllo. Call Evaluate()
per valutare
un argomento Expr*
, restituendo un valore Value*
. Se Evaluate()
restituisce NULL, devi liberare le risorse che stai utilizzando e restituire immediatamente NULL (in questo modo
le interruzioni vengono propagate verso l'alto nello stack edify). In caso contrario, diventi proprietario del valore restituito e
sei responsabile di chiamare alla fine
FreeValue()
.
Supponiamo che la funzione richieda due argomenti: una chiave con valore stringa e un'immagine con valore blob. 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; }
Il controllo di NULL e la liberazione degli argomenti valutati in precedenza possono diventare noiosi per più
argomenti. La funzione ReadValueArgs()
può semplificare questa operazione. Invece del codice
precedente, avresti potuto scrivere 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 dei tipi, quindi devi farlo qui. È più
comodo farlo con un'istruzione if a costo di produrre un messaggio di errore un po' meno
specifico in caso di errore. Tuttavia, ReadValueArgs()
gestisce la valutazione
di ogni argomento e la liberazione di tutti gli argomenti valutati in precedenza (oltre a impostare un messaggio di errore utile)
se una delle valutazioni non va a buon fine. Puoi utilizzare una
funzione di convenienza ReadValueVarArgs()
per valutare un numero variabile di
argomenti (restituisce un array di Value*
).
Dopo aver valutato gli argomenti, esegui l'operazione 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 verrà trasferita al
chiamante. Il chiamante acquisisce la proprietà di tutti i dati a cui fa riferimento questo
Value*
, in particolare il membro dati.
In questo caso, vuoi restituire un valore true o false per indicare la riuscita dell'operazione. Ricorda la
convenzione secondo cui la stringa vuota è false e tutte le altre stringhe sono true. Devi
allocare un oggetto Value con una copia allocata della stringa costante da restituire, poiché
il chiamante free()
entrambi. Non dimenticare di chiamare FreeValue()
sugli
oggetti 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()
racchiude una stringa in un nuovo oggetto Value.
Utilizza per scrivere il codice precedente in modo più conciso:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
Per collegare le funzioni all'interprete edify, fornisci la funzione
Register_foo
dove foo è il nome della libreria statica contenente
questo codice. Chiama RegisterFunction()
per registrare ogni funzione di estensione. Per
convenzione, assegna un nome alle funzioni specifiche del dispositivo device.whatever
per evitare
conflitti con le future funzioni integrate aggiunte.
void Register_librecovery_updater_tardis() { RegisterFunction("tardis.reprogram", ReprogramTardisFn); }
Ora puoi configurare il makefile per creare una libreria statica con il tuo codice. (Si tratta dello stesso makefile utilizzato per personalizzare la UI di ripristino nella sezione precedente; il tuo dispositivo potrebbe avere entrambe le 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 funzione
Register_libname
contenuta al suo interno.
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
Infine, configura la build del recupero per importare la libreria. Aggiungi la tua libreria a
TARGET_RECOVERY_UPDATER_LIBS (che può contenere più librerie; vengono tutte registrate).
Se il tuo codice dipende da altre librerie statiche che non sono estensioni edify (ad es.
non hanno una funzione Register_libname
), puoi elencarle in
TARGET_RECOVERY_UPDATER_EXTRA_LIBS per collegarle all'updater senza chiamare la loro
funzione di registrazione (inesistente). Ad esempio, se il codice specifico del dispositivo volesse utilizzare
zlib per decomprimere i dati, includeresti 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 di aggiornamento nel pacchetto OTA ora possono chiamare la tua funzione come qualsiasi altra. Per riprogrammare
il tuo dispositivo tardis, lo script di aggiornamento potrebbe contenere:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. Utilizza
la versione a un solo argomento della funzione integrata package_extract_file()
,
che restituisce i contenuti di un file estratto dal pacchetto di aggiornamento come blob per produrre
il secondo argomento della nuova funzione di estensione.
Generazione del pacchetto OTA
L'ultimo componente consiste nel fare in modo che gli strumenti di generazione dei pacchetti OTA conoscano i dati specifici del dispositivo ed emettano script di aggiornamento che includano chiamate alle funzioni di estensione.
Per prima cosa, fai in modo che il sistema di compilazione riconosca un blob di dati specifico del dispositivo. Supponendo che il file di dati si trovi in device/yoyodyne/tardis/tardis.dat
, dichiara quanto segue in AndroidBoard.mk del dispositivo:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
Puoi anche inserirlo in un file Android.mk, ma in questo caso deve essere protetto da un controllo del dispositivo, poiché tutti i file Android.mk nell'albero vengono caricati indipendentemente dal dispositivo in fase di compilazione. (Se l'albero include più dispositivi, vuoi che il file tardis.dat venga aggiunto solo quando crei 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 file vengono chiamati file radio per motivi storici e potrebbero non avere nulla a che fare con la
radio del dispositivo (se presente). Sono semplicemente blob di dati opachi che il sistema di compilazione copia
nel file .zip target-files utilizzato dagli strumenti di generazione OTA. Quando esegui una build, tardis.dat viene
memorizzato in 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) che gli strumenti possono 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"));""")
Una funzione separata gestisce il caso della generazione di un pacchetto OTA incrementale. Per questo esempio, supponiamo che tu debba riprogrammare il 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
Puoi fornire le seguenti funzioni nel modulo (implementa solo quelle che ti servono).
FullOTA_Assertions()
- Chiamato all'inizio della generazione di un aggiornamento OTA completo. Questo è un buon punto per emettere asserzioni sullo stato attuale del dispositivo. Non emettere comandi di script che apportano modifiche al dispositivo.
FullOTA_InstallBegin()
- Chiamato dopo che tutte le asserzioni sullo stato del dispositivo sono state superate, ma prima che vengano apportate modifiche. Puoi emettere comandi per aggiornamenti specifici del dispositivo che devono essere eseguiti prima di qualsiasi altra modifica sul dispositivo.
FullOTA_InstallEnd()
- Chiamato alla fine della generazione dello script, dopo l'emissione dei comandi dello script per aggiornare le partizioni di avvio e di sistema. Puoi anche emettere comandi aggiuntivi per aggiornamenti specifici del dispositivo.
IncrementalOTA_Assertions()
-
Simile a
FullOTA_Assertions()
, ma viene chiamato durante la generazione di un pacchetto di aggiornamento incrementale. IncrementalOTA_VerifyBegin()
- Chiamato dopo che tutte le asserzioni sullo stato del dispositivo sono state superate, ma prima che vengano apportate modifiche. Puoi emettere comandi per aggiornamenti specifici del dispositivo che devono essere eseguiti prima di qualsiasi altra operazione sul dispositivo.
IncrementalOTA_VerifyEnd()
- Chiamato al termine della fase di verifica, quando lo script ha terminato di confermare che i file che toccherà hanno i contenuti iniziali previsti. A questo punto, non è stato modificato nulla sul dispositivo. Puoi anche generare codice per ulteriori verifiche specifiche per il dispositivo.
IncrementalOTA_InstallBegin()
- Chiamato dopo che i file da applicare sono stati verificati come aventi lo stato before previsto, ma prima che siano state apportate modifiche. Puoi emettere comandi per aggiornamenti specifici del dispositivo che devono essere eseguiti prima di qualsiasi altra modifica sul dispositivo.
IncrementalOTA_InstallEnd()
- Simile alla sua controparte del pacchetto OTA completo, viene chiamato alla fine della generazione dello script, dopo l'emissione dei comandi dello script per aggiornare le partizioni di avvio e di sistema. Puoi anche emettere comandi aggiuntivi per aggiornamenti specifici del dispositivo.
Nota:se il dispositivo si spegne, l'installazione OTA potrebbe riavviarsi dall'inizio. Preparati a gestire i dispositivi su cui questi comandi sono già stati eseguiti, completamente o parzialmente.
Passare funzioni agli oggetti informativi
Passa le funzioni a un singolo oggetto info che contiene vari elementi utili:
-
info.input_zip. (Solo OTA complete) L'oggetto
zipfile.ZipFile
per il file ZIP dei file di destinazione di input. -
info.source_zip. (Solo OTA incrementali) L'oggetto
zipfile.ZipFile
per il file .zip target-files di origine (la build già presente sul dispositivo quando viene installato il pacchetto incrementale). -
info.target_zip. (Solo OTA incrementali) L'oggetto
zipfile.ZipFile
per il file .zip target (la build del pacchetto incrementale inserita sul dispositivo). -
info.output_zip. Pacchetto in fase di creazione; un oggetto
zipfile.ZipFile
aperto per la scrittura. Utilizza common.ZipWriteStr(info.output_zip, filename, data) per aggiungere un file al pacchetto. -
info.script. Oggetto script a cui puoi aggiungere comandi. Call
info.script.AppendExtra(script_text)
to output text into the script. Assicurati che il testo di output termini con un punto e virgola in modo che non si imbatta nei comandi emessi successivamente.
Per informazioni dettagliate sull'oggetto info, consulta la documentazione della Python Software Foundation per gli archivi ZIP.
Specificare 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 è impostato, per impostazione predefinita viene utilizzata la directory
$(TARGET_DEVICE_DIR)/../common
(device/yoyodyne/common
in questo esempio). È consigliabile definire in modo esplicito la posizione dello script releasetools.py.
Quando viene creato il dispositivo tardis, lo script releasetools.py è incluso nel file ZIP target-files (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 target-files, se
presente, ha la precedenza su quello dell'albero dei sorgenti di Android. Puoi anche specificare
esplicitamente il percorso delle estensioni specifiche del dispositivo con l'opzione -s
(o
--device_specific
), che ha la massima priorità. In questo modo puoi
correggere gli errori e apportare modifiche nelle estensioni releasetools e applicarle ai vecchi
file di destinazione.
Ora, quando esegui ota_from_target_files
, il modulo specifico per il dispositivo viene selezionato automaticamente dal file .zip target_files e utilizzato durante la generazione dei pacchetti OTA:
./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 del 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, consulta i commenti ota_from_target_files
in build/make/tools/releasetools/ota_from_target_files
.
Meccanismo di sideload
Il ripristino ha un meccanismo di sideloading per installare manualmente un pacchetto di aggiornamento senza scaricarlo via OTA dal sistema principale. Il sideload è utile per il debug o per apportare modifiche sui dispositivi in cui non è possibile avviare il sistema principale.
In passato, il sideloading veniva eseguito caricando pacchetti dalla scheda SD del dispositivo. In caso di un dispositivo che non si avvia, il pacchetto può essere inserito nella scheda SD utilizzando un altro computer e poi la scheda SD può essere inserita nel dispositivo. Per ospitare i dispositivi Android senza memoria esterna rimovibile, il ripristino supporta due meccanismi aggiuntivi per il sideload: il caricamento dei pacchetti dalla partizione della cache e il caricamento tramite USB utilizzando ADB.
Per richiamare ogni meccanismo di caricamento laterale, il metodo Device::InvokeMenuItem()
del dispositivo
può restituire i seguenti valori di BuiltinAction:
-
APPLY_EXT. Esegui il sideload di un pacchetto di aggiornamento da una memoria esterna (directory
/sdcard
). Il file recovery.fstab deve definire il punto di montaggio/sdcard
. Questa non è utilizzabile sui dispositivi che emulano una scheda SD con un collegamento simbolico a/data
(o un meccanismo simile)./data
in genere non è disponibile per il recupero perché potrebbe essere criptato. L'interfaccia utente di recupero mostra un menu di file .zip in/sdcard
e consente all'utente di selezionarne uno. -
APPLY_CACHE. Simile al caricamento di un pacchetto da
/sdcard
, tranne per il fatto che viene utilizzata la directory/cache
(che è sempre disponibile per il recupero). Dal sistema normale,/cache
è scrivibile solo da utenti con privilegi. Se il dispositivo non è avviabile, non è possibile scrivere nella directory/cache
(il che rende questo meccanismo di utilità limitata). -
APPLY_ADB_SIDELOAD. Consente all'utente di inviare un pacchetto al dispositivo tramite un cavo USB e
lo strumento di sviluppo ADB. Quando questo meccanismo viene richiamato, il ripristino avvia una propria mini
versione del daemon adbd per consentire ad adb su un computer host connesso di comunicare con esso. Questa mini
versione supporta un solo comando:
adb sideload filename
. Il file denominato viene inviato dalla macchina host al dispositivo, che lo verifica e lo installa come se fosse stato nello spazio di archiviazione locale.
Alcune avvertenze:
- È supportato solo il trasporto USB.
-
Se il ripristino esegue adbd normalmente (in genere vero per le build userdebug e eng), questo
verrà arrestato mentre il dispositivo è in modalità di caricamento laterale adb e verrà riavviato al termine
della ricezione di un pacchetto da parte di adb sideload. In modalità di caricamento laterale ADB, nessun comando ADB diverso da
sideload
funziona (logcat
,reboot
,push
,pull
,shell
e così via non funzionano). -
Non puoi uscire dalla modalità di caricamento laterale adb sul dispositivo. Per interrompere l'operazione, puoi inviare
/dev/null
(o qualsiasi altro elemento che non sia un pacchetto valido) come pacchetto e il dispositivo non riuscirà a verificarlo e interromperà la procedura di installazione. Il metodoCheckKey()
dell'implementazione di RecoveryUI continuerà a essere chiamato per le pressioni dei tasti, in modo da poter fornire una sequenza di tasti che riavvii il dispositivo e funzioni in modalità di caricamento laterale adb.