Formato de archivo APEX

El formato de contenedor Android Pony EXpress (APEX) se introdujo en Android 10 y se usa en el flujo de instalación de módulos del sistema de nivel inferior. Este formato facilita las actualizaciones de los componentes del sistema que no se ajustan al modelo estándar de aplicaciones para Android. Algunos ejemplos de componentes son los servicios y las bibliotecas nativos, las capas de abstracción de hardware (HAL), el tiempo de ejecución (ART) y las bibliotecas de clases.

El término "APEX" también puede hacer referencia a un archivo APEX.

Información general

Si bien Android admite actualizaciones de módulos que se ajustan al modelo de app estándar (por ejemplo, servicios y actividades) a través de apps de instalación de paquetes (como la app de Google Play Store), usar un modelo similar para los componentes del SO de nivel inferior tiene los siguientes inconvenientes:

  • Los módulos basados en APK no se pueden usar al principio de la secuencia de inicio. El administrador de paquetes es el repositorio central de información sobre las apps y solo se puede iniciar desde el administrador de actividades, que se prepara en una etapa posterior del procedimiento de inicio.
  • El formato APK (en particular, el manifiesto) está diseñado para apps de Android, y los módulos del sistema no siempre son una buena opción.

Diseño

En esta sección, se describe el diseño de alto nivel del formato de archivo APEX y el administrador de APEX, que es un servicio que administra archivos APEX.

Para obtener más información sobre por qué se seleccionó este diseño para APEX, consulta Alternativas consideradas durante el desarrollo de APEX.

Formato APEX

Este es el formato de un archivo APEX.

Formato de archivo APEX

Figura 1: Formato de archivo APEX

En el nivel superior, un archivo APEX es un archivo zip en el que los archivos se almacenan sin comprimir y se ubican en límites de 4 KB.

Los cuatro archivos de un archivo APEX son los siguientes:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

El archivo apex_manifest.json contiene el nombre y la versión del paquete, que identifican un archivo APEX. Es un búfer de protocolo ApexManifest en formato JSON.

El archivo AndroidManifest.xml permite que el archivo APEX use herramientas e infraestructura relacionadas con APK, como ADB, PackageManager y apps de instalación de paquetes (como Play Store). Por ejemplo, el archivo APEX puede usar una herramienta existente, como aapt, para inspeccionar los metadatos básicos del archivo. El archivo contiene el nombre del paquete y la información de la versión. Por lo general, esta información también está disponible en apex_manifest.json.

Se recomienda apex_manifest.json en lugar de AndroidManifest.xml para el código y los sistemas nuevos que trabajan con APEX. AndroidManifest.xml puede contener información de segmentación adicional que pueden usar las herramientas de publicación de apps existentes.

apex_payload.img es una imagen del sistema de archivos ext4 respaldada por dm-verity. La imagen se activa en el tiempo de ejecución a través de un dispositivo de bucle invertido. Específicamente, el árbol hash y el bloque de metadatos se crean con la biblioteca libavb. No se analiza la carga útil del sistema de archivos (porque la imagen debería poderse activar en el lugar). Los archivos normales se incluyen dentro del archivo apex_payload.img.

apex_pubkey es la clave pública que se usa para firmar la imagen del sistema de archivos. En el tiempo de ejecución, esta clave garantiza que el APEX descargado esté firmado con la misma entidad que firma el mismo APEX en las particiones integradas.

Lineamientos para asignar nombres a APEX

Para ayudar a evitar conflictos de nombres entre los nuevos APEX a medida que avanza la plataforma, usa los siguientes lineamientos de nomenclatura:

  • com.android.*
    • Se reserva para los APEX del AOSP. No es exclusivo de ninguna empresa ni dispositivo.
  • com.<companyname>.*
    • Reservado para una empresa. Es posible que varios dispositivos de esa empresa lo usen.
  • com.<companyname>.<devicename>.*
    • Se reserva para los APEX únicos de un dispositivo específico (o un subconjunto de dispositivos).

Administrador de APEX

El administrador de APEX (o apexd) es un proceso nativo independiente responsable de verificar, instalar y desinstalar archivos APEX. Este proceso se inicia y está listo al principio de la secuencia de arranque. Por lo general, los archivos APEX se preinstalan en el dispositivo en /system/apex. El administrador de APEX usa estos paquetes de forma predeterminada si no hay actualizaciones disponibles.

La secuencia de actualización de un APEX usa la clase PackageManager y es la siguiente:

  1. Se descarga un archivo APEX a través de una app de instalación de paquetes, ADB o alguna otra fuente.
  2. El administrador de paquetes inicia el procedimiento de instalación. Cuando reconoce que el archivo es un APEX, el administrador de paquetes transfiere el control al administrador de APEX.
  3. El administrador de APEX verifica el archivo APEX.
  4. Si se verifica el archivo APEX, la base de datos interna del administrador de APEX se actualiza para reflejar que el archivo APEX se activará en el próximo inicio.
  5. El solicitante de la instalación recibe una transmisión cuando se verifica correctamente el paquete.
  6. Para continuar con la instalación, se debe reiniciar el sistema.
  7. En el siguiente inicio, se inicia el administrador de APEX, se lee la base de datos interna y se realiza lo siguiente para cada archivo APEX que se indica:

    1. Verifica el archivo APEX.
    2. Crea un dispositivo de bucle invertido a partir del archivo APEX.
    3. Crea un dispositivo de bloques de asignador de dispositivos sobre el dispositivo de bucle invertido.
    4. Monta el dispositivo de bloques del asignador de dispositivos en una ruta única (por ejemplo, /apex/name@ver).

Cuando se activan todos los archivos APEX que se indican en la base de datos interna, el administrador de APEX proporciona un servicio de Binder para que otros componentes del sistema consulten información sobre los archivos APEX instalados. Por ejemplo, los otros componentes del sistema pueden consultar la lista de archivos APEX instalados en el dispositivo o la ruta de acceso exacta en la que se activó un APEX específico para poder acceder a los archivos.

Los archivos APEX son archivos APK

Los archivos APEX son archivos APK válidos porque son archivos ZIP firmados (con el esquema de firma de APK) que contienen un archivo AndroidManifest.xml. Esto permite que los archivos APEX usen la infraestructura de los archivos APK, como una app de instalación de paquetes, la utilidad de firma y el administrador de paquetes.

El archivo AndroidManifest.xml dentro de un archivo APEX es mínimo y consta del paquete name, versionCode y los archivos targetSdkVersion, minSdkVersion y maxSdkVersion opcionales para una segmentación detallada. Esta información permite que los archivos APEX se entreguen a través de canales existentes, como las apps de instalación de paquetes y ADB.

Tipos de archivos admitidos

El formato APEX admite los siguientes tipos de archivos:

  • Bibliotecas compartidas nativas
  • Ejecutables nativos
  • Archivos JAR
  • Archivos de datos
  • Archivos de configuración

Esto no significa que APEX pueda actualizar todos estos tipos de archivos. La posibilidad de actualizar un tipo de archivo depende de la plataforma y de la estabilidad de las definiciones de las interfaces para los tipos de archivos.

Opciones de firma

Los archivos APEX se firman de dos maneras. Primero, el archivo apex_payload.img (específicamente, el descriptor vbmeta anexado a apex_payload.img) se firma con una clave. Luego, todo el APEX se firma con el esquema de firma de APK v3. En este proceso, se usan dos claves diferentes.

En el dispositivo, se instala una clave pública que corresponde a la clave privada que se usó para firmar el descriptor de vbmeta. El administrador de APEX usa la clave pública para verificar los APEX que se solicitan instalar. Cada APEX debe firmarse con claves diferentes y se aplica tanto en el tiempo de compilación como en el tiempo de ejecución.

APEX en particiones integradas

Los archivos APEX se pueden ubicar en particiones integradas, como /system. La partición ya está sobre dm-verity, por lo que los archivos APEX se activan directamente sobre el dispositivo de bucle invertido.

Si un APEX está presente en una partición integrada, se puede actualizar proporcionando un paquete APEX con el mismo nombre de paquete y un código de versión mayor o igual. El nuevo APEX se almacena en /data y, de manera similar a los APKs, la versión recién instalada reemplaza a la versión que ya está presente en la partición integrada. Sin embargo, a diferencia de los APKs, la versión del APEX recién instalada solo se activa después de reiniciar el dispositivo.

Requisitos del kernel

Para admitir módulos de la línea principal de APEX en un dispositivo Android, se requieren las siguientes funciones del kernel de Linux: el controlador de bucle invertido y dm-verity. El controlador de bucle invertido activa la imagen del sistema de archivos en un módulo APEX, y dm-verity verifica el módulo APEX.

El rendimiento del controlador de bucle invertido y de dm-verity es importante para lograr un buen rendimiento del sistema cuando se usan módulos APEX.

Versiones de kernel compatibles

Los módulos de APEX de la línea principal son compatibles con los dispositivos que usan versiones del kernel 4.4 o posteriores. Los dispositivos nuevos que se lancen con Android 10 o versiones posteriores deben usar la versión del kernel 4.9 o una posterior para admitir módulos APEX.

Parches de kernel obligatorios

Los parches de kernel necesarios para admitir módulos APEX se incluyen en el árbol común de Android. Para obtener los parches que admiten APEX, usa la versión más reciente del árbol común de Android.

Versión de kernel 4.4

Esta versión solo es compatible con los dispositivos que se actualizaron de Android 9 a Android 10 y que desean admitir módulos APEX. Para obtener los parches necesarios, se recomienda realizar una combinación descendente desde la rama android-4.4. A continuación, se incluye una lista de los parches individuales necesarios para la versión 4.4 del kernel.

  • UPSTREAM: loop: add ioctl for changing logical block size (4.4)
  • BACKPORT: block/loop: set hw_sectors (4.4)
  • UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl (4.4)
  • ANDROID: mnt: Se corrigió next_descendent (4.4)
  • ANDROID: mnt: remount should propagate to slaves of slaves (4.4)
  • ANDROID: mnt: Propaga el nuevo montaje correctamente (4.4)
  • Revierte "ANDROID: dm verity: add minimum prefetch size" (4.4).
  • UPSTREAM: loop: drop caches if offset or block_size are changed (4.4)

Versiones de kernel 4.9, 4.14 y 4.19

Para obtener los parches necesarios para las versiones del kernel 4.9, 4.14 y 4.19, realiza una fusión descendente desde la rama android-common.

Opciones de configuración del kernel requeridas

En la siguiente lista, se muestran los requisitos de configuración base para admitir módulos APEX que se introdujeron en Android 10. Los elementos con un asterisco (*) son requisitos existentes de Android 9 y versiones anteriores.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

Requisitos de los parámetros de la línea de comandos del kernel

Para admitir APEX, asegúrate de que los parámetros de la línea de comandos del kernel cumplan con los siguientes requisitos:

  • loop.max_loop NO debe establecerse
  • loop.max_part debe ser <= 8

Compila un APEX

En esta sección, se describe cómo compilar un APEX con el sistema de compilación de Android. A continuación, se muestra un ejemplo de Android.bp para un APEX llamado apex.test.

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

Ejemplo de apex_manifest.json:

{
  "name": "com.android.example.apex",
  "version": 1
}

Ejemplo de file_contexts:

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

Tipos de archivos y ubicaciones en APEX

Tipo de archivo Ubicación en APEX
Bibliotecas compartidas /lib y /lib64 (/lib/arm para ARM traducido en x86)
Ejecutables /bin
Bibliotecas de Java /javalib
Compilaciones previas /etc

Dependencias transitivas

Los archivos APEX incluyen automáticamente las dependencias transitivas de las bibliotecas compartidas nativas o los ejecutables. Por ejemplo, si libFoo depende de libBar, las dos bibliotecas se incluyen cuando solo libFoo aparece en la propiedad native_shared_libs.

Cómo controlar varias ABIs

Instala la propiedad native_shared_libs para las interfaces binarias de la aplicación (ABI) principal y secundaria del dispositivo. Si un APEX segmenta dispositivos con una sola ABI (es decir, solo de 32 bits o solo de 64 bits), solo se instalan las bibliotecas con la ABI correspondiente.

Instala la propiedad binaries solo para la ABI principal del dispositivo, como se describe a continuación:

  • Si el dispositivo es solo de 32 bits, solo se instala la variante de 32 bits del binario.
  • Si el dispositivo es solo de 64 bits, solo se instala la variante de 64 bits del archivo binario.

Para agregar un control detallado sobre las ABIs de las bibliotecas y los archivos binarios nativos, usa las propiedades multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries].

  • first: Coincide con la ABI principal del dispositivo. Este es el valor predeterminado para los archivos binarios.
  • lib32: Coincide con la ABI de 32 bits del dispositivo, si es compatible.
  • lib64: Coincide con la ABI de 64 bits del dispositivo compatible.
  • prefer32: Coincide con la ABI de 32 bits del dispositivo, si es compatible. Si no se admite la ABI de 32 bits, coincide con la ABI de 64 bits.
  • both: Coincide con ambas ABIs. Este es el valor predeterminado para native_shared_libraries.

Las propiedades java, libraries y prebuilts son independientes de la ABI.

Este ejemplo es para un dispositivo que admite 32/64 y no prefiere 32:

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

Firma de vbmeta

Firma cada APEX con claves diferentes. Cuando se requiere una clave nueva, crea un par de claves pública y privada, y crea un módulo apex_key. Usa la propiedad key para firmar el APEX con la clave. La clave pública se incluye automáticamente en el APEX con el nombre avb_pubkey.

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

En el ejemplo anterior, el nombre de la clave pública (foo) se convierte en el ID de la clave. El ID de la clave que se usa para firmar un APEX se escribe en el APEX. En el tiempo de ejecución, apexd verifica el APEX con una clave pública que tiene el mismo ID en el dispositivo.

Firma de APEX

Firma los APEX de la misma manera que firmas los APKs. Firma los APEX dos veces: una para el sistema de archivos mínimo (archivo apex_payload.img) y otra para todo el archivo.

Para firmar un APEX a nivel del archivo, establece la propiedad certificate de una de las siguientes tres maneras:

  • Sin configurar: Si no se establece ningún valor, el APEX se firma con el certificado ubicado en PRODUCT_DEFAULT_DEV_CERTIFICATE. Si no se establece ninguna marca, la ruta de acceso se establece de forma predeterminada en build/target/product/security/testkey.
  • <name>: El APEX está firmado con el certificado <name> en el mismo directorio que PRODUCT_DEFAULT_DEV_CERTIFICATE.
  • :<name>: El APEX está firmado con el certificado que define el módulo de Soong llamado <name>. El módulo de certificado se puede definir de la siguiente manera.
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

Cómo instalar un APEX

Para instalar un APEX, usa ADB.

adb install apex_file_name
adb reboot

Si supportsRebootlessUpdate se establece en true en apex_manifest.json y el APEX instalado actualmente no se usa (por ejemplo, se detuvieron todos los servicios que contiene), se puede instalar un nuevo APEX sin reiniciar con la marca --force-non-staged.

adb install --force-non-staged apex_file_name

Cómo usar un APEX

Después del reinicio, el APEX se activa en el directorio /apex/<apex_name>@<version>. Se pueden activar varias versiones del mismo APEX al mismo tiempo. Entre las rutas de acceso de la vinculación, la que corresponde a la versión más reciente se vincula en /apex/<apex_name>.

Los clientes pueden usar la ruta de acceso vinculada para leer o ejecutar archivos desde APEX.

Por lo general, los APEX se usan de la siguiente manera:

  1. Un OEM o un ODM precarga un APEX en /system/apex cuando se envía el dispositivo.
  2. Se accede a los archivos en el APEX a través de la ruta de acceso /apex/<apex_name>/.
  3. Cuando se instala una versión actualizada del APEX en /data/apex, la ruta apunta al nuevo APEX después del reinicio.

Actualiza un servicio con un APEX

Para actualizar un servicio con un APEX, haz lo siguiente:

  1. Marca el servicio en la partición del sistema como actualizable. Agrega la opción updatable a la definición del servicio.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. Crea un archivo .rc nuevo para el servicio actualizado. Usa la opción override para redefinir el servicio existente.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

Las definiciones de servicios solo se pueden definir en el archivo .rc de un APEX. Los activadores de acciones no se admiten en los APEX.

Si un servicio marcado como actualizable se inicia antes de que se activen los APEX, el inicio se retrasa hasta que se complete la activación de los APEX.

Configura el sistema para que admita actualizaciones de APEX

Establece la siguiente propiedad del sistema en true para admitir actualizaciones de archivos APEX.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

o solo

<device.mk>:

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

APEX aplanado

En el caso de los dispositivos heredados, a veces es imposible o inviable actualizar el kernel anterior para que sea totalmente compatible con APEX. Por ejemplo, es posible que el kernel se haya compilado sin CONFIG_BLK_DEV_LOOP=Y, que es fundamental para montar la imagen del sistema de archivos dentro de un APEX.

Un APEX aplanado es un APEX especialmente compilado que se puede activar en dispositivos con un kernel heredado. Los archivos de un APEX aplanado se instalan directamente en un directorio de la partición integrada. Por ejemplo, lib/libFoo.so en un APEX aplanado my.apex se instala en /system/apex/my.apex/lib/libFoo.so.

La activación de un APEX aplanado no involucra el dispositivo de bucle. Todo el directorio /system/apex/my.apex se vincula directamente a /apex/name@ver.

Los APEX aplanados no se pueden actualizar descargando versiones actualizadas de los APEX desde la red, ya que los APEX descargados no se pueden aplanar. Los APEX aplanados solo se pueden actualizar a través de una OTA normal.

La configuración predeterminada es APEX aplanado. Esto significa que todos los APEX se aplanan de forma predeterminada, a menos que configures explícitamente tu dispositivo para compilar APEX sin aplanar y admitir actualizaciones de APEX (como se explicó anteriormente).

NO se admite la combinación de APEX aplanados y no aplanados en un dispositivo. Los APEX de un dispositivo deben ser todos sin aplanar o todos aplanados. Esto es especialmente importante cuando se envían APEX precompilados firmados previamente para proyectos como Mainline. Los APEX que no están prefirmados (es decir, compilados desde la fuente) también deben estar sin aplanar y firmados con las claves adecuadas. El dispositivo debe heredar de updatable_apex.mk, como se explica en Cómo actualizar un servicio con un APEX.

APEX comprimidos

Android 12 y versiones posteriores incluyen compresión de APEX para reducir el impacto en el almacenamiento de los paquetes de APEX actualizables. Después de que se instala una actualización de un APEX, aunque su versión preinstalada ya no se usa, sigue ocupando la misma cantidad de espacio. Ese espacio ocupado no estará disponible.

La compresión de APEX minimiza este impacto de almacenamiento con un conjunto muy comprimido de archivos de APEX en particiones de solo lectura (como la partición /system). Android 12 y versiones posteriores usan un algoritmo de compresión zip DEFLATE.

La compresión no proporciona optimización para lo siguiente:

  • Son los APEX de arranque que deben montarse muy temprano en la secuencia de arranque.

  • APEX que no se pueden actualizar. La compresión solo es beneficiosa si se instala una versión actualizada de un APEX en la partición /data. En la página Componentes modulares del sistema, se encuentra disponible una lista completa de los APEX que se pueden actualizar.

  • APEXes de libs compartidas dinámicas. Dado que apexd siempre activa ambas versiones de esos APEX (preinstalados y actualizados), comprimirlos no agrega valor.

Formato de archivo APEX comprimido

Este es el formato de un archivo APEX comprimido.

El diagrama muestra el formato de un archivo APEX comprimido

Figura 2: Formato de archivo APEX comprimido

En el nivel superior, un archivo APEX comprimido es un archivo ZIP que contiene el archivo APEX original en formato comprimido con un nivel de compresión de 9 y con otros archivos almacenados sin comprimir.

Un archivo APEX consta de cuatro archivos:

  • original_apex: Comprimido con el nivel de compresión 9. Este es el archivo APEX original sin comprimir.
  • apex_manifest.pb: Solo se almacena.
  • AndroidManifest.xml: Solo se almacena.
  • apex_pubkey: Solo se almacena.

Los archivos apex_manifest.pb, AndroidManifest.xml y apex_pubkey son copias de sus archivos correspondientes en original_apex.

Cómo compilar un APEX comprimido

Los APEX comprimidos se pueden compilar con la herramienta apex_compression_tool.py ubicada en system/apex/tools.

En el sistema de compilación, hay varios parámetros relacionados con la compresión de APEX.

En Android.bp, la propiedad compressible controla si un archivo APEX es comprimible:

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Un parámetro de configuración del producto PRODUCT_COMPRESSED_APEX controla si una imagen del sistema compilada a partir de la fuente debe contener archivos APEX comprimidos.

Para la experimentación local, puedes forzar una compilación para que comprima los APEX estableciendo OVERRIDE_PRODUCT_COMPRESSED_APEX= en true.

Los archivos APEX comprimidos que genera el sistema de compilación tienen la extensión .capex. La extensión facilita la distinción entre las versiones comprimidas y sin comprimir de un archivo APEX.

Algoritmos de compresión compatibles

Android 12 solo admite la compresión deflate-zip.

Cómo activar un archivo APEX comprimido durante el inicio

Antes de que se pueda activar un APEX comprimido, el archivo original_apex que contiene se descomprime en el directorio /data/apex/decompressed. El archivo APEX descomprimido resultante se vincula de forma rígida al directorio /data/apex/active.

Considera el siguiente ejemplo como una ilustración del proceso descrito anteriormente.

Considera /system/apex/com.android.foo.capex como un APEX comprimido que se activa, con versionCode 37.

  1. El archivo original_apex dentro de /system/apex/com.android.foo.capex se descomprime en /data/apex/decompressed/com.android.foo@37.apex.
  2. Se realiza restorecon /data/apex/decompressed/com.android.foo@37.apex para verificar que tenga una etiqueta de SELinux correcta.
  3. Se realizan verificaciones en /data/apex/decompressed/com.android.foo@37.apex para garantizar su validez: apexd verifica la clave pública incluida en /data/apex/decompressed/com.android.foo@37.apex para comprobar que sea igual a la incluida en /system/apex/com.android.foo.capex.
  4. El archivo /data/apex/decompressed/com.android.foo@37.apex está vinculado de forma rígida al directorio /data/apex/active/com.android.foo@37.apex.
  5. La lógica de activación normal para los archivos APEX sin comprimir se realiza en /data/apex/active/com.android.foo@37.apex.

Interacción con la OTA

Los archivos APEX comprimidos tienen implicaciones en la entrega y aplicación de OTA. Dado que una actualización OTA puede contener un archivo APEX comprimido con un nivel de versión más alto que el que está activo en un dispositivo, se debe reservar una cierta cantidad de espacio libre antes de reiniciar un dispositivo para aplicar una actualización OTA.

Para admitir el sistema OTA, apexd expone estas dos APIs de Binder:

  • calculateSizeForCompressedApex: Calcula el tamaño necesario para descomprimir los archivos APEX en un paquete de actualización inalámbrica. Se puede usar para verificar que un dispositivo tenga suficiente espacio antes de que se descargue una actualización OTA.
  • reserveSpaceForCompressedApex: Reserva espacio en el disco para que apexd lo use en el futuro para descomprimir archivos APEX comprimidos dentro del paquete de actualización inalámbrica.

En el caso de una actualización inalámbrica A/B, apexd intenta realizar la descompresión en segundo plano como parte de la rutina inalámbrica posterior a la instalación. Si falla la descompresión, apexd realiza la descompresión durante el arranque que aplica la actualización OTA.

Alternativas consideradas durante el desarrollo de APEX

Estas son algunas opciones que se consideraron en el AOSP al diseñar el formato de archivo APEX y los motivos por los que se incluyeron o excluyeron.

Sistemas de administración de paquetes habituales

Las distribuciones de Linux tienen sistemas de administración de paquetes, como dpkg y rpm, que son potentes, maduros y sólidos. Sin embargo, no se adoptaron para APEX porque no pueden proteger los paquetes después de la instalación. La verificación solo se realiza cuando se instalan paquetes. Los atacantes pueden vulnerar la integridad de los paquetes instalados sin que se note. Esta es una regresión para Android en la que todos los componentes del sistema se almacenaban en sistemas de archivos de solo lectura cuya integridad está protegida por dm-verity para cada E/S. Cualquier manipulación de los componentes del sistema debe estar prohibida o ser detectable para que el dispositivo pueda negarse a iniciarse si se ve comprometido.

dm-crypt para la integridad

Los archivos de un contenedor APEX provienen de particiones integradas (por ejemplo, la partición /system) que están protegidas por dm-verity, en las que se prohíbe cualquier modificación de los archivos, incluso después de que se instalan las particiones. Para proporcionar el mismo nivel de seguridad a los archivos, todos los archivos de un APEX se almacenan en una imagen del sistema de archivos que se vincula con un árbol hash y un descriptor vbmeta. Sin dm-verity, un APEX en la partición /data es vulnerable a modificaciones no deseadas que se realizan después de que se verifica y se instala.

De hecho, la partición /data también está protegida por capas de encriptación, como dm-crypt. Si bien esto proporciona cierto nivel de protección contra la manipulación, su propósito principal es la privacidad, no la integridad. Cuando un atacante obtiene acceso a la partición /data, no puede haber más protección, y esto vuelve a ser una regresión en comparación con cada componente del sistema en la partición /system. El árbol hash dentro de un archivo APEX junto con dm-verity proporciona el mismo nivel de protección de contenido.

Redirecciona rutas de acceso de /system a /apex

Se puede acceder a los archivos de componentes del sistema empaquetados en un APEX a través de rutas nuevas, como /apex/<name>/lib/libfoo.so. Cuando los archivos formaban parte de la partición /system, se podía acceder a ellos a través de rutas de acceso como /system/lib/libfoo.so. Un cliente de un archivo APEX (otros archivos APEX o la plataforma) debe usar las nuevas rutas de acceso. Es posible que debas actualizar el código existente como resultado del cambio de ruta.

Si bien una forma de evitar el cambio de ruta es superponer el contenido del archivo en un archivo APEX en la partición /system, el equipo de Android decidió no superponer archivos en la partición /system porque esto podría afectar el rendimiento a medida que aumentara la cantidad de archivos superpuestos (posiblemente, incluso apilados uno tras otro).

Otra opción era secuestrar funciones de acceso a archivos, como open, stat y readlink, de modo que las rutas que comenzaran con /system se redireccionaran a sus rutas correspondientes en /apex. El equipo de Android descartó esta opción porque es inviable cambiar todas las funciones que aceptan rutas de acceso. Por ejemplo, algunas apps vinculan Bionic de forma estática, lo que implementa las funciones. En esos casos, no se redireccionan esas apps.