HIDL è costruito attorno alle interfacce, un tipo astratto utilizzato nei linguaggi orientati agli oggetti per definire i comportamenti. Ogni interfaccia fa parte di un pacchetto.
Pacchetti
I nomi dei pacchetti possono avere sottolivelli come package.subpackage
. La directory principale per i pacchetti HIDL pubblicati è hardware/interfaces
o vendor/vendorName
(ad esempio vendor/google
per i dispositivi Pixel). Il nome del pacchetto forma una o più sottodirectory nella directory root; tutti i file che definiscono un pacchetto si trovano nella stessa directory. Ad esempio, package android.hardware.example.extension.light@2.0
può essere trovato in hardware/interfaces/example/extension/light/2.0
.
La tabella seguente elenca i prefissi e le posizioni dei pacchetti:
Prefisso del pacchetto | Posizione | Tipi di interfaccia |
---|---|---|
android.hardware.* | hardware/interfaces/* | HAL |
android.frameworks.* | frameworks/hardware/interfaces/* | quadri/correlati |
android.system.* | system/hardware/interfaces/* | sistema/relativo |
android.hidl.* | system/libhidl/transport/* | nucleo |
La directory del pacchetto contiene file con estensione .hal
. Ogni file deve contenere un'istruzione package
che nomina il pacchetto e la versione di cui fa parte il file. Il file types.hal
, se presente, non definisce un'interfaccia ma definisce invece i tipi di dati accessibili a ogni interfaccia nel pacchetto.
Definizione dell'interfaccia
A parte types.hal
, ogni altro file .hal
definisce un'interfaccia. Un'interfaccia è generalmente definita come segue:
interface IBar extends IFoo { // IFoo is another interface // embedded types struct MyStruct {/*...*/}; // interface methods create(int32_t id) generates (MyStruct s); close(); };
Un'interfaccia senza una dichiarazione extends
esplicita si estende implicitamente da android.hidl.base@1.0::IBase
(simile a java.lang.Object
in Java.) L'interfaccia IBase, importata implicitamente, dichiara diversi metodi riservati che non dovrebbero e non possono essere dichiarati nuovamente in interfacce definite dall'utente o utilizzate in altro modo. Questi metodi includono:
-
ping
-
interfaceChain
-
interfaceDescriptor
-
notifySyspropsChanged
-
linkToDeath
-
unlinkToDeath
-
setHALInstrumentation
-
getDebugInfo
-
debug
-
getHashChain
Importazione
L'istruzione import
è un meccanismo HIDL per accedere alle interfacce e ai tipi del pacchetto in un altro pacchetto. Una dichiarazione import
riguarda due entità:
- L'entità importatrice , che può essere un pacchetto o un'interfaccia; E
- L' entità importata, che può essere anch'essa un pacchetto o un'interfaccia.
L'entità importatrice è determinata dall'ubicazione della dichiarazione import
. Quando l'istruzione si trova all'interno del types.hal
di un pacchetto, ciò che viene importato è visibile dall'intero pacchetto; questa è un'importazione a livello di pacchetto . Quando l'istruzione si trova all'interno di un file di interfaccia, l'entità importante è l'interfaccia stessa; questa è un'importazione a livello di interfaccia .
L'entità importata è determinata dal valore dopo la parola chiave import
. Non è necessario che il valore sia un nome completo; se un componente viene omesso, viene automaticamente riempito con le informazioni del pacchetto corrente. Per i valori completi, sono supportati i seguenti casi di importazione:
- Importazioni di pacchetti interi . Se il valore è il nome di un pacchetto e una versione (sintassi descritta di seguito), l'intero pacchetto viene importato nell'entità di importazione.
- Importazioni parziali . Se il valore è:
- Un'interfaccia, il
types.hal
del pacchetto e quell'interfaccia vengono importati nell'entità di importazione. - Un UDT definito in
types.hal
, quindi solo quell'UDT viene importato nell'entità importatrice (gli altri tipi intypes.hal
non vengono importati).
- Un'interfaccia, il
- Importazioni di soli tipi . Se il valore utilizza la sintassi di un'importazione parziale descritta sopra, ma con la parola chiave
types
invece del nome dell'interfaccia, vengono importati solo gli UDT intypes.hal
del pacchetto designato.
L'entità importatrice ottiene l'accesso a una combinazione di:
- Gli UDT comuni del pacchetto importato definiti in
types.hal
; - Le interfacce del pacchetto importato (per un'importazione dell'intero pacchetto) o l'interfaccia specificata (per un'importazione parziale) allo scopo di richiamarle, passare loro gli handle e/o ereditare da essi.
L'istruzione import utilizza la sintassi del nome del tipo completo per fornire il nome e la versione del pacchetto o dell'interfaccia da importare:
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
Eredità dell'interfaccia
Un'interfaccia può essere un'estensione di un'interfaccia definita in precedenza. Le estensioni possono essere di tre tipi:
- L'interfaccia può aggiungere funzionalità ad un'altra, incorporando la sua API invariata.
- Il pacchetto può aggiungere funzionalità a un altro, incorporando la sua API invariata.
- L'interfaccia può importare tipi da un pacchetto o da un'interfaccia specifica.
Un'interfaccia può estendere solo un'altra interfaccia (nessuna ereditarietà multipla). Ciascuna interfaccia in un pacchetto con un numero di versione secondaria diverso da zero deve estendere un'interfaccia nella versione precedente del pacchetto. Ad esempio, se un'interfaccia IBar
nella versione 4.0 del pacchetto derivative
è basata su (estende) un'interfaccia IFoo
nella versione 1.2 del pacchetto original
e viene creata una versione 1.3 del pacchetto original
, IBar
versione 4.1 non può estendere la versione 1.3 di IFoo
. Invece, IBar
versione 4.1 deve estendere IBar
versione 4.0, che è legata a IFoo
versione 1.2. Se lo si desidera, la versione 5.0 IBar
potrebbe estendere la versione 1.3 di IFoo
.
Le estensioni dell'interfaccia non implicano dipendenza dalla libreria o inclusione tra HAL nel codice generato: importano semplicemente la struttura dei dati e le definizioni dei metodi a livello HIDL. Ogni metodo in un HAL deve essere implementato in quell'HAL.
Estensioni del fornitore
In alcuni casi, le estensioni del fornitore verranno implementate come sottoclasse dell'oggetto base che rappresenta l'interfaccia principale che estendono. Lo stesso oggetto verrà registrato con il nome e la versione HAL di base e con il nome e la versione HAL dell'estensione (fornitore).
Controllo delle versioni
I pacchetti hanno una versione e le interfacce hanno la versione del loro pacchetto. Le versioni sono espresse in due numeri interi, major . minore .
- Le versioni principali non sono compatibili con le versioni precedenti. L'incremento del numero della versione principale reimposta il numero della versione secondaria su 0.
- Le versioni minori sono retrocompatibili. L'incremento del numero minore indica che la versione più recente è completamente compatibile con la versione precedente. È possibile aggiungere nuove strutture dati e metodi, ma non è possibile modificare le strutture dati esistenti o le firme dei metodi.
Su un dispositivo possono essere presenti contemporaneamente più versioni principali o secondarie di un HAL. Tuttavia, una versione minore dovrebbe essere preferita rispetto a una versione maggiore perché il codice client che funziona con un'interfaccia di versione minore precedente funzionerà anche con le versioni minori successive della stessa interfaccia. Per ulteriori dettagli sul controllo delle versioni e sulle estensioni del fornitore, vedere Controllo delle versioni HIDL .
Riepilogo del layout dell'interfaccia
Questa sezione riassume come gestire un pacchetto di interfacce HIDL (come hardware/interfaces
) e consolida le informazioni presentate nella sezione HIDL. Prima di leggere, assicurati di avere familiarità con il controllo delle versioni HIDL , i concetti di hashing in Hashing con hidl-gen , i dettagli su come lavorare con HIDL in generale e le seguenti definizioni:
Termine | Definizione |
---|---|
Interfaccia binaria dell'applicazione (ABI) | Interfaccia di programmazione dell'applicazione + eventuali collegamenti binari richiesti. |
Nome completo (fqName) | Nome per distinguere un tipo hidl. Esempio: android.hardware.foo@1.0::IFoo . |
Pacchetto | Pacchetto contenente un'interfaccia e tipi HIDL. Esempio: android.hardware.foo@1.0 . |
Radice del pacchetto | Pacchetto root che contiene le interfacce HIDL. Esempio: l'interfaccia HIDL android.hardware si trova nel pacchetto root android.hardware.foo@1.0 . |
Percorso radice del pacchetto | Posizione nell'albero dei sorgenti Android a cui viene mappata la radice del pacchetto. |
Per ulteriori definizioni, vedere Terminologia HIDL.
Ogni file può essere trovato dalla mappatura root del pacchetto e dal suo nome completo
Le radici del pacchetto vengono specificate in hidl-gen
come argomento -r android.hardware:hardware/interfaces
. Ad esempio, se il pacchetto è vendor.awesome.foo@1.0::IFoo
e hidl-gen
viene inviato -r vendor.awesome:some/device/independent/path/interfaces
, il file di interfaccia dovrebbe trovarsi in $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal
.
In pratica, si consiglia a un fornitore o OEM denominato awesome
di inserire le proprie interfacce standard in vendor.awesome
. Dopo che è stato selezionato il percorso di un pacchetto, non deve essere modificato poiché è integrato nell'ABI dell'interfaccia.
La mappatura del percorso del pacchetto deve essere univoca
Ad esempio, se hai -rsome.package:$PATH_A
e -rsome.package:$PATH_B
, $PATH_A
deve essere uguale a $PATH_B
per una directory di interfaccia coerente (questo rende anche molto più semplice il controllo delle versioni delle interfacce ).
La radice del pacchetto deve avere un file di controllo delle versioni
Se crei un percorso del pacchetto come -r vendor.awesome:vendor/awesome/interfaces
, dovresti creare anche il file $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt
, che dovrebbe contenere hash delle interfacce realizzate utilizzando -Lhash
opzione in hidl-gen
(questo è ampiamente discusso in Hashing con hidl-gen ).
Le interfacce vanno in posizioni indipendenti dal dispositivo
In pratica si consiglia di condividere le interfacce tra le filiali. Ciò consente il massimo riutilizzo del codice e il massimo test del codice su diversi dispositivi e casi d'uso.