APEX del proveedor

Puedes usar el formato de archivo APEX para empaquetar e instalar módulos de SO Android de nivel inferior. Permite la compilación e instalación independientes de componentes como servicios y bibliotecas nativos, implementaciones de HAL, firmware, archivos de configuración, etcétera.

El sistema de compilación instala automáticamente los APEX de proveedores en la partición /vendor y apexd los activa en el tiempo de ejecución, al igual que los APEX en otras particiones.

Casos de uso

Modularización de imágenes de proveedores

Los APEX facilitan la agrupación y modularización naturales de las implementaciones de funciones en las imágenes del proveedor.

Cuando las imágenes del proveedor se compilan como una combinación de APEX del proveedor compilados de forma independiente, los fabricantes de dispositivos pueden elegir fácilmente las implementaciones específicas del proveedor que desean en su dispositivo. Los fabricantes pueden incluso crear un nuevo APEX de proveedor si ninguno de los APEX proporcionados se ajusta a sus necesidades o si tienen un hardware personalizado completamente nuevo.

Por ejemplo, un OEM puede optar por componer su dispositivo con el APEX de implementación de Wi-Fi del AOSP, el APEX de implementación de Bluetooth del SoC y un APEX de implementación de telefonía personalizado del OEM.

Sin los APEX de proveedores, una implementación con tantas dependencias entre los componentes del proveedor requiere una coordinación y un seguimiento cuidadosos. Al encapsular todos los componentes (incluidos los archivos de configuración y las bibliotecas adicionales) en APEX con interfaces claramente definidas en cualquier punto de comunicación entre funciones, los diferentes componentes se vuelven intercambiables.

Iteración del desarrollador

Los APEX de proveedores ayudan a los desarrolladores a iterar más rápido mientras desarrollan módulos de proveedores, ya que agrupan una implementación completa de funciones, como el HAL de Wi-Fi, dentro de un APEX de proveedor. Luego, los desarrolladores pueden compilar y enviar individualmente el APEX del proveedor para probar los cambios, en lugar de recompilar toda la imagen del proveedor.

Esto simplifica y acelera el ciclo de iteración de los desarrolladores que trabajan principalmente en un área de funciones y desean iterar solo en esa área.

El agrupamiento natural de un área de funciones en un APEX también simplifica el proceso de compilación, envío y prueba de cambios para esa área de funciones. Por ejemplo, reinstalar un APEX actualiza automáticamente cualquier biblioteca o archivo de configuración incluidos en el APEX.

Agrupar un área de funciones en un APEX también simplifica la depuración o la reversión cuando se observa un comportamiento incorrecto del dispositivo. Por ejemplo, si la telefonía funciona mal en una compilación nueva, los desarrolladores podrían intentar instalar un APEX de implementación de telefonía anterior en un dispositivo (sin necesidad de escribir una compilación completa) y ver si se restablece el buen comportamiento.

Ejemplo de flujo de trabajo:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Ejemplos

Conceptos básicos

Consulta la página principal Formato de archivo APEX para obtener información general sobre APEX, incluidos los requisitos del dispositivo, los detalles del formato de archivo y los pasos de instalación.

En Android.bp, establecer la propiedad vendor: true convierte un módulo APEX en un APEX del proveedor.

apex {
  ..
  vendor: true,
  ..
}

Archivos binarios y bibliotecas compartidas

Un APEX incluye dependencias transitivas dentro de la carga útil del APEX, a menos que tengan interfaces estables.

Las interfaces nativas estables para las dependencias de APEX del proveedor incluyen cc_library con las bibliotecas stubs y LLNDK. Estas dependencias se excluyen del empaquetado y se registran en el manifiesto de APEX. linkerconfig procesa el manifiesto para que las dependencias nativas externas estén disponibles en el tiempo de ejecución.

En el siguiente fragmento, el APEX contiene el archivo binario (my_service) y sus dependencias no estables (archivos *.so).

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

En el siguiente fragmento, el APEX contiene la biblioteca compartida my_standalone_lib y cualquiera de sus dependencias no estables (como se describió anteriormente).

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Cómo reducir el tamaño de APEX

Es posible que APEX aumente de tamaño porque incluye dependencias no estables. Te recomendamos que uses la vinculación estática. Las bibliotecas comunes, como libc++.so y libbase.so, se pueden vincular de forma estática a los archivos binarios del HAL. Crear una dependencia para proporcionar una interfaz estable puede ser otra opción. La dependencia no se incluirá en el APEX.

Implementaciones de HAL

Para definir una implementación de HAL, proporciona los archivos binarios y las bibliotecas correspondientes dentro de un APEX del proveedor similar a los siguientes ejemplos:

Para encapsular por completo la implementación del HAL, el APEX también debe especificar los fragmentos de VINTF y los secuencias de comandos de inicialización pertinentes.

Fragmentos de VINTF

Los fragmentos del VINTF se pueden entregar desde un APEX del proveedor cuando se encuentran en etc/vintf del APEX.

Usa la propiedad prebuilts para incorporar los fragmentos de VINTF en el APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

APIs de consulta

Cuando se agregan fragmentos de VINTF a APEX, usa las APIs de libbinder_ndk para obtener las asignaciones de las interfaces de HAL y los nombres de APEX.

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : true si la instancia de HAL se define en APEX.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...): Obtiene el nombre del APEX que define la instancia de HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...): Se usa para abrir un HAL de transferencia.

Secuencias de comandos de init

Los APEX pueden incluir secuencias de comandos de inicialización de dos maneras: (A) un archivo de texto prediseñado dentro de la carga útil del APEX o (B) una secuencia de comandos de inicialización normal en /vendor/etc. Puedes establecer ambos para el mismo APEX.

Secuencia de comandos de inicialización en APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Los scripts de inicialización en APEX de proveedores pueden tener definiciones de service y directivas de on <property or event>.

Asegúrate de que una definición de service apunte a un archivo binario en el mismo APEX. Por ejemplo, un APEX com.android.foo puede definir un servicio llamado foo-service.

on foo-service /apex/com.android.foo/bin/foo
  ...

Ten cuidado cuando uses las directivas on. Dado que los scripts de inicialización en los APEX se analizan y ejecutan después de que se activan los APEX, no se pueden usar algunos eventos o propiedades. Usa apex.all.ready=true para activar acciones lo antes posible. Los APEX de arranque pueden usar on init, pero no on early-init.

Firmware

Ejemplo:

Incorpora el firmware en un APEX del proveedor con el tipo de módulo prebuilt_firmware, de la siguiente manera.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

Los módulos prebuilt_firmware se instalan en el directorio <apex name>/etc/firmware del APEX. ueventd analiza los directorios /apex/*/etc/firmware para encontrar módulos de firmware.

El file_contexts del APEX debe etiquetar correctamente todas las entradas de carga útil del firmware para garantizar que ueventd pueda acceder a estos archivos durante el tiempo de ejecución. Por lo general, la etiqueta vendor_file es suficiente. Por ejemplo:

(/.*)? u:object_r:vendor_file:s0

Módulos del kernel

Incorpora módulos del kernel en un APEX del proveedor como módulos compilados previamente, de la siguiente manera.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

El file_contexts del APEX debe etiquetar correctamente todas las entradas de carga útil del módulo del kernel. Por ejemplo:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Los módulos del kernel se deben instalar de forma explícita. La siguiente secuencia de comandos de inicio de ejemplo en la partición del proveedor muestra la instalación a través de insmod:

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Superposiciones de recursos en tiempo de ejecución

Ejemplo:

Incorpora superposiciones de recursos de tiempo de ejecución en un APEX del proveedor con la propiedad rros.

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Otros archivos de configuración

Los APEX de proveedores admiten varios otros archivos de configuración que suelen encontrarse en la partición del proveedor como elementos precompilados dentro de los APEX de proveedores, y se están agregando más.

Ejemplos:

APEX de proveedores de arranque

Algunos servicios de HAL, como keymint, deben estar disponibles antes de que se activen los APEX. Por lo general, esos HAL establecen early_hal en su definición de servicio en el script de init. Otro ejemplo es la clase animation, que suele iniciarse antes que el evento post-fs-data. Cuando un servicio HAL inicial de este tipo se incluye en un APEX del proveedor, haz que el APEX sea "vendorBootstrap": true en su APEX Manifest para que se pueda activar antes. Ten en cuenta que los APEX de arranque solo se pueden activar desde la ubicación prediseñada, como /vendor/apex, no desde /data/apex.

Propiedades del sistema

Estas son las propiedades del sistema que el framework lee para admitir los APEX del proveedor:

  • input_device.config_file.apex=<apex name>: Cuando se configura, los archivos de configuración de entrada (*.idc, *.kl y *.kcm) se buscan en el directorio /etc/usr del APEX.
  • ro.vulkan.apex=<apex name>: Cuando se establece, el controlador de Vulkan se carga desde el APEX. Dado que los HAL iniciales usan el controlador de Vulkan, crea el APEX Bootstrap APEX y configura ese espacio de nombres del vinculador como visible.

Configura las propiedades del sistema en secuencias de comandos de init con el comando setprop.

Funciones adicionales

Selección de APEX durante el inicio

Ejemplo:

Los APEX de proveedores se pueden activar de forma opcional durante el inicio. Si especificas un nombre de archivo con la propiedad del sistema ro.vendor.apex.<apex name>, solo se activará el APEX que coincida con el nombre de archivo para el <apex name> específico. El APEX con <apex name> se ignora (no se activa) si esta propiedad del sistema se establece en none. Puedes usar esta función para instalar varias copias de un APEX con el mismo nombre. Si hay varias versiones del mismo APEX, deben compartir la misma clave.

Ejemplos de casos de uso:

  • Instala 3 versiones del APEX del proveedor del HAL de Wi-Fi: Los equipos de QA pueden ejecutar pruebas manuales o automatizadas con una versión, luego reiniciar el sistema en otra versión y volver a ejecutar las pruebas, y, por último, comparar los resultados finales.
  • Instala 2 versiones del APEX del proveedor del HAL de la cámara, actual y experimental: Los dogfooders pueden usar la versión experimental sin descargar ni instalar un archivo adicional, por lo que pueden volver a la versión anterior fácilmente.

Durante el inicio, apexd busca sysprops con un formato específico para activar la versión correcta de APEX.

Los formatos esperados para la clave de la propiedad son los siguientes:

  • Bootconfig
    • Se usa para establecer el valor predeterminado en BoardConfig.mk.
    • androidboot.vendor.apex.<apex name>
  • Sysprop persistente
    • Se usa para cambiar el valor predeterminado establecido en un dispositivo que ya se inició.
    • Anula el valor de bootconfig si está presente.
    • persist.vendor.apex.<apex name>

El valor de la propiedad debe ser el nombre de archivo del APEX que se debe activar o none para inhabilitar el APEX.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

La versión predeterminada también se debe configurar con bootconfig en BoardConfig.mk:

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Después de que se inicie el dispositivo, cambia la versión activada configurando la propiedad del sistema persistente:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Si el dispositivo admite la actualización de bootconfig después de la escritura flash (por ejemplo, a través de comandos fastboot oem), cambiar la propiedad bootconfig para el APEX instalado varias veces también cambia la versión activada durante el inicio.

En el caso de los dispositivos de referencia virtuales basados en Cuttlefish, puedes usar el comando --extra_bootconfig_args para establecer la propiedad bootconfig directamente durante el inicio. Por ejemplo:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";