Interfaces

Jede in einem HIDL-Paket definierte Schnittstelle verfügt über eine eigene automatisch generierte C++-Klasse im Namensraum ihres Pakets. Clients und Server gehen auf unterschiedliche Weise mit Schnittstellen um:

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

Schnittstellen können entweder namentlich vom Server registriert oder als Parameter an HIDL-definierte Methoden übergeben werden. Beispielsweise kann Framework-Code als Schnittstelle dienen, 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 Schnittstelle implementiert, muss die automatisch generierte IFoo Header-Datei enthalten:

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

Der Header wird automatisch von der gemeinsam genutzten Bibliothek der IFoo Schnittstelle exportiert, um eine Verknüpfung herzustellen. Beispiel IFoo.hal :

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

Beispielgerüst 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();
    }
    ...
};

Um einem Client die Implementierung einer Serverschnittstelle zur Verfügung zu stellen, können Sie:

  1. Registrieren Sie die Schnittstellenimplementierung beim hwservicemanager (siehe Details unten).

    ODER

  2. Übergeben Sie die Schnittstellenimplementierung als Argument einer Schnittstellenmethode (Einzelheiten finden Sie unter Asynchrone Rückrufe ).

Beim Registrieren der Schnittstellenimplementierung verfolgt der hwservicemanager Prozess die registrierten HIDL-Schnittstellen, die auf dem Gerät ausgeführt werden, nach Name und Version. Server können eine HIDL-Schnittstellenimplementierung nach Namen registrieren und Clients können Dienstimplementierungen nach Name und Version anfordern. Dieser Prozess bedient die HIDL-Schnittstelle android.hidl.manager@1.0::IServiceManager .

Jede automatisch generierte HIDL-Schnittstellen-Header-Datei (z. B. IFoo.h ) verfügt über eine registerAsService() Methode, mit der die Schnittstellenimplementierung beim hwservicemanager registriert werden kann. Das einzige erforderliche Argument ist der Name der Schnittstellenimplementierungen, da Clients diesen Namen später verwenden, um die Schnittstelle vom 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, um die Registrierung verschiedener Schnittstellen (oder verschiedener Versionen derselben Schnittstelle) mit identischen Instanznamen ohne Konflikte zu ermöglichen. Wenn Sie registerAsService() mit genau derselben Paketversion, Schnittstelle und demselben Instanznamen aufrufen, löscht der hwservicemanager seinen Verweis auf den zuvor registrierten Dienst und verwendet den neuen.

Client-Implementierung

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

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

Ein Client kann eine Schnittstelle auf zwei Arten erhalten:

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

Jede automatisch generierte Schnittstellen-Header-Datei verfügt über eine statische getService Methode, mit der eine Dienstinstanz vom hwservicemanager abgerufen werden kann:

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

Jetzt verfügt der Client über eine IFoo Schnittstelle und kann Methoden dafür aufrufen, als wäre es eine lokale Klassenimplementierung. In Wirklichkeit kann die Implementierung im selben Prozess, einem anderen Prozess oder sogar auf einem anderen Gerät (mit HAL-Remoting) ausgeführt werden. Da der Client getService für ein IFoo Objekt aus Version 1.0 des Pakets aufrief, gibt der hwservicemanager nur dann eine Serverimplementierung zurück, wenn diese Implementierung mit 1.0 Clients kompatibel ist. In der Praxis bedeutet dies, dass nur Serverimplementierungen mit Version 1.n (Version x.(y+1) einer Schnittstelle müssen xy erweitern (von erben)).

Zusätzlich steht die Methode castFrom zur Verfügung, um zwischen verschiedenen Schnittstellen zu konvertieren. Diese Methode führt einen IPC-Aufruf an die Remote-Schnittstelle durch, 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 Rückrufe

Viele bestehende HAL-Implementierungen kommunizieren mit asynchroner Hardware, was bedeutet, dass sie eine asynchrone Möglichkeit benötigen, Clients über neu aufgetretene Ereignisse zu benachrichtigen. Eine HIDL-Schnittstelle kann als asynchroner Rückruf verwendet werden, da HIDL-Schnittstellenfunktionen HIDL-Schnittstellenobjekte als Parameter annehmen können.

Beispielschnittstellendatei 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 akzeptiert:

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; Es stellt eine Implementierung von IFooCallback bereit:

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 dies auch einfach über eine vorhandene Instanz der IFoo Schnittstelle übergeben:

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

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

Sterbeempfänger

Da Dienstimplementierungen in einem anderen Prozess ausgeführt werden können, kann es vorkommen, dass der Prozess, der eine Schnittstelle implementiert, abstürzt, während der Client aktiv bleibt. Alle Aufrufe eines Schnittstellenobjekts, das in einem abgebrochenen Prozess gehostet wird, 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 Sie I<InterfaceName>::getService() aufrufen. Dies funktioniert nur, wenn der abgestürzte Prozess neu gestartet und seine Dienste erneut beim servicemanager registriert hat (was im Allgemeinen für HAL-Implementierungen gilt).

Anstatt reaktiv damit umzugehen, können Clients einer Schnittstelle auch einen Todesempfänger registrieren, um eine Benachrichtigung zu erhalten, wenn ein Dienst ausfällt. Um sich für solche Benachrichtigungen auf einer abgerufenen IFoo Schnittstelle zu registrieren, kann ein Client Folgendes tun:

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

Der 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, stirbt:

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 cookie Parameter enthält das Cookie, das mit linkToDeath() übergeben wurde, während der who -Parameter einen schwachen Zeiger auf das Objekt enthält, das den Dienst im Client darstellt. Bei dem oben angegebenen Beispielaufruf ist cookie gleich 1481 und who gleich foo .

Es ist auch möglich, einen Sterbeempfänger nach der Registrierung wieder abzumelden:

foo->unlinkToDeath(recipient);