HIDL

Le langage de définition d'interface HAL ou HIDL est un langage de description d'interface (IDL) permettant 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 bases de code pouvant être compilées indépendamment. Depuis Android 10, HIDL est obsolète et Android migre pour utiliser AIDL partout.

HIDL est destiné à être utilisé pour la communication inter-processus (IPC). Les HAL créés avec HDL sont appelés HAL binderisés dans le sens où ils peuvent communiquer avec d'autres couches d'architecture à l'aide d'appels de communication inter-processus (IPC) de binder. Les HAL groupés s'exécutent dans un processus distinct de celui du client qui les utilise. Pour les bibliothèques qui doivent être liées à un processus, un mode passthrough est également disponible (non pris en charge en Java).

HIDL spécifie les structures de données et les signatures de méthodes, organisées en interfaces (semblables à 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 :

liant Indique que HIDL est utilisé pour les appels de procédure à distance entre processus, implémentés via un mécanisme de type Binder. Voir aussi passage .
rappel, asynchrone Interface servie par un utilisateur HAL, transmise à HAL (à l'aide d'une méthode HIDL) et appelée par 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. Inutilisé pour les méthodes qui renvoient void ou une seule valeur primitive.
client Processus qui appelle les méthodes d’une interface particulière. Un processus framework HAL ou Android peut être client d’une interface et serveur d’une autre. Voir aussi passage .
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 un incrément de version mineur dans le même nom de package ou pour un nouveau package (par exemple une extension de fournisseur) pour construire sur 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 classe en C++ ou Java. Toutes les méthodes d'une interface sont appelées dans le même sens : un processus client invoque des méthodes implémentées par un processus serveur.
Sens Unique Lorsqu'elle est appliquée à une méthode HIDL, indique que la méthode ne renvoie aucune valeur et ne bloque pas.
emballer Collection d'interfaces et de types de données partageant une version.
traverser Mode de HIDL dans lequel le serveur est une bibliothèque partagée, dlopen par le client. En mode passthrough, le client et le serveur sont le même processus mais des bases de code distinctes. Utilisé uniquement pour intégrer les bases de code héritées dans le modèle HIDL. Voir aussi Binderisé .
serveur Processus qui implémente les méthodes d’une interface. Voir aussi passage .
transport Infrastructure HIDL qui déplace les données entre le serveur et le client.
version Version d'un package. Se compose de deux entiers, majeur et mineur. Des incréments de version mineurs peuvent ajouter (mais pas modifier) ​​des types et des méthodes.

Conception HIDL

L'objectif de HIDL est que le framework Android puisse être remplacé sans avoir à reconstruire les HAL. Les HAL seront construits par les fournisseurs ou les fabricants de SOC et placés dans une partition /vendor sur l'appareil, permettant au framework Android, dans sa propre partition, d'être remplacé par un OTA sans recompiler les HAL.

La conception HIDL équilibre les préoccupations suivantes :

  • Interopérabilité . Créez des interfaces interopérables de manière fiable entre les processus qui peuvent être compilés avec diverses architectures, chaînes d'outils et configurations de construction. Les interfaces HIDL sont versionnées et ne peuvent pas être modifiées après leur publication.
  • 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 présentation standard 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 quelque peu lents, HIDL prend en charge deux manières 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).
  • Intuitif . HIDL évite les problèmes épineux de propriété de la mémoire en utilisant uniquement in paramètres pour RPC (voir Android Interface Definition Language (AIDL) ) ; les valeurs qui ne peuvent pas être renvoyées efficacement par les méthodes sont renvoyées via des fonctions de rappel. Ni le transfert de données vers HIDL pour le transfert ni la réception de données depuis HIDL ne modifient la propriété des données : la propriété reste toujours la propriété de 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 relais

Pour mettre à jour les appareils exécutant des versions antérieures d'Android vers Android O, vous pouvez envelopper les HAL conventionnels (et hérités) dans une nouvelle interface HIDL qui sert le HAL en modes binderized et même processus (passthrough). Cet emballage est transparent à la fois pour le framework HAL et Android.

Le mode Passthrough est disponible uniquement pour les clients et les implémentations C++. Les appareils exécutant des versions antérieures d'Android n'ont pas de HAL écrits en Java, les HAL Java sont donc intrinsèquement binderisés.

Lorsqu'un fichier .hal est compilé, hidl-gen produit un fichier d'en-tête supplémentaire BsFoo.h en plus des en-têtes utilisés pour la communication du classeur ; cet en-tête définit les fonctions à dlopen . Comme les HAL passthrough s'exécutent dans le même processus dans lequel ils sont appelés, dans la plupart des cas, les méthodes passthrough sont invoqué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 (telles que l'exécution de transactions oneway dans un autre thread). Ce fichier est similaire à BpFoo.h , cependant au lieu de transmettre les appels IPC à l'aide de binder, les fonctions souhaitées sont directement invoquées. Les futures implémentations de HAL pourraient fournir plusieurs implémentations, telles que FooFast HAL et FooAccurate HAL. Dans de tels cas, un fichier pour chaque implémentation supplémentaire serait créé (par exemple, PTFooFast.cpp et PTFooAccurate.cpp ).

Liaison des HAL pass-through

Vous pouvez relier les implémentations HAL qui prennent en charge le mode passthrough. Étant donné une interface HAL abcd@MN::IFoo , deux packages sont créés :

  • abcd@MN::IFoo-impl . Contient l'implémentation de HAL et expose la fonction IFoo* HIDL_FETCH_IFoo(const char* name) . Sur les appareils existants, 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 en utilisant hidl-gen et -Lc++-impl et -Landroidbp-impl .
  • abcd@MN::IFoo-service . Ouvre le HAL passthrough et s'enregistre en tant que service binderisé, permettant à la même implémentation HAL d'être utilisée à la fois comme service passthrough et binderisé.

É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 vrai, getService tente d'ouvrir le HAL uniquement en mode relais. Si getStub est faux, getService tente de trouver un service binderisé ; si cela échoue, il essaie alors de trouver le service d'intercommunication. 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, donc l'ouverture d'un service en mode passthrough est interdite.)

grammaire HIDL

De par sa conception, le langage HIDL est similaire au C (mais n'utilise pas le préprocesseur C). Toute ponctuation non décrite ci-dessous (mis à part l'utilisation évidente de = et | ) fait partie de la grammaire.

Remarque : Pour plus de détails sur le style de code HIDL, consultez le Code Style Guide .

  • /** */ indique un commentaire de 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. Mis à part // , les nouvelles lignes sont les mêmes que n'importe quel autre espace.
  • Dans l'exemple de grammaire ci-dessous, le texte de // jusqu'à la fin de la ligne ne fait pas partie de la grammaire mais est plutôt un commentaire sur la grammaire.
  • [empty] signifie que le terme peut être vide.
  • ? suivre un littéral ou un terme signifie qu'il est facultatif.
  • ... indique une séquence contenant zéro ou plusieurs éléments avec une ponctuation de séparation comme indiqué. Il n'y a pas d'arguments variadiques dans HIDL.
  • Des virgules séparent les éléments de séquence.
  • Des points-virgules terminent chaque élément, y compris le dernier élément.
  • UPPERCASE est un non-terminal.
  • italics est une famille de jetons telle qu'un integer ou identifier (règles d'analyse C standard).
  • 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 HIDL Versioning .
  • 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