Panoramica A/B virtuale

Virtual A/B è il meccanismo di aggiornamento principale di Android. La funzionalità A/B virtuale si basa sugli aggiornamenti A/B legacy (vedi Aggiornamenti di sistema A/B) e non A/B, che è deprecata nella versione 15 per ridurre l'overhead di spazio degli aggiornamenti.

Il test A/B virtuale non ha in realtà uno slot aggiuntivo per le partizioni dinamiche, vedi partizioni dinamiche. Il delta viene invece scritto in uno snapshot e poi unito alla partizione di base dopo la conferma di un avvio riuscito. Il test A/B virtuale utilizza un formato di snapshot specifico per Android. Consulta Formato COW per snapshot compressi, che consente di comprimere gli snapshot e ridurre al minimo l'utilizzo dello spazio su disco. In un aggiornamento OTA completo, le dimensioni dello snapshot vengono ridotte di circa il 45% con la compressione, mentre in un aggiornamento OTA incrementale vengono ridotte di circa il 55%.

Android 12 offre l'opzione di compressione A/B virtuale per comprimere le partizioni di snapshot. Virtual A/B offre quanto segue:

  • Gli aggiornamenti A/B virtuali sono istantanei (l'aggiornamento avviene interamente in background mentre il dispositivo è operativo) come gli aggiornamenti A/B. Gli aggiornamenti A/B virtuali riducono al minimo il tempo in cui un dispositivo è offline e inutilizzabile.
  • Gli aggiornamenti A/B virtuali possono essere annullati. Se il nuovo sistema operativo non si avvia, i dispositivi eseguono automaticamente il rollback alla versione precedente.
  • Gli aggiornamenti A/B virtuali utilizzano uno spazio aggiuntivo minimo duplicando solo le partizioni utilizzate dal bootloader. Le altre partizioni aggiornabili vengono snapshotate.

Background e terminologia

Questa sezione definisce la terminologia e descrive la tecnologia che supporta il test A/B virtuale. Durante l'installazione OTA, i nuovi dati del sistema operativo vengono scritti nel nuovo slot per le partizioni fisiche o in un dispositivo COW specifico per Android. Dopo il riavvio del dispositivo, i dati della partizione dinamica vengono nuovamente uniti al dispositivo di base tramite l'utilizzo dei daemon dm-user e snapuserd. Questo processo avviene interamente nello spazio utente.

Device-mapper

Device-mapper è un livello di blocchi virtuali Linux utilizzato spesso in Android. Con le partizioni dinamiche, le partizioni come /system sono uno stack di dispositivi a più livelli:

  • Nella parte inferiore dello stack si trova la partizione super fisica (ad esempio, /dev/block/by-name/super).
  • Al centro si trova un dispositivo dm-linear, che specifica quali blocchi nella superpartizione formano la partizione dinamica specificata. Viene visualizzato come /dev/block/mapper/system_[a|b] su un dispositivo A/B o /dev/block/mapper/system su un dispositivo non A/B.
  • In alto si trova un dispositivo dm-verity, creato per le partizioni verificate. Questo dispositivo verifica che i blocchi sul dispositivo dm-linear siano firmati correttamente. Viene visualizzato come /dev/block/mapper/system-verity ed è l'origine del punto di montaggio /system.

La Figura 1 mostra l'aspetto dello stack sotto il punto di montaggio /system.

Impilamento delle partizioni sotto
il sistema

Figura 1. Stack sotto il punto di montaggio /system

Snapshot compressi

In Android 12 e versioni successive, poiché i requisiti di spazio nella partizione /data possono essere elevati, puoi attivare gli snapshot compressi nella build per soddisfare i requisiti di spazio più elevati della partizione /data.

Gli snapshot compressi A/B virtuali sono basati sui seguenti componenti disponibili in Android 12 e versioni successive:

  • dm-user, un modulo kernel simile a FUSE che consente allo spazio utente di implementare dispositivi a blocchi.
  • snapuserd, un daemon userspace per implementare un nuovo formato di snapshot.

Questi componenti consentono la compressione. Le altre modifiche necessarie apportate per implementare le funzionalità degli snapshot compressi sono riportate nelle sezioni successive: formato COW per gli snapshot compressi, dm-user e snapuserd.

Formato COW per gli snapshot compressi

In Android 12 e versioni successive, gli snapshot compressi utilizzano un formato COW specifico di Android. Il formato COW contiene metadati sull'OTA e buffer distinti contenenti operazioni COW e nuovi dati del sistema operativo. Rispetto al formato snapshot del kernel che consentiva solo operazioni di sostituzione (sostituzione del blocco X nell'immagine di base con i contenuti del blocco Y nello snapshot), il formato COW degli snapshot compressi di Android è più espressivo e supporta le seguenti operazioni:

  • Copia: il blocco X nel dispositivo base deve essere sostituito con il blocco Y nel dispositivo base.
  • Sostituisci: il blocco X nel dispositivo di base deve essere sostituito con i contenuti del blocco Y nello snapshot. Ciascuno di questi blocchi è compresso con gz.
  • Zero: il blocco X nel dispositivo base deve essere sostituito con tutti zeri.
  • XOR: il dispositivo COW memorizza i byte compressi XOR tra il blocco X e il blocco Y. (Disponibile su Android 13 e versioni successive.)

Gli aggiornamenti OTA completi sono costituiti solo da operazioni di sostituzione e zero. Gli aggiornamenti OTA incrementali possono inoltre includere operazioni di copia.

Il layout completo dello snapshot sul disco è il seguente:

formato mucca

Figura 2. Formato COW di Android sul disco

dm-user

Il modulo del kernel dm-user consente a userspace di implementare dispositivi a blocchi di Device Mapper. Una voce della tabella dm-user crea un dispositivo generico in /dev/dm-user/<control-name>. Un processo userspace può eseguire il polling del dispositivo per ricevere richieste di lettura e scrittura dal kernel. Ogni richiesta ha un buffer associato per lo spazio utente da compilare (per una lettura) o propagare (per una scrittura).

Il modulo kernel dm-user fornisce una nuova interfaccia visibile all'utente per il kernel che non fa parte della base di codice upstream di kernel.org. Fino ad allora, Google si riserva il diritto di modificare l'interfaccia di dm-user in Android.

snapuserd

Il componente userspace snapuserd per dm-user implementa la compressione A/B virtuale. Snapuserd è un daemon userspace responsabile della scrittura e della lettura dei dispositivi COW Android. Tutti gli I/O allo snapshot devono passare attraverso questo servizio. Durante l'installazione OTA, i nuovi dati del sistema operativo vengono scritti nello snapshot da snapuserd (con compressione). Qui viene gestita anche l'analisi dei metadati e l'estrazione dei nuovi dati dei blocchi.

Compressione XOR

Per i dispositivi lanciati con Android 13 e versioni successive, la funzionalità di compressione XOR, abilitata per impostazione predefinita, consente agli snapshot di spazio utente di archiviare i byte compressi XOR tra i blocchi precedenti e quelli nuovi. Quando solo pochi byte di un blocco vengono modificati in un aggiornamento A/B virtuale, lo schema di archiviazione con compressione XOR utilizza meno spazio rispetto allo schema di archiviazione predefinito perché gli snapshot non memorizzano 4096 byte completi. Questa riduzione delle dimensioni dello snapshot è possibile perché i dati XOR contengono molti zeri e sono più facili da comprimere rispetto ai dati grezzi dei blocchi. Sui dispositivi Pixel, la compressione XOR riduce le dimensioni degli snapshot del 25-40%.

Per i dispositivi che eseguono l'upgrade ad Android 13 e versioni successive, la compressione XOR deve essere attivata. Per maggiori dettagli, vedi Compressione XOR.

Unione degli snapshot

Per i dispositivi lanciati con Android 13 e versioni successive, i processi di snapshot e unione degli snapshot nella compressione A/B virtuale vengono eseguiti dal componente userspace snapuserd. Per i dispositivi che eseguono l'upgrade ad Android 13 e versioni successive, questa funzionalità deve essere abilitata. Per maggiori dettagli, vedi Unione dello spazio utente.

Di seguito viene descritta la procedura di compressione A/B virtuale:

  1. Il framework monta la partizione /system su un dispositivo dm-verity, che è impilato sopra un dispositivo dm-user. Ciò significa che ogni I/O dal file system root viene indirizzato a dm-user.
  2. dm-user instrada l'I/O al daemon snapuserd userspace, che gestisce la richiesta di I/O.
  3. Al termine dell'operazione di unione, il framework comprime dm-verity sopra dm-linear (system_base) e rimuove dm-user.

Procedura di compressione
A/B virtuale

Figura 3. Procedura di compressione A/B virtuale

Il processo di unione degli snapshot può essere interrotto. Se il dispositivo viene riavviato durante il processo di unione, quest'ultimo riprende dopo il riavvio.

Init transitions

Quando si avvia il sistema con snapshot compressi, init di primo livello deve avviare snapuserd per montare le partizioni. Ciò pone un problema: quando sepolicy viene caricato e applicato, snapuserd viene inserito nel contesto sbagliato e le sue richieste di lettura non vanno a buon fine, con errori di SELinux.

Per risolvere il problema, snapuserd esegue la transizione in modo sincrono con init, come segue:

  1. La fase 1 init avvia snapuserd dal ramdisk e salva un descrittore di file aperto in una variabile di ambiente.
  2. La prima fase di init sposta il file system root nella partizione di sistema, quindi esegue la copia di sistema di init.
  3. La copia di sistema di init legge sepolicy combinato in una stringa.
  4. Init richiama mlock() su tutte le pagine supportate da ext4. Quindi disattiva tutte le tabelle device-mapper per i dispositivi snapshot e arresta snapuserd. Dopo questa operazione, è vietato leggere dalle partizioni, poiché ciò causa un deadlock.
  5. L'utilizzo del descrittore aperto per la copia ramdisk di snapuserd, init riavvia il daemon con il contesto SELinux corretto. Le tabelle di Device Mapper per i dispositivi snapshot vengono riattivate.
  6. Init richiama munlockall(): è sicuro eseguire di nuovo l'I/O.

Utilizzo dello spazio

La tabella seguente fornisce un confronto dell'utilizzo dello spazio per diversi meccanismi OTA utilizzando le dimensioni del sistema operativo e degli aggiornamenti OTA di Pixel.

Impatto delle dimensioni non-A/B A/B Virtual A/B Test A/B virtuale (compresso)
Immagine di fabbrica originale 4,5 GB super (immagine da 3,8 GB + 700 MB riservati)1 9 GB super (3,8 GB + 700 MB riservati, per due slot) 4,5 GB super (immagine da 3,8 GB + 700 MB riservati) 4,5 GB super (immagine da 3,8 GB + 700 MB riservati)
Altre partizioni statiche /cache Nessuno Nessuno Nessuno
Spazio di archiviazione aggiuntivo durante l'OTA (spazio restituito dopo l'applicazione dell'OTA) 1,4 GB su /data 0 3,8 GB2 su /data 2,1 GB2 su /data
Spazio di archiviazione totale necessario per applicare l'aggiornamento OTA 5,9 GB3 (super e dati) 9GB (super) 8,3 GB3 (super e dati) 6,6 GB3 (super e dati)

1Indica il layout presunto in base alla mappatura di Pixel.

2Presuppone che la nuova immagine di sistema abbia le stesse dimensioni dell'originale.

3Il requisito di spazio è temporaneo fino al riavvio.

Android 11 Virtual A/B

Android 11 di Virtual A/B ha scritto nella partizione dinamica utilizzando il formato Kernel COW. Alla fine è stato ritirato perché il formato COW del kernel non supporta la compressione.

Virtual A/B di Android 12

In Android 12, la compressione è supportata sotto forma di formato COW specifico per Android. Questa versione di Virtual A/B richiedeva la conversione del COW specifico per Android nel formato COW del kernel. Alla fine è stato sostituito in Android 13, che ha rimosso la dipendenza dal formato COW del kernel e anche dm-snapshot.

Per implementare Virtual A/B o utilizzare le funzionalità di snapshot compressi, consulta Implementazione di Virtual A/B.