Le langage de définition d'interface HAL, ou HIDL, est un langage de description d'interface (IDL, Interface Description Language) permettant de spécifier l'interface entre une 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 généralement, HIDL est un système de communication entre codebases qui peut être compilé indépendamment.
Le protocole HIDL est destiné à la communication inter-processus (IPC). Les HAL créées avec des HDL sont appelées HAL liées, car elles peuvent communiquer avec d'autres couches d'architecture à l'aide d'appels de communication inter-processus (IPC) de liaison. Les HAL avec liaison s'exécutent dans un processus distinct de celui du client qui les utilise. Pour les bibliothèques devant être associées à un processus, un mode 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 (semblable à une classe) qui sont collectées dans des packages. La syntaxe 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 au HIDL:
lié | Indique que HIDL est utilisé pour les appels de procédure à distance entre les processus, mis en œuvre via un mécanisme de type Binder. Voir aussi passthrough. |
---|---|
rappel, asynchrone | Interface diffusée par un utilisateur HAL, transmise au HAL (à l'aide d'une méthode HIDL) et appelée par celle-ci pour renvoyer des données à tout moment. |
rappel, synchrone | Renvoie au client les données issues de l'implémentation de la méthode HIDL d'un serveur. Cet attribut n'est pas utilisé pour les méthodes qui renvoient une valeur nulle ou une seule valeur primitive. |
client | Processus qui appelle les méthodes d'une interface particulière. Un processus HAL ou de framework Android peut être le client d'une interface et le serveur d'une autre. Voir aussi contournement. |
é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ée pour un incrément de version mineur dans le même nom de package ou pour un nouveau package (par exemple, une extension de fournisseur) afin de compiler à partir d'un package plus ancien. |
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 une classe C++ ou Java. Toutes les méthodes d'une interface sont appelées dans la même direction: un processus client appelle des méthodes implémentées par un processus serveur. |
aller simple | Lorsqu'elle est appliquée à 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 des codebases distincts. Utilisé uniquement pour intégrer d'anciens codebases dans le modèle HIDL.
Voir aussi la section Binderized. |
serveur | Processus qui implémente les méthodes d'une interface. Voir aussi contournement. |
transport | Infrastructure HIDL qui déplace les données entre le serveur et le client. |
version | Version d'un package. Il se compose de deux entiers, majeur et mineur. Des incréments mineurs de version peuvent ajouter des types et des méthodes (mais pas les modifier). |
Conception HIDL
L'objectif de HIDL est de permettre de remplacer le framework Android sans avoir à recompiler les HAL. Les HAL seront créées par des fournisseurs ou des fabricants de SOC et placées dans une partition /vendor
sur l'appareil, ce qui permettra au framework Android, dans sa propre partition, d'être remplacé par une OTA sans recompiler les HAL.
La conception HIDL permet de contrebalancer les problèmes suivants:
- Interopérabilité. Créez des interfaces interopérables de manière fiable entre les processus qui peuvent être compilées avec différentes architectures, chaînes d'outils et configurations de compilation. Les interfaces HIDL sont gérées par version et ne peuvent pas être modifiées après leur publication.
- Efficacité. HIDL tente 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 standards C++ qui peuvent être utilisées sans décompression. HIDL fournit également des interfaces de mémoire partagée et, comme les RPC sont intrinsèquement lents, HIDL prend en charge deux façons de transférer des données sans utiliser d'appel RPC: la mémoire partagée et une file d'attente de messages rapide (FMQ, Fast Message Queue).
- Intuitif. HIDL évite les problèmes épineux de propriété de la mémoire en n'utilisant que des paramètres
in
pour RPC (consultez 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 la transmission de données à HIDL pour le transfert, ni la réception de données de HIDL ne modifient la propriété des données. La propriété reste toujours associée à 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 à jour les appareils exécutant des versions antérieures d'Android vers Android O, vous pouvez encapsuler les HAL classiques (et anciens) dans une nouvelle interface HIDL qui diffuse le HAL en mode lié et en mode même processus (passthrough). Cette encapsulation est transparente pour le HAL et le framework Android.
Le mode passthrough n'est disponible que pour les clients et les implémentations C++. Les HAL des appareils exécutant des versions antérieures d'Android ne disposent pas de HAL écrites en Java. Par conséquent, les HAL Java sont intrinsèquement liées.
Fichiers d'en-tête Passthrough
Lorsqu'un fichier .hal
est compilé, hidl-gen
génère un fichier d'en-tête passthrough supplémentaire BsFoo.h
en plus des en-têtes utilisés pour la communication de liaison. Cet en-tête définit les fonctions à dlopen
. Étant donné que les HAL passthrough s'exécutent dans le même processus que celui où elles sont appelées, les méthodes de passthrough sont généralement 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 toute HAL qui utilise des méthodes oneway
en mode passthrough doit être thread-safe).
Pour un IFoo.hal
donné, BsFoo.h
encapsule les méthodes générées par HIDL pour fournir des fonctionnalités supplémentaires (par exemple, l'exécution des transactions oneway
dans un autre thread). Ce fichier est semblable à BpFoo.h
, mais au lieu de transmettre des appels IPC à l'aide de binder, les fonctions souhaitées sont directement appelées. Les futures implémentations d'HAL pourront inclure plusieurs implémentations, telles que FooFast HAL et FooBefore HAL. Dans ce cas, un fichier est créé pour chaque implémentation supplémentaire (par exemple, PTFooFast.cpp
et PTFooAccurate.cpp
).
Binderging Passthrough HAL (HAL)
Vous pouvez associer des implémentations HAL compatibles avec le mode passthrough. Avec l'interface a.b.c.d@M.N::IFoo
de HAL, 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
, de-Lc++-impl
et de-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 pour le passthrough et la liaison.
Compte tenu du 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é. En cas d'échec, il tente de trouver le service passthrough. Le paramètre getStub
ne doit jamais être utilisé, sauf dans defaultPassthroughServiceImplementation
. (Les appareils lancés avec Android O sont entièrement liés. Par conséquent, l'ouverture d'un service en mode passthrough n'est pas autorisée.)
Grammaire HIDL
De par sa 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 sur la documentation. Celles-ci ne peuvent être appliquées qu'aux déclarations de type, de méthode, de champ et de valeur d'énumération./* */
indique un commentaire multiligne.//
indique un commentaire en fin de ligne. Hormis//
, les sauts de ligne sont identiques à tous les autres espaces blancs.- Dans l'exemple de grammaire ci-dessous, le texte compris entre
//
et la fin de la ligne ne fait pas partie de la grammaire, mais constitue un commentaire sur celle-ci. [empty]
signifie que le terme peut être vide.?
après un littéral ou un terme signifie qu'il est facultatif....
indique une séquence contenant zéro ou plusieurs éléments, avec des signes de ponctuation séparés, comme indiqué. Il n'y a pas d'arguments variables en HIDL.- Les éléments de séquence sont séparés par des virgules.
- Un point-virgule termine chaque élément, y compris le dernier.
- MAJUSCULES est un élément non final.
italics
est une famille de jetons telle queinteger
ouidentifier
(règles d'analyse C standards).constexpr
est une expression constante de style C (telle que1 + 1
et1L << 3
).import_name
est un nom de package ou d'interface, qualifié comme décrit dans la section Gestion des versions 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