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:
- Registrieren Sie die Benutzeroberfläche mit der
hwservicemanager
(siehe Details unten),
ODER
- Ü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
(überhwservicemanager
) - Ü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);