Le langage de définition d'interface HAL (HIDL) est un langage de description d'interface (IDL) qui permet de spécifier l'interface entre un HAL et ses utilisateurs. HIDL permet de spécifier des types et des appels de méthode, collectés dans des interfaces et des packages. Plus largement, HIDL est un système de communication entre des codebases pouvant être compilés indépendamment.
HIDL est destiné à être utilisé pour la communication inter-processus (IPC). Les HAL créés avec le HDL sont appelés HAL liés, car ils peuvent communiquer avec d'autres couches d'architecture à l'aide d'appels de communication inter-processus (IPC) de liaison. Les HAL liés s'exécutent dans un processus distinct du client qui les utilise. Pour les bibliothèques qui doivent être associées à un processus, un mode de passthrough est également disponible (non compatible avec Java).
HIDL spécifie les structures de données et les signatures de méthode, organisées en interfaces (similaires à une classe) qui sont collectées dans des packages. La syntaxe de HIDL semble familière aux programmeurs C++ et Java, mais avec un ensemble de mots clés différent. HIDL utilise également des annotations de style Java.
Terminologie
Cette section utilise les termes suivants liés à HIDL:
lié | Indique que HIDL est utilisé pour les appels de procédure à distance entre les processus, implémentés sur un mécanisme semblable à un liant. Voir également passthrough. |
---|---|
rappel, asynchrone | Interface gérée par un utilisateur HAL, transmise au HAL (à l'aide d'une méthode HIDL) et appelée par le HAL pour renvoyer des données à tout moment. |
rappel, synchrone | Renvoie les données de l'implémentation de la méthode HIDL d'un serveur au client. Non utilisé pour les méthodes qui renvoient une valeur vide ou une seule valeur primitive. |
client | Processus qui appelle les méthodes d'une interface particulière. Un processus HAL ou Android Framework peut être un client d'une interface et un serveur d'une autre. Voir aussi la section Passthrough. |
s'étend | Indique une interface qui ajoute des méthodes et/ou des types à une autre interface. Une interface ne peut étendre qu'une seule autre interface. Peut être utilisé pour une incrémentation de version mineure dans le même nom de package ou pour un nouveau package (par exemple, une extension du fournisseur) à compiler sur un ancien package. |
génère | Indique une méthode d'interface qui renvoie des valeurs au client. Pour renvoyer une valeur non primitive ou plusieurs valeurs, une fonction de rappel synchrone est générée. |
interface | Collection de méthodes et de types. Traduit en classe en C++ ou Java. Toutes les méthodes d'une interface sont appelées dans la même direction: un processus client appelle les méthodes implémentées par un processus serveur. |
aller simple | Lorsqu'il est appliqué à une méthode HIDL, indique que la méthode ne renvoie aucune valeur et ne bloque pas. |
colis | Collection d'interfaces et de types de données partageant une version. |
passthrough | Mode HIDL dans lequel le serveur est une bibliothèque partagée, dlopen ed par le client. En mode passthrough, le client et le serveur sont le même processus, mais avec des codebases distincts. Utilisé uniquement pour intégrer les anciens codebases au modèle HIDL.
Voir également Binderisé. |
serveur | Processus qui implémente les méthodes d'une interface. Voir aussi la section Passthrough. |
transport | Infrastructure HIDL qui transfère des données entre le serveur et le client. |
version | Version d'un package. Il se compose de deux entiers, majeur et mineur. Les incréments de version mineurs peuvent ajouter (mais pas modifier) des types et des méthodes. |
Conception HIDL
L'objectif de HIDL est de permettre de remplacer le framework Android sans avoir à reconstruire les HAL. Les HAL sont créés par les fournisseurs ou les fabricants de SoC et placés dans une partition /vendor
sur l'appareil, ce qui permet de remplacer le framework Android, dans sa propre partition, par un OTA sans recompiler les HAL.
La conception HIDL équilibre les préoccupations suivantes:
- Interopérabilité Créez des interfaces interopérables fiables entre les processus pouvant être compilés avec différentes architectures, chaînes d'outils et configurations de compilation. Les interfaces HIDL sont versionnées et ne peuvent pas être modifiées une fois publiées.
- Efficacité. HIDL essaie de minimiser le nombre d'opérations de copie. Les données définies par HIDL sont transmises au code C++ dans des structures de données de mise en page standard C++ pouvant être utilisées sans décompression. HIDL fournit également des interfaces de mémoire partagée. Étant donné que les RPC sont intrinsèquement un peu lents, HIDL propose deux méthodes de transfert de données sans utiliser d'appel RPC: la mémoire partagée et une file d'attente de messages rapide (FMQ).
- Intuitifs HIDL évite les problèmes épineux de propriété de la mémoire en n'utilisant que des paramètres
in
pour les RPC (voir Android Interface Definition Language (AIDL)). Les valeurs qui ne peuvent pas être renvoyées efficacement à partir de méthodes sont renvoyées via des fonctions de rappel. Ni le transfert de données vers HIDL, ni la réception de données à partir de HIDL ne modifie la propriété des données. La propriété reste toujours la fonction appelante. Les données ne doivent persister que pendant la durée de la fonction appelée et peuvent être détruites immédiatement après le retour de la fonction appelée.
Utiliser le mode passthrough
Pour mettre à niveau les appareils exécutant des versions antérieures d'Android vers Android O, vous pouvez encapsuler les HAL conventionnels (et anciens) dans une nouvelle interface HIDL qui les sert en mode binderisé et en mode même processus (passthrough). Ce wrapper est transparent à la fois pour le HAL et le framework Android.
Le mode passthrough n'est disponible que pour les clients et implémentations C++. Les appareils exécutant des versions antérieures d'Android ne disposent pas de HAL écrits en Java. Les HAL Java sont donc intrinsèquement liés.
Fichiers d'en-tête de passthrough
Lorsqu'un fichier .hal
est compilé, hidl-gen
génère un fichier d'en-tête de passthrough supplémentaire BsFoo.h
en plus des en-têtes utilisés pour la communication du liaisonneur. Cet en-tête définit les fonctions à dlopen
er. Comme les HAL de passthrough s'exécutent dans le même processus dans lequel ils sont appelés, dans la plupart des cas, les méthodes de passthrough sont appelées par un appel de fonction direct (même thread). Les méthodes oneway
s'exécutent dans leur propre thread, car elles ne sont pas destinées à attendre que le HAL les traite (cela signifie que tout HAL qui utilise des méthodes oneway
en mode passthrough doit être thread-safe).
Étant donné un IFoo.hal
, BsFoo.h
encapsule les méthodes générées par HIDL pour fournir des fonctionnalités supplémentaires (par exemple, exécuter des transactions oneway
dans un autre thread). Ce fichier est semblable à BpFoo.h
. Toutefois, au lieu de transmettre des appels IPC à l'aide d'un liaisonneur, les fonctions souhaitées sont directement appelées. Les futures implémentations des HAL peuvent fournir plusieurs implémentations, telles que le HAL FooFast et un HAL FooAccurate. Dans ce cas, un fichier est créé pour chaque implémentation supplémentaire (par exemple, PTFooFast.cpp
et PTFooAccurate.cpp
).
Lier les HAL passthrough
Vous pouvez binderiser les implémentations HAL compatibles avec le mode passthrough. Étant donné une interface HAL a.b.c.d@M.N::IFoo
, deux packages sont créés:
a.b.c.d@M.N::IFoo-impl
: contient l'implémentation du HAL et expose la fonctionIFoo* HIDL_FETCH_IFoo(const char* name)
. Sur les anciens appareils, ce package estdlopen
é et l'implémentation est instanciée à l'aide deHIDL_FETCH_IFoo
. Vous pouvez générer le code de base à l'aide dehidl-gen
,-Lc++-impl
et-Landroidbp-impl
.a.b.c.d@M.N::IFoo-service
. Ouvre le HAL passthrough et s'enregistre en tant que service lié, ce qui permet d'utiliser la même implémentation HAL à la fois en passthrough et en liaison.
Étant donné le type IFoo
, vous pouvez appeler sp<IFoo>
IFoo::getService(string name, bool getStub)
pour accéder à une instance de IFoo
. Si getStub
est défini sur "true", getService
tente d'ouvrir le HAL uniquement en mode passthrough. Si getStub
est défini sur "false", getService
tente de trouver un service lié. Si cela échoue, il tente de trouver le service de passthrough. Le paramètre getStub
ne doit jamais être utilisé, sauf dans defaultPassthroughServiceImplementation
. (Les appareils lancés avec Android O sont des appareils entièrement liés. L'ouverture d'un service en mode passthrough n'est donc pas autorisée.)
Grammaire HIDL
Par conception, le langage HIDL est semblable au langage C (mais n'utilise pas le préprocesseur C). Tous les signes de ponctuation non décrits ci-dessous (à l'exception de l'utilisation évidente de =
et |
) font partie de la grammaire.
Remarque:Pour en savoir plus sur le style de code HIDL, consultez le guide de style de code.
/** */
indique un commentaire de documentation. Ils ne peuvent être appliqués qu'aux déclarations de type, de méthode, de champ et de valeur d'énumération./* */
indique un commentaire multiligne.//
indique un commentaire à la fin de la ligne. À l'exception de//
, les sauts de ligne sont identiques à tous les autres espaces blancs.- Dans l'exemple de grammaire ci-dessous, le texte de
//
à la fin de la ligne ne fait pas partie de la grammaire, mais constitue un commentaire sur la grammaire. [empty]
signifie que le terme peut être vide.?
suivi d'un littéral ou d'un terme signifie qu'il est facultatif....
indique une séquence contenant zéro ou plusieurs éléments avec des signes de ponctuation de séparation comme indiqué. Il n'y a pas d'arguments variadiques dans HIDL.- Les éléments de séquence sont séparés par des virgules.
- Les points-virgules terminent chaque élément, y compris le dernier.
- UPPERCASE est un non-terminal.
italics
est une famille de jetons tels queinteger
ouidentifier
(règles d'analyse C standard).constexpr
est une expression constante de style C (comme1 + 1
et1L << 3
).import_name
est un nom de package ou d'interface, qualifié comme décrit dans la section Versionnement HIDL.- Les
words
minuscules sont des jetons littéraux.
Exemple :
ROOT = PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal | PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions ITEM = ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?; | safe_union identifier { UFIELD; UFIELD; ...}; | struct identifier { SFIELD; SFIELD; ...}; // Note - no forward declarations | union identifier { UFIELD; UFIELD; ...}; | enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar | typedef TYPE identifier; VERSION = integer.integer; PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION; PREAMBLE = interface identifier EXTENDS EXTENDS = <empty> | extends import_name // must be interface, not package GENERATES = generates (FIELD, FIELD ...) // allows the Binder interface to be used as a type // (similar to typedef'ing the final identifier) IMPORTS = [empty] | IMPORTS import import_name; TYPE = uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t | float | double | bool | string | identifier // must be defined as a typedef, struct, union, enum or import // including those defined later in the file | memory | pointer | vec<TYPE> | bitfield<TYPE> // TYPE is user-defined enum | fmq_sync<TYPE> | fmq_unsync<TYPE> | TYPE[SIZE] FIELD = TYPE identifier UFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...}; | struct identifier { FIELD; FIELD; ...}; | union identifier { FIELD; FIELD; ...}; | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SIZE = // Must be greater than zero constexpr ANNOTATIONS = [empty] | ANNOTATIONS ANNOTATION ANNOTATION = | @identifier | @identifier(VALUE) | @identifier(ANNO_ENTRY, ANNO_ENTRY ...) ANNO_ENTRY = identifier=VALUE VALUE = "any text including \" and other escapes" | constexpr | {VALUE, VALUE ...} // only in annotations ENUM_ENTRY = identifier | identifier = constexpr