Implementar actualizaciones A/B

Los fabricantes de OEM y proveedores de SoC que deseen implementar actualizaciones del sistema A/B deben asegurarse de que su gestor de arranque implemente boot_control HAL y pase los parámetros correctos al kernel.

Implementar el control de arranque HAL

Los cargadores de arranque con capacidad A/B deben implementar boot_control HAL en hardware/libhardware/include/hardware/boot_control.h . Puede probar implementaciones utilizando la utilidad system/extras/bootctl y system/extras/tests/bootloader/ .

También debes implementar la máquina de estados que se muestra a continuación:

Figura 1. Máquina de estado del cargador de arranque

Configurar el kernel

Para implementar actualizaciones del sistema A/B:

  1. Seleccione la siguiente serie de parches del kernel (si es necesario):
  2. Asegúrese de que los argumentos de la línea de comando del kernel contengan los siguientes argumentos adicionales:
    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>"
    ... donde el valor <public-key-id> es el ID de la clave pública utilizada para verificar la firma de la tabla de verdad (para obtener más detalles, consulte dm-verity ). .
  3. Agregue el certificado .X509 que contiene la clave pública al conjunto de claves del sistema:
    1. Copie el certificado .X509 formateado en formato .der a la raíz del directorio kernel . Si el certificado .X509 está formateado como un archivo .pem , use el siguiente comando openssl para convertir del formato .pem al formato .der :
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Cree zImage para incluir el certificado como parte del conjunto de claves del sistema. Para verificar, verifique la entrada procfs (requiere que KEYS_CONFIG_DEBUG_PROC_KEYS esté habilitado):
      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
      La inclusión exitosa del certificado .X509 indica la presencia de la clave pública en el conjunto de claves del sistema (resaltado indica el ID de la clave pública).
    3. Reemplace el espacio con # y páselo como <public-key-id> en la línea de comando del kernel. Por ejemplo, pase Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f en lugar de <public-key-id> .

Establecer variables de compilación

Los cargadores de arranque con capacidad A/B deben cumplir los siguientes criterios de variables de compilación:

Debe definirse para el objetivo A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
    boot \
    system \
    vendor
    y otras particiones actualizadas a través de update_engine (radio, gestor de arranque, etc.)
  • PRODUCT_PACKAGES += \
    update_engine \
    update_verifier
Para ver un ejemplo, consulte /device/google/marlin/+/android-7.1.0_r1/device-common.mk . Opcionalmente, puede realizar el paso dex2oat posterior a la instalación (pero previo al reinicio) que se describe en Compilación .
Altamente recomendado para el objetivo A/B
  • Definir TARGET_NO_RECOVERY := true
  • Definir BOARD_USES_RECOVERY_AS_BOOT := true
  • No definir BOARD_RECOVERYIMAGE_PARTITION_SIZE
No se puede definir para el objetivo A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
Opcional para compilaciones de depuración PRODUCT_PACKAGES_DEBUG += update_engine_client

Establecer particiones (ranuras)

Los dispositivos A/B no necesitan una partición de recuperación o una partición de caché porque Android ya no usa estas particiones. La partición de datos ahora se usa para el paquete OTA descargado y el código de la imagen de recuperación está en la partición de inicio. Todas las particiones A/B deben denominarse de la siguiente manera (las ranuras siempre se denominan a , b , etc.): boot_a , boot_b , system_a , system_b , vendor_a , vendor_b .

Cache

Para las actualizaciones que no son A/B, la partición de caché se usó para almacenar paquetes OTA descargados y para ocultar bloques temporalmente mientras se aplican las actualizaciones. Nunca hubo una buena manera de dimensionar la partición de caché: su tamaño dependía de las actualizaciones que deseaba aplicar. El peor de los casos sería una partición de caché tan grande como la imagen del sistema. Con las actualizaciones A/B no hay necesidad de guardar bloques (porque siempre estás escribiendo en una partición que no se usa actualmente) y con la transmisión A/B no hay necesidad de descargar el paquete OTA completo antes de aplicarlo.

Recuperación

El disco RAM de recuperación ahora está contenido en el archivo boot.img . Al entrar en recuperación, el gestor de arranque no puede colocar la opción skip_initramfs en la línea de comando del kernel.

Para actualizaciones que no son A/B, la partición de recuperación contiene el código utilizado para aplicar las actualizaciones. Las actualizaciones A/B se aplican mediante update_engine que se ejecuta en la imagen del sistema de inicio normal. Todavía hay un modo de recuperación que se utiliza para implementar el restablecimiento de datos de fábrica y la descarga de paquetes de actualización (de donde proviene el nombre "recuperación"). El código y los datos para el modo de recuperación se almacenan en la partición de inicio normal en un disco RAM; para arrancar en la imagen del sistema, el gestor de arranque le dice al kernel que omita el disco RAM (de lo contrario, el dispositivo arranca en modo de recuperación. El modo de recuperación es pequeño (y gran parte ya estaba en la partición de arranque), por lo que la partición de arranque no aumenta en tamaño.

Fstab

El argumento slotselect debe estar en la línea para las particiones editadas en A/B. Por ejemplo:

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

Ninguna partición debe denominarse vendor . En su lugar, se seleccionará la partición vendor_a o vendor_b y se montará en el punto de montaje /vendor .

Argumentos de la ranura del kernel

El sufijo de ranura actual debe pasarse a través de un nodo del árbol de dispositivos (DT) específico ( /firmware/android/slot_suffix ) o a través de la línea de comando del kernel androidboot.slot_suffix o el argumento bootconfig.

De forma predeterminada, fastboot muestra la ranura actual en un dispositivo A/B. Si el paquete de actualización también contiene imágenes para la otra ranura no actual, fastboot también muestra esas imágenes. Las opciones disponibles incluyen:

  • --slot SLOT . Anule el comportamiento predeterminado y solicite a fastboot que actualice la ranura que se pasa como argumento.
  • --set-active [ SLOT ] . Establece la ranura como activa. Si no se especifica ningún argumento opcional, entonces la ranura actual se establece como activa.
  • fastboot --help . Obtenga detalles sobre los comandos.

Si el gestor de arranque implementa fastboot, debería admitir el comando set_active <slot> que establece la ranura activa actual en la ranura dada (esto también debe borrar el indicador de no arranque para esa ranura y restablecer el recuento de reintentos a los valores predeterminados). El gestor de arranque también debería admitir las siguientes variables:

  • has-slot:<partition-base-name-without-suffix> . Devuelve "sí" si la partición dada admite ranuras, "no" en caso contrario.
  • current-slot . Devuelve el sufijo de la ranura que se iniciará a continuación.
  • slot-count . Devuelve un número entero que representa el número de espacios disponibles. Actualmente, se admiten dos ranuras, por lo que este valor es 2 .
  • slot-successful:<slot-suffix> . Devuelve "sí" si la ranura dada se ha marcado como arrancando exitosamente, "no" en caso contrario.
  • slot-unbootable:<slot-suffix> . Devuelve "sí" si la ranura dada está marcada como no arrancable, "no" en caso contrario.
  • slot-retry-count . Número de reintentos restantes para intentar iniciar la ranura determinada.

Para ver todas las variables, ejecute fastboot getvar all .

Generar paquetes OTA

Las herramientas del paquete OTA siguen los mismos comandos que los comandos para dispositivos que no son A/B. El archivo target_files.zip debe generarse definiendo las variables de compilación para el destino A/B. Las herramientas de paquetes OTA identifican y generan automáticamente paquetes en el formato para el actualizador A/B.

Ejemplos:

  • Para generar una OTA completa:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • Para generar una OTA incremental:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

Configurar particiones

update_engine puede actualizar cualquier par de particiones A/B definidas en el mismo disco. Un par de particiones tiene un prefijo común (como system o boot ) y un sufijo por ranura (como _a ). La lista de particiones para las cuales el generador de carga útil define una actualización se configura mediante la variable de creación AB_OTA_PARTITIONS .

Por ejemplo, si se incluyen un par de particiones bootloader_a y booloader_b ( _a y _b son los sufijos de ranura), puede actualizar estas particiones especificando lo siguiente en la configuración del producto o de la placa:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Todas las particiones actualizadas por update_engine no deben ser modificadas por el resto del sistema. Durante las actualizaciones incrementales o delta , los datos binarios de la ranura actual se utilizan para generar los datos en la nueva ranura. Cualquier modificación puede hacer que los datos de la nueva ranura no superen la verificación durante el proceso de actualización y, por lo tanto, falle la actualización.

Configurar la postinstalación

Puede configurar el paso posterior a la instalación de manera diferente para cada partición actualizada utilizando un conjunto de pares clave-valor. Para ejecutar un programa ubicado en /system/usr/bin/postinst en una nueva imagen, especifique la ruta relativa a la raíz del sistema de archivos en la partición del sistema.

Por ejemplo, usr/bin/postinst es system/usr/bin/postinst (si no se utiliza un disco RAM). Además, especifique el tipo de sistema de archivos que se pasará a la llamada al sistema mount(2) . Agregue lo siguiente a los archivos .mk del producto o dispositivo (si corresponde):

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

compilar aplicaciones

Las aplicaciones se pueden compilar en segundo plano antes del reinicio con la nueva imagen del sistema. Para compilar aplicaciones en segundo plano, agregue lo siguiente a la configuración del dispositivo del producto (en el archivo device.mk del producto):

  1. Incluya los componentes nativos en la compilación para garantizar que el script de compilación y los archivos binarios se compilen e incluyan en la imagen del sistema.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. Conecte el script de compilación a update_engine de modo que se ejecute como un paso posterior a la instalación.
      # 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
    

Para obtener ayuda con la instalación de los archivos preoptados en la segunda partición del sistema no utilizada, consulte Primera instalación de arranque de archivos DEX_PREOPT .