Interfaces et amp; Paquets

HIDL est construit autour des interfaces, un type abstrait utilisé dans les langages orientés objet pour définir des comportements. Chaque interface fait partie d'un package.

Paquets

Les noms de packages peuvent avoir des sous-niveaux tels que package.subpackage . Le répertoire racine des packages HIDL publiés est hardware/interfaces ou vendor/vendorName (par exemple vendor/google pour les appareils Pixel). Le nom du package forme un ou plusieurs sous-répertoires sous le répertoire racine ; tous les fichiers définissant un package se trouvent dans le même répertoire. Par exemple, package android.hardware.example.extension.light@2.0 se trouve sous hardware/interfaces/example/extension/light/2.0 .

Le tableau suivant répertorie les préfixes et les emplacements des packages :

Préfixe du package Emplacement Types d'interfaces
android.hardware.* hardware/interfaces/* HAL
android.frameworks.* frameworks/hardware/interfaces/* frameworks/connexes
android.system.* system/hardware/interfaces/* système/connexe
android.hidl.* system/libhidl/transport/* cœur

Le répertoire du package contient des fichiers avec l'extension .hal . Chaque fichier doit contenir une instruction package nommant le package et la version dont il fait partie. Le fichier types.hal , s'il est présent, ne définit pas d'interface mais définit plutôt des types de données accessibles à chaque interface du package.

Définition de l'interface

Mis à part types.hal , tous les autres fichiers .hal définissent une interface. Une interface est généralement définie comme suit :

interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

    // interface methods
    create(int32_t id) generates (MyStruct s);
    close();
};

Une interface sans déclaration extends explicite s'étend implicitement depuis android.hidl.base@1.0::IBase (similaire à java.lang.Object en Java.) L'interface IBase, implicitement importée, déclare plusieurs méthodes réservées qui ne doivent pas et ne peuvent pas être redéclarées. dans des interfaces définies par l'utilisateur ou utilisé autrement. Ces méthodes comprennent :

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

Importation

L'instruction import est un mécanisme HIDL permettant d'accéder aux interfaces et aux types de package dans un autre package. Une instruction import concerne deux entités :

  • L'entité d' importation, qui peut être soit un package, soit une interface ; et
  • L' entité importée, qui peut également être soit un package, soit une interface.

L'entité importatrice est déterminée par l'emplacement de la déclaration import . Lorsque l'instruction se trouve dans le types.hal d'un package, ce qui est importé est visible par l'ensemble du package ; il s'agit d'une importation au niveau du package . Lorsque l'instruction se trouve dans un fichier d'interface, l'entité importatrice est l'interface elle-même ; il s'agit d'une importation au niveau de l'interface .

L'entité importée est déterminée par la valeur après le mot-clé import . Il n'est pas nécessaire que la valeur soit un nom complet ; si un composant est omis, il est automatiquement rempli avec les informations du package actuel. Pour les valeurs complètes, les cas d'importation suivants sont pris en charge :

  • Importations de paquets complets . Si la valeur est un nom de package et une version (syntaxe décrite ci-dessous), alors l'intégralité du package est importée dans l'entité d'importation.
  • Importations partielles . Si la valeur est :
    • Une interface, les types.hal du package et cette interface sont importés dans l'entité importatrice.
    • Un UDT défini dans types.hal , alors seul cet UDT est importé dans l'entité importatrice (les autres types dans types.hal ne sont pas importés).
  • Importations de types uniquement . Si la valeur utilise la syntaxe d'une importation partielle décrite ci-dessus, mais avec le mot-clé types au lieu d'un nom d'interface, seuls les UDT dans types.hal du package désigné sont importés.

L'entité importatrice a accès à une combinaison de :

  • Les UDT communs du package importé définis dans types.hal ;
  • Les interfaces du package importé (pour une importation de package complet) ou l’interface spécifiée (pour une importation partielle) dans le but de les appeler, de leur transmettre des handles et/ou d’en hériter.

L'instruction import utilise la syntaxe full-qualified-type-name pour fournir le nom et la version du package ou de l'interface en cours d'importation :

import android.hardware.nfc@1.0;            // import a whole package
import android.hardware.example@1.0::IQuux; // import an interface and types.hal
import android.hardware.example@1.0::types; // import just types.hal

Héritage d'interface

Une interface peut être une extension d'une interface préalablement définie. Les extensions peuvent être de l’un des trois types suivants :

  • L'interface peut ajouter des fonctionnalités à une autre, en incorporant son API inchangée.
  • Le package peut ajouter des fonctionnalités à un autre, en incorporant son API inchangée.
  • L'interface peut importer des types à partir d'un package ou d'une interface spécifique.

Une interface ne peut étendre qu’une seule autre interface (pas d’héritage multiple). Chaque interface d'un package avec un numéro de version mineure non nulle doit étendre une interface dans la version précédente du package. Par exemple, si une interface IBar dans la version 4.0 du package derivative est basée sur (étend) une interface IFoo dans la version 1.2 du package original et qu'une version 1.3 du package original est créée, la version 4.1 IBar ne peut pas étendre la version 1.3 de IFoo . Au lieu de cela, la version 4.1 IBar doit étendre la version 4.0 IBar , qui est liée à la version 1.2 IFoo . La version 5.0 IBar pourrait étendre la version 1.3 IFoo , si vous le souhaitez.

Les extensions d'interface n'impliquent pas de dépendance à la bibliothèque ou d'inclusion croisée de HAL dans le code généré : elles importent simplement la structure des données et les définitions de méthodes au niveau HIDL. Chaque méthode d'une HAL doit être implémentée dans cette HAL.

Extensions de fournisseur

Dans certains cas, les extensions des fournisseurs seront implémentées en tant que sous-classe de l'objet de base qui représente l'interface principale qu'elles étendent. Le même objet sera enregistré sous le nom et la version HAL de base, et sous le nom et la version HAL de l'extension (fournisseur).

Gestion des versions

Les packages sont versionnés et les interfaces ont la version de leur package. Les versions sont exprimées en deux entiers majeurs . mineure .

  • Les versions majeures ne sont pas rétrocompatibles. L'incrémentation du numéro de version majeure réinitialise le numéro de version mineure à 0.
  • Les versions mineures sont rétrocompatibles. L'incrémentation du numéro mineur indique que la version la plus récente est entièrement rétrocompatible avec la version précédente. De nouvelles structures de données et méthodes peuvent être ajoutées, mais aucune structure de données ou signature de méthode existante ne peut être modifiée.

Plusieurs versions majeures ou mineures d’un HAL peuvent être présentes simultanément sur un appareil. Cependant, une version mineure doit être préférée à une version majeure, car le code client qui fonctionne avec une interface de version mineure précédente fonctionnera également avec les versions mineures ultérieures de cette même interface. Pour plus de détails sur la gestion des versions et les extensions de fournisseur, consultez Gestion des versions HIDL .

Résumé de la disposition de l'interface

Cette section résume comment gérer un package d'interface HIDL (tel que hardware/interfaces ) et consolide les informations présentées tout au long de la section HIDL. Avant de lire, assurez-vous de bien connaître HIDL Versioning , les concepts de hachage dans Hashing avec hidl-gen , les détails de l'utilisation de HIDL en général et les définitions suivantes :

Terme Définition
Interface binaire d'application (ABI) Interface de programmation d'application + toutes liaisons binaires requises.
Nom complet (fqName) Nom pour distinguer un type hidl. Exemple : android.hardware.foo@1.0::IFoo .
Emballer Package contenant une interface et des types HIDL. Exemple : android.hardware.foo@1.0 .
Racine du paquet Package racine contenant les interfaces HIDL. Exemple : l'interface HIDL android.hardware est dans le package root android.hardware.foo@1.0 .
Chemin racine du package Emplacement dans l'arborescence des sources Android vers lequel la racine d'un package est mappée.

Pour plus de définitions, consultez Terminologie HIDL .

Chaque fichier peut être trouvé à partir du mappage racine du package et de son nom complet

Les racines du package sont spécifiées à hidl-gen comme argument -r android.hardware:hardware/interfaces . Par exemple, si le package est vendor.awesome.foo@1.0::IFoo et hidl-gen est envoyé -r vendor.awesome:some/device/independent/path/interfaces , alors le fichier d'interface doit être situé dans $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal .

En pratique, il est recommandé à un fournisseur ou à un OEM nommé awesome de mettre ses interfaces standards dans vendor.awesome . Une fois qu'un chemin de package a été sélectionné, il ne doit pas être modifié car il est intégré à l'ABI de l'interface.

Le mappage du chemin du package doit être unique

Par exemple, si vous avez -rsome.package:$PATH_A et -rsome.package:$PATH_B , $PATH_A doit être égal à $PATH_B pour un répertoire d'interface cohérent (cela facilite également la gestion des versions des interfaces ).

La racine du package doit avoir un fichier de versionnage

Si vous créez un chemin de package tel que -r vendor.awesome:vendor/awesome/interfaces , vous devez également créer le fichier $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt , qui doit contenir les hachages d'interfaces créés à l'aide du -Lhash option dans hidl-gen (ceci est discuté en détail dans Hashing avec hidl-gen ).

Les interfaces sont placées dans des emplacements indépendants du périphérique

En pratique, il est recommandé de partager les interfaces entre branches. Cela permet une réutilisation maximale du code et un test maximal du code sur différents appareils et cas d'utilisation.