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 soit enregistrées par leur nom par le serveur, soit transmises en tant que paramètres aux méthodes définies par HIDL. Par exemple, le code-cadre peut servir à une interface pour recevoir des messages asynchrones de la HAL et transmettre cette interface directement à la HAL sans l'enregistrer.
Implémentation du 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
pour établir un lien. Exemple IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Exemple de squelette pour une implémentation 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 accessible à un client l'implémentation d'une interface serveur, vous pouvez :
- Enregistrez l'implémentation de l'interface auprès du
hwservicemanager
(voir les détails ci-dessous),
OU - Transmettez l'implémentation de l'interface comme argument d'une méthode d'interface (pour plus de détails, voir Rappels asynchrones ).
Lors de l'enregistrement de l'implémentation de l'interface, le processus hwservicemanager
assure le suivi des interfaces HIDL enregistrées exécutées sur le périphérique 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 service 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 (tel que IFoo.h
) possède 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 de l'interface, car les clients utiliseront ce nom pour récupérer l'interface du hwservicemanager
ultérieurement :
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
Le 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 conflits. Si vous appelez registerAsService()
avec exactement la même version de package, la même interface et le même 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
(via lehwservicemanager
) - Via une méthode d'interface
Chaque fichier d'en-tête d'interface généré automatiquement possède une méthode getService
statique qui peut être utilisée pour récupérer une instance de service à partir du hwservicemanager
:
// getService will return 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 processus différent ou même sur un autre appareil (avec HAL remoting). Étant donné que le client a appelé getService
sur un objet IFoo
inclus dans la version 1.0
du package, hwservicemanager
renvoie une implémentation de serveur uniquement si cette implémentation est compatible avec les clients 1.0
. En pratique, cela signifie uniquement les implémentations de serveur avec la version 1.n
(la version x.(y+1)
d'une interface doit étendre (hériter de) xy
).
De plus, la méthode castFrom
est fournie pour effectuer un cast entre différentes interfaces. Cette méthode fonctionne en effectuant un appel IPC à l'interface distante pour s'assurer que le type sous-jacent est le même que le 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 pour informer les clients des nouveaux événements survenus. Une interface HIDL peut être utilisée comme rappel asynchrone car les fonctions d'interface HIDL peuvent prendre 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 prend 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 utilisant 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 transmettre cela sur une instance existante de l'interface IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
Le serveur implémentant IFoo
reçoit cela en tant qu'objet sp<IFooCallback>
. Il peut stocker le rappel et rappeler le client chaque fois qu'il souhaite utiliser cette interface.
Destinataires du décès
Comme les implémentations de services peuvent s'exécuter dans un processus différent, il peut arriver que le processus implémentant une interface meure alors que le client reste en vie. Tout appel sur un objet d'interface hébergé dans un processus décédé échouera avec une erreur de transport ( isOK()
renverra false). La seule façon de récupérer d'un tel échec est de demander une nouvelle instance du service en appelant I<InterfaceName>::getService()
. Cela ne fonctionne que si le processus qui s'est écrasé a redémarré et réenregistré ses services auprès du 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 décédé pour recevoir une notification en cas de décès d'un service. 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()
qui sera 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 qui a été 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 donné ci-dessus, cookie
est égal à 1481 et who
est égal à foo
.
Il est également possible de désinscrire un destinataire de décès après l'avoir enregistré :
foo->unlinkToDeath(recipient);