Interfaces

Chaque interface définie dans un package HIDL a 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 dans différentes manières:

  • Les serveurs implémentent les interfaces.
  • Les clients appellent des méthodes au niveau des interfaces.

Les interfaces peuvent être enregistrées à l'aide de leur nom par le serveur ou transmises en tant que aux méthodes définies par HIDL. Par exemple, le code du framework peut diffuser pour recevoir des messages asynchrones du HAL et transmettre cette interface directement au HAL sans l'enregistrer.

Implémentation du serveur

Un serveur qui implémente l'interface IFoo doit inclure l'élément 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 du Interface IFoo à associer. 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 l'implémentation d'une interface de serveur disponible pour un client, vous permet:

  1. Enregistrez l'implémentation de l'interface à l'aide de la commande hwservicemanager (voir détails ci-dessous),

    OU

  2. Transmettez l'implémentation de l'interface en tant qu'argument d'une d'interface unique (pour les données, consultez la section Asynchrone ).

Lors de l'enregistrement de l'implémentation de l'interface, Le processus hwservicemanager assure le suivi des interfaces HIDL enregistrées en cours d'exécution sur l'appareil par nom et par version. Les serveurs peuvent enregistrer une interface HIDL l'implémentation par nom et les clients peuvent demander des implémentations de service par nom et sa 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) comporte une méthode registerAsService() qui peut être utilisée pour enregistrer le l'implémentation de l'interface avec hwservicemanager. La seule L'argument requis est le nom des implémentations d'interface en tant que clients utilisez 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 à activer différentes interfaces (ou différentes versions de la même interface) à enregistrer avec des noms d'instances identiques sans conflit. Si vous appelez registerAsService() avec exactement la même version de package, l'interface et le nom de l'instance, hwservicemanager supprime sa référence au précédemment enregistré et utilise le nouveau.

Implémentation client

Tout comme le serveur, un client doit #include à chaque interface elle 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 le hwservicemanager).
  • Via une méthode d'interface

Chaque fichier d'en-tête d'interface généré automatiquement possède un getService statique permettant de récupérer une instance de service 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 maintenant d'une interface IFoo et peut appeler des méthodes pour comme s'il s'agissait d'une implémentation de classe locale. En réalité, l'implémentation peuvent s'exécuter dans le même processus, dans un processus différent, voire sur un autre appareil (avec communication à distance HAL). Comme 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 celle-ci est compatible avec les clients 1.0. En pratique, signifie que seules les implémentations de serveur avec la version 1.n (version Le champ x.(y+1) d'une interface doit être étendu (hériter de) x.y).

De plus, la méthode castFrom est fournie pour caster entre différentes interfaces. Cette méthode fonctionne en effectuant un appel d'IPC à la pour s'assurer que le type sous-jacent est identique à celui utilisé demandée. 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 elles ont besoin d'une méthode asynchrone pour informer les clients des nouveaux événements s'est produit. Une interface HIDL peut être utilisée comme rappel asynchrone, car HIDL les fonctions d'interface 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 accepte une 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 server de l'interface IFooCallback. elle fournit une l'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 le transmettre à une instance existante du Interface IFoo:

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

Le serveur qui implémente IFoo le reçoit en tant que sp<IFooCallback>. Il peut stocker le rappel et appeler au client chaque fois qu'il veut utiliser cette interface.

Destinataires du décès

Comme les implémentations de services peuvent s'exécuter selon un processus différent, cela peut se produire que le processus d'implémentation d'une interface manque pendant que le client reste actif. Tous les appels sur un objet d'interface hébergé dans un processus qui n'est plus actif échouent avec une erreur de transport (isOK() renvoie false). Le seul moyen de la récupération après un tel échec consiste à demander une nouvelle instance du service Appel de I<InterfaceName>::getService() en cours. Cela ne fonctionne que si le processus qui avait planté 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 traiter cela de façon réactive, les clients d'une interface peuvent également enregistrer un destinataire du décès pour recevoir une notification lorsqu'un service cesse de fonctionner. Pour recevoir de telles notifications sur une interface IFoo récupérée, une peut effectuer les opérations suivantes:

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

Le paramètre recipient doit être une implémentation de android::hardware::hidl_death_recipient fournie par HIDL, qui contient une seule méthode serviceDied() appelée à partir d'un thread du pool de threads RPC lorsque le processus hébergeant l'interface cesse de fonctionner:

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(), alors que le paramètre who contient pointeur faible vers l'objet représentant le service dans le client. Avec l'attribut exemple d'appel fourni ci-dessus, cookie est égal à 1481, et who est égal à foo.

Vous pouvez également annuler l'enregistrement d'un destinataire du décès après l'avoir enregistré:

foo->unlinkToDeath(recipient);