Actualizaciones dinámicas del sistema

Las actualizaciones dinámicas del sistema (DSU) te permiten crear una imagen del sistema Android que los usuarios pueden descargar de Internet y probar sin correr el riesgo de dañar la imagen actual del sistema. En este documento, se describe cómo admitir la función DSU.

Requisitos del kernel

Consulta Cómo implementar particiones dinámicas para conocer los requisitos del kernel.

Además, la función DSU se basa en la función del kernel de mapper-verity de dispositivos (dm-verity). para verificar la imagen del sistema Android. Por lo tanto, debes habilitar el siguiente kernel parámetros de configuración:

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

Requisitos de partición

A partir de Android 11, la DSU requiere que la partición /data use el sistema de archivos F2FS o ext4. F2FS proporciona un mejor rendimiento y se recomienda, pero la diferencia debería ser insignificante.

Estos son algunos ejemplos del tiempo que tarda una actualización dinámica del sistema con un teléfono Pixel. dispositivo:

  • Con F2FS:
    • 109s, usuario de 8G, sistema de 867M, tipo de sistema de archivos: F2FS: encriptación=aes-256-xts:aes-256-cts
    • 104 s, usuario de 8 G, sistema de 867 M, tipo de sistema de archivos: F2FS: encryption=ice
  • Con ext4:
    • 135 s, usuario de 8 G, sistema de 867 M, tipo de sistema de archivos: ext4: encryption=aes-256-xts:aes-256-cts

Si tarda mucho más en tu plataforma, comprueba si el soporte contiene cualquier marca que haga que “sync” escriba, o puedes especificar una marca “async” de forma explícita para obtener un mejor rendimiento.

Se requiere la partición metadata (16 MB o más) para almacenar datos relacionados a las imágenes instaladas. Se debe colocar durante la primera etapa.

La partición userdata debe usar el sistema de archivos F2FS o ext4. Cuando uses F2FS, incluye todos los parches relacionados con F2FS disponibles en el kernel común de Android.

DSU se desarrolló y probó con el kernel/común 4.9. Se recomienda usar el kernel 4.9 y versiones posteriores para esta función.

Comportamiento de la HAL del proveedor

HAL de Weaver

El HAL de Weaver proporciona una cantidad fija de ranuras para almacenar claves de usuario. La DSU consume dos ranuras de teclas adicionales. Si un OEM tiene un HAL de Weaver, debe tener suficientes ranuras para una imagen genérica del sistema (GSI) y una imagen de host.

HAL de Gatekeeper

La HAL del recepcionista debe hacer lo siguiente: admiten valores USER_ID grandes, porque la GSI desplaza los UIDs a la HAL +1,000,000.

Cómo verificar el inicio

Si deseas admitir el inicio de imágenes de GSI para desarrolladores en estado LOCKED sin inhabilitar el inicio verificado, incluye las claves de GSI para desarrolladores. Para ello, agrega la siguiente línea al archivo device/<device_name>/device.mk:

$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)

Protección contra la reversión

Si usas DSU, la imagen del sistema Android descargada debe ser más reciente que la imagen del sistema actual en el dispositivo. Para ello, se comparan los parches de seguridad niveles en la Inicio verificado de Android (AVB) Descriptor de propiedades de AVB de ambas imágenes del sistema: Prop: com.android.build.system.security_patch -> '2019-04-05'.

En el caso de los dispositivos que no usan AVB, coloca el nivel de parche de seguridad de la imagen del sistema actual en la línea de comandos del kernel o en bootconfig con el bootloader: androidboot.system.security_patch=2019-04-05.

Requisitos de hardware

Cuando inicias una instancia de DSU, se asignan dos archivos temporales:

  • Una partición lógica para almacenar GSI.img (de 1 a 1.5 G)
  • Una partición /data vacía de 8 GB como zona de pruebas para ejecutar la GSI

Te recomendamos reservar al menos 10 GB de espacio libre antes de iniciar una instancia de DSU. DSU también admite la asignación desde una tarjeta SD. Cuando se borra una tarjeta SD tiene la prioridad más alta para la asignación. La compatibilidad con tarjetas SD es fundamental para los dispositivos de menor potencia que podrían no tener suficiente almacenamiento interno. Cuando haya una tarjeta SD, asegúrate de que no esté adoptada. La función DSU no es compatible tarjetas SD adoptadas.

Frontends disponibles

Puedes iniciar la función DSU con adb, una app de OEM o el cargador de DSU con un clic (en Android 11 o versiones posteriores).

Cómo iniciar la función DSU con adb

Para iniciar DSU con adb, ingresa estos comandos:

$ simg2img out/target/product/.../system.img system.raw
$ gzip -c system.raw > system.raw.gz
$ adb push system.raw.gz /storage/emulated/0/Download
$ adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity  \
-a android.os.image.action.START_INSTALL    \
-d file:///storage/emulated/0/Download/system.raw.gz  \
--el KEY_SYSTEM_SIZE $(du -b system.raw|cut -f1)  \
--el KEY_USERDATA_SIZE 8589934592

Cómo iniciar la función DSU con una app

El punto de entrada principal a la DSU es la API de android.os.image.DynamicSystemClient.java:

public class DynamicSystemClient {


...
...

     /**
     * Start installing DynamicSystem from URL with default userdata size.
     *
     * @param systemUrl A network URL or a file URL to system image.
     * @param systemSize size of system image.
     */
    public void start(String systemUrl, long systemSize) {
        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
    }

Debes empaquetar o preinstalar esta app en el dispositivo. Debido a que DynamicSystemClient es una API del sistema, no puedes compilar la app con la API normal del SDK ni publicarla en Google Play. El propósito de esta app es el siguiente:

  1. Recupera una lista de imágenes y la URL correspondiente con un esquema definido por el proveedor.
  2. Haz coincidir las imágenes de la lista con el dispositivo y muestra imágenes compatibles para que el usuario las seleccione.
  3. Invoca DynamicSystemClient.start de la siguiente manera:

    DynamicSystemClient aot = new DynamicSystemClient(...)
       aot.start(
            ...URL of the selected image...,
            ...uncompressed size of the selected image...);
    
    

La URL apunta a un archivo de imagen del sistema comprimido con gzip y no disperso, que puedes crear con los siguientes comandos:

$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz

El nombre del archivo debe seguir este formato:

<android version>.<lunch name>.<user defined title>.raw.gz

Ejemplos:

  • o.aosp_taimen-userdebug.2018dev.raw.gz
  • p.aosp_taimen-userdebug.2018dev.raw.gz

Cargador de DSU con un clic

En Android 11, se introdujo el cargador de DSU de un clic, que es un frontend en la configuración para desarrolladores.

Cómo iniciar el cargador de DSU

Figura 1: Cómo iniciar el cargador de DSU

Cuando el desarrollador hace clic en el botón DSU Loader, recupera un descriptor JSON de DSU preconfigurado de la Web y muestra todas las imágenes aplicables en el menú flotante. Selecciona una imagen para iniciar la instalación de la función DSU y el progreso aparece en la barra de notificaciones.

Progreso de la instalación de la imagen de DSU

Figura 2: Progreso de la instalación de la imagen de DSU

De forma predeterminada, el cargador de DSU carga un descriptor JSON que contiene las imágenes de GSI. En las siguientes secciones, se muestra cómo crear paquetes de DSU firmados por el OEM y cargarlos desde el cargador de DSU.

Marca de función

La función DSU se encuentra bajo la marca de función settings_dynamic_android. Antes con DSU, asegúrate de que esté habilitada la marca de función correspondiente.

Habilita la marca de función.

Figura 3: Habilita la marca de función

Es posible que la IU de la marca de función no esté disponible en un dispositivo que ejecute una compilación de usuario. En En este caso, usa el comando adb en su lugar:

$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1

Imágenes del sistema del host del proveedor en GCE (opcional)

Una de las posibles ubicaciones de almacenamiento para las imágenes del sistema es la de Compute Engine (GCE). El administrador de la versión usa el la consola de almacenamiento de GCP agregar, borrar o cambiar la imagen del sistema lanzada.

Las imágenes deben ser de acceso público, como se muestra a continuación:

Acceso público en GCE

Figura 4: Acceso público en GCE

El procedimiento para hacer público un elemento está disponible en la documentación de Google Cloud.

DSU de varias particiones en un archivo ZIP

A partir de Android 11, la función DSU puede tener más de un por cada partición. Por ejemplo, puede contener un product.img además de system.img. Cuando se inicia el dispositivo, la primera etapa de init detecta la instaló particiones de DSU y reemplaza la partición en el dispositivo temporalmente, cuando se habilite la DSU instalada. El paquete DSU puede contener una partición que no tener una partición correspondiente en el dispositivo.

Proceso de DSU con varias particiones

Figura 5: Proceso de DSU con varias particiones

DSU firmada por OEM

Para garantizar que todas las imágenes que se ejecutan en el dispositivo estén autorizadas por el dispositivo del fabricante, todas las imágenes incluidas en un paquete de DSU deben estar firmadas. Por ejemplo: Supongamos que hay un paquete de DSU que contiene dos imágenes de partición como la siguiente:

dsu.zip {
    - system.img
    - product.img
}

Tanto system.img como product.img deben estar firmados por la clave OEM antes de poder se colocan en el archivo ZIP. La práctica común es usar un algoritmo asimétrico, por ejemplo, RSA, en el que se usa la clave secreta para firmar el paquete y la clave pública para verificarlo. El ramdisk de la primera etapa debe incluir el emparejamiento clave pública, por ejemplo, /avb/*.avbpubkey. Si el dispositivo ya adoptó AVB, será suficiente con el procedimiento de firma existente. En las siguientes secciones, se ilustra el proceso de firma y se destaca la ubicación de la clave pública de AVB que se usa para verificar las imágenes en el paquete de DSU.

Descriptor JSON de DSU

El descriptor JSON de DSU describe los paquetes de DSU. Admite dos primitivas. Primero, la primitiva include incluye redireccionamientos o descriptores JSON adicionales el cargador de DSU a una ubicación nueva. Por ejemplo:

{
    "include": ["https://.../gsi-release/gsi-src.json"]
}

En segundo lugar, la primitiva image se usa para describir los paquetes de DSU lanzados. Interior Existen varios atributos para el primitivo de la imagen:

  • Los atributos name y details son cadenas que se muestran en el cuadro de diálogo que el usuario debe seleccionar.

  • Los atributos cpu_api, vndk y os_version se usan para las verificaciones de compatibilidad, que se describen en la siguiente sección.

  • El atributo opcional pubkey describe la clave pública que se vincula con la clave secreta que se usa para firmar el paquete de DSU. Cuando se especifica, el servicio de DSU puede verificar si el dispositivo tiene la clave que se usa para verificar el paquete de DSU. De esta manera, se evita instalar una DSU no reconocida (por ejemplo, la instalación de un DSU firmado por OEM-A en un dispositivo creado por OEM-B.

  • El atributo opcional tos apunta a un archivo de texto que describe las Condiciones del Servicio del paquete de DSU correspondiente. Cuando un desarrollador selecciona un paquete de DSU con el atributo de condiciones del servicio especificado, se abre el cuadro de diálogo que se muestra en la Figura 6 y se le solicita al desarrollador que acepte las condiciones del servicio antes de instalar el paquete de DSU.

    Cuadro de diálogo de las Condiciones del Servicio

    Figura 6: Cuadro de diálogo de las Condiciones del Servicio

A modo de referencia, aquí tienes un descriptor de JSON de DSU para la GSI:

{
   "images":[
      {
         "name":"GSI+GMS x86",
         "os_version":"10",
         "cpu_abi": "x86",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_x86-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI+GMS ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI x86_64",
         "os_version":"10",
         "cpu_abi": "x86_64",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_x86_64-exp-QP1A.190711.020.C4-5928301.zip"
      }
   ]
}

Administración de compatibilidad

Se usan varios atributos para especificar la compatibilidad entre un paquete de DSU y el dispositivo local:

  • cpu_api es una cadena que describe la arquitectura del dispositivo. Este atributo es obligatorio y se compara con la propiedad del sistema ro.product.cpu.abi. Sus valores deben coincidir exactamente.

  • os_version es un número entero opcional que especifica una versión de Android. Para Por ejemplo, en Android 10, os_version es 10. En Android 11, os_version es 11. Cuando esta se especifica este atributo, debe ser igual o mayor que el valor de ro.system.build.version.release del sistema. Esta verificación se usa para evitar el inicio de una imagen de GSI de Android 10 en un dispositivo de proveedor de Android 11, que actualmente no es compatible. Se permite el inicio de una imagen de GSI de Android 11 en un dispositivo con Android 10.

  • vndk es un array opcional que especifica todos los VNDK que se incluyen en el paquete de DSU. Cuando se especifica, el cargador de DSU verifica si la cifra extraída de la propiedad del sistema ro.vndk.version.

Cómo revocar las llaves de DSU por seguridad

En el caso poco frecuente de que el par de claves RSA usado para firmar las imágenes de DSU sea vulnerado, el ramdisk debe actualizarse lo antes posible para quitar el clave comprometida. Además de actualizar la partición de inicio, puedes bloquear las claves comprometidas con una lista de revocación de claves de DSU (lista negra de claves) desde una URL HTTPS.

La lista de revocación de claves de la DSU contiene una lista de claves públicas AVB revocadas. Durante la instalación de la función DSU, se validan las claves públicas dentro de las imágenes de la función DSU. con la lista de revocación. Si se descubre que las imágenes contienen un se detendrá el proceso de instalación de DSU.

La URL de la lista de revocación de claves debe ser una URL HTTPS para garantizar la seguridad y se especifica en una cadena de recursos:

frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url

El valor de la cadena es https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json, que es un de revocación de claves para las claves de GSI que lanzó Google. Esta cadena de recursos puede ser se superponen y personalizan para que los OEMs que adopten la función DSU puedan mantener su propia lista negra de claves. De esta forma, el OEM puede bloquear ciertas claves públicas sin actualizar la imagen del ramdisk del dispositivo.

El formato de la lista de revocación es el siguiente:

{
   "entries":[
      {
         "public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      },
      {
         "public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      }
   ]
}
  • public_key es el resumen SHA-1 de la clave revocada, en el formato que se describe. en la sección de generación de la clave pública de AVB sección.
  • status indica el estado de revocación de la clave. Actualmente, el único el valor admitido es REVOKED.
  • reason es una cadena opcional que describe el motivo de la revocación.

Procedimientos de DSU

En esta sección, se describe cómo realizar varios procedimientos de configuración de la DSU.

Genera un par de claves nuevo

Usa el comando openssl para generar un par de claves pública/privada RSA en .pem. (por ejemplo, con un tamaño de 2,048 bits):

$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem

Es posible que no se pueda acceder a la clave privada, y solo se mantiene en un módulo de seguridad de hardware (HSM). En este caso, es posible que haya un certificado de clave pública X509 disponible después de la generación de claves. Consulta Cómo agregar la clave pub de vinculación al disco RAM. para obtener instrucciones sobre cómo generar la clave pública de AVB a partir de un certificado x509.

Para convertir un certificado x509 a formato PEM, sigue estos pasos:

$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem

Omite este paso si el certificado ya es un archivo PEM.

Agrega la clave pub sincronizada al ramdisk

El oem_cert.avbpubkey se debe colocar en /avb/*.avbpubkey para verificar la paquete de DSU firmado. Primero, convierte la clave pública en formato PEM a AVB public. formato de clave:

$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey

Luego, sigue estos pasos para incluir la clave pública en el primer disco RAM de la etapa.

  1. Agrega un módulo precompilado para copiar el avbpubkey. Por ejemplo, agrega device/<company>/<board>/oem_cert.avbpubkey y device/<company>/<board>/avb/Android.mk con contenido como este:

    include $(CLEAR_VARS)
    
    LOCAL_MODULE := oem_cert.avbpubkey
    LOCAL_MODULE_CLASS := ETC
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
    LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
    else
    LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
    endif
    
    include $(BUILD_PREBUILT)
    
  2. Haz que el objetivo de droidcore dependa del oem_cert.avbpubkey agregado:

    droidcore: oem_cert.avbpubkey
    

Genera el atributo de clave pública de AVB en el descriptor JSON

oem_cert.avbpubkey está en el formato binario de clave pública de AVB. Usa SHA-1 para haz que sea legible antes de colocarlo en el descriptor JSON:

$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20

Este será el contenido del atributo pubkey del descriptor JSON.

   "images":[
      {
         ...
         "pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
         ...
      },

Firma un paquete de DSU

Usa uno de estos métodos para firmar un paquete de DSU:

  • Método 1: Reutiliza el artefacto creado por el proceso original de firma del AVB para hacer un paquete DSU. Un enfoque alternativo es extraer las imágenes ya firmadas del paquete de lanzamiento y usarlas para crear el archivo ZIP directamente.

  • Método 2: Usa los siguientes comandos para firmar particiones de DSU si la ventana está disponible. Cada img dentro de un paquete de DSU (el archivo ZIP) se firma por separado:

    $ key_len=$(openssl rsa -in oem_cert_pri.pem -text | grep Private-Key | sed -e 's/.*(\(.*\) bit.*/\1/')
    $ for partition in system product; do
        avbtool add_hashtree_footer \
            --image ${OUT}/${partition}.img \
            --partition_name ${partition} \
            --algorithm SHA256_RSA${key_len} \
            --key oem_cert_pri.pem
    done
    

Para obtener más información sobre cómo agregar add_hashtree_footer con avbtool, consulta Cómo usar avbtool.

Verifica el paquete de DSU de forma local

Se recomienda verificar todas las imágenes locales con la clave pública de vinculación con estos comandos:


for partition in system product; do
    avbtool verify_image --image ${OUT}/${partition}.img  --key oem_cert_pub.pem
done

El resultado esperado se ve de la siguiente manera:

Verifying image dsu/system.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/system.img
: Successfully verified sha1 hashtree of dsu/system.img for image of 898494464 bytes

Verifying image dsu/product.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/product.img
: Successfully verified sha1 hashtree of dsu/product.img for image of 905830400 bytes

Cómo crear un paquete de DSU

En el siguiente ejemplo, se crea un paquete de DSU que contiene un system.img y un product.img:

dsu.zip {
    - system.img
    - product.img
}

Después de firmar ambas imágenes, usa el siguiente comando para crear el archivo ZIP:

$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -

Personaliza la DSU de un clic

De forma predeterminada, el cargador de DSU apunta a un metadato de imágenes de GSI que es https://...google.com/.../gsi-src.json.

Los OEMs pueden reemplazar la lista definiendo la propiedad persist.sys.fflag.override.settings_dynamic_system.list que apunta a su propio descriptor JSON. Por ejemplo, un OEM puede proporcionar metadatos JSON que incluyan GSI, así como imágenes propias del OEM, de la siguiente manera:

{
    "include": ["https://dl.google.com/.../gsi-src.JSON"]
    "images":[
      {
         "name":"OEM image",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"...",
         "vndk":[
            27,
            28,
            29
         ],
         "spl":"...",
         "pubkey":"",
         "uri":"https://.../....zip"
      },

}

Es posible que un OEM encadena los metadatos publicados de DSU, como se muestra en la Figura 7.

Encadena los metadatos de la DSU publicados

Figura 7: Encadena los metadatos de la DSU publicados