HIDL

HAL Interface Definition Language (HIDL) ist eine Interface Description Language (IDL), mit der die Schnittstelle zwischen einer HAL und ihren Nutzern angegeben wird. Mit HIDL können Typen und Methodenaufrufe angegeben werden, die in Schnittstellen und Paketen zusammengefasst werden. Im weiteren Sinne ist HIDL ein System zur Kommunikation zwischen Codebases, die unabhängig kompiliert werden können.

HIDL ist für die prozessübergreifende Kommunikation (Inter-Process Communication, IPC) vorgesehen. HALs, die mit HDL erstellt wurden, werden als gebundene HALs bezeichnet, da sie über Binder-IPC-Aufrufe (Inter-Process Communication) mit anderen Architekturschichten kommunizieren können. Binderisierte HALs werden in einem separaten Prozess vom Client ausgeführt, der sie verwendet. Für Bibliotheken, die mit einem Prozess verknüpft werden müssen, ist auch ein Durchlassmodus verfügbar (wird in Java nicht unterstützt).

HIDL gibt Datenstrukturen und Methodensignaturen an, die in Schnittstellen (ähnlich einer Klasse) organisiert und in Paketen zusammengefasst sind. Die Syntax von HIDL ist C++- und Java-Programmierern vertraut, verwendet aber eine andere Reihe von Schlüsselwörtern. HIDL verwendet auch Java-Annotationen.

Terminologie

In diesem Abschnitt werden die folgenden HIDL-bezogenen Begriffe verwendet:

gebunden Gibt an, dass HIDL für Remote-Prozeduraufrufe zwischen Prozessen verwendet wird, die über einen Binder-ähnlichen Mechanismus implementiert werden. Siehe auch Passthrough.
callback, asynchronous Eine von einem HAL-Nutzer bereitgestellte Schnittstelle, die an die HAL (über eine HIDL-Methode) übergeben und von der HAL jederzeit aufgerufen wird, um Daten zurückzugeben.
callback, synchronous Gibt Daten aus der HIDL-Methodenimplementierung eines Servers an den Client zurück. Wird nicht für Methoden verwendet, die „void“ oder einen einzelnen primitiven Wert zurückgeben.
client Prozess, der Methoden einer bestimmten Schnittstelle aufruft. Ein HAL- oder Android-Framework-Prozess kann ein Client einer Schnittstelle und ein Server einer anderen sein. Siehe auch Passthrough.
extends Gibt eine Schnittstelle an, die einer anderen Schnittstelle Methoden und/oder Typen hinzufügt. Eine Schnittstelle kann nur eine andere Schnittstelle erweitern. Kann für eine Nebenversionssteigerung mit demselben Paketnamen oder für ein neues Paket (z.B. eine Anbietererweiterung) verwendet werden, das auf einem älteren Paket basiert.
generiert Gibt eine Schnittstellenmethode an, die Werte an den Client zurückgibt. Wenn ein nicht primitiver Wert oder mehrere Werte zurückgegeben werden sollen, wird eine synchrone Callback-Funktion generiert.
interface Sammlung von Methoden und Typen. In eine Klasse in C++ oder Java übersetzt. Alle Methoden in einer Schnittstelle werden in dieselbe Richtung aufgerufen: Ein Clientprozess ruft Methoden auf, die von einem Serverprozess implementiert wurden.
Nur Hinflug Wenn diese Option auf eine HIDL-Methode angewendet wird, gibt sie an, dass die Methode keine Werte zurückgibt und nicht blockiert.
Paket Sammlung von Schnittstellen und Datentypen, die dieselbe Version haben.
Durchschleifen HIDL-Modus, bei dem der Server eine gemeinsam genutzte Bibliothek ist, die vom Client dlopenwird. Im Passthrough-Modus sind Client und Server derselbe Prozess, haben aber separate Codebases. Wird nur verwendet, um alte Codebases in das HIDL-Modell zu übertragen. Weitere Informationen finden Sie unter Binderized.
Server Prozess, der Methoden einer Schnittstelle implementiert. Siehe auch Passthrough.
Verkehrsmittel HIDL-Infrastruktur, die Daten zwischen Server und Client überträgt.
Version Version eines Pakets. Sie besteht aus zwei Ganzzahlen, Major und Minor. Bei kleineren Versionsschritten können Typen und Methoden hinzugefügt, aber nicht geändert werden.

HIDL-Design

Ziel von HIDL ist es, dass das Android-Framework ersetzt werden kann, ohne dass HALs neu erstellt werden müssen. HALs werden von Anbietern oder SoC-Herstellern erstellt und in einer /vendor-Partition auf dem Gerät abgelegt. So kann das Android-Framework in seiner eigenen Partition durch ein Over-the-air-Update ersetzt werden, ohne dass die HALs neu kompiliert werden müssen.

Beim HIDL-Design werden die folgenden Aspekte berücksichtigt:

  • Interoperabilität Zuverlässig interoperable Schnittstellen zwischen Prozessen erstellen, die mit verschiedenen Architekturen, Toolchains und Build-Konfigurationen kompiliert werden können. HIDL-Schnittstellen haben Versionen und können nach der Veröffentlichung nicht mehr geändert werden.
  • Effizienz. HIDL versucht, die Anzahl der Kopiervorgänge zu minimieren. HIDL-definierte Daten werden in C++-Code in C++-Standardlayout-Datenstrukturen übergeben, die ohne Entpacken verwendet werden können. HIDL bietet auch Shared-Memory-Schnittstellen. Da RPCs von Natur aus etwas langsam sind, unterstützt HIDL zwei Möglichkeiten zur Datenübertragung ohne RPC-Aufruf: Shared Memory und eine Fast Message Queue (FMQ).
  • Intuitiv. HIDL vermeidet heikle Probleme mit der Speichereigentümerschaft, indem nur in-Parameter für RPC verwendet werden (siehe Android Interface Definition Language (AIDL)). Werte, die nicht effizient aus Methoden zurückgegeben werden können, werden über Callback-Funktionen zurückgegeben. Weder das Übergeben von Daten an HIDL zur Übertragung noch das Empfangen von Daten von HIDL ändert den Inhaber der Daten. Die Inhaberschaft verbleibt immer bei der aufrufenden Funktion. Die Daten müssen nur für die Dauer der aufgerufenen Funktion gespeichert bleiben und können sofort nach der Rückkehr der aufgerufenen Funktion gelöscht werden.

Passthrough-Modus verwenden

Wenn Sie Geräte mit älteren Android-Versionen auf Android O aktualisieren möchten, können Sie sowohl herkömmliche als auch alte HALs in eine neue HIDL-Schnittstelle einbinden, die die HAL im Binder- und im Modus „Im selben Prozess“ (Passthrough) bereitstellt. Diese Umhüllung ist sowohl für die HAL als auch für das Android-Framework transparent.

Der Passthrough-Modus ist nur für C++-Clients und ‑Implementierungen verfügbar. Geräte mit älteren Android-Versionen haben keine in Java geschriebenen HALs. Daher sind Java-HALs von Natur aus gebunden.

Wenn eine .hal-Datei kompiliert wird, generiert hidl-gen zusätzlich zu den Headern, die für die Binderkommunikation verwendet werden, eine zusätzliche Passthrough-Headerdatei BsFoo.h. Dieser Header definiert Funktionen, die dlopened werden sollen. Da Passthrough-HALs im selben Prozess ausgeführt werden, in dem sie aufgerufen werden, werden Passthrough-Methoden in den meisten Fällen durch direkten Funktionsaufruf (im selben Thread) aufgerufen. oneway-Methoden werden in einem eigenen Thread ausgeführt, da sie nicht darauf warten sollen, dass die HAL sie verarbeitet. Das bedeutet, dass jede HAL, die oneway-Methoden im Passthrough-Modus verwendet, threadsicher sein muss.

Wenn IFoo.hal übergeben wird, umhüllt BsFoo.h die von HIDL generierten Methoden, um zusätzliche Funktionen bereitzustellen, z. B. die Ausführung von oneway-Transaktionen in einem anderen Thread. Diese Datei ähnelt BpFoo.h. Anstatt IPC-Aufrufe über den Binder weiterzuleiten, werden die gewünschten Funktionen jedoch direkt aufgerufen. Künftige Implementierungen von HALs können mehrere Implementierungen bereitstellen, z. B. die HAL „FooFast“ und die HAL „FooAccurate“. In solchen Fällen wird für jede zusätzliche Implementierung eine Datei erstellt (z.B. PTFooFast.cpp und PTFooAccurate.cpp).

Passthrough-HALs binden

Sie können HAL-Implementierungen, die den Passthrough-Modus unterstützen, binden. Für eine HAL-Schnittstelle a.b.c.d@M.N::IFoo werden zwei Pakete erstellt:

  • a.b.c.d@M.N::IFoo-impl: Enthält die Implementierung der HAL und stellt die Funktion IFoo* HIDL_FETCH_IFoo(const char* name) bereit. Auf älteren Geräten wird dieses Paket dlopen und die Implementierung mit HIDL_FETCH_IFoo instanziiert. Sie können den Basiscode mit hidl-gen, -Lc++-impl und -Landroidbp-impl generieren.
  • a.b.c.d@M.N::IFoo-service. Öffnet die Passthrough-HAL und registriert sich als gebundener Dienst, sodass dieselbe HAL-Implementierung sowohl als Passthrough- als auch als gebundener Dienst verwendet werden kann.

Wenn Sie den Typ IFoo angeben, können Sie sp<IFoo> IFoo::getService(string name, bool getStub) aufrufen, um Zugriff auf eine Instanz von IFoo zu erhalten. Wenn getStub wahr ist, versucht getService, die HAL nur im Passthrough-Modus zu öffnen. Wenn getStub „false“ ist, versucht getService, einen gebundenen Dienst zu finden. Wenn das fehlschlägt, wird versucht, den Passthrough-Dienst zu finden. Der Parameter getStub sollte nur in defaultPassthroughServiceImplementation verwendet werden. Geräte, die mit Android O ausgeliefert werden, sind vollständig gebunden. Das Öffnen eines Dienstes im Passthrough-Modus ist daher nicht zulässig.

HIDL-Grammatik

Die HIDL-Sprache ähnelt C, verwendet aber keinen C-Preprozessor. Alle Zeichensetzung, die unten nicht beschrieben wird (abgesehen von der offensichtlichen Verwendung von = und |), ist Teil der Grammatik.

Hinweis:Weitere Informationen zum HIDL-Codestil finden Sie im Code-Styleguide.

  • /** */ steht für einen Dokumentationskommentar. Sie können nur auf Deklarationen von Typ, Methode, Feld und Enum-Wert angewendet werden.
  • /* */ steht für einen mehrzeiligen Kommentar.
  • // gibt einen Kommentar bis zum Ende der Zeile an. Abgesehen von // sind Zeilenumbrüche wie jedes andere Leerzeichen.
  • In der Beispielgrammatik unten ist der Text von // bis zum Ende der Zeile kein Teil der Grammatik, sondern ein Kommentar zur Grammatik.
  • [empty] bedeutet, dass der Begriff leer sein kann.
  • Wenn nach einem Literal oder Begriff ? steht, ist es optional.
  • ... gibt eine Sequenz mit null oder mehr Elementen mit den angegebenen Satzzeichen an. In HIDL gibt es keine variadischen Argumente.
  • Sequenzelemente werden durch Kommas getrennt.
  • Jedes Element, auch das letzte, wird durch einen Semikolon abgeschlossen.
  • GROẞBUCHSTABEN ist ein Nichtterminal.
  • italics ist eine Tokenfamilie wie integer oder identifier (Standard-C-Parsing-Regeln).
  • constexpr ist ein konstanter Ausdruck im C-Format (z. B. 1 + 1 und 1L << 3).
  • import_name ist ein Paket- oder Schnittstellenname, der wie unter HIDL-Versionierung beschrieben qualifiziert ist.
  • Kleingeschriebene words sind Literal-Token.

Beispiel:

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