Chaque interface définie dans un package HIDL possède sa propre classe C++ générée automatiquement dans l'espace de noms de son package. Les clients et les serveurs gèrent les interfaces de différentes manières:
- Les serveurs implémentent des interfaces.
- Les clients appellent des méthodes sur les interfaces.
Les interfaces peuvent être enregistrées par nom par le serveur ou transmises en tant que paramètres aux méthodes définies par HIDL. Par exemple, le code du framework peut servir une interface pour recevoir des messages asynchrones de la HAL et transmettre cette interface directement à la HAL sans l'enregistrer.
Implémentation côté serveur
Un serveur implémentant l'interface IFoo
doit inclure le fichier d'en-tête IFoo
généré automatiquement:
#include <android/hardware/samples/1.0/IFoo.h>
L'en-tête est automatiquement exporté par la bibliothèque partagée de l'interface IFoo
à associer. Exemple IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Exemple de squelette pour une implémentation de serveur de l'interface 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(); } ... };
Pour rendre l'implémentation d'une interface de serveur disponible pour un client, vous pouvez:
- Enregistrez l'implémentation de l'interface avec
hwservicemanager
(voir les détails ci-dessous),
OU
- Transmettez l'implémentation de l'interface en tant qu'argument d'une méthode d'interface (pour en savoir plus, consultez la section Rappels asynchrones).
Lors de l'enregistrement de l'implémentation de l'interface, le processus hwservicemanager
suit les interfaces HIDL enregistrées exécutées sur l'appareil par nom et version. Les serveurs peuvent enregistrer une implémentation d'interface HIDL par nom, et les clients peuvent demander des implémentations de services par nom et version. Ce processus sert l'interface HIDL android.hidl.manager@1.0::IServiceManager
.
Chaque fichier d'en-tête d'interface HIDL généré automatiquement (par exemple, IFoo.h
) comporte une méthode registerAsService()
qui peut être utilisée pour enregistrer l'implémentation de l'interface auprès de hwservicemanager
. Le seul argument obligatoire est le nom des implémentations d'interface, car les clients utilisent ce nom pour récupérer l'interface à partir de hwservicemanager
plus tard:
::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
traite la combinaison de [package@version::interface, instance_name]
comme unique pour permettre à différentes interfaces (ou différentes versions de la même interface) de s'enregistrer avec des noms d'instance identiques sans conflit. Si vous appelez registerAsService()
avec exactement la même version de package, l'interface et le nom d'instance, hwservicemanager
supprime sa référence au service précédemment enregistré et utilise le nouveau.
Implémentation client
Tout comme le serveur, un client doit #include
chaque interface à laquelle il fait référence:
#include <android/hardware/samples/1.0/IFoo.h>
Un client peut obtenir une interface de deux manières:
- Via
I<InterfaceName>::getService
(viahwservicemanager
) - Via une méthode d'interface
Chaque fichier d'en-tête d'interface généré automatiquement comporte une méthode getService
statique qui peut être utilisée pour récupérer une instance de service à partir de hwservicemanager
:
// getService returns nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
Le client dispose désormais d'une interface IFoo
et peut y appeler des méthodes comme s'il s'agissait d'une implémentation de classe locale. En réalité, l'implémentation peut s'exécuter dans le même processus, dans un autre processus ou même sur un autre appareil (avec la télécommande HAL). Étant donné que le client a appelé getService
sur un objet IFoo
inclus à partir de la version 1.0
du package, hwservicemanager
ne renvoie une implémentation de serveur que si cette implémentation est compatible avec les clients 1.0
. En pratique, cela signifie que seules les implémentations de serveur avec la version 1.n
sont autorisées (la version x.(y+1)
d'une interface doit étendre (hériter de) x.y
).
De plus, la méthode castFrom
est fournie pour caster entre différentes interfaces. Cette méthode consiste à effectuer un appel IPC à l'interface distante pour s'assurer que le type sous-jacent est identique au type demandé. Si le type demandé n'est pas disponible, nullptr
est renvoyé.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
Rappels asynchrones
De nombreuses implémentations HAL existantes communiquent avec du matériel asynchrone, ce qui signifie qu'elles ont besoin d'un moyen asynchrone d'informer les clients des nouveaux événements qui se sont produits. Une interface HIDL peut être utilisée comme rappel asynchrone, car les fonctions d'interface HIDL peuvent utiliser des objets d'interface HIDL comme paramètres.
Exemple de fichier d'interface IFooCallback.hal
:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
Exemple de nouvelle méthode dans IFoo
qui utilise un paramètre 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); };
Le client qui utilise l'interface IFoo
est le serveur de l'interface IFooCallback
. Il fournit une implémentation 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 } };
Il peut également simplement le transmettre à une instance existante de l'interface IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
Le serveur implémentant IFoo
le reçoit en tant qu'objet sp<IFooCallback>
. Il peut stocker le rappel et appeler le client chaque fois qu'il souhaite utiliser cette interface.
Destinataires des notifications de décès
Comme les implémentations de services peuvent s'exécuter dans un autre processus, il peut arriver que le processus implémentant une interface s'arrête alors que le client reste actif.
Tous les appels sur un objet d'interface hébergé dans un processus qui a expiré échouent avec une erreur de transport (isOK()
renvoie false
). Le seul moyen de se rétablir après un tel échec est de demander une nouvelle instance du service en appelant I<InterfaceName>::getService()
. Cela ne fonctionne que si le processus qui a planté a redémarré et réenregistré ses services auprès de servicemanager
(ce qui est généralement vrai pour les implémentations HAL).
Au lieu de gérer cela de manière réactive, les clients d'une interface peuvent également enregistrer un destinataire de fin de vie pour recevoir une notification lorsqu'un service prend fin.
Pour s'inscrire à de telles notifications sur une interface IFoo
récupérée, un client peut procéder comme suit:
foo->linkToDeath(recipient, 1481 /* cookie */);
Le paramètre recipient
doit être une implémentation de l'interface android::hardware::hidl_death_recipient
fournie par HIDL, qui contient une seule méthode serviceDied()
appelée à partir d'un thread dans le pool de threads RPC lorsque le processus hébergeant l'interface meurt:
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 } }
Le paramètre cookie
contient le cookie transmis avec linkToDeath()
, tandis que le paramètre who
contient un pointeur faible vers l'objet représentant le service dans le client. Avec l'exemple d'appel ci-dessus, cookie
est égal à 1481 et who
est égal à foo
.
Vous pouvez également annuler l'enregistrement d'un bénéficiaire de décès après l'avoir enregistré:
foo->unlinkToDeath(recipient);