HIDL

HAL-Schnittstellendefinitionssprache (HIDL) ist eine Interface Description Language (IDL), um die Schnittstelle zwischen einem HAL und seinen Nutzern anzugeben. Mit HIDL können Typen und Methodenaufrufe angegeben werden, die in Schnittstellen und Paketen erfasst werden. Im Allgemeinen ist HIDL ein System zur Kommunikation zwischen Codebasen, die unabhängig kompiliert werden können.

HIDL ist für die Inter-Process-Kommunikation (IPC) vorgesehen. Mit HDL erstellte HALs werden als binderisierte HALs bezeichnet, da sie über IPC-Aufrufe (binder Inter-Process Communication) mit anderen Architekturebenen kommunizieren können. Gebundene HALs werden in einem anderen Prozess als der Client ausgeführt, der sie verwendet. Für Bibliotheken, die mit einem Prozess verknüpft sein müssen, ist auch ein Passthrough-Modus verfügbar, der in Java nicht unterstützt wird.

HIDL gibt Datenstrukturen und Methodensignaturen an, die (ähnlich einer Klasse) in Schnittstellen organisiert sind und in Paketen gesammelt werden. Die Syntax von HIDL sieht C++- und Java-Programmierern vertraut aus, hat aber einen anderen Satz von Suchbegriffen. HIDL verwendet auch Anmerkungen im Java-Stil.

Terminologie

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

binderhaltig Gibt an, dass HIDL für Remote-Prozeduraufrufe zwischen Prozessen verwendet wird und über einen Binder-ähnlichen Mechanismus implementiert wird. Siehe auch Passthrough.
Callback, asynchron Schnittstelle, die von einem HAL-Nutzer bereitgestellt wird, an den HAL (mit einer HIDL-Methode) übergeben und vom HAL aufgerufen wird, um jederzeit Daten zurückzugeben.
Callback, synchron Gibt Daten von der HIDL-Methodenimplementierung eines Servers an den Client zurück. Nicht verwendet für Methoden, die void oder einen einzelnen primitiven Wert zurückgeben.
client, kunde 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 Methoden und/oder Typen einer anderen Schnittstelle hinzufügt. Eine Schnittstelle kann nur eine andere Schnittstelle erweitern. Kann für ein Nebenversionsinkrement im selben Paketnamen oder für ein neues Paket (z.B. eine Anbietererweiterung) verwendet werden, das auf einem älteren Paket aufbaut.
generieren 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 Callback-Funktion generiert.
benutzeroberfläche 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 wurden.
Nur Hinflug Bei Anwendung auf eine HIDL-Methode gibt die Methode an, dass sie keine Werte zurückgibt und nicht blockiert wird.
Paket Sammlung von Schnittstellen und Datentypen mit derselben Version.
Durchschleifen Modus von HIDL, bei dem der Server eine gemeinsam genutzte Bibliothek ist, die vom Client dlopenedient wird. Im Passthrough-Modus sind Client und Server der gleiche Prozess, aber unterschiedliche Codebasen. Wird nur verwendet, um Legacy-Codebasen in das HIDL-Modell zu importieren. Siehe auch Binderized.
Server Prozess, der Methoden einer Schnittstelle implementiert. Siehe auch Passthrough.
Verkehrsmittel HIDL-Infrastruktur, die Daten zwischen dem Server und dem Client verschiebt
Version Version eines Pakets. Besteht aus zwei Ganzzahlen, groß und klein. Mit Inkrementen von Nebenversionen können Typen und Methoden hinzugefügt, aber nicht geändert werden.

HIDL-Design

Das Ziel von HIDL ist es, dass das Android-Framework ersetzt werden kann, ohne HALs neu erstellen zu müssen. HALs werden von Anbietern oder SOC-Herstellern erstellt und in eine /vendor-Partition auf dem Gerät eingefügt. Dadurch kann das Android-Framework in seiner eigenen Partition durch ein OTA-System ersetzt werden, ohne die HALs neu kompilieren zu müssen.

Das HIDL-Design bietet einen Ausgleich zu den folgenden Anforderungen:

  • 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 ihrer Veröffentlichung nicht mehr geändert werden.
  • Effizienz: HIDL versucht, die Anzahl der Kopiervorgänge zu minimieren. HIDL-definierte Daten werden an C++-Code in C++-Standardlayoutdatenstrukturen geliefert, die ohne Entpacken verwendet werden können. HIDL bietet auch Schnittstellen für gemeinsamen Speicher. Da RPCs grundsätzlich etwas langsam sind, unterstützt HIDL zwei Möglichkeiten zur Übertragung von Daten ohne RPC-Aufruf: gemeinsamer Speicher und eine schnelle Nachrichtenwarteschlange (FMQ).
  • Intuitiv: HIDL vermeidet schwierige Probleme bei der Speicherinhaberschaft, indem es nur in-Parameter für RPC verwendet (siehe Android Interface Definition Language (AIDL)); Werte, die nicht effizient von Methoden zurückgegeben werden können, werden über Callback-Funktionen zurückgegeben. Weder die Übergabe von Daten in das HIDL zur Übertragung noch der Empfang von Daten von HIDL ändert den Inhaber der Daten – die Inhaberschaft bleibt immer bei der aufrufenden Funktion. Daten müssen nur für die Dauer der aufgerufenen Funktion bestehen bleiben und können sofort nach Rückgabe der aufgerufenen Funktion gelöscht werden.

Passthrough-Modus verwenden

Um Geräte, auf denen frühere Versionen von Android ausgeführt werden, auf Android O zu aktualisieren, können Sie sowohl herkömmliche (als auch ältere) HALs in einer neuen HIDL-Schnittstelle zusammenfassen, die den HAL im Binder- und im selben Prozessmodus (Passthrough) bereitstellt. Dieses Wrapping 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. Auf Geräten mit früheren Versionen von Android sind HALs nicht in Java geschrieben. Java-HALs sind daher von Natur aus in Bindestriche geschrieben.

Wenn eine .hal-Datei kompiliert wird, erzeugt hidl-gen zusätzlich zu den Headern, die für die Binder-Kommunikation verwendet werden, eine zusätzliche Passthrough-Header-Datei BsFoo.h. Dieser Header definiert Funktionen, die dlopen zu verwenden sind. 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 einem eigenen Thread ausgeführt, da sie nicht darauf warten sollen, dass der HAL sie verarbeitet. Das bedeutet, dass alle HAL, die oneway-Methoden im Passthrough-Modus verwenden, threadsicher sein müssen.

Bei einem IFoo.hal umschließt BsFoo.h die HIDL-generierten Methoden, um zusätzliche Features bereitzustellen (z. B. oneway-Transaktionen in einem anderen Thread ausführen). Diese Datei ähnelt BpFoo.h, allerdings werden die gewünschten Funktionen direkt aufgerufen, anstatt IPC-Aufrufe mit binder zu übergeben. Zukünftige HAL-Implementierungen bieten möglicherweise mehrere Implementierungen, z. B. FooFast HAL und FooCorrect HAL. In solchen Fällen wird eine Datei für jede zusätzliche Implementierung erstellt (z.B. PTFooFast.cpp und PTFooAccurate.cpp).

Passthrough-HALs binden

Sie können HAL-Implementierungen, die den Passthrough-Modus unterstützen, in eine Bindung einbinden. Bei einer HAL-Schnittstelle a.b.c.d@M.N::IFoo werden zwei Pakete erstellt:

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

Beim 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 „true“ ist, versucht getService, den HAL nur im Passthrough-Modus zu öffnen. Wenn getStub „false“ ist, versucht getService, einen Binder-Dienst zu finden. Wenn dies fehlschlägt, versucht es dann, den Passthrough-Dienst zu finden. Der Parameter getStub sollte ausschließlich in defaultPassthroughServiceImplementation verwendet werden. Geräte, die mit Android O gestartet werden, sind vollständig gebundene Geräte. Daher ist das Öffnen eines Dienstes im Passthrough-Modus nicht zulässig.

HIDL-Grammatik

Die HIDL-Sprache ähnelt standardmäßig C, verwendet aber nicht den C-Präprozessor. Alle Satzzeichen, die unten nicht beschrieben sind (abgesehen von der offensichtlichen Verwendung von = und |), sind Teil der Grammatik.

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

  • /** */ gibt einen Kommentar in der Dokumentation an. Sie können nur auf Typ-, Methoden-, Feld- und ENUM-Wertdeklarationen angewendet werden.
  • /* */ gibt einen mehrzeiligen Kommentar an.
  • // steht für einen Kommentar bis zum Zeilenende. Abgesehen von // sind Zeilenumbrüche identisch mit allen anderen Leerzeichen.
  • In der folgenden Beispielgrammatik ist der Text vom // bis zum Zeilenende nicht Teil der Grammatik, sondern ein Kommentar zur Grammatik.
  • [empty] bedeutet, dass der Begriff leer sein kann.
  • ? nach einem Literal oder Begriff bedeutet, dass es optional ist.
  • ... gibt eine Sequenz an, die null oder mehr Elemente mit wie angegeben trennenden Satzzeichen enthält. In HIDL gibt es keine variadischen Argumente.
  • Kommas trennen Sequenzelemente.
  • Semikolons enden jedes Element, auch das letzte.
  • GROSSBUCHSTABEN sind kein Terminal.
  • italics ist eine Tokenfamilie wie integer oder identifier (Standard-C-Parsing-Regeln).
  • constexpr ist ein konstanter C-Stilausdruck (z. B. 1 + 1 und 1L << 3).
  • import_name ist ein Paket- oder Schnittstellenname, wie in HIDL-Versionsverwaltung beschrieben.
  • words in Kleinbuchstaben sind Literaltokens.

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