Übersicht über virtuelles A/B

Virtual A/B ist der Hauptmechanismus für Updates unter Android. Virtuelle A/B-Builds bauen auf älteren A/B-Updates (siehe A/B-Systemupdates) und nicht A/B-Updates auf, die in Version 15 eingestellt werden, um den Speicheraufwand von Updates zu reduzieren.

Bei virtuellen A/B-Tests gibt es keinen zusätzlichen Slot für dynamische Partitionen. Weitere Informationen finden Sie unter Dynamische Partitionen. Stattdessen wird das Delta in einen Snapshot geschrieben und nach Bestätigung eines erfolgreichen Starts in die Basispartition zusammengeführt. Für virtuelle A/B-Tests wird ein Android-spezifisches Snapshot-Format verwendet. Das COW-Format für komprimierte Snapshots ermöglicht das Komprimieren von Snapshots und minimiert die Speicherplatznutzung. Bei einer vollständigen OTA-Aktualisierung wird die Snapshot-Größe durch Komprimierung um etwa 45% reduziert. Bei einer inkrementellen OTA-Aktualisierung wird die Snapshot-Größe um etwa 55 % reduziert.

Android 12 bietet die Möglichkeit zur virtuellen A/B-Komprimierung, um Snapshot-Partitionen zu komprimieren. Virtual A/B bietet Folgendes:

  • Virtuelle A/B-Updates sind wie A/B-Updates nahtlos (das Update erfolgt vollständig im Hintergrund, während das Gerät in Betrieb ist). Durch virtuelle A/B-Updates wird minimiert, wie lange ein Gerät offline und unbrauchbar ist.
  • Virtuelle A/B-Updates können rückgängig gemacht werden. Wenn das neue Betriebssystem nicht gestartet werden kann, wird auf den Geräten automatisch ein Rollback auf die vorherige Version durchgeführt.
  • Bei virtuellen A/B-Updates wird nur ein Minimum an zusätzlichem Speicherplatz belegt, da nur die Partitionen dupliziert werden, die vom Bootloader verwendet werden. Von anderen aktualisierbaren Partitionen wird ein Snapshot erstellt.

Hintergrund und Terminologie

In diesem Abschnitt werden die Terminologie und die Technologie beschrieben, die virtuelles A/B unterstützt. Bei der OTA-Installation werden neue Betriebssystemdaten entweder in den neuen Slot für physische Partitionen oder auf ein Android-spezifisches COW-Gerät geschrieben. Nach dem Neustart des Geräts werden die Daten der dynamischen Partition mithilfe des dm-user- und des snapuserd-Daemons wieder mit dem Basisgerät zusammengeführt. Dieser Vorgang findet vollständig im Nutzerbereich statt.

Device-mapper

Device Mapper ist eine virtuelle Linux-Blockschicht, die häufig in Android verwendet wird. Bei dynamischen Partitionen sind Partitionen wie /system ein Stapel aus mehreren Geräten:

  • Unten im Stack befindet sich die physische Superpartition (z. B. /dev/block/by-name/super).
  • In der Mitte befindet sich ein dm-linear-Gerät, das angibt, welche Blöcke in der Superpartition die angegebene dynamische Partition bilden. Auf einem A/B-Gerät wird /dev/block/mapper/system_[a|b] angezeigt, auf einem Nicht-A/B-Gerät /dev/block/mapper/system.
  • Oben befindet sich ein dm-verity-Gerät, das für bestätigte Partitionen erstellt wurde. Dieses Gerät prüft, ob Blöcke auf dem dm-linear-Gerät korrekt signiert sind. Er wird als /dev/block/mapper/system-verity angezeigt und ist die Quelle des Bereitstellungspunkts /system.

Abbildung 1 zeigt, wie der Stack unter dem Bereitstellungspunkt /system aussieht.

Partitionstapelung unter dem System

Abbildung 1: Stack unter dem Bereitstellungspunkt „/system“

Komprimierte Snapshots

Da der Speicherplatzbedarf der /data-Partition in Android 12 und höher hoch sein kann, können Sie komprimierte Snapshots in Ihrem Build aktivieren, um den höheren Speicherplatzbedarf der /data-Partition zu erfüllen.

Komprimierte virtuelle A/B-Snapshots basieren auf den folgenden Komponenten, die in Android 12 und höher verfügbar sind:

  • dm-user, ein Kernelmodul ähnlich wie FUSE, mit dem Blockgeräte im Userspace implementiert werden können.
  • snapuserd, ein Userspace-Daemon zur Implementierung eines neuen Snapshot-Formats.

Diese Komponenten ermöglichen die Komprimierung. Die weiteren erforderlichen Änderungen, die zum Implementieren der Funktionen für komprimierte Snapshots vorgenommen werden, finden Sie in den nächsten Abschnitten: COW-Format für komprimierte Snapshots, dm-user und snapuserd.

COW-Format für komprimierte Snapshots

Unter Android 12 und höher wird für komprimierte Snapshots ein Android-spezifisches COW-Format verwendet. Das COW-Format enthält Metadaten über das OTA und verschiedene Zwischenspeicher mit COW-Vorgängen und neuen Betriebssystemdaten. Im Vergleich zum Kernel-Snapshot-Format, das nur Ersetzen-Vorgänge zuließ (Block X im Basis-Image durch den Inhalt von Block Y im Snapshot ersetzen), ist das COW-Format für komprimierte Android-Snapshots ausdrucksstärker und unterstützt die folgenden Vorgänge:

  • Kopieren: Block X im Basisgerät muss durch Block Y im Basisgerät ersetzt werden.
  • Ersetzen: Block X auf dem Basisgerät sollte durch den Inhalt von Block Y im Snapshot ersetzt werden. Jeder dieser Blöcke ist gz-komprimiert.
  • Null: Block X im Basisgerät sollte durch Nullen ersetzt werden.
  • XOR: Das COW-Gerät speichert mit XOR komprimierte Byte zwischen Block X und Block Y. (Verfügbar ab Android 13.)

Vollständige OTA-Updates bestehen nur aus replace- und zero-Vorgängen. Inkrementelle OTA-Updates können zusätzlich Kopiervorgänge umfassen.

Das vollständige Snapshot-Layout auf dem Laufwerk sieht so aus:

Kuhformat

Abbildung 2: Android-COW-Format auf dem Laufwerk

dm-nutzer

Das dm-user-Kernelmodul ermöglicht es userspace, Device Mapper-Blockgeräte zu implementieren. Ein dm-user-Tabelleneintrag erstellt ein sonstiges Gerät unter /dev/dm-user/<control-name>. Ein userspace-Prozess kann das Gerät abfragen, um Lese- und Schreibanfragen vom Kernel zu erhalten. Jede Anfrage hat einen zugeordneten Puffer für den Userspace, der entweder gefüllt (beim Lesen) oder weitergegeben (beim Schreiben) wird.

Das Kernelmodul dm-user bietet eine neue für den Nutzer sichtbare Schnittstelle für den Kernel, die nicht Teil der vorgelagerten Kernel.org-Codebasis ist. Bis dahin behält sich Google das Recht vor, die dm-user-Oberfläche in Android zu ändern.

snapuserd

Die snapuserd-Nutzerbereichskomponente für dm-user implementiert die virtuelle A/B-Komprimierung. Snapuserd ist ein Userspace-Daemon, der für das Schreiben und Lesen der Android COW-Geräte zuständig ist. Alle I/O-Vorgänge für den Snapshot müssen über diesen Dienst erfolgen. Während der OTA-Installation werden neue Betriebssystemdaten von snapuserd (mit Komprimierung) in den Snapshot geschrieben. Hier werden auch die Metadaten geparst und neue Blockdaten entpackt.

XOR-Komprimierung

Auf Geräten, die mit Android 13 oder höher ausgeliefert werden, ermöglicht die standardmäßig aktivierte XOR-Komprimierungsfunktion, dass in Userspace-Snapshots XOR-komprimierte Bytes zwischen alten und neuen Blöcken gespeichert werden. Wenn bei einem virtuellen A/B-Update nur wenige Byte in einem Block geändert werden, benötigt das XOR-Komprimierungsspeicherschema weniger Speicherplatz als das Standardspeicherschema, da in Snapshots keine vollständigen 4 KB gespeichert werden. Diese Verringerung der Snapshot-Größe ist möglich, da XOR-Daten viele Nullen enthalten und leichter komprimiert werden können als Rohblockdaten. Auf Pixel-Geräten reduziert die XOR-Komprimierung die Größe des Snapshots um 25 bis 40 %.

Bei Geräten, die auf Android 13 oder höher umgestellt werden, muss die XOR-Komprimierung aktiviert sein. Weitere Informationen finden Sie unter XOR-Komprimierung.

Snapshot-Zusammenführung

Auf Geräten, die mit Android 13 oder höher gestartet werden, werden die Snapshot- und Snapshot-Merge-Prozesse bei der virtuellen A/B-Komprimierung von der snapuserd-Userspace-Komponente ausgeführt. Bei Geräten, die auf Android 13 oder höher umgestellt werden, muss diese Funktion aktiviert sein. Weitere Informationen finden Sie unter Userspace-Merge.

Im Folgenden wird der Virtual A/B-Komprimierungsprozess beschrieben:

  1. Das Framework bindet die /system-Partition von einem dm-verity-Gerät aus, das auf einem dm-user-Gerät gestapelt ist. Das bedeutet, dass jede I/O-Operation aus dem Stammdateisystem an dm-user weitergeleitet wird.
  2. dm-user leitet die E/A an den Userspace-snapuserd-Daemon weiter, der die E/A-Anfrage verarbeitet.
  3. Wenn der Zusammenführungsvorgang abgeschlossen ist, minimiert das Framework dm-verity auf dm-linear (system_base) und entfernt dm-user.

Virtueller A/B-Komprimierungsprozess

Abbildung 3: Virtuelles A/B-Komprimierungsverfahren

Der Zusammenführungsprozess von Snapshots kann unterbrochen werden. Wenn das Gerät während des Zusammenführungsprozesses neu gestartet wird, wird der Zusammenführungsprozess nach dem Neustart fortgesetzt.

Init-Übergänge

Beim Starten mit komprimierten Snapshots muss die Initialisierung in der ersten Phase snapuserd starten, um Partitionen bereitzustellen. Das stellt ein Problem dar: Wenn sepolicy geladen und erzwungen wird, wird snapuserd in den falschen Kontext gesetzt und seine Leseanfragen schlagen fehl, da sie von SELinux abgelehnt werden.

Um dies zu vermeiden, werden snapuserd und init synchron übergangen:

  1. Die erste Phase von init startet snapuserd über das RAM-Disk und speichert einen offenen Dateideskriptor in einer Umgebungsvariablen.
  2. Die erste Phase von init wechselt das Stammdateisystem zur Systempartition und führt dann die Systemkopie von init aus.
  3. Die Systemkopie von init liest die kombinierte SEPolicy in einen String ein.
  4. Init ruft mlock() auf allen mit ext4 gesicherten Seiten auf. Anschließend werden alle Device-Mapper-Tabellen für Snapshot-Geräte deaktiviert und snapuserd beendet. Danach ist das Lesen aus Partitionen nicht mehr zulässig, da dies zu einem Deadlock führt.
  5. Mit dem offenen Descriptor für die Ramdisk-Kopie von snapuserd startet init den Daemon mit dem richtigen SELinux-Kontext neu. Device-Mapper-Tabellen für Snapshot-Geräte werden wieder aktiviert.
  6. Init ruft munlockall() auf – es ist wieder sicher, I/O auszuführen.

Gruppenbereichsnutzung

In der folgenden Tabelle wird die Speichernutzung für verschiedene OTA-Mechanismen verglichen. Dabei werden das Betriebssystem und die OTA-Größe von Pixel verwendet.

Auswirkungen der Größe Nicht A/B A/B Virtuelles A/B Virtuelle A/B-Tests (komprimiert)
Originales Werks-Image 4,5 GB Super (3,8 GB Image + 700 MB reserviert)1 9 GB Super (3,8 G + 700 M reserviert, für zwei Slots) 4,5 GB Super (3,8 GB Image + 700 MB reserviert) 4,5 GB Super (3,8 GB Image + 700 M reserviert)
Sonstige statische Partitionen /cache Keine Keine Keine
Zusätzlicher Speicherplatz während der OTA-Aktualisierung (Speicherplatz, der nach der OTA-Aktualisierung zurückgegeben wird) 1,4 GB auf /data 0 3,8 GB2 unter /data 2,1 GB2 für /data
Gesamtspeicherplatz, der für die Anwendung der OTA-Aktualisierung erforderlich ist 5,9 GB3 (Super- und Datenebene) 9 GB (Super) 8,3 GB3 (Super und Daten) 6,6 GB3 (Super und Daten)

1: Das Layout wird anhand der Pixelzuordnung angenommen.

2: Es wird davon ausgegangen, dass das neue Systemimage dieselbe Größe wie das Original hat.

3: Der Speicherplatzbedarf ist bis zum Neustart vorübergehend.

Android 11 Virtual A/B

Android 11 von Virtual A/B hat mit dem Kernel COW-Format in eine dynamische Partition geschrieben. Dies wurde später verworfen, da das Kernel-COW-Format die Komprimierung nicht unterstützt.

Android 12: Virtuelle A/B-Tests

Unter Android 12 wird die Komprimierung in Form eines Android-spezifischen COW-Formats unterstützt. Für diese Version von Virtual A/B war eine Übersetzung des Android-spezifischen COW in das Kernel COW-Format erforderlich. In Android 13 wurde dies schließlich ersetzt, wodurch die Abhängigkeit vom Kernel-COW-Format und auch von dm-snapshot aufgehoben wurde.

Informationen zum Implementieren von virtuellen A/B-Tests oder zum Verwenden komprimierter Snapshot-Funktionen finden Sie unter Virtuelle A/B-Tests implementieren.