HIDL

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, dlopened 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.

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 fonction IFoo* HIDL_FETCH_IFoo(const char* name). Sur les anciens appareils, ce package est dlopen et l'implémentation est instanciée à l'aide de HIDL_FETCH_IFoo. Vous pouvez générer le code de base à l'aide de hidl-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 que integer ou identifier (règles d'analyse C standards).
  • constexpr est une expression constante de style C (telle que 1 + 1 et 1L << 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