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 danstypes.hal
ne sont pas importés).
- Une interface, les
- 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 danstypes.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.