Schnittstellen und Pakete

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 in types.hal werden nicht importiert.
  • 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 in types.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.