Descripción general de A/B virtual

Virtual A/B es el principal mecanismo de actualización de Android. A/B virtual se basa en las actualizaciones A/B heredadas (consulta Actualizaciones del sistema A/B) y las que no son A/B, que se dejaron de usar en la versión 15 para reducir la sobrecarga de espacio de las actualizaciones.

En realidad, Virtual A/B no tiene una ranura adicional para las particiones dinámicas. Consulta particiones dinámicas. En cambio, el delta se escribe en una instantánea y, luego, se combina con la partición base después de confirmar un inicio correcto. A/B virtual usa un formato de instantáneas específico de Android. Consulta Formato COW para instantáneas comprimidas, que permite comprimir instantáneas y minimizar el uso del espacio en disco. En una OTA completa, el tamaño de la instantánea se reduce en alrededor de un 45% con la compresión, y en una OTA incremental, se reduce en alrededor de un 55%.

Android 12 ofrece la opción de compresión de A/B virtual para comprimir particiones con instantáneas. Las actualizaciones de A/B virtuales ofrecen lo siguiente:

  • Las actualizaciones de A/B virtuales son sin interrupciones (la actualización se realiza por completo en segundo plano mientras el dispositivo está en funcionamiento), al igual que las actualizaciones de A/B. Las actualizaciones de A/B virtual minimizan el tiempo que un dispositivo está sin conexión y no se puede usar.
  • Las actualizaciones de A/B virtuales se pueden revertir. Si el nuevo SO no se inicia, los dispositivos revierten automáticamente a la versión anterior.
  • Las actualizaciones virtuales de A/B usan un mínimo de espacio adicional, ya que solo duplican las particiones que usa el bootloader. Se crean instantáneas de otras particiones actualizables.

Información general y terminología

En esta sección, se define la terminología y se describe la tecnología que admite las pruebas A/B virtuales. Durante la instalación OTA, los datos nuevos del sistema operativo se escriben en su nueva ranura para particiones físicas o en un dispositivo COW específico de Android. Después de reiniciar el dispositivo, los datos de la partición dinámica se vuelven a combinar en su dispositivo base a través del uso de dm-user y el daemon snapuserd. Este proceso se realiza por completo en el espacio del usuario.

Device-mapper

Device-mapper es una capa de bloques virtuales de Linux que se usa con frecuencia en Android. Con las particiones dinámicas, las particiones como /system son una pila de dispositivos en capas:

  • En la parte inferior de la pila, se encuentra la partición física super (por ejemplo, /dev/block/by-name/super).
  • En el medio, hay un dispositivo dm-linear que especifica qué bloques de la superpartición forman la partición dinámica determinada. Aparece como /dev/block/mapper/system_[a|b] en un dispositivo A/B o como /dev/block/mapper/system en un dispositivo que no es A/B.
  • En la parte superior, se encuentra un dispositivo dm-verity, creado para particiones verificadas. Este dispositivo verifica que los bloques del dispositivo dm-linear estén firmados correctamente. Aparece como /dev/block/mapper/system-verity y es la fuente del punto de activación /system.

En la figura 1, se muestra cómo se ve la pila debajo del punto de activación /system.

Apilamiento de particiones debajo del sistema

Figura 1: Pila debajo del punto de activación /system

Instantáneas comprimidas

En Android 12 y versiones posteriores, debido a que los requisitos de espacio en la partición /data pueden ser altos, puedes habilitar instantáneas comprimidas en tu compilación para abordar los requisitos de espacio más altos de la partición /data.

Las instantáneas comprimidas de A/B virtual se compilan sobre los siguientes componentes disponibles en Android 12 y versiones posteriores:

  • dm-user, un módulo de kernel similar a FUSE que permite que el espacio del usuario implemente dispositivos de bloqueo.
  • snapuserd, un daemon de espacio de usuario para implementar un nuevo formato de instantánea.

Estos componentes habilitan la compresión. Los otros cambios necesarios que se realizaron para implementar las capacidades de instantáneas comprimidas se indican en las siguientes secciones: Formato COW para instantáneas comprimidas, dm-user y snapuserd.

Formato COW para instantáneas comprimidas

En Android 12 y versiones posteriores, las instantáneas comprimidas usan un formato COW específico de Android. El formato COW contiene metadatos sobre la OTA y tiene búferes distintos que contienen operaciones COW y datos nuevos del sistema operativo. En comparación con el formato de instantáneas del kernel, que solo permitía operaciones de reemplazo (reemplazar el bloque X en la imagen base por el contenido del bloque Y en la instantánea), el formato COW de instantáneas comprimidas de Android es más expresivo y admite las siguientes operaciones:

  • Copia: El bloque X del dispositivo base se debe reemplazar por el bloque Y del dispositivo base.
  • Reemplazar: El bloque X del dispositivo base se debe reemplazar por el contenido del bloque Y de la instantánea. Cada uno de estos bloques está comprimido con gz.
  • Cero: El bloqueo X en el dispositivo base debe reemplazarse por ceros.
  • XOR: El dispositivo COW almacena bytes comprimidos con XOR entre el bloque X y el bloque Y. (Disponible en Android 13 y versiones posteriores)

Las actualizaciones OTA completas solo constan de operaciones de reemplazo y cero. Las actualizaciones OTA incrementales también pueden tener operaciones de copia.

El diseño completo de la instantánea en el disco se ve de la siguiente manera:

formato de vaca

Figura 2: Formato COW de Android en el disco

dm-user

El módulo del kernel dm-user permite que userspace implemente dispositivos de bloques de device-mapper. Una entrada de la tabla dm-user crea un dispositivo diverso en /dev/dm-user/<control-name>. Un proceso userspace puede sondear el dispositivo para recibir solicitudes de lectura y escritura del kernel. Cada solicitud tiene un búfer asociado para que el espacio del usuario lo complete (para una lectura) o lo propague (para una escritura).

El módulo del kernel dm-user proporciona una nueva interfaz visible para el usuario en el kernel que no forma parte de la base de código de kernel.org upstream. Hasta entonces, Google se reserva el derecho de modificar la interfaz de dm-user en Android.

snapuserd

El componente de espacio de usuario snapuserd para dm-user implementa la compresión A/B virtual. Snapuserd es un daemon de espacio de usuario encargado de escribir y leer los dispositivos COW de Android. Todas las E/S a la instantánea deben pasar por este servicio. Durante la instalación de OTA, snapuserd escribe los datos del nuevo sistema operativo en la instantánea (con compresión). Aquí también se maneja el análisis de los metadatos y el desempaquetado de los datos de bloques nuevos.

Compresión XOR

En el caso de los dispositivos que se lanzan con Android 13 y versiones posteriores, la función de compresión XOR, que está habilitada de forma predeterminada, permite que los resúmenes del espacio del usuario almacenen bytes comprimidos con XOR entre bloques antiguos y bloques nuevos. Cuando solo se cambian unos pocos bytes en un bloque en una actualización de Virtual A/B, el esquema de almacenamiento de compresión XOR usa menos espacio que el esquema de almacenamiento predeterminado, ya que las instantáneas no almacenan bytes completos de 4 K. Esta reducción en el tamaño de la instantánea es posible porque los datos XOR contienen muchos ceros y son más fáciles de comprimir que los datos de bloque sin procesar. En los dispositivos Pixel, la compresión XOR reduce el tamaño del resumen entre un 25% y un 40%.

En el caso de los dispositivos que se actualizan a Android 13 y versiones posteriores, se debe habilitar la compresión XOR. Para obtener más información, consulta Compresión XOR.

Fusión de instantáneas

En el caso de los dispositivos que se lanzan con Android 13 y versiones posteriores, los procesos de instantánea y combinación de instantáneas en la compresión A/B virtual se realizan con el componente de espacio de usuario snapuserd. Esta función debe habilitarse en los dispositivos que se actualicen a Android 13 y versiones posteriores. Para obtener más información, consulta Combinación del espacio del usuario.

A continuación, se describe el proceso de compresión de Virtual A/B:

  1. El framework monta la partición /system desde un dispositivo dm-verity, que se apila sobre un dispositivo dm-user. Esto significa que cada E/S del sistema de archivos raíz se dirige a dm-user.
  2. dm-user enruta la E/S al daemon snapuserd del espacio del usuario, que controla la solicitud de E/S.
  3. Cuando se completa la operación de combinación, el framework contrae dm-verity sobre dm-linear (system_base) y quita dm-user.

Proceso de compresión de A/B virtual

Figura 3: Proceso de compresión de A/B virtual

Se puede interrumpir el proceso de combinación de instantáneas. Si se reinicia el dispositivo durante el proceso de combinación, este se reanudará después del reinicio.

Transiciones de inicialización

Cuando se inicia con snapshots comprimidos, el init de primera etapa debe iniciar snapuserd para montar particiones. Esto plantea un problema: cuando se carga y aplica sepolicy, snapuserd se coloca en el contexto incorrecto y sus solicitudes de lectura fallan, con denegaciones de SELinux.

Para abordar este problema, snapuserd realiza la transición en sincronía con init, de la siguiente manera:

  1. La etapa 1 de init inicia snapuserd desde el disco RAM y guarda un descriptor de archivo abierto en él en una variable de entorno.
  2. init de primera etapa cambia el sistema de archivos raíz a la partición del sistema y, luego, ejecuta la copia del sistema de init.
  3. La copia del sistema de init lee la política de seguridad combinada en una cadena.
  4. Init invoca mlock() en todas las páginas respaldadas por ext4. Luego, desactiva todas las tablas de device-mapper para los dispositivos de instantáneas y detiene snapuserd. Después de esto, está prohibido leer desde las particiones, ya que esto provoca un bloqueo.
  5. Con el descriptor abierto para la copia de snapuserd en el disco RAM, init reinicia el daemon con el contexto de SELinux correcto. Se reactivan las tablas de device-mapper para los dispositivos de instantáneas.
  6. Init invoca munlockall(), por lo que es seguro volver a realizar E/S.

Uso del espacio

En la siguiente tabla, se proporciona una comparación del uso del espacio para diferentes mecanismos de OTA con los tamaños del SO y de la OTA de Pixel.

Impacto en el tamaño non-A/B A/B A/B virtual A/B virtual (comprimido)
Imagen original de fábrica 4.5 GB súper (3.8 GB de imagen y 700 MB reservados)1 9 GB de memoria súper (3.8 GB + 700 M reservados, para dos ranuras) 4.5 GB súper (imagen de 3.8 GB + 700 M reservados) 4.5 GB súper (imagen de 3.8 GB + 700 M reservados)
Otras particiones estáticas /cache Ninguno Ninguno Ninguno
Almacenamiento adicional durante la OTA (espacio que se recupera después de aplicar la OTA) 1.4 GB en /data 0 3.8 GB2 en /data 2.1 GB2 en /data
Almacenamiento total necesario para aplicar la OTA 5.9 GB3 (súper y datos) 9 GB (súper) 8.3 GB3 (súper y datos) 6.6 GB3 (súper y datos)

1Indica el diseño supuesto según la asignación de píxeles.

2Supone que la nueva imagen del sistema tiene el mismo tamaño que la original.

3El requisito de espacio es transitorio hasta el reinicio.

Actualizaciones A/B virtuales en Android 11

Android 11 de Virtual A/B escribió en la partición dinámica con el formato COW del kernel. Finalmente, se dejó de usar porque el formato COW del kernel no admite la compresión.

Actualizaciones A/B virtuales en Android 12

En Android 12, la compresión se admite en forma de un formato COW específico de Android. Esta versión de A/B virtual requería una traducción del COW específico de Android al formato COW del kernel. Finalmente, se reemplazó en Android 13, que quitó la dependencia del formato COW del kernel y también dm-snapshot.

Para implementar A/B virtual o usar las capacidades de instantáneas comprimidas, consulta Implementación de A/B virtual.