HIDL

Le langage de définition d'interface HAL ou HIDL est un langage de description d'interface (IDL) pour 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 qui peuvent ê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éées avec HDL sont appelées HAL bindérisées en ce sens qu'elles peuvent communiquer avec d'autres couches d'architecture à l'aide d'appels de communication inter-processus (IPC) de liant. Les HAL bindérisés s'exécutent dans un processus distinct 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éthode, organisées en interfaces (semblables à une classe) qui sont rassemblé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 liés à HIDL suivants :

liant Indique que HIDL est utilisé pour les appels de procédure à distance entre les processus, implémentés sur un mécanisme de type Binder. Voir aussi relais .
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 de framework HAL ou Android peut être un client d'une interface et un serveur d'une autre. Voir aussi relais .
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) à 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 une classe en C++ ou Java. Toutes les méthodes d'une interface sont appelées dans le même sens : un processus client appelle des méthodes implémentées par un processus serveur.
une manière Lorsqu'il est appliqué à une méthode HIDL, indique que la méthode ne renvoie aucune valeur et ne bloque pas.
forfait 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 relais .
le transport Infrastructure HIDL qui déplace les données entre le serveur et le client.
version Version d'un paquet. 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 HID

L'objectif de HIDL est que le framework Android puisse être remplacé sans avoir à reconstruire les HAL. Les HAL seront construits par des fournisseurs ou des 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 livrées au code C++ dans des structures de données de mise en page 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 un peu 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 rapide de messages (FMQ).
  • Intuitif . HIDL évite les problèmes épineux de propriété de la mémoire en n'utilisant que 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 la transmission de données dans 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 avec la fonction appelante. Les données doivent persister uniquement 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.

Utilisation du 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 habillage est transparent à la fois pour HAL et pour le framework 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, de sorte que les HAL Java sont intrinsèquement lié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 elles sont appelées, dans la plupart des cas, les méthodes 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 HAL les traite (cela signifie que toute HAL qui utilise des méthodes unidirectionnelles en mode oneway doit être thread-safe).

Étant donné un IFoo.hal , BsFoo.h encapsule les méthodes générées par oneway pour fournir des fonctionnalités supplémentaires (telles que l'exécution de transactions à sens unique 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 appelées. Les futures implémentations de HAL peuvent 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 ).

Binding passthrough HALs

Vous pouvez binderiser 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 hérités, ce package est dlopen et l'implémentation est instanciée à l'aide HIDL_FETCH_IFoo . Vous pouvez générer le code de base en utilisant hidl-gen et -Lc++-impl impl et -Landroidbp-impl .
  • abcd@MN::IFoo-service . Ouvre le relais HAL et s'enregistre en tant que service lié, permettant à la même implémentation HAL d'être utilisée à la fois comme relais et lié.

É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 la couche HAL uniquement en mode relais. Si getStub est faux, getService tente de trouver un service lié ; si cela échoue, il essaie alors de trouver le service de relais. Le paramètre getStub ne doit jamais être utilisé sauf dans defaultPassthroughServiceImplementation . (Les appareils lancés avec Android O sont des appareils entièrement binderisés, donc l'ouverture d'un service en mode relais n'est pas autorisée.)

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 (à 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 retours à la ligne sont les mêmes que tout autre espace blanc.
  • Dans l'exemple de grammaire ci-dessous, le texte de // à 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.
  • Les virgules séparent les éléments de séquence.
  • Les points-virgules terminent chaque élément, y compris le dernier élément.
  • MAJUSCULE est un non-terminal.
  • l' italics est une famille de jetons telle qu'un integer ou un 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 package ou un nom d'interface, qualifié comme décrit dans HIDL Versioning .
  • Les words en 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