Die HAL-Schnittstellendefinitionssprache oder HIDL ist eine Schnittstellenbeschreibungssprache (IDL), um die Schnittstelle zwischen einer HAL und ihren Benutzern zu spezifizieren. HIDL ermöglicht die Angabe von Typen und Methodenaufrufen, die in Schnittstellen und Paketen zusammengefasst sind. 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 überall AIDL zu verwenden.
HIDL soll für die Interprozesskommunikation (IPC) verwendet werden. Mit HDL erstellte HALs werden als binderisierte HALs bezeichnet, da sie mit anderen Architekturschichten über IPC-Aufrufe (Binder Inter Process Communication) kommunizieren können. Binderisierte HALs werden in einem separaten Prozess von dem 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 (in Java nicht unterstützt).
HIDL spezifiziert Datenstrukturen und Methodensignaturen, die in Schnittstellen organisiert sind (ähnlich einer Klasse), die in Paketen gesammelt werden. Die Syntax von HIDL kommt C++- und Java-Programmierern bekannt vor, jedoch mit einem anderen Satz von Schlüsselwörtern. HIDL verwendet auch Anmerkungen im Java-Stil.
Terminologie
In diesem Abschnitt werden die folgenden HIDL-bezogenen Begriffe verwendet:
gebunden | Zeigt an, dass HIDL für Remoteprozeduraufrufe zwischen Prozessen verwendet wird, die über einen Binder-ähnlichen Mechanismus implementiert werden. Siehe auch Durchgang . |
---|---|
Rückruf, asynchron | Schnittstelle, die von einem HAL-Benutzer bereitgestellt, an die HAL übergeben (unter Verwendung 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. Nicht verwendet für Methoden, 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 Durchgang . |
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 ein kleineres Versionsinkrement im selben Paketnamen oder für ein neues Paket (z. B. eine Herstellererweiterung) verwendet werden, um auf einem älteren Paket aufzubauen. |
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 Callback-Funktion generiert. |
Schnittstelle | Sammlung von Methoden und Typen. Übersetzt in eine Klasse in C++ oder Java. Alle Methoden in einer Schnittstelle werden in derselben Richtung aufgerufen: Ein Client-Prozess ruft Methoden auf, die von einem Server-Prozess implementiert werden. |
Einweg | Gibt bei Anwendung auf eine HIDL-Methode an, dass die Methode keine Werte zurückgibt und nicht blockiert. |
Paket | Sammlung von Schnittstellen und Datentypen, die sich eine Version teilen. |
Durchgang | Modus von dlopen , in dem der Server eine gemeinsam genutzte Bibliothek ist, die vom Client geöffnet wird. Im Passthrough-Modus sind Client und Server derselbe Prozess, aber separate 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 Durchgang . |
Transport | HIDL-Infrastruktur, die Daten zwischen Server und Client verschiebt. |
Ausführung | Version eines Pakets. Besteht aus zwei ganzen Zahlen, Major und Minor. Inkremente von Nebenversionen können Typen und Methoden hinzufügen (aber nicht ändern). |
HIDL-Design
Das Ziel von HIDL ist, dass das Android-Framework ersetzt werden kann, ohne HALs neu erstellen zu 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 ihrer 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 geliefert, 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, Daten ohne Verwendung eines RPC-Aufrufs zu übertragen: Shared Memory und eine Fast Message Queue (FMQ).
- Intuitiv . HIDL vermeidet heikle Probleme des Speicherbesitzes, indem
in
nur 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 das Übergeben von Daten an HIDL zur Übertragung noch das Empfangen von Daten von HIDL ändert das Eigentum an den Daten – das Eigentum 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.
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 eine neue HIDL-Schnittstelle einschließen, die die HAL im Binderized- und Same-Process (Passthrough)-Modus bedient. 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 mit früheren Android-Versionen haben keine in Java geschriebenen HALs, daher sind Java-HALs von Natur aus gebunden.
Passthrough-Header-Dateien
Wenn eine .hal
-Datei kompiliert wird, erzeugt hidl-gen
eine zusätzliche Passthrough-Header-Datei BsFoo.h
zusätzlich zu den Headern, die für die Binder-Kommunikation verwendet werden; Dieser Header definiert zu dlopen
Funktionen. Da Passthrough-HALs in demselben Prozess ausgeführt werden, in dem sie aufgerufen werden, werden Passthrough-Methoden in den meisten Fällen durch direkten Funktionsaufruf (gleicher Thread) aufgerufen. oneway
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
im Passthrough-Modus verwendet, Thread-sicher sein muss).
Bei einem IFoo.hal
BsFoo.h
die von HIDL generierten Methoden, um zusätzliche Funktionen bereitzustellen (z. B. das Ausführen von oneway
-Transaktionen in einem anderen Thread). Diese Datei ist ähnlich wie BpFoo.h
, jedoch werden die gewünschten Funktionen direkt aufgerufen, anstatt Aufrufe per Binder an IPC weiterzuleiten. Zukünftige Implementierungen von HALs können mehrere Implementierungen bereitstellen , wie z. B. FooFast HAL und eine 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
).
Binderisierende Durchgangs-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 der HAL und macht die FunktionIFoo* HIDL_FETCH_IFoo(const char* name)
. Auf Legacy-Geräten wird dieses Paket geöffnet und die Implementierung wird mitdlopen
HIDL_FETCH_IFoo
. Sie können den Basiscode mithidl-gen
und-Lc++-impl
und-Landroidbp-impl
. -
abcd@MN::IFoo-service
. Öffnet die Passthrough-HAL und registriert sich selbst als gebundener Dienst, sodass dieselbe HAL-Implementierung sowohl als Passthrough als auch als gebundener Dienst verwendet werden kann.
Bei gegebenem Typ IFoo
können Sie sp<IFoo> IFoo::getService(string name, bool getStub)
, 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
falsch ist, versucht getService
, einen gebundenen Dienst zu finden; Wenn dies fehlschlägt, versucht es, den Passthrough-Dienst zu finden. Der Parameter getStub
sollte nie verwendet werden, außer in defaultPassthroughServiceImplementation
. (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 C (aber verwendet nicht den C-Präprozessor). Alle unten nicht beschriebenen Satzzeichen (abgesehen von der offensichtlichen Verwendung von =
und |
) sind Teil der Grammatik.
Hinweis: Einzelheiten zum HIDL-Codestil finden Sie im Codestil- Leitfaden .
-
/** */
gibt einen Dokumentationskommentar an. Diese können nur auf Typ-, Methoden-, Feld- und Aufzählungswertdeklarationen angewendet werden. -
/* */
gibt einen mehrzeiligen Kommentar an. -
//
gibt einen Kommentar am Zeilenende an. Abgesehen von//
sind Zeilenumbrüche die gleichen wie alle anderen Leerzeichen. - In der Beispielgrammatik unten ist Text von
//
bis zum Ende der Zeile kein Teil der Grammatik, sondern ein Kommentar zur Grammatik. -
[empty]
bedeutet, dass der Begriff leer sein darf. -
?
nach einem wörtlichen oder Begriff bedeutet, dass es optional ist. -
...
gibt eine Sequenz an, die null oder mehr Elemente enthält, mit Trennzeichen wie angegeben. Es gibt keine variadischen Argumente in HIDL. - Kommas trennen Sequenzelemente.
- Semikolons beenden jedes Element, einschließlich des letzten Elements.
- GROSSBUCHSTABEN ist ein Nichtterminal.
-
italics
ist eine Token-Familie wieinteger
oderidentifier
(Standard-C-Parsing-Regeln). -
constexpr
ist ein konstanter Ausdruck im C-Stil (z. B.1 + 1
und1L << 3
). -
import_name
ist ein Paket- oder Schnittstellenname, der wie in HIDL-Versionierung beschrieben qualifiziert ist. - Kleingeschriebene
words
sind wörtliche 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