Google se compromete a promover la equidad racial para las comunidades negras. Ver cómo.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Implementación de actualizaciones A / B

Los fabricantes de equipos originales y los proveedores de SoC que desean implementar actualizaciones del sistema A / B deben asegurarse de que su gestor de arranque implemente el HAL boot_control y pase los parámetros correctos al núcleo.

Implementando el control de arranque HAL

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

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

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

Configurando el núcleo

Para implementar actualizaciones del sistema A / B:

  1. Cherrypick la siguiente serie de parches de kernel (si es necesario):
  2. Asegúrese de que los argumentos de la línea de comandos 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 la ID de la clave pública utilizada para verificar la firma de la tabla de veridad (para 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 el formato .der en la raíz del directorio del kernel . Si el certificado .X509 está formateado como un archivo .pem , use el siguiente comando openssl para convertir de formato .pem a .der :
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Cree la imagen 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 (el resaltado indica la 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 núcleo. Por ejemplo, pase Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f en lugar de <public-key-id> .

Establecer variables de compilación

Los gestores 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 antes del reinicio) descrito en Compilación .
Muy recomendable para el objetivo A / B
  • Definir TARGET_NO_RECOVERY := true
  • Definir BOARD_USES_RECOVERY_AS_BOOT := true
  • No defina 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 arranque. Todas las particiones que son A / B-ed deben nombrarse 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 almacenar bloques temporalmente mientras se aplicaban las actualizaciones. Nunca hubo una buena manera de dimensionar la partición de la memoria caché: el tamaño que debía depender de las actualizaciones que deseara 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 todo el paquete OTA 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 poner la opción skip_initramfs en la línea de comando del kernel.

Para actualizaciones no A / B, la partición de recuperación contiene el código utilizado para aplicar actualizaciones. Las actualizaciones A / B se aplican mediante update_engine ejecuta en la imagen del sistema de arranque normal. Todavía hay un modo de recuperación utilizado para implementar el restablecimiento de datos de fábrica y la carga lateral de paquetes de actualización (que es 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 arranque normal en un disco RAM; para arrancar en la imagen del sistema, el gestor de arranque le dice al kernel que omita el ramdisk (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 de selección de slotselect debe estar en la línea para las particiones A / B-ed. Por ejemplo:

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

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

Argumentos de ranura de kernel

El sufijo de ranura actual se debe pasar a través de un nodo de árbol de dispositivo (DT) específico ( /firmware/android/slot_suffix ) o mediante el argumento de línea de comando androidboot.slot_suffix .

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 fastboot para actualizar la ranura que se pasa como argumento.
  • --set-active [ SLOT ] . Establezca la ranura como activa. Si no se especifica ningún argumento opcional, la ranura actual se establece como activa.
  • fastboot --help . Obtenga detalles sobre los comandos.

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

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

Para ver todas las variables, ejecute fastboot getvar all .

Generando 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 objetivo A / B. Las herramientas de paquete OTA identifican y generan paquetes automáticamente 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
    

Configurando 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 AB_OTA_PARTITIONS configura la lista de particiones para las que el generador de carga útil define una actualización.

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 la placa:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

El resto del sistema no debe modificar todas las particiones actualizadas por update_engine . 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 nuevos datos de la ranura fallen la verificación durante el proceso de actualización y, por lo tanto, la actualización falle.

Configuración posterior a la instalació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 usa un disco RAM). Además, especifique el tipo de sistema de archivos para 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

Compilando

Por razones de seguridad, system_server no puede usar la compilación justo a tiempo (JIT) . Esto significa que debe compilar con anticipación archivos odex para system_server y sus dependencias como mínimo; Cualquier otra cosa es opcional.

Para compilar aplicaciones en segundo plano, debe agregar lo siguiente a la configuración del dispositivo del producto (en el dispositivo.mk del producto):

  1. Incluya los componentes nativos en la compilación para garantizar que el script de compilación y los 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 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 para instalar los archivos preoptados en la segunda partición del sistema no utilizada, consulte Primera instalación de arranque de los archivos DEX_PREOPT .