C++ HIDL

Android O réarchitecte 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 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 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 dans le HAL devient le client. Les implémentations par défaut peuvent servir à la fois des HAL relais et binderisés, et peuvent changer au fil du temps :

Figure 1. Progression du développement des 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
  • Bientôt : 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 disposer des 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 cela pour fichiers HAL internes et constitue une bonne référence). Lors du transfert de HAL depuis libhardware , vous pouvez effectuer facilement une grande partie de ce travail en utilisant c2hal.

Pour créer les fichiers nécessaires à la mise en œuvre 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 passthrough. Les exigences du mode passthrough sont automatiquement satisfaites 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, OPTIONAL_IDENTIFIER ici inclut la version réelle de HAL. En nommant le binaire ainsi, les clients 2.0 peuvent le récupérer directement et les clients 2.1 peuvent convertir l'implémentation en version ascendante.

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

#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 binderisé. Exemple de code démon (pour un service pur binderisé) :

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).