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:
- XMLs de declaración de funciones
- Archivos XML de funciones de sensores como precompilados en un APEX del proveedor de la HAL de sensores
- Archivos de configuración de entrada
- Configuraciones de pantalla táctil como precompilados en un APEX del proveedor 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 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>
- Se usa para establecer el valor predeterminado en
- 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";