Interfaces

Jede in einem HIDL-Paket definierte Schnittstelle hat eine eigene automatisch generierte C++-Klasse im Namespace des Pakets. Clients und Server gehen unterschiedlich mit Schnittstellen um:

  • Server implementieren Schnittstellen.
  • Clients rufen Methoden auf Schnittstellen auf.

Schnittstellen können entweder vom Server per Namen registriert oder als Parameter an HIDL-definierte Methoden übergeben werden. Framework-Code kann beispielsweise eine Schnittstelle bereitstellen, um asynchrone Nachrichten von der HAL zu empfangen und diese Schnittstelle direkt an die HAL weiterzuleiten, ohne sie zu registrieren.

Serverimplementierung

Ein Server, der die IFoo-Oberfläche implementiert, muss die automatisch generierte IFoo-Headerdatei enthalten:

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

Der Header wird automatisch von der freigegebenen Bibliothek der IFoo-Benutzeroberfläche exportiert, mit der eine Verknüpfung hergestellt werden soll. Beispiel IFoo.hal:

// IFoo.hal
interface IFoo {
    someMethod() generates (vec<uint32_t>);
    ...
}

Beispiel-Skelett für eine Serverimplementierung der IFoo-Schnittstelle:

// 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();
    }
    ...
};

So stellen Sie die Implementierung einer Serveroberfläche für einen Client zur Verfügung:

  1. Registrieren Sie die Benutzeroberfläche mit der hwservicemanager (siehe Details unten),

    ODER

  2. Übergeben Sie die Schnittstellenimplementierung als Argument einer Schnittstellenmethode. Weitere Informationen finden Sie unter Asynchrone Callbacks.

Bei der Registrierung der Schnittstellenimplementierung überwacht der hwservicemanager-Prozess die registrierten HIDL-Schnittstellen, die auf dem Gerät ausgeführt werden, anhand von Name und Version. Server können eine HIDL-Schnittstellenimplementierung per Name registrieren und Clients können Dienstimplementierungen per Name und Version anfordern. Dieser Prozess dient der HIDL-Schnittstelle android.hidl.manager@1.0::IServiceManager.

Jede automatisch generierte HIDL-Schnittstellen-Headerdatei (z. B. IFoo.h) hat eine registerAsService()-Methode, mit der die Schnittstellenimplementierung bei der hwservicemanager registriert werden kann. Das einzige erforderliche Argument ist der Name der Schnittstellenimplementierungen, da Clients diesen Namen verwenden, um die Schnittstelle später aus der hwservicemanager abzurufen:

::android::sp<IFoo> myFoo = new FooImpl();
::android::sp<IFoo> mySecondFoo = new FooAnotherImpl();
status_t status = myFoo->registerAsService();
status_t anotherStatus = mySecondFoo->registerAsService("another_foo");

Der hwservicemanager behandelt die Kombination von [package@version::interface, instance_name] als eindeutig, damit sich verschiedene Oberflächen (oder verschiedene Versionen derselben Oberfläche) mit identischen Instanznamen ohne Konflikte registrieren lassen. Wenn Sie registerAsService() mit genau derselben Paketversion, Schnittstelle und demselben Instanznamen aufrufen, verwirft hwservicemanager den Verweis auf den zuvor registrierten Dienst und verwendet den neuen.

Client-Implementierung

Genau wie der Server muss ein Client #include jede Schnittstelle, auf die er sich bezieht, angeben:

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

Ein Kunde kann eine Schnittstelle auf zwei Arten abrufen:

  • Über I<InterfaceName>::getService (über hwservicemanager)
  • Über eine Schnittstellenmethode

Jede automatisch generierte Interface-Headerdatei hat eine statische getService-Methode, mit der eine Dienstinstanz aus dem hwservicemanager abgerufen werden kann:

// getService returns nullptr if the service can't be found
sp<IFoo> myFoo = IFoo::getService();
sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");

Jetzt hat der Client eine IFoo-Schnittstelle und kann Methoden aufrufen, als wäre es eine lokale Klassenimplementierung. In der Praxis kann die Implementierung im selben Prozess, in einem anderen Prozess oder sogar auf einem anderen Gerät ausgeführt werden (mit HAL-Remotezugriff). Da der Client getService für ein IFoo-Objekt aufgerufen hat, das in Version 1.0 des Pakets enthalten ist, gibt hwservicemanager nur dann eine Serverimplementierung zurück, wenn diese Implementierung mit 1.0-Clients kompatibel ist. In der Praxis bedeutet das, dass nur Serverimplementierungen mit Version 1.n zulässig sind (Version x.(y+1) einer Schnittstelle muss x.y erweitern (von x.y erben)).

Außerdem gibt es die Methode castFrom, um zwischen verschiedenen Schnittstellen zu übertragen. Bei dieser Methode wird ein IPC-Aufruf an die Remote-Schnittstelle gesendet, um sicherzustellen, dass der zugrunde liegende Typ mit dem angeforderten Typ übereinstimmt. Wenn der angeforderte Typ nicht verfügbar ist, wird nullptr zurückgegeben.

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

Asynchrone Callbacks

Viele vorhandene HAL-Implementierungen kommunizieren mit asynchroner Hardware. Das bedeutet, dass sie Clients asynchron über neue Ereignisse informieren müssen. Eine HIDL-Schnittstelle kann als asynchroner Rückruf verwendet werden, da HIDL-Schnittstellenfunktionen HIDL-Schnittstellenobjekte als Parameter annehmen können.

Beispiel für eine Benutzeroberflächendatei IFooCallback.hal:

package android.hardware.samples@1.0;
interface IFooCallback {
    sendEvent(uint32_t event_id);
    sendData(vec<uint8_t> data);
}

Beispiel für eine neue Methode in IFoo, die einen IFooCallback-Parameter verwendet:

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);
};

Der Client, der die IFoo-Schnittstelle verwendet, ist der Server der IFooCallback-Schnittstelle. Er bietet eine Implementierung von 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
    }
};

Es kann auch einfach an eine vorhandene Instanz der IFoo-Benutzeroberfläche übergeben werden:

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

Der Server, auf dem IFoo implementiert ist, empfängt dies als sp<IFooCallback>-Objekt. Sie kann den Rückruf speichern und den Client jederzeit zurückrufen, wenn sie diese Schnittstelle verwenden möchte.

Empfänger von Todesnachrichten

Da Dienstimplementierungen in einem anderen Prozess ausgeführt werden können, kann es vorkommen, dass der Prozess, der eine Schnittstelle implementiert, beendet wird, während der Client aktiv bleibt. Alle Aufrufe an ein Interface-Objekt, das in einem Prozess gehostet wird, der beendet wurde, schlagen mit einem Transportfehler fehl (isOK() gibt false zurück). Die einzige Möglichkeit, einen solchen Fehler zu beheben, besteht darin, eine neue Instanz des Dienstes anzufordern, indem I<InterfaceName>::getService() aufgerufen wird. Dies funktioniert nur, wenn der abgestürzte Prozess neu gestartet und seine Dienste bei der servicemanager neu registriert wurden. Das ist bei HAL-Implementierungen in der Regel der Fall.

Anstatt dies reaktiv zu behandeln, können Clients einer Benutzeroberfläche auch einen Empfänger für den Diensttod registrieren, um benachrichtigt zu werden, wenn ein Dienst ausfällt. So registriert sich ein Client für solche Benachrichtigungen auf einer abgerufenen IFoo-Benutzeroberfläche:

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

Der Parameter recipient muss eine Implementierung der von HIDL bereitgestellten Schnittstelle android::hardware::hidl_death_recipient sein, die eine einzelne Methode serviceDied() enthält, die von einem Thread im RPC-Threadpool aufgerufen wird, wenn der Prozess, der die Schnittstelle hostet, beendet wird:

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
    }
}

Der Parameter cookie enthält das Cookie, das mit linkToDeath() übergeben wurde, während der Parameter who einen schwachen Verweis auf das Objekt enthält, das den Dienst im Client darstellt. Im obigen Beispielaufruf entspricht cookie 1481 und who foo.

Sie können die Registrierung eines Erben auch nach der Registrierung aufheben:

foo->unlinkToDeath(recipient);