Puedes usar el formato de archivo APEX para empaquetar e 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 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 la modularización naturales de las implementaciones de funciones en imágenes de proveedores.
Cuando las imágenes de proveedores se compilan como una combinación de APEX de proveedores compilados de forma independiente, los fabricantes de dispositivos pueden elegir fácilmente las implementaciones de proveedores específicas que desean en su dispositivo. Los fabricantes pueden incluso crear un nuevo APEX de proveedores 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 de SoC y un APEX de implementación de telefonía de OEM personalizado.
Sin los APEX de proveedores, una implementación con tantas dependencias entre los componentes del proveedor requiere una coordinación y un seguimiento minuciosos. Si se encapsulan 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 de desarrolladores
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 proveedores. Luego, los desarrolladores pueden compilar y enviar individualmente el APEX de proveedores 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 desarrolladores para los desarrolladores que trabajan principalmente en un área de funciones y desean iterar solo en esa área.
La agrupación 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, si se reinstala un APEX, se actualiza automáticamente cualquier biblioteca o archivo de configuración agrupados que incluya 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 no funciona bien en una compilación nueva, los desarrolladores podrían intentar instalar un APEX de implementación de telefonía más antiguo 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, si se establece la propiedad vendor: true, un módulo APEX se convierte en un APEX de proveedores.
apex {
..
vendor: true,
..
}
Objetos 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 de proveedores incluyen cc_library con stubs y bibliotecas 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 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 aumente de tamaño porque agrupa 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 objetos binarios de HAL. Otra opción es crear una dependencia para proporcionar una interfaz estable. La dependencia no se agrupará 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 de proveedores similar a los siguientes ejemplos:
Para encapsular por completo la implementación de HAL, el APEX también debe especificar los fragmentos de VINTF y las secuencias de comandos de inicialización pertinentes.
Fragmentos de VINTF
Los fragmentos de VINTF se pueden entregar desde un APEX de proveedores 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 interfaces HAL y nombres de APEX.
AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default"):truesi 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 inicialización
Los APEX pueden incluir secuencias de comandos de inicialización de dos maneras: (A) un archivo de texto precompilado dentro de la carga útil del APEX o (B) una secuencia de comandos de inicialización normal en /vendor/etc. Puedes configurar 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"],
..
}
Las secuencias de comandos de inicialización en APEX de proveedores pueden tener service definiciones y
on <property or event> directivas.
Asegúrate de que una definición service apunte a un objeto binario en el mismo APEX.
Por ejemplo, el APEX com.android.foo 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 inicialización 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 de proveedores 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
del APEX. ueventd analiza los directorios /apex/*/etc/firmware para encontrar módulos de firmware.
El file_contexts del APEX debe etiquetar correctamente cualquier entrada de carga útil de firmware 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 de proveedores como módulos precompilados, como se muestra a continuación.
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 cualquier entrada de carga útil del módulo de kernel. Por ejemplo:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
Los módulos de kernel se deben instalar de forma explícita. En el siguiente ejemplo de secuencia de comandos de inicialización 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 en tiempo de ejecución en un APEX de proveedores
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 agregan más.
Ejemplos:
- XML de declaración de funciones
- XML de funciones de sensores como precompilados en un APEX de proveedores de HAL de sensores
- Archivos de configuración de entrada
- Configuraciones de pantalla táctil como precompilados en un APEX de proveedores solo de configuración
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 la secuencia de comandos de inicialización. Otro ejemplo es la clase animation, que suele iniciarse antes que el evento post-fs-data. Cuando un servicio HAL temprano de este tipo se
empaqueta en APEX de proveedores, haz que el APEX "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 precompilada, como /vendor/apex, no desde /data/apex.
Propiedades del sistema
Estas son las propiedades del sistema que lee el framework para admitir APEX de proveedores:
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/usrdel APEX.ro.vulkan.apex=<apex name>: Cuando se establece, el controlador de Vulkan se carga desde el APEX. Dado que los HAL tempranos usan el controlador de Vulkan, haz que el APEX sea un APEX de arranque y configura ese espacio de nombres del vinculador visible.
Configura las propiedades del sistema en secuencias de comandos de inicialización con el setprop
comando.
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>.
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 de proveedores de HAL de Wi-Fi: Los equipos de QA pueden ejecutar pruebas manuales o automatizadas con una versión, luego reiniciar en otra versión y volver a ejecutar las pruebas, y, luego, comparar los resultados finales.
- Instala 2 versiones del APEX de proveedores de HAL de cámara, current y experimental: Los usuarios internos 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
- Sysprop persistente
- Se usa para cambiar el valor predeterminado, establecido en un dispositivo ya iniciado.
- 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 el 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 del flasheo (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.
Para los dispositivos de referencia virtuales basados en Cuttlefish,
puedes usar el comando --extra_bootconfig_args para establecer la propiedad bootconfig
directamente durante el lanzamiento. Por ejemplo:
launch_cvd --noresume \
--extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";