En Android 8.0, se volvió a diseñar la arquitectura del SO Android para definir interfaces claras entre la plataforma de Android independiente del dispositivo y el código específico del dispositivo y del proveedor. Android ya definió muchas de esas interfaces en forma de interfaces de HAL, definidas como encabezados C en hardware/libhardware
. HIDL reemplazó estas interfaces de HAL por interfaces estables con control de versión, que pueden estar en Java (se describe a continuación) o ser interfaces HIDL del cliente y del servidor en C++.
Las interfaces HIDL están diseñadas para usarse principalmente desde el código nativo y, como resultado, HIDL se enfoca en la generación automática de código eficiente en C++. Sin embargo, las interfaces HIDL también deben estar disponibles para usarse directamente desde Java, ya que algunos subsistemas de Android (como Telephony) tienen interfaces HIDL de Java.
En las páginas de esta sección, se describe el frontend de Java para interfaces HIDL, se detalla cómo crear, registrar y usar servicios, y se explica cómo los HAL y los clientes de HAL escritos en Java interactúan con el sistema de RPC de HIDL.
Ejemplo de cliente
Este es un ejemplo de un cliente para una interfaz IFoo
en el paquete android.hardware.foo@1.0
que está registrado como nombre de servicio default
y un servicio adicional con el nombre de servicio personalizado second_impl
.
Cómo agregar bibliotecas
Si quieres usarlo, debes agregar dependencias en la biblioteca de stub de HIDL correspondiente. Por lo general, es una biblioteca estática:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
Si sabes que ya extraes dependencias de estas bibliotecas, también puedes usar el vínculo compartido:
// in Android.bp libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
Consideraciones adicionales para agregar bibliotecas en Android 10
Si tienes una app del sistema o del proveedor que se orienta a Android 10 o versiones posteriores, puedes incluir estas bibliotecas de forma estática. También puedes usar (solo) clases HIDL desde archivos JAR personalizados instalados en el dispositivo con APIs de Java estables que se ponen a disposición con el mecanismo uses-library
existente para apps del sistema. El último enfoque ahorra espacio en el dispositivo. Para obtener más detalles, consulta Cómo implementar la biblioteca del SDK de Java. En el caso de las apps más antiguas, se conserva el comportamiento anterior.
A partir de Android 10, también están disponibles las versiones "superficiales" de estas bibliotecas. Estos incluyen la clase en cuestión, pero no incluyen ninguna de las clases dependientes. Por ejemplo, android.hardware.foo-V1.0-java-shallow
incluye clases en el paquete foo, pero no incluye clases en android.hidl.base-V1.0-java
, que contiene la clase base de todas las interfaces de HIDL. Si creas una biblioteca que ya tiene las clases básicas de la interfaz preferida disponibles como dependencia, puedes usar lo siguiente:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java-shallow", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow
Las bibliotecas de base y de administrador de HIDL tampoco están disponibles en el bootclasspath de las apps (antes, a veces se usaban como API ocultas, debido al cargador de clases de Android que prioriza a los delegados). En su lugar, se movieron a un nuevo espacio de nombres con jarjar
, y las apps que los usan (necesariamente apps privadas) deben tener sus propias copias independientes. Los módulos de la ruta de acceso de clases de inicio que usan HIDL deben usar las variantes superficiales de estas bibliotecas de Java y agregar jarjar_rules: ":framework-jarjar-rules"
a su Android.bp
para usar la versión de estas bibliotecas que existe en la ruta de acceso de clases de inicio.
Modifica tu fuente de Java
Solo hay una versión (@1.0
) de este servicio, por lo que este código solo recupera esa versión. Consulta las extensiones de interfaz para saber cómo controlar varias versiones diferentes del servicio.
import android.hardware.foo.V1_0.IFoo; ... // retry to wait until the service starts up if it is in the manifest IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IFoo anotherServer = IFoo.getService("second_impl", true /* retry */); server.doSomething(…);
Proporciona un servicio
Es posible que el código del framework en Java deba entregar interfaces para recibir devoluciones de llamada asíncronas de los HAL.
Para la interfaz IFooCallback
en la versión 1.0 del paquete android.hardware.foo
, puedes implementar tu interfaz en Java siguiendo estos pasos:
- Define tu interfaz en HIDL.
- Abre
/tmp/android/hardware/foo/IFooCallback.java
como referencia. - Crea un módulo nuevo para tu implementación de Java.
- Examina la clase abstracta
android.hardware.foo.V1_0.IFooCallback.Stub
y, luego, escribe una clase nueva para extenderla e implementar los métodos abstractos.
Cómo ver los archivos generados automáticamente
Para ver los archivos generados automáticamente, ejecuta lo siguiente:
hidl-gen -o /tmp -Ljava \ -randroid.hardware:hardware/interfaces \ -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0
Estos comandos generan el directorio /tmp/android/hardware/foo/1.0
. Para el archivo hardware/interfaces/foo/1.0/IFooCallback.hal
, se genera el archivo /tmp/android/hardware/foo/1.0/IFooCallback.java
, que encapsula la interfaz de Java, el código del proxy y los stub (tanto el proxy como los stub se ajustan a la interfaz).
-Lmakefile
genera las reglas que ejecutan este comando en el momento de la compilación y te permiten incluir android.hardware.foo-V1.0-java
y vincularlo con los archivos adecuados. En hardware/interfaces/update-makefiles.sh
, puedes encontrar una secuencia de comandos que hace esto automáticamente para un proyecto lleno de interfaces.
Las rutas de acceso en este ejemplo son relativas; el hardware o las interfaces pueden ser un directorio temporal en el árbol de código para permitirte desarrollar un HAL antes de publicarlo.
Ejecuta un servicio
La HAL proporciona la interfaz IFoo
, que debe realizar devoluciones de llamada asíncronas al framework a través de la interfaz IFooCallback
. La interfaz IFooCallback
no se registra por nombre como un servicio detectable. En su lugar, IFoo
debe contener un método como setFooCallback(IFooCallback x)
.
Para configurar IFooCallback
desde la versión 1.0 del paquete android.hardware.foo
, agrega android.hardware.foo-V1.0-java
a Android.mk
. El código para ejecutar el servicio es el siguiente:
import android.hardware.foo.V1_0.IFoo; import android.hardware.foo.V1_0.IFooCallback.Stub; .... class FooCallback extends IFooCallback.Stub { // implement methods } .... // Get the service from which you will be receiving callbacks. // This also starts the threadpool for your callback service. IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available .... // This must be a persistent instance variable, not local, // to avoid premature garbage collection. FooCallback mFooCallback = new FooCallback(); .... // Do this once to create the callback service and tell the "foo-bar" service server.setFooCallback(mFooCallback);
Extensiones de interfaz
Suponiendo que un servicio determinado implemente la interfaz IFoo
en todos los dispositivos, es posible que, en un dispositivo en particular, el servicio proporcione capacidades adicionales implementadas en la extensión de interfaz IBetterFoo
, de la siguiente manera:
interface IFoo { ... }; interface IBetterFoo extends IFoo { ... };
El código de llamada que reconoce la interfaz extendida puede usar el método castFrom()
de Java para transmitir de forma segura la interfaz base a la interfaz extendida:
IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IBetterFoo extendedService = IBetterFoo.castFrom(baseService); if (extendedService != null) { // The service implements the extended interface. } else { // The service implements only the base interface. }