Implementazione degli aggiornamenti A/B

Gli OEM e i fornitori di SoC che desiderano implementare gli aggiornamenti del sistema A/B devono assicurarsi che il loro bootloader implementi boot_control HAL e passi i parametri corretti al kernel.

Implementazione del controllo di avvio HAL

I bootloader compatibili con A/B devono implementare l'HAL boot_control in hardware/libhardware/include/hardware/boot_control.h . È possibile testare le implementazioni utilizzando l'utilità system/extras/bootctl e system/extras/tests/bootloader/ .

È inoltre necessario implementare la macchina a stati mostrata di seguito:

Figura 1. Macchina a stati del bootloader

Configurazione del kernel

Per implementare gli aggiornamenti del sistema A/B:

  1. Cherrypick la seguente serie di patch del kernel (se necessario):
  2. Assicurati che gli argomenti della riga di comando del kernel contengano i seguenti argomenti aggiuntivi:
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    ... dove il valore <public-key-id> è l'ID della chiave pubblica utilizzata per verificare la firma della tabella di verità (per i dettagli, vedere dm-verity ) .
  3. Aggiungi il certificato .X509 contenente la chiave pubblica al portachiavi di sistema:
    1. Copia il certificato .X509 formattato nel formato .der nella radice della directory del kernel . Se il certificato .X509 è formattato come file .pem , utilizzare il comando openssl seguente per convertire dal formato .pem a .der :
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Crea zImage per includere il certificato come parte del keyring di sistema. Per verificare, controllare la voce procfs (richiede l'abilitazione di KEYS_CONFIG_DEBUG_PROC_KEYS ):
      angler:/# cat /proc/keys
      
      1c8a217e I------     1 perm 1f010000     0     0 asymmetri
      Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f []
      2d454e3e I------     1 perm 1f030000     0     0 keyring
      .system_keyring: 1/4
      L'inclusione riuscita del certificato .X509 indica la presenza della chiave pubblica nel keyring di sistema (l'evidenziazione indica l'ID della chiave pubblica).
    3. Sostituisci lo spazio con # e passalo come <public-key-id> nella riga di comando del kernel. Ad esempio, passa Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f al posto di <public-key-id> .

Impostazione delle variabili di build

I bootloader compatibili con A/B devono soddisfare i seguenti criteri per le variabili di build:

Deve definire per il target A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
    boot \
    system \
    vendor
    e altre partizioni aggiornate tramite update_engine (radio, bootloader, ecc.)
  • PRODUCT_PACKAGES += \
    update_engine \
    update_verifier
Per un esempio, fare riferimento a /device/google/marlin/+/android-7.1.0_r1/device-common.mk . È possibile facoltativamente eseguire il passaggio dex2oat post-installazione (ma pre-riavvio) descritto in Compilazione .
Fortemente raccomandato per target A/B
  • Definisci TARGET_NO_RECOVERY := true
  • Definisci BOARD_USES_RECOVERY_AS_BOOT := true
  • Non definire BOARD_RECOVERYIMAGE_PARTITION_SIZE
Impossibile definire per la destinazione A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
Facoltativo per build di debug PRODUCT_PACKAGES_DEBUG += update_engine_client

Impostazione delle partizioni (slot)

I dispositivi A/B non necessitano di una partizione di ripristino o di una partizione cache perché Android non utilizza più queste partizioni. La partizione dati viene ora utilizzata per il pacchetto OTA scaricato e il codice dell'immagine di ripristino si trova nella partizione di avvio. Tutte le partizioni A/B-ed devono essere denominate come segue (gli slot sono sempre denominati a , b , ecc.): boot_a , boot_b , system_a , system_b , vendor_a , vendor_b .

cache

Per gli aggiornamenti non A/B, la partizione cache è stata utilizzata per archiviare i pacchetti OTA scaricati e per riporre temporaneamente i blocchi durante l'applicazione degli aggiornamenti. Non c'è mai stato un buon modo per ridimensionare la partizione della cache: quanto grande doveva dipendere dagli aggiornamenti che si desiderava applicare. Il caso peggiore sarebbe una partizione cache grande quanto l'immagine di sistema. Con gli aggiornamenti A/B non è necessario riporre i blocchi (perché si scrive sempre su una partizione che non è attualmente utilizzata) e con lo streaming A/B non è necessario scaricare l'intero pacchetto OTA prima di applicarlo.

Recupero

Il disco RAM di ripristino è ora contenuto nel file boot.img . Quando si avvia il ripristino, il bootloader non può inserire l'opzione skip_initramfs sulla riga di comando del kernel.

Per gli aggiornamenti non A/B, la partizione di ripristino contiene il codice utilizzato per applicare gli aggiornamenti. Gli aggiornamenti A/B vengono applicati da update_engine in esecuzione nella normale immagine di sistema avviata. Esiste ancora una modalità di ripristino utilizzata per implementare il ripristino dei dati di fabbrica e il sideloading dei pacchetti di aggiornamento (da cui deriva il nome "ripristino"). Il codice e i dati per la modalità di ripristino sono archiviati nella normale partizione di avvio in un ramdisk; per avviare l'immagine di sistema, il bootloader dice al kernel di saltare il ramdisk (altrimenti il ​​dispositivo si avvia in modalità di ripristino. La modalità di ripristino è piccola (e gran parte era già nella partizione di avvio), quindi la partizione di avvio non aumenta in misura.

Fstab

L'argomento slotselect deve essere sulla riga per le partizioni A/B-ed. Per esempio:

<path-to-block-device>/vendor  /vendor  ext4  ro
wait,verify=<path-to-block-device>/metadata,slotselect

Nessuna partizione deve essere denominata vendor . Al contrario, la partizione vendor_a o vendor_b verrà selezionata e montata sul punto di montaggio /vendor .

Argomenti dello slot del kernel

Il suffisso dello slot corrente deve essere passato tramite un nodo specifico dell'albero del dispositivo (DT) ( /firmware/android/slot_suffix ) o tramite la riga di comando del kernel androidboot.slot_suffix o l'argomento bootconfig.

Per impostazione predefinita, l'avvio rapido esegue il flash dello slot corrente su un dispositivo A/B. Se il pacchetto di aggiornamento contiene anche immagini per l'altro slot non corrente, anche fastboot esegue il flashing di tali immagini. Le opzioni disponibili includono:

  • --slot SLOT . Sovrascrivere il comportamento predefinito e richiedere all'avvio rapido di eseguire il flashing dello slot passato come argomento.
  • --set-active [ SLOT ] . Imposta lo slot come attivo. Se non viene specificato alcun argomento facoltativo, lo slot corrente viene impostato come attivo.
  • fastboot --help . Ottieni dettagli sui comandi.

Se il bootloader implementa l'avvio rapido, dovrebbe supportare il comando set_active <slot> che imposta lo slot attivo corrente sullo slot specificato (questo deve anche cancellare il flag non avviabile per quello slot e reimpostare il conteggio dei tentativi sui valori predefiniti). Il bootloader dovrebbe supportare anche le seguenti variabili:

  • has-slot:<partition-base-name-without-suffix> . Restituisce "yes" se la partizione specificata supporta gli slot, "no" in caso contrario.
  • current-slot . Restituisce il suffisso dello slot che verrà avviato dal prossimo.
  • slot-count . Restituisce un numero intero che rappresenta il numero di slot disponibili. Attualmente sono supportati due slot, quindi questo valore è 2 .
  • slot-successful:<slot-suffix> . Restituisce "yes" se lo slot specificato è stato contrassegnato come avviato correttamente, "no" in caso contrario.
  • slot-unbootable:<slot-suffix> . Restituisce "yes" se lo slot specificato è contrassegnato come non avviabile, "no" in caso contrario.
  • slot-retry-count . Numero di tentativi rimanenti per tentare di avviare lo slot specificato.

Per visualizzare tutte le variabili, eseguire fastboot getvar all .

Generazione di pacchetti OTA

Gli strumenti del pacchetto OTA seguono gli stessi comandi dei comandi per i dispositivi non A/B. Il file target_files.zip deve essere generato definendo le variabili di build per la destinazione A/B. Gli strumenti dei pacchetti OTA identificano e generano automaticamente i pacchetti nel formato per l'aggiornamento A/B.

Esempi:

  • Per generare un'OTA completa:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • Per generare un OTA incrementale:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

Configurazione delle partizioni

update_engine può aggiornare qualsiasi coppia di partizioni A/B definite nello stesso disco. Una coppia di partizioni ha un prefisso comune (come system o boot ) e un suffisso per slot (come _a ). L'elenco delle partizioni per le quali il generatore di payload definisce un aggiornamento è configurato dalla variabile make AB_OTA_PARTITIONS .

Ad esempio, se sono incluse una coppia di partizioni bootloader_a e booloader_b ( _a e _b sono i suffissi degli slot), è possibile aggiornare queste partizioni specificando quanto segue nella configurazione del prodotto o della scheda:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Tutte le partizioni aggiornate da update_engine non devono essere modificate dal resto del sistema. Durante gli aggiornamenti incrementali o delta , i dati binari dello slot corrente vengono utilizzati per generare i dati nel nuovo slot. Qualsiasi modifica potrebbe causare la mancata verifica dei dati del nuovo slot durante il processo di aggiornamento e quindi l'aggiornamento non riuscito.

Configurazione post-installazione

È possibile configurare il passaggio successivo all'installazione in modo diverso per ciascuna partizione aggiornata utilizzando un set di coppie chiave-valore. Per eseguire un programma che si trova in /system/usr/bin/postinst in una nuova immagine, specificare il percorso relativo alla radice del filesystem nella partizione di sistema.

Ad esempio, usr/bin/postinst è system/usr/bin/postinst (se non si utilizza un disco RAM). Specificare inoltre il tipo di filesystem da passare alla chiamata di sistema mount(2) . Aggiungi quanto segue ai file .mk del prodotto o del dispositivo (se applicabile):

AB_OTA_POSTINSTALL_CONFIG += \
  RUN_POSTINSTALL_system=true \
  POSTINSTALL_PATH_system=usr/bin/postinst \
  FILESYSTEM_TYPE_system=ext4

Compilazione

Per motivi di sicurezza, system_server non può utilizzare la compilazione JIT (just-in-time) . Ciò significa che devi compilare in anticipo almeno i file odex per system_server e le sue dipendenze; tutto il resto è facoltativo.

Per compilare le app in background, è necessario aggiungere quanto segue alla configurazione del dispositivo del prodotto (nel device.mk del prodotto):

  1. Includere i componenti nativi nella build per garantire che lo script di compilazione ei binari siano compilati e inclusi nell'immagine di sistema.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. Collega lo script di compilazione a update_engine in modo che venga eseguito come passaggio successivo all'installazione.
      # A/B OTA dexopt update_engine hookup
      AB_OTA_POSTINSTALL_CONFIG += \
        RUN_POSTINSTALL_system=true \
        POSTINSTALL_PATH_system=system/bin/otapreopt_script \
        FILESYSTEM_TYPE_system=ext4 \
        POSTINSTALL_OPTIONAL_system=true
    

Per assistenza sull'installazione dei file preoptati nella seconda partizione di sistema inutilizzata, fare riferimento a Prima installazione di avvio dei file DEX_PREOPT .