Proveedor APEX

Puedes usar el formato de archivo APEX para empaquetar y instalar módulos de SO Android de nivel inferior. Permite la compilación y la 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 del proveedor 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 de un proveedor se crean como una combinación de imágenes de proveedores creadas de forma independiente APEX, los fabricantes de dispositivos pueden elegir fácilmente el software de las implementaciones de proveedores que quisieran en su dispositivo. Los fabricantes incluso pueden crear nuevo APEX del proveedor si ninguno de los APEX proporcionados satisface sus necesidades o si tiene hardware personalizado nuevo.

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

Sin APEX del proveedor, una implementación con tantas dependencias entre los componentes del proveedor requiere una coordinación y un seguimiento cuidadosos. Uniendo todo (archivos de configuración y bibliotecas adicionales) en APEX con interfaces definidas con claridad en cualquier punto de comunicación 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 Cómo agrupar una implementación de función completa, como la HAL de Wi-Fi, dentro de un proveedor APEX Luego, los desarrolladores pueden compilar y enviar individualmente el APEX del proveedor para que lo pruebe. en lugar de reconstruir 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 quieren iterar solo en esa función en una sola área de almacenamiento en etapa intermedia.

El empaquetado natural de un área de componentes en un APEX también simplifica el proceso de compilación, envío y prueba de cambios para esa área de componentes. Por ejemplo: La reinstalación de APEX actualiza automáticamente las bibliotecas o los archivos de configuración agrupados. que incluye APEX.

Combinar un área de componentes 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 sistema de implementación de APEX en un dispositivo (sin necesidad de escribir una compilación completa en la memoria flash) 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 genérica sobre APEX, incluidos los requisitos del dispositivo, los detalles del formato de archivo y los pasos de instalación.

En Android.bp, configurar la propiedad vendor: true hace que un módulo APEX sea un APEX del proveedor.

apex {
  ..
  vendor: true,
  ..
}

Objetos binarios y bibliotecas compartidas

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

Las interfaces nativas estables para dependencias de APEX del proveedor incluyen cc_library con stubs y LLNDK. Estas dependencias se excluyen de paquetes y dependencias se registran en el manifiesto de APEX. El manifiesto es procesados por linkerconfig para que las dependencias nativas externas se disponibles en el tiempo de ejecución.

En el siguiente fragmento, APEX contiene el objeto 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 crezca porque agrupa dependencias no estables. Recomendaciones con la vinculación estática. Puedes usar las bibliotecas comunes, como libc++.so y libbase.so, vinculados estáticamente a objetos binarios de HAL. Crear una dependencia para proporcionar una interfaz estable puede ser otra opción. La dependencia no se agrupará en APEX.

Implementaciones de HAL

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

Para encapsular por completo la implementación de HAL, APEX también debe especificar cualquier fragmento y secuencia de comandos de init de VINTF relevante.

Fragmentos de VINTF

Los fragmentos de VINTF se pueden entregar desde un APEX de un proveedor cuando se ubican los fragmentos en etc/vintf de 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 agreguen fragmentos de VINTF a APEX, usa las APIs de libbinder_ndk para obtener la asignaciones de interfaces de HAL y 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 de APEX que define la instancia de HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...): Úsalo para abrir un HAL de transferencia.

Secuencias de comandos Init

Los APEX pueden incluir secuencias de comandos de inicio de dos maneras: (A) con un archivo de texto precompilado dentro del Carga útil de APEX o (B) una secuencia de comandos init normal en /vendor/etc Puedes configurar ambos para el mismo APEX.

Secuencia de comandos Init en APEX:

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

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

Las secuencias de comandos Init en los APEX de proveedores pueden tener definiciones de service y directivas on <property or event>.

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

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

Ten cuidado cuando uses directivas on. Dado que las secuencias de comandos init en APEX son analizarse y ejecutarse después de que se activen los APEX, algunos eventos o propiedades no se pueden usar. 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, como se muestra a continuación.

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 <apex name>/etc/firmware de APEX. ueventd analiza /apex/*/etc/firmware directorios para buscar módulos de firmware.

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

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

Módulos de kernel

Incorpora módulos de kernel en un APEX del proveedor como módulos precompilados, 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 de APEX debe etiquetar cualquier entrada de carga útil del módulo kernel correctamente. Por ejemplo:

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

Los módulos de kernel deben instalarse de forma explícita. El siguiente ejemplo de secuencia de comandos init En la partición de proveedor, se 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:

Incorporar superposiciones de recursos de tiempo de ejecución en el APEX de un 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 precompilados dentro de los APEX de proveedores, y se agregarán más.

Ejemplos:

APEX del proveedor de arranque

Algunos servicios de HAL, como keymint, deberían estar disponibles antes de que se activen los APEX. Por lo general, esos HAL establecen early_hal en su definición de servicio en la secuencia de comandos de init. Otro ejemplo es la clase animation, que generalmente se inicia antes que el evento post-fs-data. Cuando un servicio HAL tan temprano se empaquetado en el proveedor APEX, haz que el dominio raíz "vendorBootstrap": true en su APEX Manifiesto para que se pueda activar antes. Ten en cuenta que los APEX de arranque solo se pueden activar desde la ubicación precompilada, como /vendor/apex, no desde /data/apex.

Propiedades del sistema

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

  • input_device.config_file.apex=<apex name>: Cuando se establece, los archivos de configuración de entrada (*.idc, *.kl y *.kcm) se buscan en el directorio /etc/usr de APEX.
  • ro.vulkan.apex=<apex name>: Cuando se establece, el controlador de Vulkan se carga desde APEX. Como las primeras HAL usan el controlador de Vulkan, haz que el archivo APEX Inicia APEX y configura ese espacio de nombres del vinculador. sean visibles.

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

Funciones de desarrollo adicionales

Selección de APEX durante el inicio

Ejemplo:

Los desarrolladores también pueden instalar varias versiones de APEX del proveedor que comparten el mismo nombre y clave de APEX y, luego, elegir qué versión se activa durante cada inicio con sysprops persistentes. Para ciertos casos de uso de desarrolladores, esto podría ser es más simple que instalar una copia nueva de APEX con adb install.

Casos de uso de ejemplo:

  • Instala 3 versiones del APEX del proveedor de HAL de Wi-Fi: Los equipos de QA pueden ejecutar pruebas manuales o automatizadas con una versión, reiniciar en otra versión y volver a ejecutar las pruebas, y luego comparar los resultados finales.
  • Instala 2 versiones actuales y APEX del proveedor de la HAL de la cámara experimental: Los participantes de la prueba interna pueden usar la versión experimental sin descargar e instalar un archivo adicional, de modo que se puedan revertir fácilmente.

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

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

  • Configuración de inicio
    • 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 de APEX, activado.

// 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 debe configurarse 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 sysprop 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 actualización de firmware (como a través de los comandos fastboot oem), cambiar la propiedad bootconfig para el APEX instalado varias veces también cambia la versión activada durante el inicio.

Para 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";