C++ HIDL

Android O réorganise le système d'exploitation 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écrit ci-dessous) ou Java .

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

Implémentations client et serveur

Les interfaces HIDL ont des 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 (si nécessaire).

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

Figure 1. Progression du développement pour les anciens HAL.

Création du client HAL

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

  • Marque : 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éation du serveur HAL

Pour créer l'implémentation HAL, vous devez avoir les fichiers .hal qui représentent votre HAL et avoir déjà généré des makefiles pour votre HAL en utilisant -Lmakefile ou -Landroidbp sur hidl-gen ( ./hardware/interfaces/update-makefiles.sh fait pour fichiers HAL internes et est une bonne référence). Lors du transfert sur HAL depuis libhardware , vous pouvez effectuer une grande partie de ce travail facilement en utilisant 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 HAL fonctionne en mode passthrough, vous devez avoir la fonction HIDL_FETCH_IModuleName résidant dans /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl( OPTIONAL_IDENTIFIER ).soOPTIONAL_IDENTIFIER est une chaîne identifiant l'implémentation du relais. Les exigences du mode passthrough sont satisfaites automatiquement 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 être dans un binaire appelé android.hardware.graphics.mapper@2.0-impl( OPTIONAL_IDENTIFIER ) . Habituellement, le OPTIONAL_IDENTIFIER ici inclurait la version réelle de HAL. En nommant le binaire comme ceci, les clients 2.0 peuvent le récupérer directement, et les clients 2.1 peuvent convertir l'implémentation.

Ensuite, remplissez les stubs avec les fonctionnalités et configurez un démon. Exemple de code démon (prenant en charge le passthrough) :

#include <hidl/LegacySupport.h>

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

defaultPassthroughServiceImplementation dlopen() la bibliothèque -impl fournie et la fournira en tant que service lié. Exemple de code démon (pour un service binderisé pur) :

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 will never exceed
    // 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 démon réside généralement dans $PACKAGE + "-service-suffix" (par exemple, android.hardware.nfc@1.0-service ), mais il peut se trouver n'importe où. La stratégie pour une classe spécifique de HAL est l'attribut hal_<module> (par exemple, hal_nfc) . Cet attribut doit être appliqué au démon qui exécute un HAL particulier (si le même processus dessert plusieurs HAL, plusieurs attributs peuvent lui être appliqués).