Cada interfaz definida en un paquete HIDL tiene su propia clase C++ generada automáticamente dentro del espacio de nombres de su paquete. Los clientes y los servidores se ocupan de las interfaces de diferentes maneras:
- Los servidores implementan interfaces.
- Los clientes llaman a métodos en interfaces.
El servidor puede registrar las interfaces por nombre o pasarlas como parámetros a los métodos definidos por HIDL. Por ejemplo, el código del framework puede entregar una interfaz para recibir mensajes asíncronos de la HAL y pasar esa interfaz directamente a la HAL sin registrarla.
Implementación del servidor
Un servidor que implemente la interfaz IFoo
debe incluir el archivo de encabezado IFoo
que se generó automáticamente:
#include <android/hardware/samples/1.0/IFoo.h>
La biblioteca compartida de la interfaz IFoo
exporta automáticamente el encabezado para vincularlo. Ejemplo de IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Esqueleto de ejemplo para una implementación de servidor de la interfaz IFoo:
// From the IFoo.h header using android::hardware::samples::V1_0::IFoo; class FooImpl : public IFoo { Return<void> someMethod(foo my_foo, someMethod_cb _cb) { vec<uint32_t> return_data; // Compute return_data _cb(return_data); return Void(); } ... };
Para que la implementación de una interfaz de servidor esté disponible para un cliente, puedes hacer lo siguiente:
- Registra la implementación de la interfaz con
hwservicemanager
(consulta los detalles a continuación),
O
- Pasa la implementación de la interfaz como argumento de un método de interfaz (para obtener más información, consulta Devoluciones de llamada asíncronas).
Cuando se registra la implementación de la interfaz, el proceso hwservicemanager
realiza un seguimiento de las interfaces HIDL registradas que se ejecutan en el dispositivo por nombre y versión. Los servidores pueden registrar una implementación de interfaz HIDL por nombre, y los clientes pueden solicitar implementaciones de servicios por nombre y versión. Este proceso entrega la interfaz HIDL android.hidl.manager@1.0::IServiceManager
.
Cada archivo de encabezado de interfaz HIDL generado automáticamente (como IFoo.h
) tiene un método registerAsService()
que se puede usar para registrar la implementación de la interfaz con hwservicemanager
. El único argumento obligatorio es el nombre de las implementaciones de la interfaz, ya que los clientes usan este nombre para recuperar la interfaz de hwservicemanager
más adelante:
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
hwservicemanager
trata la combinación de [package@version::interface, instance_name]
como única para permitir que diferentes interfaces (o diferentes versiones de la misma interfaz) se registren con nombres de instancias idénticos sin conflictos. Si llamas a registerAsService()
con la misma versión, interfaz y nombre de instancia del paquete, hwservicemanager
descarta su referencia al servicio registrado anteriormente y usa el nuevo.
Implementación del cliente
Al igual que el servidor, un cliente debe #include
cada interfaz a la que hace referencia:
#include <android/hardware/samples/1.0/IFoo.h>
Un cliente puede obtener una interfaz de dos maneras:
- A través de
I<InterfaceName>::getService
(a través dehwservicemanager
) - A través de un método de interfaz
Cada archivo de encabezado de interfaz generado automáticamente tiene un método getService
estático que se puede usar para recuperar una instancia de servicio desde el
hwservicemanager
:
// getService returns nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
Ahora, el cliente tiene una interfaz IFoo
y puede llamar a métodos como si fuera una implementación de clase local. En realidad, la implementación puede ejecutarse en el mismo proceso, en un proceso diferente o incluso en otro dispositivo (con la conexión remota de HAL). Como el cliente llamó a getService
en un objeto IFoo
incluido desde la versión 1.0
del paquete, hwservicemanager
muestra una implementación de servidor solo si esa implementación es compatible con los clientes 1.0
. En la práctica, esto significa que solo las implementaciones del servidor con la versión 1.n
(la versión x.(y+1)
de una interfaz debe extender (heredar) x.y
).
Además, se proporciona el método castFrom
para transmitir entre diferentes interfaces. Este método funciona haciendo una llamada de IPC a la interfaz remota para asegurarse de que el tipo subyacente sea el mismo que el que se solicita. Si el tipo solicitado no está disponible, se muestra nullptr
.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
Llamadas de devolución de llamada asíncronas
Muchas implementaciones de HAL existentes se comunican con hardware asíncrono, lo que significa que necesitan una forma asíncrona de notificar a los clientes sobre los eventos nuevos que ocurrieron. Una interfaz HIDL se puede usar como devolución de llamada asíncrona porque las funciones de la interfaz HIDL pueden tomar objetos de interfaz HIDL como parámetros.
Ejemplo de archivo de interfaz IFooCallback.hal
:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
Ejemplo de método nuevo en IFoo
que toma un parámetro IFooCallback
:
package android.hardware.samples@1.0; interface IFoo { struct Foo { int64_t someValue; handle myHandle; }; someMethod(Foo foo) generates (int32_t ret); anotherMethod() generates (vec<uint32_t>); registerCallback(IFooCallback callback); };
El cliente que usa la interfaz IFoo
es el servidor de la interfaz IFooCallback
. Proporciona una implementación de IFooCallback
:
class FooCallback : public IFooCallback { Return<void> sendEvent(uint32_t event_id) { // process the event from the HAL } Return<void> sendData(const hidl_vec<uint8_t>& data) { // process data from the HAL } };
También puede pasarlo a una instancia existente de la interfaz IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
El servidor que implementa IFoo
lo recibe como un objeto sp<IFooCallback>
. Puede almacenar la devolución de llamada y volver a llamar al cliente cada vez que quiera usar esta interfaz.
Destinatarios fallecidos
Como las implementaciones de servicios se pueden ejecutar en un proceso diferente, puede suceder que el proceso que implementa una interfaz se cierre mientras el cliente permanece activo.
Cualquier llamada a un objeto de interfaz alojado en un proceso que se cerró falla con un error de transporte (isOK()
muestra false
). La única forma de recuperarse de esa falla es solicitar una instancia nueva del servicio llamando a I<InterfaceName>::getService()
. Esto solo funciona si el proceso que falló se reinició y volvió a registrar sus servicios con servicemanager
(lo que suele ser cierto para las implementaciones de HAL).
En lugar de abordar esto de forma reactiva, los clientes de una interfaz también pueden registrar un destinatario de error para recibir una notificación cuando un servicio falle.
Para registrarse para recibir esas notificaciones en una interfaz IFoo
recuperada, un cliente puede hacer lo siguiente:
foo->linkToDeath(recipient, 1481 /* cookie */);
El parámetro recipient
debe ser una implementación de la interfaz android::hardware::hidl_death_recipient
que proporciona HIDL, que contiene un solo método serviceDied()
al que se llama desde un subproceso en el grupo de subprocesos de RPC cuando se cierra el proceso que aloja la interfaz:
class MyDeathRecipient : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) { // Deal with the fact that the service died } }
El parámetro cookie
contiene la cookie que se pasó con linkToDeath()
, mientras que el parámetro who
contiene un puntero débil al objeto que representa el servicio en el cliente. Con la llamada de ejemplo anterior, cookie
es igual a 1481 y who
es igual a foo
.
También es posible cancelar el registro de un beneficiario de fallecimiento después de registrarlo:
foo->unlinkToDeath(recipient);