HIDL C++

Android 8 remanie l'architecture de l'OS Android pour définir des interfaces claires entre la plate-forme Android indépendante de l'appareil et le code spécifique à l'appareil et au fournisseur. Android définit déjà de nombreuses interfaces de ce type sous la forme d'interfaces HAL, définies comme des en-têtes C dans hardware/libhardware. HIDL remplace ces interfaces HAL par des interfaces stables et versionnées, qui peuvent être des interfaces HIDL côté client et côté serveur en C++ (décrites ci-dessous) ou en Java.

Les pages de cette section décrivent les implémentations C++ des interfaces HIDL, y compris des informations sur les fichiers générés automatiquement à partir des fichiers .hal HIDL par le compilateur hidl-gen, la façon dont ces fichiers sont empaquetés et comment les intégrer au code C++ qui les utilise.

Implémentations client et serveur

Les interfaces HIDL disposent d'implémentations client et serveur:

  • Un client d'une interface HIDL est le code qui utilise l'interface en appelant des méthodes dessus.
  • Un serveur est une implémentation d'une interface HIDL qui reçoit les appels des clients et renvoie les résultats (le cas échéant).

Lors de la transition des HAL libhardware aux HAL HIDL, l'implémentation du HAL devient le serveur et le processus qui appelle le HAL devient le client. Les implémentations par défaut peuvent servir à la fois des HAL passthrough et binderisées, et peuvent changer au fil du temps:

Figure 1 : Progression du développement des anciens HAL.

Créer le client HAL

Commencez par inclure les bibliothèques HAL dans le fichier makefile:

  • Compilation: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Soong: shared_libs: [ …, android.hardware.nfc@1.0 ]

Ensuite, incluez les fichiers d'en-tête HAL:

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

// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

Créer le serveur HAL

Pour créer l'implémentation du HAL, vous devez disposer des fichiers .hal qui représentent votre HAL et avoir déjà généré des fichiers de compilation pour votre HAL à l'aide de -Lmakefile ou -Landroidbp sur hidl-gen (./hardware/interfaces/update-makefiles.sh effectue cette opération pour les fichiers HAL internes et constitue une bonne référence). Lorsque vous transférez des HAL à partir de libhardware, vous pouvez effectuer une grande partie de ce travail facilement à l'aide de c2hal.

Pour créer les fichiers nécessaires à l'implémentation de votre HAL:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

Pour que le HAL fonctionne en mode passthrough, la fonction HIDL_FETCH_IModuleName doit résider dans /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so, où OPTIONAL_IDENTIFIER est une chaîne identifiant l'implémentation de passthrough. Les exigences du mode passthrough sont automatiquement remplies par les commandes ci-dessus, qui créent également la cible android.hardware.nfc@1.0-impl, mais n'importe quelle extension peut être utilisée. Par exemple, android.hardware.nfc@1.0-impl-foo utilise -foo pour se différencier.

Si un HAL est une version mineure ou une extension d'un autre HAL, le HAL de base doit être utilisé pour nommer ce binaire. Par exemple, les implémentations android.hardware.graphics.mapper@2.1 doivent toujours se trouver dans un binaire appelé android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER). En général, OPTIONAL_IDENTIFIER inclut la version HAL réelle. En nommant le binaire de cette manière, les clients 2.0 peuvent le récupérer directement, et les clients 2.1 peuvent convertir l'implémentation.

Ensuite, remplissez les bouchons avec des fonctionnalités et configurez un daemon. Exemple de code de démon (compatible avec le passthrough):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation appelle dlopen() pour la bibliothèque -impl fournie et la fournit en tant que service lié. Exemple de code de daemon (pour un service entièrement lié):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

Ce daemon se trouve généralement dans $PACKAGE + "-service-suffix" (par exemple, android.hardware.nfc@1.0-service), mais il peut se trouver n'importe où. La sepolicy pour une classe spécifique de HAL est l'attribut hal_<module> (par exemple, hal_nfc). Cet attribut doit être appliqué au daemon qui exécute un HAL particulier (si le même processus sert plusieurs HAL, plusieurs attributs peuvent lui être appliqués).