Interfaces

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 los métodos en las interfaces.

El servidor puede registrar las interfaces por su nombre o pasarlas como parámetros a los métodos definidos por HIDL. Por ejemplo, el código del marco puede funcionar como una interfaz para recibir mensajes asincrónicos de la HAL y pasar esa interfaz directamente a la HAL sin registrarla.

Implementación del servidor

Un servidor que implemente la interfaz de IFoo debe incluir el archivo de encabezado de IFoo que se generó automáticamente:

#include <android/hardware/samples/1.0/IFoo.h>

La biblioteca compartida de la interfaz de IFoo exporta automáticamente el encabezado para vincularlo. Ejemplo 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, puede:

  1. Registre la implementación de la interfaz con hwservicemanager (consulte los detalles a continuación),

    O

  2. Pase la implementación de la interfaz como un argumento de un método de interfaz (para obtener detalles, consulte Devoluciones de llamada asincrónicas ).

Al registrar 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 sirve a 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 requerido es el nombre de las implementaciones de la interfaz, ya que los clientes usarán este nombre para recuperar la interfaz del 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");

El 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 instancia idénticos sin conflictos. Si llama a registerAsService() con exactamente la misma versión de paquete, interfaz y nombre de instancia, hwservicemanager elimina su referencia al servicio registrado anteriormente y usa el nuevo.

Implementación del cliente

Tal como lo hace el servidor, un cliente debe #include todas las interfaces a las que se refiere:

#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 de hwservicemanager )
  • 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 de hwservicemanager :

// getService will return 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 comunicación remota HAL). Debido a que el cliente llamó a getService en un objeto IFoo incluido desde la versión 1.0 del paquete, hwservicemanager devuelve 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 de servidor con la versión 1.n (la versión x.(y+1) de una interfaz debe extenderse (heredarse de) xy ).

Además, se proporciona el método castFrom para transmitir entre diferentes interfaces. Este método funciona haciendo una llamada IPC a la interfaz remota para asegurarse de que el tipo subyacente sea el mismo que el tipo que se solicita. Si el tipo solicitado no está disponible, se devuelve nullptr .

sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService();
sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);

Devoluciones de llamadas asincrónicas

Muchas implementaciones de HAL existentes hablan con hardware asíncrono, lo que significa que necesitan una forma asíncrona de notificar a los clientes sobre los nuevos eventos que han ocurrido. Una interfaz HIDL se puede utilizar como devolución de llamada asincrónica porque las funciones de 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 nuevo método 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 utiliza 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 simplemente pasar eso a través de una instancia existente de la interfaz IFoo :

sp<IFooCallback> myFooCallback = new FooCallback();
myFoo.registerCallback(myFooCallback);

El servidor que implementa IFoo recibe esto como un objeto sp<IFooCallback> . Puede almacenar la devolución de llamada y volver a llamar al cliente cuando quiera usar esta interfaz.

Destinatarios de la muerte

Como las implementaciones de servicios pueden ejecutarse en un proceso diferente, puede suceder que el proceso que implementa una interfaz muera mientras el cliente permanece vivo. Cualquier llamada a un objeto de interfaz alojado en un proceso que haya muerto fallará con un error de transporte ( isOK() devolverá falso). La única forma de recuperarse de una falla de este tipo es solicitar una nueva instancia del servicio llamando a I<InterfaceName>::getService() . Esto funciona solo si el proceso que falló se reinició y volvió a registrar sus servicios con el administrador de servicemanager (lo que generalmente es cierto para las implementaciones de HAL).

En lugar de lidiar con esto de forma reactiva, los clientes de una interfaz también pueden registrar un destinatario muerto para recibir una notificación cuando un servicio muere. Para registrarse para tales notificaciones en una interfaz IFoo recuperada, un cliente puede hacer lo siguiente:

foo->linkToDeath(recipient, 1481 /* cookie */);

El parámetro del recipient debe ser una implementación de la interfaz android::hardware::hidl_death_recipient proporcionada por HIDL, que contiene un único método serviceDied() que se llamará desde un subproceso en el grupo de subprocesos RPC cuando el proceso que aloja la interfaz muere:

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 destinatario fallecido después de registrarlo:

foo->unlinkToDeath(recipient);