Android 11 introduce la capacidad de usar AIDL para HALs en Android. Esto hace que posible implementar partes de Android sin HIDL. HAL de transición para usar el AIDL exclusivamente cuando sea posible (cuando los HAL ascendentes usan HIDL, se debe usar el HIDL)
HAL que usan AIDL para comunicarse entre componentes del framework, como los de
system.img
y los componentes de hardware, como los de vendor.img
, deben usar
AIDL estable Sin embargo, para comunicarse dentro de una partición, por ejemplo, desde una
HAL a otra, no hay restricciones en cuanto al mecanismo de IPC que se debe usar.
Motivación
El AIDL existe desde hace más tiempo que el HIDL y se usa en muchos otros lugares, como entre los componentes del framework de Android o las apps. Ahora que el AIDL tiene estabilidad es posible implementar una pila completa con un solo tiempo de ejecución de IPC. El AIDL también tiene un mejor sistema de control de versiones que el HIDL.
- Usar un único lenguaje de IPC significa tener solo una cosa que aprender, depurar optimizar y proteger.
- El AIDL admite el control de versiones local para los propietarios de una interfaz:
- Los propietarios pueden agregar métodos al final de las interfaces o campos a los objetos parcelables. Esto significa que es más fácil cambiar las versiones del código a lo largo de los años el costo anual es menor (los tipos pueden modificarse localmente y no hay necesidad de bibliotecas adicionales para cada versión de la interfaz).
- Las interfaces de extensión se pueden conectar durante el tiempo de ejecución, en lugar de en el tipo por lo que no es necesario reubicar las extensiones downstream versiones de interfaces.
- Una interfaz de AIDL existente se puede usar directamente cuando el propietario elige para estabilizarlo. Antes, se debía tener una copia completa de la interfaz crearse en HIDL.
Compila en el entorno de ejecución de AIDL
El AIDL tiene tres backends diferentes: Java, NDK y CPP. Para usar AIDL estable, debes
usar siempre la copia del sistema de libbinder en system/lib*/libbinder.so
y hablar
en /dev/binder
. Para el código en la imagen del proveedor, significa que libbinder
(del VNDK), ya que esta biblioteca tiene una API de C++ inestable y
internas inestables. En su lugar, el código nativo del proveedor debe usar el backend del NDK de
AIDL, vínculo con libbinder_ndk
(que está respaldado por el sistema libbinder.so
)
y vincular con las bibliotecas del NDK que crean las entradas aidl_interface
. Para
los nombres exactos de los módulos, consulta
reglas de nomenclatura de los módulos.
Escribe una interfaz de la HAL del AIDL
Para que se use una interfaz de AIDL entre el sistema y el proveedor, debe dos cambios:
- Cada definición de tipo debe anotarse con
@VintfStability
. - La declaración
aidl_interface
debe incluirstability: "vintf",
.
Solo el propietario de una interfaz puede realizar estos cambios.
Cuando realices estos cambios, la interfaz debe estar en
manifiesto de VINTF para funcionar. Prueba esto (y los relacionados
adicionales, como verificar que las interfaces actualizadas estén bloqueadas) con el
Prueba de VTS vts_treble_vintf_vendor_test
. Puedes usar un @VintfStability
sin estos requisitos llamando a
AIBinder_forceDowngradeToLocalStability
en el backend del NDK,
android::Stability::forceDowngradeToLocalStability
en el backend de C++,
o android.os.Binder#forceDowngradeToSystemStability
en el backend de Java
en un objeto vinculante antes de que se envíe a otro proceso. Cambia un servicio a una versión inferior
con la estabilidad del proveedor no es compatible con Java porque todas las apps se ejecutan en un sistema
adicional.
Además, para lograr la máxima portabilidad del código y evitar posibles problemas, como como bibliotecas adicionales innecesarias, inhabilita el backend de CPP.
Ten en cuenta que el uso de backends
en el siguiente ejemplo de código es correcto, ya que se
hay tres backends (Java, NDK y CPP). El siguiente código te indica cómo seleccionar el
el backend de CPP específicamente, para inhabilitarlo.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Cómo buscar interfaces de HAL del AIDL
Las interfaces estables del AIDL del AOSP para HAL están en los mismos directorios base que
Interfaces HIDL, en aidl
carpetas.
- hardware/interfaces
- frameworks/hardware/interfaces
- sistema/hardware/interfaces
Debes colocar interfaces de extensión en otros hardware/interfaces
subdirectorios en vendor
o hardware
.
Interfaces de extensión
Android tiene un conjunto de interfaces oficiales del AOSP con cada versión. En Android los socios quieren agregar funcionalidad a estas interfaces, no deberían cambiar porque el tiempo de ejecución de Android incompatible con el tiempo de ejecución de AOSP para Android. En el caso de los dispositivos con GMS, evitar cambiar estas interfaces también garantizan que la imagen de GSI pueda seguir funcionando.
Las extensiones se pueden registrar de dos maneras diferentes:
- en el entorno de ejecución, consulta Extensiones adjuntas.
- independientes, registrados globalmente y en VINTF.
Sin embargo, se registra una extensión cuando es específica del proveedor (es decir, no forma parte de del AOSP) usan la interfaz, no hay posibilidad de fusión conflicto. Sin embargo, cuando se modifican las modificaciones descendentes en componentes ascendentes del AOSP se realizan, se pueden producir conflictos de combinación, y se recomiendan las siguientes estrategias:
- Las incorporaciones de interfaz se pueden subir a AOSP en la próxima versión
- adiciones de interfaz que permiten mayor flexibilidad, sin conflictos de combinación, puede ser ascendente en la próxima versión
Extensiones parcelables: ParcelableHolder
ParcelableHolder
es un Parcelable
que puede contener otro Parcelable
.
El caso de uso principal de ParcelableHolder
es hacer que un elemento Parcelable
sea extensible.
Por ejemplo, la imagen que los implementadores de dispositivos esperan poder extender una
Parcelable
definido por AOSP, AospDefinedParcelable
, para incluir su valor agregado
atributos.
Antes sin ParcelableHolder
, los implementadores de dispositivos no podían modificar
una interfaz de AIDL estable definida por AOSP porque sería un error agregar más
campos:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Como se ve en el código anterior, esta práctica no funciona porque los campos agregados por el implementador de dispositivos podrían tener un conflicto cuando el elemento revisadas en las próximas versiones de Android.
Con ParcelableHolder
, el propietario de un objeto parcelable puede definir una extensión.
punto en un Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Luego, los implementadores de dispositivos pueden definir su propio Parcelable
para su
.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Por último, el nuevo Parcelable
se puede adjuntar al Parcelable
original con
el campo ParcelableHolder
.
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
Nombres de las instancias del servidor de la HAL del AIDL
Por convención, los servicios de la HAL del AIDL tienen un nombre de instancia con este formato
$package.$type/$instance
Por ejemplo, una instancia de la HAL del vibrador se
registrado como android.hardware.vibrator.IVibrator/default
.
Escribe un servidor de la HAL del AIDL
@VintfStability
Los servidores de AIDL deben declararse en el manifiesto de VINTF, para
ejemplo como este:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
De lo contrario, deberían registrar un servicio de AIDL normalmente. Cuando se ejecuta VTS , se espera que todas las HAL del AIDL declaradas estén disponibles.
Escribe un cliente de AIDL
Los clientes del AIDL deben declararse en la matriz de compatibilidad, por ejemplo, así:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Cómo convertir una HAL existente de HIDL a AIDL
Usa la herramienta hidl2aidl
para convertir una interfaz HIDL en AIDL.
Funciones de hidl2aidl
:
- Crear archivos
.aidl
basados en los archivos.hal
para el paquete determinado - Crear reglas de compilación para el paquete AIDL recién creado con todos los backends habilitado
- Crea métodos de traducción en los backends de Java, CPP y NDK para la traducción. desde los tipos HIDL hasta los tipos de AIDL
- Cómo crear reglas de compilación para bibliotecas de Traductor con las dependencias requeridas
- Crea aserciones estáticas para asegurarte de que los enumeradores HIDL y AIDL tengan los mismos valores en los backends de CPP y NDK
Sigue estos pasos para convertir un paquete de archivos .hal en archivos .aidl:
Compila la herramienta ubicada en
system/tools/hidl/hidl2aidl
.Compilar esta herramienta a partir de la fuente más reciente proporciona la información más completa una experiencia fluida a los desarrolladores. Puedes usar la última versión para convertir interfaces ramas de versiones anteriores.
m hidl2aidl
Ejecuta la herramienta con un directorio de salida seguido del paquete que se convertido.
De manera opcional, usa el argumento
-l
para agregar el contenido de un archivo de licencia nuevo. a la parte superior de todos los archivos generados. Asegúrate de usar la licencia y la fecha correctas.hidl2aidl -o <output directory> -l <file with license> <package>
Por ejemplo:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Lee los archivos generados y soluciona cualquier problema con la conversión.
conversion.log
contiene problemas no controlados que se deben corregir primero.- Es posible que los archivos
.aidl
generados tengan advertencias y sugerencias necesitan alguna acción. Estos comentarios comienzan con//
. - Aprovecha la oportunidad de realizar una limpieza y realizar mejoras en el paquete.
- Revisa el
@JavaDerive
. para los atributos que podrían ser necesarios, comotoString
oequals
Compila solo los objetivos que necesitas.
- Inhabilita los backends que no se usarán. Opta por el backend del NDK en lugar del CPP backend, consulta cómo elegir el entorno de ejecución.
- Quita las bibliotecas de Traductor o cualquier código generado que no se utilice.
Consulta Principales diferencias de AIDL y HIDL.
- El uso del
Status
integrado del AIDL y las excepciones suelen mejorar el y elimina la necesidad de otro tipo de estado específico de la interfaz. - Los argumentos de la interfaz del AIDL en los métodos no son
@nullable
de forma predeterminada, como estaban en HIDL.
- El uso del
SEPolicy para HAL de AIDL
Un tipo de servicio de AIDL que sea visible para el código del proveedor debe tener la
atributo hal_service_type
. De lo contrario, la configuración de sepolicy es la misma
como cualquier otro servicio de AIDL (aunque existen atributos especiales para las HAL). Aquí
Esta es una definición de ejemplo del contexto del servicio de HAL:
type hal_foo_service, service_manager_type, hal_service_type;
Para la mayoría de los servicios definidos por la plataforma, un contexto de servicio con la
ya se agregó (por ejemplo, android.hardware.foo.IFoo/default
ya está marcado como hal_foo_service
). Sin embargo, si un cliente del framework admite
varios nombres de instancias, se deben agregar nombres de instancia adicionales en
archivos service_contexts
específicos del dispositivo.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Los atributos de la HAL deben agregarse cuando creamos un tipo nuevo de HAL. Una HAL específica
pueden asociarse con varios tipos de servicios (cada uno de los cuales puede
tener varias instancias, como acabamos de mencionar). Para una HAL, foo
, tenemos
hal_attribute(foo)
Esta macro define los atributos hal_foo_client
y
hal_foo_server
Para un dominio dado, hal_client_domain
y
Las macros hal_server_domain
asocian un dominio con un atributo de HAL determinado. Para
ejemplo, si el servidor del sistema es cliente de esta HAL,
hal_client_domain(system_server, hal_foo)
Un servidor de HAL también incluye
hal_server_domain(my_hal_domain, hal_foo)
Por lo general, para una HAL determinada
, también creamos un dominio como hal_foo_default
como referencia o
HAL de ejemplo. Sin embargo, algunos dispositivos usan estos dominios para sus propios servidores.
La distinción entre dominios para varios servidores solo es importante si
varios servidores que usan la misma interfaz y necesitan un permiso diferente
establecer en sus implementaciones. En todas estas macros, hal_foo
no es en realidad
un objeto sepolicy. En cambio, estas macros usan este token para hacer referencia a
el grupo de atributos asociados
con un par cliente-servidor.
Sin embargo, hasta ahora, no hemos asociado hal_foo_service
ni hal_foo
.
(el par de atributos de hal_attribute(foo)
). Se asocia un atributo de la HAL
con los servicios de HAL del AIDL usando la macro hal_attribute_service
(las HAL de HIDL usan
la macro hal_attribute_hwservice
). Por ejemplo:
hal_attribute_service(hal_foo, hal_foo_service)
Esto significa que
Los procesos hal_foo_client
pueden obtener la HAL y hal_foo_server
pueden registrar la HAL. La aplicación de estas reglas de registro
que realiza el administrador de contexto (servicemanager
). Ten en cuenta que los nombres de los servicios
no siempre se corresponden con los atributos de la HAL. Por ejemplo, podríamos ver
hal_attribute_service(hal_foo, hal_foo2_service)
Sin embargo, en general, ya que
esto implica que los servicios siempre se usan juntos, podríamos quitar
el hal_foo2_service
y el uso de hal_foo_service
para todo nuestro servicio
diferentes. La mayoría de las HAL que establecen varios hal_attribute_service
se deben a los siguientes motivos:
el nombre del atributo original de HAL no es lo suficientemente general y no se puede cambiar.
Con todo esto, una HAL de ejemplo se ve de la siguiente manera:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Interfaces de extensión conectadas
Una extensión se puede adjuntar a cualquier interfaz de Binder, ya sea una de nivel superior registrada directamente con Service Manager o una subinterfaz. Cuando obtengas una extensión, debes confirmar que su tipo sea: lo esperado. Las extensiones solo se pueden configurar desde el proceso que publica un Binder.
Las extensiones adjuntas se deben utilizar siempre que una extensión modifica la de una HAL existente. Cuando se necesita una funcionalidad completamente nueva, este mecanismo no es necesario, y se puede aplicar una interfaz de extensión registrados directamente con el administrador del servicio. Interfaces de extensión conectadas tienen más sentido cuando se adjuntan a subinterfaces, porque estas y las jerarquías pueden ser profundas o de instancias múltiples. Usa una extensión global para duplicar la jerarquía de la interfaz Binder de otro servicio requeriría una gran la contabilidad para proporcionar una funcionalidad equivalente a las extensiones adjuntas directamente.
Para configurar una extensión en Binder, usa las siguientes APIs:
- En el backend del NDK:
AIBinder_setExtension
- En el backend de Java:
android.os.Binder.setExtension
- En el backend de la CPP:
android::Binder::setExtension
- En el backend de Rust:
binder::Binder::set_extension
Para obtener una extensión en un Binder, usa las siguientes APIs:
- En el backend del NDK:
AIBinder_getExtension
- En el backend de Java:
android.os.IBinder.getExtension
- En el backend de la CPP:
android::IBinder::getExtension
- En el backend de Rust:
binder::Binder::get_extension
Para obtener más información sobre estas APIs, consulta la documentación de la
getExtension
en el backend correspondiente. Un ejemplo de cómo usar
puedes encontrar las extensiones
hardware/interfaces/tests/extension/vibrator:
Principales diferencias de AIDL y HIDL
Cuando uses las HAL del AIDL o las interfaces de la HAL del AIDL, ten en cuenta las diferencias en comparación con la escritura de HAL de HIDL.
- La sintaxis del lenguaje de AIDL es más cercana a Java. La sintaxis del HIDL es similar a la de C++.
- Todas las interfaces de AIDL tienen estados de error integrados. En vez de crear modelos personalizados
tipos de estado, crear int de estado constantes en archivos de interfaz y usar
EX_SERVICE_SPECIFIC
en los backends de CPP/NDK yServiceSpecificException
en el backend de Java. Consulta Error Manipulación. - El AIDL no inicia automáticamente los grupos de subprocesos cuando se envían objetos Binder. Deben iniciarse de forma manual (consulta conversación administración de configuraciones).
- El AIDL no se anula en errores de transporte no verificados (HIDL
Return
se anula el errores desmarcados). - El AIDL solo puede declarar un tipo por archivo.
- Los argumentos del AIDL se pueden especificar como entrada, salida o entrada, además de la salida. (no hay “devoluciones de llamada síncronas”).
- El AIDL usa un fd como el tipo primitivo en lugar de un handle.
- HIDL usa versiones principales para cambios incompatibles y versiones secundarias para
cambios compatibles. En el AIDL, se realizan los cambios retrocompatibles.
El AIDL no tiene un concepto explícito de versiones principales. en cambio, esto es
se incorporan en los nombres de los paquetes. Por ejemplo, el AIDL podría usar el nombre del paquete
bluetooth2
- El AIDL no hereda la prioridad en tiempo real de forma predeterminada. El
setInheritRt
se debe usar por vinculador para habilitar la herencia de prioridad en tiempo real.