Bei HIDL muss jede in HIDL geschriebene Schnittstelle versioniert sein. Nachdem eine HAL-Benutzeroberfläche veröffentlicht wurde, ist sie eingefroren und alle weiteren Änderungen müssen an einer neuen Version dieser Benutzeroberfläche vorgenommen werden. Eine veröffentlichte Benutzeroberfläche kann nicht geändert, aber durch eine andere erweitert werden.
HIDL-Codestruktur
HIDL-Code ist in benutzerdefinierten Typen, Schnittstellen und Paketen organisiert:
- Nutzerdefinierte Typen (User-Defined Types, UDTs) HIDL bietet Zugriff auf eine Reihe von primitiven Datentypen, mit denen sich komplexere Typen über Strukturen, Unionen und Enumerationen zusammenstellen lassen. Benutzerdefinierte Typen werden an Methoden von Schnittstellen übergeben und können auf Paketebene (für alle Schnittstellen gemeinsam) oder lokal für eine Schnittstelle definiert werden.
- Benutzeroberflächen: Als grundlegender Baustein von HIDL besteht eine Schnittstelle aus UDT- und Methodendeklarationen. Schnittstellen können auch von einer anderen Schnittstelle abgeleitet werden.
- Pakete Hier werden zugehörige HIDL-Schnittstellen und die Datentypen organisiert, mit denen sie arbeiten. Ein Paket wird durch einen Namen und eine Version identifiziert und enthält Folgendes:
- Datei mit Datentypdefinitionen namens
types.hal
- Null oder mehr Schnittstellen, jeweils in einer eigenen
.hal
-Datei.
- Datei mit Datentypdefinitionen namens
Die Datei mit der Datentypdefinition types.hal
enthält nur UDTs. Alle UDTs auf Paketebene werden in einer einzigen Datei aufbewahrt. Darstellungen in der Zielsprache sind für alle Schnittstellen im Paket verfügbar.
Versionierungsphilosophie
Ein HIDL-Paket (z. B. android.hardware.nfc
), das für eine bestimmte Version (z. B. 1.0
) veröffentlicht wurde, ist unveränderlich und kann nicht geändert werden. Änderungen an den Schnittstellen im Paket oder an den UDTs können nur in einem anderen Paket vorgenommen werden.
In HIDL gilt die Versionierung auf Paketebene und nicht auf Schnittstellenebene. Alle Schnittstellen und UDTs in einem Paket haben dieselbe Version. Paketversionen folgen dem semantischen Versionierungsschema ohne die Komponenten „Patchebene“ und „Build-Metadaten“. Innerhalb eines bestimmten Pakets bedeutet eine Erhöhung der Nebenversion, dass die neue Version des Pakets abwärtskompatibel mit der alten Version ist. Eine Erhöhung der Hauptversion bedeutet, dass die neue Version des Pakets nicht abwärtskompatibel mit der alten Version ist.
Ein Paket kann auf verschiedene Arten mit einem anderen Paket in Beziehung stehen:
- Überhaupt nicht.
- Abwärtskompatible Erweiterbarkeit auf Paketebene Das tritt bei neuen Nebenversionen (nächste inkrementelle Überarbeitung) eines Pakets auf. Das neue Paket hat denselben Namen und dieselbe Hauptversion wie das alte Paket, aber eine höhere Nebenversion. Funktionell ist das neue Paket eine Obermenge des alten Pakets. Das bedeutet:
- Die Schnittstellen der obersten Ebene des übergeordneten Pakets sind im neuen Paket vorhanden. Die Schnittstellen können jedoch neue Methoden, neue schnittstellenlokale UDTs (die unten beschriebene Erweiterung auf Schnittstellenebene) und neue UDTs in
types.hal
enthalten. - Dem neuen Paket können auch neue Oberflächen hinzugefügt werden.
- Alle Datentypen des übergeordneten Pakets sind im neuen Paket vorhanden und können von den (möglicherweise neu implementierten) Methoden aus dem alten Paket verarbeitet werden.
- Neue Datentypen können auch für die Verwendung durch neue Methoden aktualisierter vorhandener oder neuer Schnittstellen hinzugefügt werden.
- Die Schnittstellen der obersten Ebene des übergeordneten Pakets sind im neuen Paket vorhanden. Die Schnittstellen können jedoch neue Methoden, neue schnittstellenlokale UDTs (die unten beschriebene Erweiterung auf Schnittstellenebene) und neue UDTs in
- Abwärtskompatible Erweiterbarkeit auf Benutzeroberflächenebene Das neue Paket kann auch das ursprüngliche Paket erweitern, indem es aus logisch getrennten Schnittstellen besteht, die lediglich zusätzliche Funktionen und nicht die Hauptfunktion bieten.
Dazu kann Folgendes sinnvoll sein:
- Schnittstellen im neuen Paket müssen auf die Datentypen des alten Pakets zurückgreifen.
- Schnittstellen im neuen Paket können Schnittstellen eines oder mehrerer alter Pakete erweitern.
- Erweitern Sie die ursprüngliche Abwärtsinkompatibilität. Dies ist eine Major-Version des Pakets und es muss keine Korrelation zwischen den beiden geben. Falls ja, kann sie mit einer Kombination von Typen aus der älteren Version des Pakets und der Vererbung einer Teilmenge der Schnittstellen des alten Pakets ausgedrückt werden.
Benutzeroberflächenstruktur
Bei einer gut strukturierten Benutzeroberfläche sollte das Hinzufügen neuer Funktionen, die nicht Teil des ursprünglichen Designs sind, eine Änderung der HIDL-Benutzeroberfläche erfordern. Umgekehrt ist die Benutzeroberfläche nicht strukturiert, wenn Sie auf beiden Seiten der Benutzeroberfläche eine Änderung vornehmen können oder erwarten, dass neue Funktionen eingeführt werden, ohne dass sich die Benutzeroberfläche selbst ändert.
Treble unterstützt separat kompilierte Anbieter- und Systemkomponenten, bei denen die vendor.img
auf einem Gerät und die system.img
separat kompiliert werden können. Alle Interaktionen zwischen vendor.img
und system.img
müssen explizit und umfassend definiert werden, damit sie noch viele Jahre lang funktionieren. Dazu gehören viele API-Oberflächen, aber ein wichtiger Aspekt ist der IPC-Mechanismus, den HIDL für die prozessübergreifende Kommunikation an der system.img
/vendor.img
-Grenze verwendet.
Voraussetzungen
Alle über HIDL übergebenen Daten müssen explizit definiert sein. Damit eine Implementierung und ein Client auch dann zusammenarbeiten können, wenn sie separat kompiliert oder unabhängig voneinander entwickelt wurden, müssen die Daten die folgenden Anforderungen erfüllen:
- Kann direkt in HIDL mithilfe von Strukturen, Enumerationen usw. mit semantischen Namen und Bedeutung beschrieben werden.
- Kann durch einen öffentlichen Standard wie ISO/IEC 7816 beschrieben werden.
- Kann durch einen Hardwarestandard oder das physische Layout der Hardware beschrieben werden.
- Kann bei Bedarf undurchsichtige Daten (z. B. öffentliche Schlüssel, IDs usw.) enthalten.
Wenn opake Daten verwendet werden, dürfen sie nur von einer Seite der HIDL-Schnittstelle gelesen werden. Wenn beispielsweise vendor.img
-Code einer Komponente auf der system.img
eine Stringnachricht oder vec<uint8_t>
-Daten sendet, können diese Daten nicht von der system.img
selbst geparst werden, sondern nur zur Interpretation an vendor.img
zurückgegeben werden. Wenn ein Wert von vendor.img
an den Anbietercode auf system.img
oder an ein anderes Gerät übergeben wird, müssen das Format der Daten und die Art ihrer Interpretation genau beschrieben werden. Diese Informationen sind weiterhin Teil der Benutzeroberfläche.
Richtlinien
Sie sollten eine Implementierung oder einen Client einer HAL nur mit den .hal-Dateien schreiben können. Sie müssen sich also nicht die Android-Quellcodes oder öffentlichen Standards ansehen. Wir empfehlen, das genaue erforderliche Verhalten anzugeben. Aussagen wie „Eine Implementierung könnte A oder B tun“ fördern die Verzahnung von Implementierungen mit den Kunden, für die sie entwickelt werden.
HIDL-Codelayout
HIDL umfasst Kern- und Anbieterpakete.
Die von Google festgelegten HIDL-Schnittstellen sind die wichtigsten. Die zugehörigen Pakete beginnen mit android.hardware.
und werden nach Subsystem benannt, möglicherweise mit verschachtelten Benennungsebenen. Angenommen, das NFC-Paket heißt android.hardware.nfc
und das Kamerapaket android.hardware.camera
. Im Allgemeinen hat ein Kernpaket den Namen android.hardware.
[name1
].[name2
]…. HIDL-Pakete haben zusätzlich zu ihrem Namen eine Version. Das Paket android.hardware.camera
hat beispielsweise die Version 3.4
. Das ist wichtig, da sich die Version eines Pakets auf seine Platzierung im Stammbaum auswirkt.
Alle Kernpakete werden im Build-System unter hardware/interfaces/
abgelegt. Das Paket android.hardware.
[name1
].[name2
]… mit der Version $m.$n
befindet sich unter hardware/interfaces/name1/name2/
…/$m.$n/
; das Paket android.hardware.camera
Version 3.4
befindet sich im Verzeichnis hardware/interfaces/camera/3.4/.
. Es gibt eine hartcodierte Zuordnung zwischen dem Paketpräfix android.hardware.
und dem Pfad hardware/interfaces/
.
Nicht zum Kern gehörende (Anbieter-)Pakete werden vom SoC-Anbieter oder ODM erstellt. Das Präfix für nicht ‑Kernpakete ist vendor.$(VENDOR).hardware.
, wobei $(VENDOR)
sich auf einen SoC-Anbieter oder OEM/ODM bezieht. Dies entspricht dem Pfad vendor/$(VENDOR)/interfaces
im Baum (diese Zuordnung ist ebenfalls hartcodiert).
Vollständig qualifizierte Namen von benutzerdefinierten Typen
In HIDL hat jede UDT einen voll qualifizierten Namen, der aus dem Namen der UDT, dem Paketnamen, in dem die UDT definiert ist, und der Paketversion besteht. Der voll qualifizierte Name wird nur verwendet, wenn Instanzen des Typs deklariert werden, nicht aber, wenn der Typ selbst definiert wird. Angenommen, das Paket android.hardware.nfc,
Version 1.0
definiert eine Struktur namens NfcData
. An der Stelle der Erklärung (sei es in types.hal
oder in der Erklärung einer Benutzeroberfläche) wird einfach Folgendes angegeben:
struct NfcData { vec<uint8_t> data; };
Verwenden Sie bei der Deklarierung einer Instanz dieses Typs (sei es innerhalb einer Datenstruktur oder als Methodenparameter) den voll qualifizierten Typnamen:
android.hardware.nfc@1.0::NfcData
Die allgemeine Syntax lautet PACKAGE@VERSION::UDT
, wobei:
PACKAGE
ist der durch Punkte getrennte Name eines HIDL-Pakets (z.B.android.hardware.nfc
).VERSION
ist das durch Punkte getrennte Format „major.minor“ der Version des Pakets (z.B.1.0
).UDT
ist der durch Punkte getrennte Name einer HIDL-UDT. Da HIDL verschachtelte UDTs unterstützt und HIDL-Schnittstellen UDTs (eine Art verschachtelte Deklaration) enthalten können, werden Punkte für den Zugriff auf die Namen verwendet.
Angenommen, die folgende verschachtelte Deklaration wurde in der Datei „common types“ im Paket android.hardware.example
Version 1.0
definiert:
// types.hal package android.hardware.example@1.0; struct Foo { struct Bar { // … }; Bar cheers; };
Der vollständig qualifizierte Name für Bar
ist android.hardware.example@1.0::Foo.Bar
. Wenn sich die verschachtelte Deklaration nicht nur im obigen Paket, sondern auch in einer Schnittstelle namens IQuux
befindet:
// IQuux.hal package android.hardware.example@1.0; interface IQuux { struct Foo { struct Bar { // … }; Bar cheers; }; doSomething(Foo f) generates (Foo.Bar fb); };
Der vollständig qualifizierte Name für Bar
ist android.hardware.example@1.0::IQuux.Foo.Bar
.
In beiden Fällen kann auf Bar
nur im Rahmen der Deklaration von Foo
als Bar
verwiesen werden. Auf Paket- oder Schnittstellenebene müssen Sie auf Bar
über Foo
:Foo.Bar
verweisen, wie in der Deklaration der Methode doSomething
oben. Alternativ können Sie die Methode ausführlicher deklarieren:
// IQuux.hal doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);
Voll qualifizierte Aufzählungswerte
Wenn es sich bei einem UDT um einen enum-Typ handelt, hat jeder Wert des enum-Typs einen voll qualifizierten Namen, der mit dem voll qualifizierten Namen des enum-Typs beginnt, gefolgt von einem Doppelpunkt und dem Namen des enum-Werts. Angenommen, das Paket android.hardware.nfc,
Version 1.0
definiert einen Enum-Typ NfcStatus
:
enum NfcStatus { STATUS_OK, STATUS_FAILED };
Der vollständig qualifizierte Name für STATUS_OK
lautet:
android.hardware.nfc@1.0::NfcStatus:STATUS_OK
Die allgemeine Syntax lautet PACKAGE@VERSION::UDT:VALUE
, wobei:
PACKAGE@VERSION::UDT
ist der exakteselbe voll qualifizierte Name für den Enumerationstyp.VALUE
ist der Name des Werts.
Regeln für die automatische Inferenz
Ein voll qualifizierter UDT-Name muss nicht angegeben werden. Folgendes kann in einem UDT-Namen problemlos weggelassen werden:
- Das Paket, z.B.
@1.0::IFoo.Type
- Sowohl Paket als auch Version, z.B.
IFoo.Type
HIDL versucht, den Namen mithilfe von Regeln zur automatischen Behebung von Interferenzen zu vervollständigen. Je niedriger die Regelnummer, desto höher die Priorität.
Regel 1
Wenn kein Paket und keine Version angegeben werden, wird eine lokale Namenssuche versucht. Beispiel:
interface Nfc { typedef string NfcErrorMessage; send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m); };
NfcErrorMessage
wird lokal abgerufen und die darüber liegende typedef
wird gefunden. NfcData
wird auch lokal abgerufen, da es jedoch nicht lokal definiert ist, werden Regel 2 und 3 verwendet. @1.0::NfcStatus
gibt eine Version an, daher gilt Regel 1 nicht.
Regel 2
Wenn Regel 1 fehlschlägt und eine Komponente des voll qualifizierten Namens fehlt (Paket, Version oder Paket und Version), wird die Komponente automatisch mit Informationen aus dem aktuellen Paket ausgefüllt. Der HIDL-Compiler sucht dann in der aktuellen Datei (und in allen Importen) nach dem automatisch ausgefüllten voll qualifizierten Namen.
Angenommen, die Deklaration von ExtendedNfcData
wurde im selben Paket (android.hardware.nfc
) in derselben Version (1.0
) wie NfcData
vorgenommen:
struct ExtendedNfcData { NfcData base; // … additional members };
Der HIDL-Compiler füllt den Paketnamen und den Versionsnamen aus dem aktuellen Paket aus, um den vollständig qualifizierten UDT-Namen android.hardware.nfc@1.0::NfcData
zu erstellen. Da der Name im aktuellen Paket vorhanden ist (vorausgesetzt, er wurde richtig importiert), wird er für die Deklaration verwendet.
Ein Name im aktuellen Paket wird nur importiert, wenn eines der folgenden Kriterien erfüllt ist:
- Es wird explizit mit einer
import
-Anweisung importiert. - Sie wird im aktuellen Paket in
types.hal
definiert.
Das gleiche Verfahren wird angewendet, wenn NfcData
nur durch die Versionsnummer qualifiziert wurde:
struct ExtendedNfcData { // autofill the current package name (android.hardware.nfc) @1.0::NfcData base; // … additional members };
Regel 3
Wenn Regel 2 nicht zu einer Übereinstimmung führt (die UDT ist nicht im aktuellen Paket definiert), sucht der HIDL-Compiler in allen importierten Paketen nach einer Übereinstimmung.
Angenommen, ExtendedNfcData
ist in Version 1.1
des Pakets android.hardware.nfc
deklariert, 1.1
importiert 1.0
wie vorgesehen (siehe Erweiterungen auf Paketebene) und die Definition gibt nur den Namen der UDT an:
struct ExtendedNfcData { NfcData base; // … additional members };
Der Compiler sucht nach einer UDT mit dem Namen NfcData
und findet eine in android.hardware.nfc
in Version 1.0
. Dies führt zu einer vollständig qualifizierten UDT von android.hardware.nfc@1.0::NfcData
. Wenn für eine bestimmte teilweise qualifizierte UDT mehr als eine Übereinstimmung gefunden wird, löst der HIDL-Compiler einen Fehler aus.
Verwendungsbeispiele
Gemäß Regel 2 wird ein im aktuellen Paket definierter importierter Typ einem importierten Typ aus einem anderen Paket vorgezogen:
// hardware/interfaces/foo/1.0/types.hal package android.hardware.foo@1.0; struct S {}; // hardware/interfaces/foo/1.0/IFooCallback.hal package android.hardware.foo@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/types.hal package android.hardware.bar@1.0; typedef string S; // hardware/interfaces/bar/1.0/IFooCallback.hal package android.hardware.bar@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/IBar.hal package android.hardware.bar@1.0; import android.hardware.foo@1.0; interface IBar { baz1(S s); // android.hardware.bar@1.0::S baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback };
S
wird alsandroid.hardware.bar@1.0::S
interpoliert und inbar/1.0/types.hal
gefunden, datypes.hal
automatisch importiert wird.IFooCallback
wird gemäß Regel 2 alsandroid.hardware.bar@1.0::IFooCallback
interpoliert, kann aber nicht gefunden werden, dabar/1.0/IFooCallback.hal
nicht automatisch importiert wird (wietypes.hal
). Daher wird es in Regel 3 stattdessen inandroid.hardware.foo@1.0::IFooCallback
aufgelöst, das überimport android.hardware.foo@1.0;
importiert wird.
types.hal
Jedes HIDL-Paket enthält eine types.hal
-Datei mit UDTs, die für alle an diesem Paket beteiligten Schnittstellen freigegeben werden. HIDL-Typen sind immer öffentlich. Unabhängig davon, ob eine UDT in types.hal
oder in einer Schnittstellendeklaration deklariert ist, sind diese Typen außerhalb des Gültigkeitsbereichs, in dem sie definiert sind, zugänglich. types.hal
dient nicht dazu, die öffentliche API eines Pakets zu beschreiben, sondern UDTs zu hosten, die von allen Schnittstellen innerhalb des Pakets verwendet werden. Aufgrund der Natur von HIDL sind alle UDTs Teil der Schnittstelle.
types.hal
besteht aus UDTs und import
-Anweisungen.
Da types.hal
für jede Schnittstelle des Pakets verfügbar ist (es ist ein impliziter Import), sind diese import
-Anweisungen per Definition auf Paketebene. Benutzerdefinierte Typen in types.hal
können auch so importierte benutzerdefinierte Typen und Schnittstellen enthalten.
Beispiel für eine IFoo.hal
:
package android.hardware.foo@1.0; // whole package import import android.hardware.bar@1.0; // types only import import android.hardware.baz@1.0::types; // partial imports import android.hardware.qux@1.0::IQux.Quux; // partial imports import android.hardware.quuz@1.0::Quuz;
Folgendes wird importiert:
android.hidl.base@1.0::IBase
(implizit)android.hardware.foo@1.0::types
(implizit)- Alles in
android.hardware.bar@1.0
(einschließlich aller Benutzeroberflächen undtypes.hal
) types.hal
ausandroid.hardware.baz@1.0::types
(Benutzeroberflächen inandroid.hardware.baz@1.0
werden nicht importiert)IQux.hal
undtypes.hal
vonandroid.hardware.qux@1.0
Quuz
ausandroid.hardware.quuz@1.0
(vorausgesetzt,Quuz
ist intypes.hal
definiert, wird die gesamtetypes.hal
-Datei geparst, aber andere Typen alsQuuz
werden nicht importiert).
Versionierung auf Benutzeroberflächenebene
Jede Benutzeroberfläche in einem Paket befindet sich in einer eigenen Datei. Das Paket, zu dem die Benutzeroberfläche gehört, wird oben in der Benutzeroberfläche mit der Anweisung package
deklariert. Nach der Paketdeklaration können null oder mehrere Importe auf Schnittstellenebene (Teil- oder Vollpaket) aufgeführt sein. Beispiel:
package android.hardware.nfc@1.0;
In HIDL können Schnittstellen mit dem Schlüsselwort extends
von anderen Schnittstellen abgeleitet werden. Damit eine Schnittstelle eine andere erweitern kann, muss sie über eine import
-Anweisung darauf zugreifen können. Der Name der erweiterten Schnittstelle (Basisschnittstelle) folgt den oben beschriebenen Regeln für die Qualifizierung von Typennamen. Eine Schnittstelle kann nur von einer anderen Schnittstelle abgeleitet werden. In HIDL wird keine Mehrfachvererbung unterstützt.
In den folgenden Beispielen für die uprev-Versionierung wird das folgende Paket verwendet:
// types.hal package android.hardware.example@1.0 struct Foo { struct Bar { vec<uint32_t> val; }; }; // IQuux.hal package android.hardware.example@1.0 interface IQuux { fromFooToBar(Foo f) generates (Foo.Bar b); }
Uprev-Regeln
Um ein Paket package@major.minor
zu definieren, muss entweder A oder B erfüllt sein:
Regel A | „Ist eine Start-Nebenversion“: Alle vorherigen Nebenversionen, package@major.0 , package@major.1 , …, package@major.(minor-1) dürfen nicht definiert sein.
|
---|
Regel B | Folgendes ist richtig:
|
---|
Aufgrund von Regel A:
- Das Paket kann mit einer beliebigen Nebenversionsnummer beginnen (z. B. beginnt
android.hardware.biometrics.fingerprint
bei@2.1
). - Die Anforderung „
android.hardware.foo@1.0
ist nicht definiert“ bedeutet, dass das Verzeichnishardware/interfaces/foo/1.0
nicht vorhanden sein darf.
Regel A wirkt sich jedoch nicht auf ein Paket mit demselben Paketnamen, aber einer anderen Hauptversion aus. Beispiel: Für android.hardware.camera.device
sind sowohl @1.0
als auch @3.2
definiert. @3.2
muss nicht mit @1.0
interagieren. Daher kann @3.2::IExtFoo
@1.0::IFoo
erweitern.
Vorausgesetzt, der Paketname ist unterschiedlich, kann package@major.minor::IBar
von einer Schnittstelle mit einem anderen Namen erweitert werden (z. B. kann android.hardware.bar@1.0::IBar
android.hardware.baz@2.2::IBaz
erweitern). Wenn in einer Schnittstelle kein übergeordneter Typ mit dem Keyword extend
explizit deklariert wird, wird android.hidl.base@1.0::IBase
erweitert (außer IBase
selbst).
B.2 und B.3 müssen gleichzeitig eingehalten werden. Auch wenn android.hardware.foo@1.1::IFoo
android.hardware.foo@1.0::IFoo
erweitert, um Regel B.2 zu erfüllen, ist android.hardware.foo@1.1::IExtBar
, das android.hardware.foo@1.0::IBar
erweitert, keine gültige Uprev.
Uprev-Schnittstellen
So führen Sie ein Uprev für android.hardware.example@1.0
(oben definiert) auf @1.1
aus:
// types.hal package android.hardware.example@1.1; import android.hardware.example@1.0; // IQuux.hal package android.hardware.example@1.1 interface IQuux extends @1.0::IQuux { fromBarToFoo(Foo.Bar b) generates (Foo f); }
Dies ist eine import
auf Paketebene der Version 1.0
von android.hardware.example
in types.hal
. In Version 1.1
des Pakets werden zwar keine neuen UDTs hinzugefügt, aber Verweise auf UDTs in Version 1.0
sind weiterhin erforderlich. Daher ist in types.hal
ein Import auf Paketebene erforderlich. Derselbe Effekt hätte auch mit einem Import auf Benutzeroberflächenebene in IQuux.hal
erreicht werden können.
In extends @1.0::IQuux
in der Deklaration von IQuux
haben wir die Version von IQuux
angegeben, die übernommen wird. Eine Unterscheidung ist erforderlich, da IQuux
verwendet wird, um eine Schnittstelle zu deklarieren und von einer Schnittstelle zu übernehmen. Da Deklarationen einfach Namen sind, die alle Paket- und Versionsattribute an der Stelle der Deklaration übernehmen, muss die Entfernung von Mehrdeutigkeiten im Namen der Basisschnittstelle erfolgen. Wir hätten auch die vollständig qualifizierte UDT verwenden können, was aber redundant gewesen wäre.
Die neue Schnittstelle IQuux
deklariert die Methode fromFooToBar()
, die sie von @1.0::IQuux
erbt, nicht noch einmal. Sie listet einfach die neue Methode auf, die sie hinzufügt: fromBarToFoo()
. In HIDL können vererbte Methoden nicht in den untergeordneten Schnittstellen noch einmal deklariert werden. Daher kann die fromFooToBar()
-Methode in der IQuux
-Schnittstelle nicht explizit deklariert werden.
Uprev-Konventionen
Manchmal müssen die Schnittstellennamen der erweiterten Schnittstelle umbenannt werden. Wir empfehlen, dass Enumerationserweiterungen, Strukturen und Unionen denselben Namen wie das Element haben, das sie erweitern, es sei denn, sie unterscheiden sich so stark, dass ein neuer Name erforderlich ist. Beispiele:
// in parent hal file enum Brightness : uint32_t { NONE, WHITE }; // in child hal file extending the existing set with additional similar values enum Brightness : @1.0::Brightness { AUTOMATIC }; // extending the existing set with values that require a new, more descriptive name: enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };
Wenn eine Methode einen neuen semantischen Namen haben kann (z. B. fooWithLocation
), ist das vorzuziehen. Andernfalls sollte der Name ähnlich wie der Name des erweiterten Elements lauten. Beispielsweise kann die Methode foo_1_1
in @1.1::IFoo
die Funktionalität der Methode foo
in @1.0::IFoo
ersetzen, wenn es keinen besseren alternativen Namen gibt.
Versionierung auf Paketebene
Die HIDL-Versionierung erfolgt auf Paketebene. Nach der Veröffentlichung eines Pakets ist es unveränderlich (die Schnittstellen und UDTs können nicht geändert werden). Pakete können auf verschiedene Arten miteinander in Beziehung stehen, die sich alle durch eine Kombination aus Vererbung auf Schnittstellenebene und Zusammensetzung von UDTs durch Komposition ausdrücken lassen.
Eine Art von Beziehung ist jedoch genau definiert und muss erzwungen werden: die rückwärtskompatible Vererbung auf Paketebene. In diesem Szenario ist das übergeordnete Paket das Paket, von dem die Elemente übernommen werden, und das untergeordnete Paket ist das Paket, das das übergeordnete Paket erweitert. Für die abwärtskompatiblen Vererbungsregeln auf Paketebene gelten folgende Regeln:
- Alle Oberflächen der obersten Ebene des übergeordneten Pakets werden von Oberflächen im untergeordneten Paket übernommen.
- Dem neuen Paket können auch neue Schnittstellen hinzugefügt werden (keine Einschränkungen hinsichtlich der Beziehungen zu anderen Schnittstellen in anderen Paketen).
- Neue Datentypen können auch für die Verwendung durch neue Methoden aktualisierter vorhandener oder neuer Schnittstellen hinzugefügt werden.
Diese Regeln können mithilfe der HIDL-Erbschaft auf Schnittstellenebene und der UDT-Zusammensetzung implementiert werden. Sie erfordern jedoch Metadaten, um zu wissen, dass diese Beziehungen eine abwärtskompatible Paketerweiterung darstellen. So wird dieses Wissen abgeleitet:
Wenn ein Paket diese Anforderung erfüllt, erzwingt hidl-gen
Regeln zur Abwärtskompatibilität.