Puedes usar el formato de archivo APEX para empaquetar e instalar módulos del SO Android de nivel inferior. Permite la compilación y la instalación independientes de componentes como bibliotecas y servicios 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 durante 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 incluso pueden crear un nuevo APEX de proveedor si ninguno de los APEX proporcionados se ajusta a sus necesidades o si tienen un 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. Cuando se unen 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 de funciones completa, como el HAL de Wi-Fi, dentro de un APEX de proveedor. Luego, los desarrolladores pueden compilar y enviar de forma individual el APEX del proveedor para probar los cambios, en lugar de volver a compilar 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 área.
La agrupación natural de un área de atributos en un APEX también simplifica el proceso de compilación, envío y prueba de cambios para esa área de funciones. Por ejemplo, la reinstalación de un APEX actualiza automáticamente cualquier biblioteca agrupada o archivos de configuración que este incluya.
Agrupar un área de funciones en un APEX también simplifica la depuración o la reversión cuando se observa un comportamiento inadecuado del dispositivo. Por ejemplo, si la telefonía funciona mal en una compilación nueva, los desarrolladores podrían intentar instalar una implementación de telefonía APEX anterior en un dispositivo (sin necesidad de escribir en la memoria flash una compilación completa) y ver si se restablece el comportamiento correcto.
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 tengan interfaces estables.
Las interfaces nativas estables para dependencias de APEX del proveedor incluyen cc_library
con 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 entorno 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, 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 APEX
Es posible que APEX aumente de tamaño porque agrupa dependencias inestables. Te recomendamos
que uses la vinculación estática. Las bibliotecas comunes, como libc++.so
y libbase.so
, pueden vincularse de forma estática a los objetos binarios de 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 objetos binarios y las bibliotecas correspondientes dentro de un APEX del 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 del proveedor cuando los fragmentos 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 interfaces 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 de init
Los APEX pueden incluir secuencias de comandos de init de dos maneras: (A) un archivo de texto precompilado dentro de la carga útil de APEX o (B) una secuencia de comandos de init normal en /vendor/etc
. Puedes configurar ambos para el mismo APEX.
Secuencia de comandos de init en APEX:
prebuilt_etc {
name: "myinit.rc",
src: "myinit.rc"
}
apex {
..
vendor: true,
prebuilts: ["myinit.rc"],
..
}
Las secuencias de comandos de init en los APEX de proveedores pueden tener directivas on <property or event>
y definiciones service
.
Asegúrate de que una definición de 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 de init en 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
, 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 el directorio <apex name>/etc/firmware
de APEX. ueventd
analiza los directorios /apex/*/etc/firmware
para encontrar módulos de firmware.
El file_contexts
de APEX debe etiquetar correctamente 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 de kernel
Incorpora módulos de kernel en un APEX de 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 las entradas de carga útil del módulo del kernel. Por ejemplo:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
Los módulos de kernel deben instalarse de forma explícita. En la siguiente secuencia de comandos de init de ejemplo en la partición del 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:
Incorpora superposiciones de recursos del entorno 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 precompilados dentro de los APEX de proveedores, y se agregarán más.
Ejemplos:
- XMLs de declaración de funciones
- Los sensores incluyen archivos XML como compilaciones previas en un APEX de proveedor de HAL de sensores.
- Archivos de configuración de entrada
- Parámetros de configuración de la pantalla táctil como compilaciones previas en un APEX de proveedor solo de configuración
APEX de proveedores 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 se empaqueta un servicio HAL anterior en el APEX del proveedor, crea el dominio raíz "vendorBootstrap": true
en su manifiesto de APEX para que se pueda activar antes. Ten en cuenta que los APEX de arranque solo se pueden activar desde la ubicación compilada previamente, 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 desde el directorio/etc/usr
del APEX.ro.vulkan.apex=<apex name>
: Cuando se establece, el controlador de Vulkan se carga desde APEX. Dado que los HAL anteriores usan el controlador de Vulkan, haz que APEX inicie APEX y configura ese espacio de nombres del vinculador para que sea visible.
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 de proveedores que comparten el mismo nombre y clave de APEX y, luego, elegir qué versión se activa durante cada inicio mediante sysprops persistentes. En algunos casos de uso de desarrolladores, esto podría ser más simple que instalar una copia nueva del APEX con adb install
.
Ejemplos de casos de uso:
- 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 de APEX actual y experimental del proveedor de la HAL de la cámara: Los participantes de la prueba interna pueden usar la versión experimental sin descargar ni instalar un archivo adicional, por lo que pueden volver a la versión anterior con facilidad.
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 propiedad son los siguientes:
- Bootconfig
- Se usa para establecer el valor predeterminado en
BoardConfig.mk
. androidboot.vendor.apex.<apex name>
- Se usa para establecer el valor predeterminado en
- propósito del sistema 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.
// 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 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.
En el caso de los dispositivos de referencia virtuales basados en Cuttlefish, puedes usar el comando --extra_bootconfig_args
para configurar 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";