HIDL

Die HAL-Schnittstellendefinitionssprache oder HIDL ist eine Schnittstellenbeschreibungssprache (IDL) zur Spezifikation der Schnittstelle zwischen einem HAL und seinen Benutzern. HIDL ermöglicht die Angabe von Typen und Methodenaufrufen, zusammengefasst in Schnittstellen und Paketen. Im weiteren Sinne ist HIDL ein System zur Kommunikation zwischen Codebasen, die unabhängig kompiliert werden können. Ab Android 10 ist HIDL veraltet und Android migriert, um AIDL überall zu verwenden.

HIDL soll für die Interprozesskommunikation (IPC) verwendet werden. Mit HDL erstellte HALs werden als binderisierte HALs bezeichnet, da sie über IPC-Aufrufe (Binder 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, steht auch ein Passthrough-Modus zur Verfügung (wird in Java nicht unterstützt).

HIDL spezifiziert Datenstrukturen und Methodensignaturen, organisiert in Schnittstellen (ähnlich einer Klasse), die in Paketen gesammelt werden. Die Syntax von HIDL kommt C++- und Java-Programmierern bekannt vor, weist jedoch einen anderen Satz an Schlüsselwörtern auf. HIDL verwendet auch Annotationen im Java-Stil.

Terminologie

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

gebunden Zeigt an, dass HIDL für Remote-Prozeduraufrufe zwischen Prozessen verwendet wird, implementiert über einen Binder-ähnlichen Mechanismus. Siehe auch Passthrough .
Rückruf, asynchron Von einem HAL-Benutzer bediente Schnittstelle, die an die HAL übergeben wird (mithilfe einer HIDL-Methode) und von der HAL aufgerufen wird, um jederzeit Daten zurückzugeben.
Rückruf, synchron Gibt Daten von 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.
Klient 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 .
erweitert 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 Nebenversionserweiterung im gleichen Paketnamen oder für ein neues Paket (z. B. eine Herstellererweiterung) verwendet werden, das auf einem älteren Paket aufbaut.
erzeugt Gibt eine Schnittstellenmethode an, die Werte an den Client zurückgibt. Um einen nicht-primitiven Wert oder mehr als einen Wert zurückzugeben, wird eine synchrone Rückruffunktion generiert.
Schnittstelle Sammlung von Methoden und Typen. Übersetzt in eine Klasse in C++ oder Java. Alle Methoden in einer Schnittstelle werden in die gleiche Richtung aufgerufen: Ein Clientprozess ruft Methoden auf, die von einem Serverprozess implementiert werden.
Ein Weg Bei Anwendung auf eine HIDL-Methode gibt dies an, dass die Methode keine Werte zurückgibt und nicht blockiert.
Paket Sammlung von Schnittstellen und Datentypen, die eine Version teilen.
Passthrough HIDL-Modus, bei dem der Server eine gemeinsam genutzte Bibliothek ist, dlopen . Im Passthrough-Modus sind Client und Server derselbe Prozess, aber unterschiedliche Codebasen. Wird nur verwendet, um ältere Codebasen in das HIDL-Modell zu integrieren. Siehe auch Binderized .
Server Prozess, der Methoden einer Schnittstelle implementiert. Siehe auch Passthrough .
Transport HIDL-Infrastruktur, die Daten zwischen Server und Client bewegt.
Ausführung Version eines Pakets. Besteht aus zwei ganzen Zahlen, einer großen und einer kleinen Zahl. Kleinere Versionsinkremente können Typen und Methoden hinzufügen (jedoch nicht ändern).

HIDL-Design

Das Ziel von HIDL besteht darin, 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, sodass das Android-Framework in seiner eigenen Partition durch ein OTA ersetzt werden kann, ohne die HALs neu zu kompilieren.

Das HIDL-Design gleicht die folgenden Bedenken aus:

  • Interoperabilität . Erstellen Sie zuverlässig interoperable Schnittstellen zwischen Prozessen, die mit verschiedenen Architekturen, Toolchains und Build-Konfigurationen kompiliert werden können. HIDL-Schnittstellen sind versioniert 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++-Standardlayout-Datenstrukturen an C++-Code übermittelt, die ohne Entpacken verwendet werden können. HIDL bietet auch Shared-Memory-Schnittstellen und da RPCs von Natur aus etwas langsam sind, unterstützt HIDL zwei Möglichkeiten zur Datenübertragung ohne Verwendung eines RPC-Aufrufs: Shared Memory und eine Fast Message Queue (FMQ).
  • Intuitiv . HIDL vermeidet heikle Probleme des Speicherbesitzes, indem es nur in für RPC verwendet (siehe Android Interface Definition Language (AIDL) ); Werte, die von Methoden nicht effizient zurückgegeben werden können, werden über Callback-Funktionen zurückgegeben. Weder die Übergabe von Daten an HIDL zur Übertragung noch der Empfang von Daten von HIDL ändert den Besitz der Daten – der Besitz verbleibt immer bei der aufrufenden Funktion. Daten müssen nur für die Dauer der aufgerufenen Funktion bestehen bleiben und können unmittelbar nach der Rückkehr der aufgerufenen Funktion zerstört werden.

Verwenden des Passthrough-Modus

Um Geräte, auf denen frühere Android-Versionen ausgeführt werden, auf Android O zu aktualisieren, können Sie sowohl herkömmliche (als auch ältere) HALs in eine neue HIDL-Schnittstelle einschließen, die die HAL im binderisierten Modus und im Gleichprozessmodus (Passthrough) bereitstellt. Diese Umhüllung ist sowohl für das HAL- als auch für das Android-Framework transparent.

Der Passthrough-Modus ist nur für C++-Clients und -Implementierungen verfügbar. Geräte, auf denen frühere Android-Versionen ausgeführt werden, verfügen nicht über in Java geschriebene HALs, daher sind Java-HALs von Natur aus gebunden.

Wenn eine .hal Datei kompiliert wird, erzeugt hidl-gen zusätzlich zu den für die Binder-Kommunikation verwendeten Headern eine zusätzliche Passthrough-Header-Datei BsFoo.h ; Dieser Header definiert die dlopen Funktionen. Da Passthrough-HALs im selben Prozess ausgeführt werden, in dem sie aufgerufen werden, werden Passthrough-Methoden in den meisten Fällen durch einen direkten Funktionsaufruf (derselbe Thread) aufgerufen. oneway Methoden werden in ihrem 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, Thread-sicher sein muss).

Bei gegebenem IFoo.hal umschließt BsFoo.h die von HIDL generierten Methoden, um zusätzliche Funktionen bereitzustellen (z. B. die Ausführung von oneway in einem anderen Thread). Diese Datei ähnelt BpFoo.h , jedoch werden die gewünschten Funktionen direkt aufgerufen, anstatt IPC-Aufrufe mithilfe von Binder weiterzuleiten. Zukünftige Implementierungen von HALs können mehrere Implementierungen bereitstellen , beispielsweise FooFast HAL und FooAccurate HAL. In solchen Fällen würde eine Datei für jede zusätzliche Implementierung erstellt werden (z. B. PTFooFast.cpp und PTFooAccurate.cpp ).

Bindung von Passthrough-HALs

Sie können HAL-Implementierungen binden, die den Passthrough-Modus unterstützen. Bei einer HAL-Schnittstelle abcd@MN::IFoo werden zwei Pakete erstellt:

  • abcd@MN::IFoo-impl . Enthält die Implementierung des HAL und macht die Funktion IFoo* HIDL_FETCH_IFoo(const char* name) verfügbar. Auf älteren Geräten wird dieses Paket dlopen und die Implementierung wird mit HIDL_FETCH_IFoo instanziiert. Sie können den Basiscode mit hidl-gen und -Lc++-impl und -Landroidbp-impl generieren.
  • abcd@MN::IFoo-service . Öffnet den Passthrough-HAL und registriert sich selbst als binderisierter Dienst, sodass dieselbe HAL-Implementierung sowohl als Passthrough- als auch als binderisierter Dienst verwendet werden kann.

Bei gegebenem Typ IFoo 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 den Wert false hat, versucht getService , einen gebundenen Dienst zu finden. Wenn dies fehlschlägt, wird versucht, den Passthrough-Dienst zu finden. Der getStub Parameter sollte niemals verwendet werden, außer in defaultPassthroughServiceImplementation . (Geräte, die mit Android O gestartet werden, sind vollständig binderisierte Geräte, daher ist das Öffnen eines Dienstes im Passthrough-Modus nicht zulässig.)

HIDL-Grammatik

Vom Design her ähnelt die HIDL-Sprache C (verwendet jedoch nicht den C-Präprozessor). Alle nachfolgend nicht beschriebenen Zeichensetzungen (abgesehen von der offensichtlichen Verwendung von = und | ) sind Teil der Grammatik.

Hinweis: Einzelheiten zum HIDL-Codestil finden Sie im Code Style Guide .

  • /** */ gibt einen Dokumentationskommentar an. Diese können nur auf Typ-, Methoden-, Feld- und Enum-Wertdeklarationen angewendet werden.
  • /* */ gibt einen mehrzeiligen Kommentar an.
  • // gibt einen Kommentar zum Zeilenende an. Abgesehen von // sind Zeilenumbrüche dasselbe wie alle anderen Leerzeichen.
  • In der folgenden Beispielgrammatik ist der Text von // bis zum Zeilenende nicht Teil der Grammatik, sondern ein Kommentar zur Grammatik.
  • [empty] bedeutet, dass der Begriff leer sein kann.
  • ? Das Folgen eines Literals oder Begriffs bedeutet, dass es optional ist.
  • ... gibt eine Sequenz an, die null oder mehr Elemente mit trennender Interpunktion wie angegeben enthält. In HIDL gibt es keine variadischen Argumente.
  • Kommas trennen Sequenzelemente.
  • Semikolons beenden jedes Element, einschließlich des letzten Elements.
  • UPPERCASE ist ein Nichtterminal.
  • italics ist eine Token-Familie wie integer oder identifier (Standard-C-Parsing-Regeln).
  • constexpr ist ein konstanter Ausdruck im C-Stil (z. B. 1 + 1 und 1L << 3 ).
  • import_name ist ein Paket- oder Schnittstellenname, qualifiziert wie in HIDL-Versionierung beschrieben.
  • Kleingeschriebene words sind wörtliche Zeichen.

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