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(); }
Build e collegamento al ripristino del dispositivo
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.
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:
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:
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:
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:
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:
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 metodoCheckKey()
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.