Interfacce e pacchetti

HIDL è basato su interfacce, un tipo astratto utilizzato nei linguaggi object-oriented 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 principale. Tutti i file che definiscono un pacchetto si trovano nella stessa directory. Ad esempio, package android.hardware.example.extension.light@2.0 potrebbe 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/* frameworks/ correlati
android.system.* system/hardware/interfaces/* correlati al sistema
android.hidl.* system/libhidl/transport/* nucleo

La directory del pacchetto contiene file con estensione .hal. Ogni file deve contenere un'istruzione package che indichi il pacchetto e la versione di cui fa parte il file. Il file types.hal, se presente, non definisce un'interfaccia, ma definisce i tipi di dati accessibili a ogni interfaccia del pacchetto.

Definizione dell'interfaccia

A parte types.hal, ogni altro file .hal definisce un'interfaccia. Un'interfaccia è in genere 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 devono e non possono essere dichiarati di nuovo nelle interfacce definite dall'utente o utilizzati in altro modo. Questi metodi includono:

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

Procedura di importazione

L'istruzione import è il meccanismo HIDL per accedere alle interfacce e ai tipi di pacchetti in un altro pacchetto. Un'istruzione import riguarda due entità:

  • L'entità di importazione, che può essere un pacchetto o un'interfaccia
  • L'entità importata, che può essere un pacchetto o un'interfaccia

L'entità di importazione è determinata dalla posizione dell'istruzione import. Quando l'istruzione si trova all'interno di un types.hal del pacchetto, gli elementi importati sono visibili all'intero pacchetto. Si tratta di un'importazione a livello di pacchetto. Quando l'istruzione si trova all'interno di un file di interfaccia, l'entità di importazione è l'interfaccia stessa; si tratta di un'importazione a livello di interfaccia.

L'entità importata è determinata dal valore dopo la parola chiave import. Il valore non deve essere un nome completo. Se un componente viene omesso, viene compilato automaticamente con le informazioni del pacchetto corrente. Per i valori completamente qualificati, sono supportati i seguenti casi di importazione:

  • Importazioni di pacchetti completi. Se il valore è un nome del pacchetto e una versione (la 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 l'interfaccia vengono importati nell'entità di importazione.
    • Un UDT definito in types.hal, quindi solo questo UDT viene importato nell'entità di importazione (gli altri tipi in types.hal non vengono importati).
  • Importazioni solo di tipi. Se il valore utilizza la sintassi di un'importazione parziale descritta sopra, ma con la parola chiave types anziché un nome di interfaccia, vengono importati solo gli UDT in types.hal del pacchetto designato.

L'entità che esegue l'importazione ottiene l'accesso a una combinazione di:

  • Le UDT comuni del pacchetto importato definite in types.hal.
  • Le interfacce del pacchetto importato (per un'importazione dell'intero pacchetto) o l'interfaccia specificata (per un'importazione parziale) ai fini della loro chiamata, del passaggio di handle e/o dell'eredità.

L'istruzione di importazione utilizza la sintassi del nome completo del tipo 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 uno dei seguenti tre tipi:

  • L'interfaccia può aggiungere funzionalità a un'altra, incorporando la sua API immutata.
  • Un pacchetto può aggiungere funzionalità a un altro, incorporando la relativa API senza modifiche.
  • L'interfaccia può importare tipi da un pacchetto o da un'interfaccia specifica.

Un'interfaccia può estendere solo un'altra interfaccia (non è ammessa l'eredità multipla). Ogni 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 si basa su (estende) un'interfaccia IFoo nella versione 1.2 del pacchetto original e viene creata una versione 1.3 del pacchetto original, la versione 4.1 di IBar non può estendere la versione 1.3 di IFoo. La versione 4.1 di IBar deve invece estendere la versione 4.0 di IBar, che è legata alla versione 1.2 di IFoo. Se lo si desidera, la versione 5.0 di IBar può estendere la versione 1.3 di IFoo.

Le estensioni dell'interfaccia non implicano dipendenza dalle librerie o inclusione di più HAL nel codice generato: importano semplicemente le definizioni di metodo e struttura di dati a livello di HIDL. Ogni metodo in un HAL deve essere implementato in quell'HAL.

Estensioni del fornitore

In alcuni casi, le estensioni del fornitore vengono implementate come sottoclasse dell'oggetto base che rappresenta l'interfaccia di base che estendono. Lo stesso oggetto viene registrato con il nome e la versione HAL di base e con il nome e la versione HAL dell'estensione (del fornitore).

Controllo delle versioni

I pacchetti sono versionati e le interfacce hanno la versione del pacchetto. Le versioni sono espresse in due numeri interi, major.minor.

  • 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 secondarie sono compatibili con le versioni precedenti. L'incremento del numero secondario indica che la versione più recente è completamente compatibile con la versione precedente. È possibile aggiungere nuove strutture di dati e metodi, ma non è possibile modificare le strutture di dati o le firme dei metodi esistenti.

Su un dispositivo possono essere presenti contemporaneamente più versioni principali o secondarie di un HAL. Tuttavia, è preferibile una versione secondaria a una maggiore perché il codice client che funziona con un'interfaccia della versione secondaria precedente funziona anche con le versioni secondarie successive della stessa interfaccia. Per maggiori dettagli sul controllo delle versioni e sulle estensioni del fornitore, consulta Controllo delle versioni HIDL.

Riepilogo del layout dell'interfaccia

Questa sezione riassume come gestire un pacchetto di interfaccia HIDL (ad esempio hardware/interfaces) e consolida le informazioni presentate nella sezione HIDL. Prima di leggere, assicurati di conoscere i concetti di versionamento HIDL, hashing con hidl-gen, i dettagli del lavoro con HIDL in generale e le seguenti definizioni:

Termine Definizione
Application Binary Interface (ABI) Interfaccia di programmazione di un'applicazione, oltre a eventuali collegamenti binari richiesti.
nome completo (fqName) Nome per distinguere un tipo HIDL. Esempio: android.hardware.foo@1.0::IFoo.
Pacco Pacchetto contenente un'interfaccia e tipi HIDL. Esempio: android.hardware.foo@1.0.
package root Pacchetto principale che contiene le interfacce HIDL. Esempio: l'interfaccia HIDL android.hardware si trova nella directory principale del pacchetto android.hardware.foo@1.0.
percorso principale del pacchetto Posizione nella struttura della sorgente di Android a cui corrisponde la radice di un pacchetto.

Per altre definizioni, consulta la terminologia HIDL.

Ogni file può essere trovato dalla mappatura della radice del pacchetto e dal suo nome completo

Le radici dei pacchetti vengono specificate per 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 deve trovarsi in $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal.

In pratica, è consigliabile che un fornitore o un OEM denominato awesome inserisca le proprie interfacce standard in vendor.awesome. Una volta selezionato un percorso del pacchetto, non deve essere modificato in quanto è 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 dell'interfaccia coerente (inoltre, questo semplifica molto anche le interfacce di gestione delle versioni).

La directory principale del pacchetto deve avere un file di controllo delle versioni

Se crei un percorso del pacchetto come -r vendor.awesome:vendor/awesome/interfaces, devi anche creare il file $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt, che deve contenere gli hash delle interfacce creati utilizzando l'opzione -Lhash in hidl-gen (questo argomento è trattato ampiamente in Hashing con hidl-gen).

Le interfacce si trovano in posizioni indipendenti dal dispositivo

In pratica, consigliamo di condividere le interfacce tra i reparti. Ciò consente il riutilizzo e il test massimo del codice su diversi dispositivi e casi d'uso.