Panoramica A/B virtuale

A/B virtuale è il principale meccanismo di aggiornamento di Android. Build A/B virtuali si basano su aggiornamenti A/B legacy (vedi Aggiornamenti di sistema A/B) e non A/B, che sono deprecati nella versione 15 per ridurre l'overhead di spazio degli aggiornamenti.

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

Android 12 offre la possibilità di compressione A/B virtuale per comprimere le partizioni acquisite tramite snapshot. La sperimentazione A/B virtuale offre quanto segue

  • Gli aggiornamenti A/B virtuali sono seamless (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 ripristinati. Se il nuovo sistema operativo non si avvia, viene eseguito automaticamente il rollback alla versione precedente.
  • Gli aggiornamenti A/B virtuali utilizzano un minimo di spazio aggiuntivo duplicando solo le partizioni utilizzate dal bootloader. Per le altre partizioni aggiornabili viene eseguito un snapshot.

Informazioni generali e terminologia

Questa sezione definisce la terminologia e descrive la tecnologia che supporta il test A/B virtuale. Durante l'installazione OTA, i dati del nuovo 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 riuniti nuovamente nel suo dispositivo di base tramite l'utilizzo del daemon dm-user e snapuserd. Questo processo avviene interamente nello spazio utente.

Device-mapper

Device-mapper è un livello di blocco virtuale Linux spesso utilizzato 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 c'è un dispositivo dm-linear, che specifica quali blocchi della 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 è presente 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 della pila sotto il punto di montaggio /system.

Impilaggio delle partizioni sotto il sistema

Figura 1. Impila sotto il punto di montaggio /system

Istantanee compresse

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

Gli snapshot compressi A/B virtuali si basano 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 demone dello spazio utente 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 snapshot compressi

In Android 12 e versioni successive, gli snapshot compressi usano un formato COW specifico per Android. Il formato COW contiene metadati sull'OTA e buffer distinti contenenti operazioni COW e nuovi dati del sistema operativo. Rispetto al formato dello snapshot del kernel che consentiva solo operazioni di sostituzione (Sostituisci il 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 di base deve essere sostituito con il blocco Y nel dispositivo di 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 di base deve essere sostituito con tutti gli zeri.
  • XOR: il dispositivo COW memorizza i byte compressi XOR tra il blocco X e il blocco Y. (Disponibile in Android 13 e versioni successive).

Gli aggiornamenti OTA completi consistono solo in operazioni di sostituzione e zero. Gli aggiornamenti OTA incrementali possono avere anche operazioni di copia.

Il layout dello snapshot completo sul disco è il seguente:

formato mucca

Figura 2. Formato Android COW su disco

dm-user

Il modulo del kernel dm-user consente a userspace di implementare dispositivi di blocco device-mapper. Una voce della tabella dm-user crea un dispositivo di tipo diverso 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 da propagare (per una scrittura).

Il modulo del kernel dm-user fornisce al kernel una nuova interfaccia visibile all'utente che non fa parte del codebase upstream kernel.org. Fino a quel momento, Google si riserva il diritto di modificare l'interfaccia dm-user in Android.

snapuserd

Il componente dello spazio utente snapuserd per dm-user implementa la compressione A/B virtuale. Snapuserd è un demone dello spazio utente responsabile della scrittura e della lettura dei dispositivi COW Android. Tutte le operazioni di I/O relative 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 vengono gestiti anche l'analisi dei metadati e lo scompattamento dei nuovi dati del blocco.

Compressione XOR

Per i dispositivi lanciati con Android 13 e versioni successive, la funzionalità di compressione XOR, abilitata per impostazione predefinita, consente agli snapshot dello spazio utente di memorizzare byte compressi XOR tra i vecchi blocchi e i nuovi blocchi. Quando solo alcuni byte in un blocco vengono modificati in un aggiornamento A/B virtuale, lo schema di archiviazione della compressione XOR utilizza meno spazio rispetto allo schema di archiviazione predefinito perché gli snapshot non memorizzano byte 4K completi. Questa riduzione delle dimensioni degli snapshot è possibile perché i dati XOR contengono molti zeri e sono più facili da comprimere rispetto ai dati dei blocchi non elaborati. Sui Pixel, la compressione XOR riduce le dimensioni degli istantanei dal 25% al 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 di snapshot

Per i dispositivi lanciati con Android 13 e versioni successive, le operazioni di unione di istantanee e istantanee nella compressione A/B virtuale vengono eseguite dal componente dello spazio utente snapuserd. Per i dispositivi che eseguono l'upgrade ad Android 13 e versioni successive, questa funzionalità deve essere attivata. Per i dettagli, consulta Unione dello spazio utente.

Di seguito viene descritto il processo di compressione A/B virtuale:

  1. Il framework monta la partizione /system di un dispositivo dm-verity, che è sovrapposto a un dispositivo dm-user. Ciò significa che ogni I/O dal file system principale viene indirizzata a dm-user.
  2. dm-user instrada l'I/O al daemon snapuserd nello spazio utente, che gestisce la richiesta 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, questo riprende dopo il riavvio.

Transizioni di inizializzazione

Durante l'avvio con snapshot compressi, l'init della prima fase deve avviare snapuserd per montare le partizioni. Questo 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 i rifiuti di selinux.

Per risolvere il problema, le transizioni di snapuserd avvengono in modo sincrono con quelle di init, come segue:

  1. init di primo livello avvia snapuserd dalla ramdisk e salva un descrittore file aperto in una variabile di ambiente.
  2. La prima fase di init passa il file system principale alla partizione di sistema, quindi esegue la copia di sistema di init.
  3. La copia di sistema di init legge il file sepolicy combinato in una stringa.
  4. Init richiama mlock() su tutte le pagine supportate da ext4. Quindi disattiva tutte le tabelle di mappatura dei dispositivi per i dispositivi snapshot e interrompe snapuserd. Dopodiché, non è più possibile leggere dalle partizioni, poiché ciò causa un deadlock.
  5. Utilizzando il descrittore aperto nella copia ramdisk di snapuserd, init riavvia il daemon con il contesto selinux corretto. Le tabelle di mapping dei dispositivi per i dispositivi snapshot vengono riattivate.
  6. Init invoca 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 dell'OTA di Pixel.

Impatto delle dimensioni non A/B A/B A/B virtuale Test A/B virtuale (compresso)
Immagine di fabbrica originale Super da 4,5 GB (immagine da 3,8 GB + 700 MB riservati)1 Super da 9 GB (3,8 G + 700 M riservati, per due slot) Super da 4,5 GB (immagine da 3,8 G + 700 M riservati) 4,5 GB super (immagine 3,8 GB + 700 M 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 in /data 2,1 GB2 in /data
Spazio di archiviazione totale necessario per applicare l'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 dei pixel.

2 Partendo dal presupposto che la nuova immagine di sistema sia delle stesse dimensioni dell'originale.

3 Il requisito di spazio è temporaneo fino al riavvio.

Virtual A/B di Android 11

L'androide 11 di Virtual A/B ha scritto su una partizione dinamica utilizzando il formato Kernel COW. Questo formato è stato infine deprecato perché il formato Kernel COW 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 una traduzione del COW specifico per Android nel formato Kernel COW. Alla fine, questo è stato sostituito in Android 13, che ha rimosso la dipendenza dal formato COW del kernel e anche da dm-snapshot.

Per implementare il test A/B virtuale o utilizzare le funzionalità di snapshot compressi, consulta Implementazione del test A/B virtuale