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, DSU se basa en la función del kernel device-mapper-verity (dm-verity) para verificar la imagen del sistema Android. Por lo tanto, debes habilitar las siguientes configuraciones del kernel:
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
Requisitos de las particiones
A partir de Android 11, la DSU requiere que la partición /data
use el sistema de archivos F2FS o ext4. F2FS ofrece un mejor rendimiento y se recomienda, pero la diferencia no debe ser significativa.
Estos son algunos ejemplos de cuánto tiempo tarda una actualización dinámica del sistema con un dispositivo Pixel:
- Si usas F2FS:
- 109 s, usuario de 8 G, sistema de 867 M, tipo de sistema de archivos: F2FS: encryption=aes-256-xts:aes-256-cts
- 104s, usuario de 8G, 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, te recomendamos que verifiques si la marca de activación de activación contiene alguna marca que haga que la escritura sea "sync", o bien 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 con las imágenes instaladas. Se debe montar durante el montaje de 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
La 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 ranuras suficientes para una imagen genérica del sistema (GSI) y una imagen de host.
HAL del recepcionista
El HAL de Gatekeeper debe admitir valores grandes de USER_ID
, ya que el GSI compensa los UIDs en el HAL en más de 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
Cuando se usa la DSU, la imagen del sistema Android descargada debe ser más reciente que la imagen del sistema actual del dispositivo. Para ello, se comparan los niveles de parche de seguridad en el descriptor de propiedades de AVB del inicio verificado de Android (AVB) de ambas imágenes del sistema: Prop: com.android.build.system.security_patch ->
'2019-04-05'
.
Para los dispositivos que no usan AVB, coloca el nivel de parche de seguridad de la imagen del sistema actual en la cmdline del kernel o en la configuración de inicio 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
(1~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. La función DSU también admite la asignación desde una tarjeta SD. Cuando hay 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. Si tienes una tarjeta SD, asegúrate de que no sea adoptada. La DSU no admite tarjetas SD adoptadas.
Frontends disponibles
Puedes iniciar la función DSU con adb
, una app del OEM o el cargador de DSU con un clic (en Android 11 o versiones posteriores).
Inicia la función DSU con adb
Para iniciar la función 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 del SDK normal y no puedes publicarla en Google Play. El propósito de esta app es:
- Recupera una lista de imágenes y la URL correspondiente con un esquema definido por el proveedor.
- Haz coincidir las imágenes de la lista con el dispositivo y muestra imágenes compatibles para que las seleccione el usuario.
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 dirige a un archivo de imagen del sistema comprimido en gzip, 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 de un clic
Android 11 presenta el cargador de DSU de un clic, que es un frontend en la configuración para desarrolladores.
Figura 1: Cómo iniciar el cargador de DSU
Cuando el desarrollador hace clic en el botón DSU Loader, recupera un descriptor JSON 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 DSU, y el progreso se mostrará en la barra de notificaciones.
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 un 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 de usar la función DSU, asegúrate de que esté habilitada la marca de la función correspondiente.
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 ejecuta una compilación de usuario. 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 ubicaciones de almacenamiento posibles para las imágenes del sistema es el bucket de Google Compute Engine (GCE). El administrador de versiones usa la consola de almacenamiento de GCP para agregar, borrar o cambiar la imagen del sistema lanzada.
Las imágenes deben ser de acceso público, como se muestra a continuación:
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, DSU puede tener más de una partición. Por ejemplo, puede contener un product.img
además del system.img
. Cuando se inicia el dispositivo, la primera etapa de init
detecta las particiones de DSU instaladas y reemplaza la partición en el dispositivo de manera temporal cuando se habilita la DSU instalada. Es posible que el paquete de DSU contenga una partición que no tenga una partición correspondiente en el dispositivo.
Figura 5: Proceso de DSU con varias particiones
DSU firmada por OEM
Para garantizar que el fabricante del dispositivo autorice todas las imágenes que se ejecutan en él, todas las imágenes de un paquete de DSU deben estar firmadas. Por ejemplo, asumamos que hay un paquete de DSU que contiene dos imágenes de partición como las siguientes:
dsu.zip {
- system.img
- product.img
}
Tanto system.img
como product.img
deben estar firmados por la clave OEM antes de que se coloquen en el archivo ZIP. La práctica común es usar un algoritmo asimétrico, por ejemplo, RSA, en el que la clave secreta se usa para firmar el paquete y la clave pública para verificarlo. El ramdisk de primera etapa debe incluir la clave pública de vinculación, por ejemplo, /avb/*.avbpubkey
. Si el dispositivo ya adoptó el AVB, el procedimiento de firma existente será suficiente. 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 la DSU
El descriptor JSON de DSU describe paquetes de DSU. Admite dos primitivas.
Primero, la primitiva include
incluye descriptores JSON adicionales o redirecciona 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. Dentro de la primitiva de imagen, hay varios atributos:
Los atributos
name
ydetails
son cadenas que se muestran en el diálogo que debe seleccionar el usuario.Los atributos
cpu_api
,vndk
yos_version
se usan para verificar la 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 comprobar si el dispositivo tiene la clave que se usó para verificar el paquete de DSU. Esto evita instalar un paquete de DSU no reconocido, por ejemplo, instalar una DSU firmada por OEM-A en un dispositivo fabricado 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.Figura 6: Diálogo de las Condiciones del Servicio
A modo de referencia, este es un descriptor 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 sistemaro.product.cpu.abi
. Sus valores deben coincidir exactamente.os_version
es un número entero opcional que especifica una versión de Android. Por ejemplo, en Android 10,os_version
es10
y para Android 11,os_version
es11
. Cuando se especifica este atributo, debe ser igual o mayor que la propiedad del sistemaro.system.build.version.release
. Esta verificación se usa para evitar que se inicie una imagen de GSI de Android 10 en un dispositivo de proveedor de Android 11, que actualmente no se admite. Se permite iniciar 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 se incluye el número extraído de la propiedad del sistemaro.vndk.version
.
Cómo revocar llaves de DSU por seguridad
En el caso poco frecuente en el que el par de claves RSA que se usa para firmar las imágenes de DSU esté comprometido, se debe actualizar el ramdisk lo antes posible para quitar la 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 de AVB revocadas. Durante la instalación de la DSU, las claves públicas dentro de las imágenes de DSU se validan con la lista de revocación. Si se descubre que las imágenes contienen una clave pública revocada, se detiene 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 de la clave 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 una lista de revocación para las claves de GSI publicadas por Google. Esta cadena de recursos se puede superponer y personalizar para que los OEM que adopten la función DSU puedan proporcionar y mantener su propia lista negra de claves. Esto proporciona una forma para que el OEM bloquee 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 Cómo generar la clave pública de AVB.status
indica el estado de revocación de la clave. Por el momento, el único valor admitido esREVOKED
.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 DSU.
Genera un par de claves nuevo
Usa el comando openssl
para generar un par de claves públicas/privadas RSA en formato .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 la sección Cómo agregar la clave pública de vinculación a la ramdisk para obtener instrucciones para 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 pública de vinculación a la ramdisk
El oem_cert.avbpubkey
debe colocarse en /avb/*.avbpubkey
para verificar el paquete de DSU firmado. Primero, convierte la clave pública en formato PEM al formato de clave pública AVB:
$ 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.
Agrega un módulo ya compilado para copiar
avbpubkey
. Por ejemplo, agregadevice/<company>/<board>/oem_cert.avbpubkey
ydevice/<company>/<board>/avb/Android.mk
con contenido como el siguiente: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)
Haz que el objetivo de droidcore dependa del
oem_cert.avbpubkey
agregado:droidcore: oem_cert.avbpubkey
Genera el atributo de clave pública AVB en el descriptor JSON
oem_cert.avbpubkey
está en el formato binario de clave pública de AVB. Usa SHA-1 para 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",
...
},
Cómo firmar un paquete de DSU
Usa uno de estos métodos para firmar un paquete de DSU:
Método 1: Vuelve a usar el artefacto creado por el proceso de firma de AVB original para elaborar un paquete de DSU. Un enfoque alternativo es extraer las imágenes ya firmadas del paquete de lanzamiento y usar las imágenes extraídas para crear el archivo ZIP directamente.
Método 2: Usa los siguientes comandos para firmar particiones de DSU si la clave privada 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 con 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 propiedad de OEM, como la siguiente:
{
"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 de DSU publicados, como se muestra en la Figura 7.
Figura 7: Encadena los metadatos de la DSU publicados