HIDL basiert auf Schnittstellen, einem abstrakten Typ, der in objektorientierten Sprachen zum Definieren von Verhaltensweisen verwendet wird. Jede Schnittstelle ist Teil eines Pakets.
Pakete
Paketnamen können untergeordnete Ebenen wie package.subpackage
haben. Das Stammverzeichnis für veröffentlichte HIDL-Pakete ist hardware/interfaces
oder vendor/vendorName
(z. B. vendor/google
für Pixel-Geräte). Der Paketname bildet ein oder mehrere Unterverzeichnisse unter dem Stammverzeichnis. Alle Dateien, die ein Paket definieren, befinden sich im selben Verzeichnis. package android.hardware.example.extension.light@2.0
könnte beispielsweise unter hardware/interfaces/example/extension/light/2.0
zu finden sein.
In der folgenden Tabelle sind Paketpräfixe und -standorte aufgeführt:
Paketpräfix | Standort | Schnittstellentypen |
---|---|---|
android.hardware.* |
hardware/interfaces/* |
HAL |
android.frameworks.* |
frameworks/hardware/interfaces/* |
Rahmenwerke/ Weitere Informationen |
android.system.* |
system/hardware/interfaces/* |
system/ |
android.hidl.* |
system/libhidl/transport/* |
Kern |
Das Paketverzeichnis enthält Dateien mit der Erweiterung .hal
. Jede Datei muss eine package
-Anweisung enthalten, in der das Paket und die Version angegeben sind, zu denen die Datei gehört. Die Datei types.hal
definiert, falls vorhanden, keine Schnittstelle, sondern Datentypen, auf die jede Schnittstelle im Paket zugreifen kann.
Schnittstellendefinition
Abgesehen von types.hal
definiert jede andere .hal
-Datei eine Schnittstelle. Eine Schnittstelle wird in der Regel so definiert:
interface IBar extends IFoo { // IFoo is another interface // embedded types struct MyStruct {/*...*/}; // interface methods create(int32_t id) generates (MyStruct s); close(); };
Eine Schnittstelle ohne explizite extends
-Deklaration erstreckt sich implizit von android.hidl.base@1.0::IBase
(ähnlich wie java.lang.Object
in Java). Die implizit importierte IBase-Schnittstelle deklariert mehrere reservierte Methoden, die nicht in benutzerdefinierten Schnittstellen neu deklariert werden dürfen und auch nicht anderweitig verwendet werden können. Zu diesen Methoden gehören:
ping
interfaceChain
interfaceDescriptor
notifySyspropsChanged
linkToDeath
unlinkToDeath
setHALInstrumentation
getDebugInfo
debug
getHashChain
Importvorgang
Die import
-Anweisung ist ein HIDL-Mechanismus, um auf Paketschnittstellen und ‑typen in einem anderen Paket zuzugreifen. Eine import
-Anweisung bezieht sich auf zwei Entitäten:
- Die importierende Entität, die entweder ein Paket oder eine Schnittstelle sein kann
- Die importierte Entität, die entweder ein Paket oder eine Schnittstelle sein kann
Die importierende Entität wird durch den Speicherort der import
-Anweisung bestimmt. Wenn sich die Anweisung in der types.hal
eines Pakets befindet, ist das Importierte für das gesamte Paket sichtbar. Dies ist ein Import auf Paketebene. Wenn sich die Anweisung in einer Interface-Datei befindet, ist die zu importierende Entität die Schnittstelle selbst. Dies ist ein Import auf Schnittstellenebene.
Die importierte Entität wird durch den Wert nach dem Keyword import
bestimmt. Der Wert muss kein vollständig qualifizierter Name sein. Wenn eine Komponente weggelassen wird, wird sie automatisch mit Informationen aus dem aktuellen Paket ausgefüllt.
Für vollständig qualifizierte Werte werden die folgenden Importfälle unterstützt:
- Importe ganzer Pakete Wenn der Wert ein Paketname und eine Version ist (Syntax siehe unten), wird das gesamte Paket in die importierende Entität importiert.
- Teilweise Importe Wenn der Wert:
- Eine Schnittstelle, die
types.hal
des Pakets und diese Schnittstelle werden in die importierende Entität importiert. - Wenn in
types.hal
ein UDT definiert ist, wird nur dieses UDT in die importierende Entität importiert. Andere Typen intypes.hal
werden nicht importiert.
- Eine Schnittstelle, die
- Nur Typen importieren Wenn der Wert die Syntax eines oben beschriebenen teilweisen Imports verwendet, aber das Keyword
types
anstelle eines Interface-Namens, werden nur die UDTs intypes.hal
des angegebenen Pakets importiert.
Die importierende Entität erhält Zugriff auf eine Kombination aus:
- Die in
types.hal
definierten gemeinsamen UDTs des importierten Pakets - Die Schnittstellen des importierten Pakets (beim Import des gesamten Pakets) oder die angegebene Schnittstelle (beim teilweisen Import), um sie aufzurufen, Handles an sie zu übergeben und/oder von ihnen zu erben.
In der Importanweisung wird die Syntax für voll qualifizierte Typennamen verwendet, um den Namen und die Version des importierten Pakets oder der importierten Schnittstelle anzugeben:
import android.hardware.nfc@1.0; // import a whole package import android.hardware.example@1.0::IQuux; // import an interface and types.hal import android.hardware.example@1.0::types; // import just types.hal
Schnittstellenübernahme
Eine Benutzeroberfläche kann eine Erweiterung einer zuvor definierten Benutzeroberfläche sein. Es gibt drei Arten von Erweiterungen:
- Eine Benutzeroberfläche kann einer anderen Funktionen hinzufügen, ohne dass die API geändert wird.
- Ein Paket kann einem anderen Paket Funktionen hinzufügen, wobei die API unverändert bleibt.
- Über die Benutzeroberfläche können Typen aus einem Paket oder aus einer bestimmten Benutzeroberfläche importiert werden.
Eine Schnittstelle kann nur eine andere Schnittstelle erweitern (keine Mehrfachvererbung).
Jede Schnittstelle in einem Paket mit einer Nebenversionsnummer ungleich null muss eine Schnittstelle in der vorherigen Version des Pakets erweitern. Wenn beispielsweise eine Schnittstelle IBar
in Version 4.0 des Pakets derivative
auf einer Schnittstelle IFoo
in Version 1.2 des Pakets original
basiert (diese erweitert), und eine Version 1.3 des Pakets original
erstellt wird, kann IBar
Version 4.1 Version 1.3 von IFoo
nicht erweitern. Stattdessen muss IBar
Version 4.1 IBar
Version 4.0 erweitern, die mit IFoo
Version 1.2 verknüpft ist.
IBar
Version 5.0 kann bei Bedarf IFoo
Version 1.3 erweitern.
Schnittstellenerweiterungen implizieren keine Bibliotheksabhängigkeit oder HAL-übergreifende Einbeziehung in den generierten Code. Sie importieren einfach die Datenstruktur- und Methodendefinitionen auf HIDL-Ebene. Jede Methode in einer HAL muss in dieser HAL implementiert sein.
Anbietererweiterungen
In einigen Fällen werden Anbietererweiterungen als Unterklasse des Basisobjekts implementiert, das die erweiterte Kernschnittstelle darstellt. Das gleiche Objekt ist unter dem Namen und der Version der Basis-HAL und unter dem Namen und der Version der HAL der Erweiterung (Anbieter) registriert.
Versionsverwaltung
Pakete haben Versionen und Schnittstellen haben die Version ihres Pakets. Versionen werden in zwei Ganzzahlen ausgedrückt: major.minor.
- Hauptversionen sind nicht abwärtskompatibel. Wenn Sie die Hauptversionsnummer erhöhen, wird die Nebenversionsnummer auf 0 zurückgesetzt.
- Nebenversionen sind abwärtskompatibel. Wenn die Nebenversion erhöht wird, ist die neuere Version vollständig abwärtskompatibel mit der vorherigen Version. Es können neue Datenstrukturen und Methoden hinzugefügt werden, aber keine vorhandenen Datenstrukturen oder Methodensignaturen dürfen geändert werden.
Auf einem Gerät können mehrere Haupt- oder Minor-Versionen einer HAL gleichzeitig vorhanden sein. Eine Nebenversion sollte jedoch einer Hauptversion vorgezogen werden, da Clientcode, der mit der Benutzeroberfläche einer vorherigen Nebenversion funktioniert, auch mit neueren Nebenversionen dieserselben Benutzeroberfläche funktioniert. Weitere Informationen zu Versionierung und Anbietererweiterungen finden Sie unter HIDL-Versionierung.
Zusammenfassung des Interface-Layouts
In diesem Abschnitt wird beschrieben, wie Sie ein HIDL-Interface-Paket (z. B. hardware/interfaces
) verwalten. Außerdem werden die Informationen im HIDL-Abschnitt zusammengefasst. Machen Sie sich vor dem Lesen mit den Konzepten zur HIDL-Versionierung, zum Hashing mit hidl-gen, zu den Details der Arbeit mit HIDL im Allgemeinen und mit den folgenden Definitionen vertraut:
Begriff | Definition |
---|---|
Application Binary Interface (ABI) | Application Programming Interface (API) und alle erforderlichen Binärverknüpfungen. |
Vollständig qualifizierter Name (FQDN) | Name, um einen HIDL-Typ zu unterscheiden. Beispiel:
android.hardware.foo@1.0::IFoo . |
Paket | Paket mit einer HIDL-Schnittstelle und ‑Typen. Beispiel:
android.hardware.foo@1.0 . |
package root | Stammpaket, das die HIDL-Schnittstellen enthält. Beispiel: Die HIDL-Schnittstelle android.hardware befindet sich im Paketstamm android.hardware.foo@1.0 . |
Paketstammpfad | Speicherort im Android-Quellbaum, auf den ein Paketstamm zugeordnet ist. |
Weitere Definitionen finden Sie unter HIDL-Terminologie.
Jede Datei kann über die Paketstammzuordnung und ihren voll qualifizierten Namen gefunden werden.
Paketknoten werden hidl-gen
als Argument -r android.hardware:hardware/interfaces
angegeben. Wenn das Paket beispielsweise vendor.awesome.foo@1.0::IFoo
ist und hidl-gen
an -r vendor.awesome:some/device/independent/path/interfaces
gesendet wird, sollte sich die Benutzeroberfläche in $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal
befinden.
In der Praxis wird einem Anbieter oder OEM mit dem Namen awesome
empfohlen, seine Standardschnittstellen unter vendor.awesome
zu platzieren. Nachdem ein Paketpfad ausgewählt wurde, darf er nicht mehr geändert werden, da er in die ABI der Schnittstelle eingebettet ist.
Paketpfadzuordnung muss eindeutig sein
Wenn Sie beispielsweise -rsome.package:$PATH_A
und -rsome.package:$PATH_B
haben, muss $PATH_A
für ein einheitliches Interfaceverzeichnis mit $PATH_B
übereinstimmen. Dies erleichtert auch die Versionierung von Interfaces.
Das Paketstammverzeichnis muss eine Versionsdatei haben
Wenn Sie einen Paketpfad wie -r vendor.awesome:vendor/awesome/interfaces
erstellen, sollten Sie auch die Datei $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt
erstellen. Diese sollte Hashes von Schnittstellen enthalten, die mit der Option -Lhash
in hidl-gen
erstellt wurden. Weitere Informationen finden Sie unter Hashing mit hidl-gen.
Benutzeroberflächen müssen an geräteunabhängigen Speicherorten abgelegt werden
In der Praxis empfehlen wir, Oberflächen zwischen Niederlassungen zu teilen. So kann Code optimal wiederverwendet und auf verschiedenen Geräten und für verschiedene Anwendungsfälle getestet werden.