HIDL richiede che ogni interfaccia scritta in HIDL sia versionata. Dopo che un'interfaccia HAL è stata pubblicata, viene bloccata ed è necessario apportare ulteriori modifiche a una nuova versione di tale interfaccia. Sebbene una determinata interfaccia pubblicata non possa essere modificata, può essere estesa da un'altra interfaccia.
Struttura del codice HIDL
Il codice HIDL è organizzato in tipi, interfacce e pacchetti definiti dall'utente:
- Tipi definiti dall'utente (UDT) . HIDL fornisce l'accesso a una serie di tipi di dati primitivi che possono essere utilizzati per comporre tipi più complessi tramite strutture, unioni ed enumerazioni. Gli UDT vengono passati ai metodi delle interfacce e possono essere definiti a livello di pacchetto (comune a tutte le interfacce) o localmente a un'interfaccia.
- Interfacce . Come elemento costitutivo di base di HIDL, un'interfaccia è costituita da UDT e dichiarazioni di metodo. Le interfacce possono anche ereditare da un'altra interfaccia.
- Pacchetti . Organizza le interfacce HIDL correlate e i tipi di dati su cui operano. Un pacchetto è identificato da un nome e una versione e include quanto segue:
- File di definizione del tipo di dati denominato
types.hal
. - Zero o più interfacce, ciascuna nel proprio file
.hal
.
- File di definizione del tipo di dati denominato
Il file di definizione del tipo di dati types.hal
contiene solo UDT (tutti gli UDT a livello di pacchetto sono conservati in un unico file). Le rappresentazioni nella lingua di destinazione sono disponibili per tutte le interfacce nel pacchetto.
Filosofia delle versioni
Un pacchetto HIDL (come android.hardware.nfc
), dopo essere stato pubblicato per una data versione (come 1.0
), è immutabile; non può essere cambiato. Le modifiche alle interfacce nel pacchetto o qualsiasi modifica ai relativi UDT possono avvenire solo in un altro pacchetto.
In HIDL, il controllo delle versioni si applica a livello di pacchetto, non a livello di interfaccia, e tutte le interfacce e gli UDT in un pacchetto condividono la stessa versione. Le versioni dei pacchetti seguono il controllo delle versioni semantico senza il livello di patch e i componenti dei metadati di compilazione. All'interno di un determinato pacchetto, un aumento della versione minore implica che la nuova versione del pacchetto è retrocompatibile con il vecchio pacchetto e un aumento della versione principale implica che la nuova versione del pacchetto non è retrocompatibile con il vecchio pacchetto.
Concettualmente, un pacchetto può essere correlato a un altro pacchetto in uno dei seguenti modi:
- Per niente .
- Estensibilità compatibile con le versioni precedenti a livello di pacchetto . Ciò si verifica per le nuove versioni secondarie uprev (prossima revisione incrementata) di un pacchetto; il nuovo pacchetto ha lo stesso nome e la versione principale del vecchio pacchetto, ma una versione secondaria superiore. Funzionalmente, il nuovo pacchetto è un superset del vecchio pacchetto, il che significa:
- Le interfacce di primo livello del pacchetto padre sono presenti nel nuovo pacchetto, sebbene le interfacce possano avere nuovi metodi, nuovi UDT locali dell'interfaccia (l'estensione a livello di interfaccia descritta di seguito) e nuovi UDT in
types.hal
. - Nuove interfacce possono anche essere aggiunte al nuovo pacchetto.
- Tutti i tipi di dati del pacchetto padre sono presenti nel nuovo pacchetto e possono essere gestiti con i metodi (possibilmente reimplementati) del vecchio pacchetto.
- Nuovi tipi di dati possono anche essere aggiunti per l'uso con nuovi metodi di interfacce esistenti aggiornate o con nuove interfacce.
- Le interfacce di primo livello del pacchetto padre sono presenti nel nuovo pacchetto, sebbene le interfacce possano avere nuovi metodi, nuovi UDT locali dell'interfaccia (l'estensione a livello di interfaccia descritta di seguito) e nuovi UDT in
- Estensibilità retrocompatibile a livello di interfaccia . Il nuovo pacchetto può anche estendere il pacchetto originale consistendo in interfacce logicamente separate che forniscono semplicemente funzionalità aggiuntive e non quella principale. A tal fine, possono essere desiderabili:
- Le interfacce nel nuovo pacchetto richiedono il ricorso ai tipi di dati del vecchio pacchetto.
- Le interfacce nel nuovo pacchetto possono estendere le interfacce di uno o più vecchi pacchetti.
- Estendi l'originale incompatibilità con le versioni precedenti . Questo è un uprev della versione principale del pacchetto e non è necessario che ci sia alcuna correlazione tra i due. Nella misura in cui esiste, può essere espresso con una combinazione di tipi della versione precedente del pacchetto e l'ereditarietà di un sottoinsieme di interfacce del vecchio pacchetto.
Interfacce di strutturazione
Per un'interfaccia ben strutturata, l'aggiunta di nuovi tipi di funzionalità che non fanno parte del progetto originale dovrebbe richiedere una modifica all'interfaccia HIDL. Al contrario, se puoi o ti aspetti di apportare una modifica su entrambi i lati dell'interfaccia che introduca nuove funzionalità senza modificare l'interfaccia stessa, l'interfaccia non è strutturata.
Treble supporta fornitori e componenti di sistema compilati separatamente in cui vendor.img
su un dispositivo e system.img
possono essere compilati separatamente. Tutte le interazioni tra vendor.img
e system.img
devono essere definite in modo esplicito e completo in modo che possano continuare a funzionare per molti anni. Ciò include molte superfici API, ma una superficie importante è il meccanismo IPC utilizzato da HIDL per la comunicazione tra processi sul confine system.img
/ vendor.img
.
Requisiti
Tutti i dati passati tramite HIDL devono essere definiti in modo esplicito. Per garantire che un'implementazione e il cliente possano continuare a lavorare insieme anche se compilati separatamente o sviluppati in modo indipendente, i dati devono soddisfare i seguenti requisiti:
- Può essere descritto direttamente in HIDL (usando structs enums, ecc.) con nomi e significati semantici.
- Può essere descritto da uno standard pubblico come ISO/IEC 7816.
- Può essere descritto da uno standard hardware o da un layout fisico dell'hardware.
- Possono essere dati opachi (come chiavi pubbliche, ID, ecc.) se necessario.
Se vengono utilizzati dati opachi, devono essere letti solo da un lato dell'interfaccia HIDL. Ad esempio, se il codice vendor.img
fornisce a un componente di system.img
un messaggio di stringa o dati vec<uint8_t>
, tali dati non possono essere analizzati dallo stesso system.img
; può essere restituito solo a vendor.img
per l'interpretazione. Quando si passa un valore da vendor.img
a vendor code su system.img
o ad un altro dispositivo, il formato dei dati e il modo in cui devono essere interpretati devono essere descritti esattamente e fanno ancora parte dell'interfaccia .
Linee guida
Dovresti essere in grado di scrivere un'implementazione o un client di un HAL usando solo i file .hal (cioè non dovresti aver bisogno di guardare la sorgente Android o gli standard pubblici). Si consiglia di specificare l'esatto comportamento richiesto. Dichiarazioni come "un'implementazione può fare A o B" incoraggiano le implementazioni a intrecciarsi con i clienti con cui vengono sviluppate.
Layout del codice HIDL
HIDL include pacchetti core e vendor.
Le interfacce HIDL principali sono quelle specificate da Google. I pacchetti a cui appartengono iniziano con android.hardware.
e sono nominati dal sottosistema, potenzialmente con livelli di denominazione nidificati. Ad esempio, il pacchetto NFC è denominato android.hardware.nfc
e il pacchetto della fotocamera è android.hardware.camera
. In generale, un pacchetto principale ha il nome android.hardware.
[ name1
].[ name2
]…. I pacchetti HIDL hanno una versione in aggiunta al loro nome. Ad esempio, il pacchetto android.hardware.camera
potrebbe essere alla versione 3.4
; questo è importante, poiché la versione di un pacchetto influisce sul suo posizionamento nell'albero dei sorgenti.
Tutti i pacchetti principali sono posizionati in hardware/interfaces/
nel sistema di build. Il pacchetto android.hardware.
[ name1
].[ name2
]… alla versione $m.$n
è in hardware/interfaces/name1/name2/
… /$m.$n/
; il pacchetto android.hardware.camera
versione 3.4
si trova nella directory hardware/interfaces/camera/3.4/.
Esiste una mappatura hardcoded tra il prefisso del pacchetto android.hardware.
e il percorso hardware/interfaces/
.
I pacchetti non core (vendor) sono quelli prodotti dal fornitore SoC o dall'ODM. Il prefisso per i pacchetti non core è vendor.$(VENDOR).hardware.
dove $(VENDOR)
si riferisce a un fornitore SoC o OEM/ODM. Questo mappa al percorso vendor/$(VENDOR)/interfaces
nell'albero (questa mappatura è anche hardcoded).
Nomi di tipi definiti dall'utente completi
In HIDL, ogni UDT ha un nome completo che consiste nel nome UDT, il nome del pacchetto in cui è definito l'UDT e la versione del pacchetto. Il nome completo viene utilizzato solo quando vengono dichiarate le istanze del tipo e non dove viene definito il tipo stesso. Ad esempio, supponiamo che il pacchetto android.hardware.nfc,
la versione 1.0
definisca una struttura denominata NfcData
. Nel sito della dichiarazione (sia in types.hal
che all'interno della dichiarazione di un'interfaccia), la dichiarazione afferma semplicemente:
struct NfcData { vec<uint8_t> data; };
Quando si dichiara un'istanza di questo tipo (all'interno di una struttura dati o come parametro di metodo), utilizzare il nome del tipo completo:
android.hardware.nfc@1.0::NfcData
La sintassi generale è PACKAGE @ VERSION :: UDT
, dove:
-
PACKAGE
è il nome separato da punti di un pacchetto HIDL (ad esempio,android.hardware.nfc
). -
VERSION
è il formato major.minor-version separato da punti del pacchetto (ad esempio,1.0
). -
UDT
è il nome separato da punti di un UDT HIDL. Poiché HIDL supporta UDT nidificati e le interfacce HIDL possono contenere UDT (un tipo di dichiarazione nidificata), i punti vengono utilizzati per accedere ai nomi.
Ad esempio, se la seguente dichiarazione nidificata è stata definita nel file di tipi comuni nel pacchetto android.hardware.example
versione 1.0
:
// types.hal package android.hardware.example@1.0; struct Foo { struct Bar { // … }; Bar cheers; };
Il nome completo per Bar
è android.hardware.example@1.0::Foo.Bar
. Se, oltre ad essere nel pacchetto precedente, la dichiarazione annidata fosse in un'interfaccia chiamata IQuux
:
// IQuux.hal package android.hardware.example@1.0; interface IQuux { struct Foo { struct Bar { // … }; Bar cheers; }; doSomething(Foo f) generates (Foo.Bar fb); };
Il nome completo per Bar
è android.hardware.example@1.0::IQuux.Foo.Bar
.
In entrambi i casi, Bar
può essere indicato come Bar
solo nell'ambito della dichiarazione di Foo
. A livello di pacchetto o interfaccia, devi fare riferimento a Bar
tramite Foo
: Foo.Bar
, come nella dichiarazione del metodo doSomething
sopra. In alternativa, potresti dichiarare il metodo in modo più dettagliato come:
// IQuux.hal doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);
Valori di enumerazione completi
Se un UDT è un tipo enum, ogni valore del tipo enum ha un nome completo che inizia con il nome completo del tipo enum, seguito da due punti, quindi seguito dal nome del valore enum. Ad esempio, supponiamo che il pacchetto android.hardware.nfc,
versione 1.0
definisca un tipo enum NfcStatus
:
enum NfcStatus { STATUS_OK, STATUS_FAILED };
Quando si fa riferimento a STATUS_OK
, il nome completo è:
android.hardware.nfc@1.0::NfcStatus:STATUS_OK
La sintassi generale è PACKAGE @ VERSION :: UDT : VALUE
, dove:
-
PACKAGE @ VERSION :: UDT
è esattamente lo stesso nome completo per il tipo enum. -
VALUE
è il nome del valore.
Regole di autoinferenza
Non è necessario specificare un nome UDT completo. Un nome UDT può tranquillamente omettere quanto segue:
- Il pacchetto, ad esempio
@1.0::IFoo.Type
- Sia il pacchetto che la versione, ad esempio
IFoo.Type
HIDL tenta di completare il nome utilizzando le regole di interferenza automatica (un numero di regola più basso significa priorità più alta).
Regola 1
Se non vengono forniti pacchetto e versione, viene tentata una ricerca del nome locale. Esempio:
interface Nfc { typedef string NfcErrorMessage; send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m); };
NfcErrorMessage
viene ricercato localmente e viene trovato il typedef
sopra di esso. NfcData
viene ricercato localmente, ma poiché non è definito localmente, vengono utilizzate le regole 2 e 3. @1.0::NfcStatus
fornisce una versione, quindi la regola 1 non si applica.
Regola 2
Se la regola 1 ha esito negativo e manca un componente del nome completo (pacchetto, versione o pacchetto e versione), il componente viene compilato automaticamente con le informazioni del pacchetto corrente. Il compilatore HIDL cerca quindi nel file corrente (e in tutte le importazioni) per trovare il nome completo compilato automaticamente. Utilizzando l'esempio sopra, supponiamo che la dichiarazione di ExtendedNfcData
sia stata effettuata nello stesso pacchetto ( android.hardware.nfc
) nella stessa versione ( 1.0
) di NfcData
, come segue:
struct ExtendedNfcData { NfcData base; // … additional members };
Il compilatore HIDL compila il nome del pacchetto e il nome della versione dal pacchetto corrente per produrre il nome UDT completo android.hardware.nfc@1.0::NfcData
. Poiché il nome esiste nel pacchetto corrente (supponendo che sia importato correttamente), viene utilizzato per la dichiarazione.
Un nome nel pacchetto corrente viene importato solo se si verifica una delle seguenti condizioni:
- Viene importato in modo esplicito con un'istruzione
import
. - È definito in
types.hal
nel pacchetto corrente
Lo stesso processo viene seguito se NfcData
è stato qualificato solo dal numero di versione:
struct ExtendedNfcData { // autofill the current package name (android.hardware.nfc) @1.0::NfcData base; // … additional members };
Regola 3
Se la regola 2 non riesce a produrre una corrispondenza (l'UDT non è definito nel pacchetto corrente), il compilatore HIDL ricerca una corrispondenza all'interno di tutti i pacchetti importati. Usando l'esempio sopra, supponiamo che ExtendedNfcData
sia dichiarato nella versione 1.1
del pacchetto android.hardware.nfc
, 1.1
importi 1.0
come dovrebbe (vedi Estensioni a livello di pacchetto ) e la definizione specifica solo il nome UDT:
struct ExtendedNfcData { NfcData base; // … additional members };
Il compilatore cerca qualsiasi UDT denominato NfcData
e ne trova uno in android.hardware.nfc
alla versione 1.0
, risultando in un UDT completo di android.hardware.nfc@1.0::NfcData
. Se viene trovata più di una corrispondenza per un determinato UDT parzialmente qualificato, il compilatore HIDL genera un errore.
Esempio
Usando la regola 2, un tipo importato definito nel pacchetto corrente viene preferito rispetto a un tipo importato da un altro pacchetto:
// 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
viene interpolato comeandroid.hardware.bar@1.0::S
e si trova inbar/1.0/types.hal
(perchétypes.hal
viene importato automaticamente). -
IFooCallback
è interpolato comeandroid.hardware.bar@1.0::IFooCallback
usando la regola 2, ma non può essere trovato perchébar/1.0/IFooCallback.hal
non viene importato automaticamente (come lo ètypes.hal
). Pertanto, la regola 3 lo risolve inandroid.hardware.foo@1.0::IFooCallback
, che viene importato tramiteimport android.hardware.foo@1.0;
).
tipi.hal
Ogni pacchetto HIDL contiene un file types.hal
contenente UDT condivisi tra tutte le interfacce che partecipano a quel pacchetto. I tipi HIDL sono sempre pubblici; indipendentemente dal fatto che un UDT sia dichiarato in types.hal
o all'interno di una dichiarazione di interfaccia, questi tipi sono accessibili al di fuori dell'ambito in cui sono definiti. types.hal
non ha lo scopo di descrivere l'API pubblica di un pacchetto, ma piuttosto di ospitare gli UDT utilizzati da tutte le interfacce all'interno del pacchetto. A causa della natura di HIDL, tutti gli UDT fanno parte dell'interfaccia.
types.hal
è costituito da UDT e istruzioni di import
. Poiché types.hal
è disponibile per ogni interfaccia del pacchetto (è un'importazione implicita), queste istruzioni di import
sono per definizione a livello di pacchetto. Gli UDT in types.hal
possono anche incorporare gli UDT e le interfacce così importate.
Ad esempio, per un 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;
Vengono importati:
-
android.hidl.base@1.0::IBase
(implicitamente) -
android.hardware.foo@1.0::types
(implicitamente) - Tutto in
android.hardware.bar@1.0
(comprese tutte le interfacce e i suoitypes.hal
) -
types.hal
daandroid.hardware.baz@1.0::types
(le interfacce inandroid.hardware.baz@1.0
non vengono importate) -
IQux.hal
etypes.hal
daandroid.hardware.qux@1.0
-
Quuz
daandroid.hardware.quuz@1.0
(supponendo cheQuuz
sia definito intypes.hal
, l'intero filetypes.hal
viene analizzato, ma i tipi diversi daQuuz
non vengono importati).
Versioni a livello di interfaccia
Ogni interfaccia all'interno di un pacchetto risiede nel proprio file. Il pacchetto a cui appartiene l'interfaccia viene dichiarato nella parte superiore dell'interfaccia utilizzando l'istruzione package
. Dopo la dichiarazione del pacchetto, possono essere elencate zero o più importazioni a livello di interfaccia (pacchetto parziale o intero). Per esempio:
package android.hardware.nfc@1.0;
In HIDL, le interfacce possono ereditare da altre interfacce utilizzando la parola chiave extends
. Affinché un'interfaccia estenda un'altra interfaccia, deve accedervi tramite un'istruzione import
. Il nome dell'interfaccia da estendere (l'interfaccia di base) segue le regole per la qualificazione del nome del tipo spiegate sopra. Un'interfaccia può ereditare solo da un'interfaccia; HIDL non supporta l'ereditarietà multipla.
Gli esempi di versione uprev riportati di seguito utilizzano il pacchetto seguente:
// 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); }
Regole Uprev
Per definire un pacchetto package@major.minor
, A o tutto B deve essere vero:
Regola A | "È una versione minore iniziale": tutte le versioni minori precedenti, package@major.0 , package@major.1 , …, package@major.(minor-1) non devono essere definite. |
---|
Regola B | Tutto quanto segue è vero:
|
---|
A causa della regola A:
- Il pacchetto può iniziare con qualsiasi numero di versione minore (ad esempio,
android.hardware.biometrics.fingerprint
inizia a@2.1
.) - Il requisito "
android.hardware.foo@1.0
non è definito" significa che la directoryhardware/interfaces/foo/1.0
non dovrebbe nemmeno esistere.
Tuttavia, la regola A non ha effetto su un pacchetto con lo stesso nome di pacchetto ma una versione principale diversa (ad esempio, android.hardware.camera.device
ha sia @1.0
che @3.2
definiti; @3.2
non ha bisogno di interagire con @1.0
.) Quindi, @3.2::IExtFoo
può estendere @1.0::IFoo
.
A condizione che il nome del pacchetto sia diverso, package@major.minor::IBar
può estendersi da un'interfaccia con un nome diverso (ad esempio, android.hardware.bar@1.0::IBar
può estendere android.hardware.baz@2.2::IBaz
). Se un'interfaccia non dichiara esplicitamente un tipo super con la parola chiave extend
, estenderà android.hidl.base@1.0::IBase
(tranne IBase
stesso).
B.2 e B.3 devono essere seguite contemporaneamente. Ad esempio, anche se android.hardware.foo@1.1::IFoo
estende android.hardware.foo@1.0::IFoo
per superare la regola B.2, se un android.hardware.foo@1.1::IExtBar
estende android.hardware.foo@1.0::IBar
, questo non è ancora un uprev valido.
Miglioramento delle interfacce
Per aggiornare android.hardware.example@1.0
(definito sopra) a @1.1
:
// 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); }
Questa è import
a livello di pacchetto della versione 1.0
di android.hardware.example
in types.hal
. Sebbene non vengano aggiunti nuovi UDT nella versione 1.1
del pacchetto, sono ancora necessari riferimenti agli UDT nella versione 1.0
, quindi l'importazione a livello di pacchetto in types.hal
. (Lo stesso effetto avrebbe potuto essere ottenuto con un'importazione a livello di interfaccia in IQuux.hal
.)
In extends @1.0::IQuux
nella dichiarazione di IQuux
, abbiamo specificato la versione di IQuux
che viene ereditata (è richiesta la disambiguazione perché IQuux
viene utilizzato per dichiarare un'interfaccia ed ereditare da un'interfaccia). Poiché le dichiarazioni sono semplicemente nomi che ereditano tutti gli attributi del pacchetto e della versione nel sito della dichiarazione, la disambiguazione deve essere nel nome dell'interfaccia di base; avremmo potuto utilizzare anche l'UDT pienamente qualificato, ma sarebbe stato ridondante.
La nuova interfaccia IQuux
non dichiara nuovamente il metodo fromFooToBar()
, ma eredita da @1.0::IQuux
; elenca semplicemente il nuovo metodo che aggiunge fromBarToFoo()
. In HIDL, i metodi ereditati potrebbero non essere dichiarati nuovamente nelle interfacce figlio, quindi l'interfaccia IQuux
non può dichiarare esplicitamente il metodo fromFooToBar()
.
Convenzioni Uprev
A volte i nomi delle interfacce devono rinominare l'interfaccia di estensione. È consigliabile che le estensioni, gli struct e le unioni enum abbiano lo stesso nome di ciò che estendono a meno che non siano sufficientemente diversi da giustificare un nuovo nome. Esempi:
// 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 };
Se un metodo può avere un nuovo nome semantico (ad esempio fooWithLocation
), allora è preferibile. In caso contrario, dovrebbe essere chiamato in modo simile a ciò che sta estendendo. Ad esempio, il metodo foo_1_1
in @1.1::IFoo
può sostituire la funzionalità del metodo foo
in @1.0::IFoo
se non esiste un nome alternativo migliore.
Versione a livello di pacchetto
Il controllo delle versioni HIDL si verifica a livello di pacchetto; dopo che un pacchetto è stato pubblicato, è immutabile (il suo insieme di interfacce e UDT non può essere modificato). I pacchetti possono essere correlati tra loro in diversi modi, tutti esprimibili tramite una combinazione di ereditarietà a livello di interfaccia e costruzione di UDT per composizione.
Tuttavia, un tipo di relazione è rigorosamente definito e deve essere applicato: Ereditarietà compatibile con le versioni precedenti a livello di pacchetto . In questo scenario, il pacchetto padre è il pacchetto da cui viene ereditato e il pacchetto figlio è quello che estende il pacchetto padre. Le regole di ereditarietà compatibili con le versioni precedenti a livello di pacchetto sono le seguenti:
- Tutte le interfacce di primo livello del pacchetto padre vengono ereditate dalle interfacce nel pacchetto figlio.
- Nuove interfacce possono anche essere aggiunte al nuovo pacchetto (nessuna restrizione sulle relazioni con altre interfacce in altri pacchetti).
- Nuovi tipi di dati possono anche essere aggiunti per l'uso con nuovi metodi di interfacce esistenti aggiornate o con nuove interfacce.
Queste regole possono essere implementate usando l'ereditarietà a livello di interfaccia HIDL e la composizione UDT, ma richiedono una conoscenza di meta-livello per sapere che queste relazioni costituiscono un'estensione del pacchetto compatibile con le versioni precedenti. Questa conoscenza è dedotta come segue:
Se un pacchetto soddisfa questo requisito, hidl-gen
applica le regole di compatibilità con le versioni precedenti.