Controllo delle versioni dell'interfaccia

HIDL richiede che ogni interfaccia scritta in HIDL sia sottoposta al controllo delle versioni. Dopo un HAL a riga di comando è pubblicata, è bloccata ed eventuali altre modifiche devono essere apportate nuova versione dell'interfaccia. Una determinata interfaccia pubblicata modificato, può essere esteso tramite un'altra interfaccia.

Struttura del codice HIDL

Il codice HIDL è organizzato in termini definiti dall'utente di tipi, interfacce e pacchetti:

  • Tipi definiti dall'utente. L'architettura HIDL fornisce accesso a una serie tipi di dati primitivi che possono essere utilizzati per comporre tipi più complessi tramite strutture, unioni ed enumerazioni. Gli UDT vengono passati ai metodi interfacce e può essere definito a livello di pacchetto (comune a tutti interfacce) o localmente a un'interfaccia.
  • Interfacce. Come componente di base dell'architettura HIDL, un'interfaccia è composto da dichiarazioni UDT e di metodo. Le interfacce possono ereditare anche un'altra interfaccia.
  • Pacchetti. Organizza le interfacce HIDL correlate e i dati tipi su cui operano. Un pacchetto è identificato da un nome e da una versione e include:
    • File di definizione del tipo di dati denominato types.hal.
    • Zero o più interfacce, ciascuna nel proprio file .hal.

Il file di definizione del tipo di dati types.hal contiene solo funzioni definite dall'utente (tutte gli UDT a livello di pacchetto vengono conservati in un unico file). Rappresentazioni nel target sono disponibili per tutte le interfacce del pacchetto.

Filosofia del controllo delle versioni

Un pacchetto HIDL (ad esempio android.hardware.nfc), dopo essere stato pubblicato per una determinata versione (ad esempio 1.0), è immutabile; questo elemento non possono essere modificate. Modifiche alle interfacce nel pacchetto o a qualsiasi le modifiche agli 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 le UDT di un pacchetto condividono la stessa versione. Pacco le versioni seguono la semantica controllo delle versioni senza i componenti a livello di patch e build-metadata. All'interno di un oggetto dato pacchetto, un bump di versione secondaria implica che la nuova versione è compatibile con le versioni precedenti del pacchetto e con un versione implica che la nuova versione del pacchetto compatibile con le versioni precedenti del pacchetto.

Concettualmente, un pacchetto può essere correlato a un altro in uno dei seguenti modi:

  • Assolutamente no.
  • Estensibilità compatibile con le versioni precedenti a livello di pacchetto. Questo si verifica per i nuovi uprev della versione secondaria (la prossima revisione incrementata) di un pacchetto; il nuovo pacchetto ha lo stesso nome e la stessa versione principale del vecchio pacchetto, ma una una versione secondaria superiore. A livello di funzionalità, il nuovo pacchetto è un soprainsieme del vecchio pacchetto, cioè:
    • Le interfacce di primo livello del pacchetto principale sono presenti nel nuovo pacchetto, sebbene le interfacce possano avere nuovi metodi, nuovi UDT locali dell'interfaccia (il a livello di interfaccia descritta di seguito) e nuovi UDT in types.hal.
    • Puoi anche aggiungere nuove interfacce al nuovo pacchetto.
    • Tutti i tipi di dati del pacchetto principale sono presenti nel nuovo pacchetto e può essere gestito con i metodi (possibilmente reimplementati) del pacchetto precedente.
    • È possibile aggiungere nuovi tipi di dati anche per l'utilizzo con entrambi i nuovi metodi di aumento delle entrate interfacce esistenti o di nuove interfacce.
  • Estensibilità compatibile con le versioni precedenti a livello di interfaccia. Il nuovo può anche estendere il pacchetto originale consistendo in pacchetti logicamente separati che forniscono semplicemente funzionalità aggiuntive e non quella principale. A questo scopo, potrebbe essere opportuno quanto segue:
    • Le interfacce nel nuovo pacchetto devono fare ricorso ai tipi di dati del vecchio pacchetto pacchetto.
    • Le interfacce nei nuovi pacchetti possono estendere le interfacce di una o più vecchie pacchetti.
  • Estendi l'incompatibilità con le versioni precedenti dell'originale. Si tratta di un l'uprev della versione principale del pacchetto e non c'è alcuna correlazione tra i due. In alcuni casi, può essere espressa con una combinazione tipi dalla versione precedente del pacchetto ed ereditarietà di un sottoinsieme le interfacce più vecchie del pacchetto.

Struttura dell'interfaccia

Per un'interfaccia ben strutturata, l'aggiunta di nuovi tipi di funzionalità che che non fanno parte del design originale, dovrebbero essere necessarie una modifica del a riga di comando. Al contrario, se puoi o aspettarti di apportare una modifica a entrambi i lati che introduce nuove funzionalità senza modificare l'interfaccia l'interfaccia non è strutturata.

Treble supporta componenti del fornitore e di sistema compilati separatamente, in cui vendor.img su un dispositivo e system.img può essere da compilare separatamente. Tutte le interazioni tra vendor.img e Il campo system.img deve essere definito in modo esplicito ed esaustivo in modo che possa continuano a funzionare per molti anni. Ciò include molte piattaforme API, ma una delle principali è il meccanismo IPC utilizzato da HIDL per la comunicazione tra processi Confine system.img/vendor.img.

Requisiti

Tutti i dati trasmessi attraverso l'HIDL devono essere definiti esplicitamente. Per garantire che di implementazione e il client possono continuare a lavorare insieme anche separatamente o in modo indipendente, i dati devono rispettare le seguenti requisiti:

  • Può essere descritto direttamente in HIDL (utilizzando enum struct e così via) 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.
  • I dati possono essere opachi (come chiavi pubbliche, ID e così via), se necessario.

Se vengono utilizzati dati opachi, devono essere letti solo da un lato dell'HIDL a riga di comando. Ad esempio, se il codice vendor.img fornisce un componente nella system.img un messaggio di stringa o vec<uint8_t> dati, tali dati non possono essere analizzati dal system.img stesso; può deve essere restituito solo a vendor.img per l'interpretazione. Quando passando un valore pari a vendor.img al codice del fornitore system.img o a un altro dispositivo, il formato dei dati e come deve essere interpretato, deve essere descritto esattamente e fa ancora parte del a riga di comando.

Linee guida

Dovresti essere in grado di scrivere un'implementazione o un client di un HAL utilizzando solo i file .hal (ad esempio, non è necessario consultare il codice sorgente Android o standard). Ti consigliamo di specificare il comportamento richiesto esatto. Dichiarazioni quali "Un'implementazione può fare A o B" incoraggiare le implementazioni a diventare intrecciati con i clienti con cui sono sviluppati.

Layout codice HIDL

HIDL include pacchetti di base e del fornitore.

Le interfacce HIDL principali sono quelle specificate da Google. I pacchetti a cui appartengono inizino con android.hardware. e sono denominati in base al sottosistema, potenzialmente con livelli di denominazione nidificati. Ad esempio, il pacchetto NFC è denominato android.hardware.nfc e la confezione della fotocamera è android.hardware.camera. In generale, un pacchetto principale ha il nome android.hardware.[name1].[name2]... Oltre al nome, i pacchetti HIDL hanno una versione. Ad esempio, il pacchetto android.hardware.camera potrebbe essere alla versione 3.4; questo è è importante, in quanto la versione di un pacchetto influisce sul suo posizionamento nell'albero di origine.

Tutti i pacchetti principali si trovano sotto hardware/interfaces/ nella di un sistema di compilazione. Il pacchetto android.hardware.[name1].[name2]... alla versione $m.$n è inferiore a hardware/interfaces/name1/name2/.../$m.$n/; pacco La versione 3.4 di android.hardware.camera si trova nella directory hardware/interfaces/camera/3.4/. Esiste una mappatura hard-coded tra il prefisso del pacchetto android.hardware. e il percorso hardware/interfaces/.

I pacchetti non principali (fornitore) sono quelli prodotti dal fornitore SoC o ODM. La il prefisso per i pacchetti non principali è vendor.$(VENDOR).hardware. dove $(VENDOR)Si riferisce a un fornitore di SoC o a un OEM/ODM. Questo viene mappato al percorso vendor/$(VENDOR)/interfaces nell'albero (questa mappatura è anche hardcoded).

Nomi di tipo definiti dall'utente completi

Nelle HIDL, ogni UDT ha un nome completo composto dal nome della stessa, il nome del pacchetto in cui è definita la funzione definita dall'utente e la versione del pacchetto. La il nome completo viene utilizzato solo quando vengono dichiarate istanze del tipo non dove viene definito il tipo stesso. Ad esempio, supponi pacchetto La versione 1.0 di android.hardware.nfc, definisce uno struct denominato NfcData. Sul sito della dichiarazione (sia types.hal o all'interno della dichiarazione di un'interfaccia), la dichiarazione afferma semplicemente:

struct NfcData {
    vec<uint8_t> data;
};

Quando dichiari un'istanza di questo tipo (all'interno di una struttura di dati o come parametro del metodo), utilizza il nome completo del tipo:

android.hardware.nfc@1.0::NfcData

La sintassi generale è PACKAGE@VERSION::UDT, dove:

  • PACKAGE è il nome separato da punti di un pacchetto HIDL (ad es. android.hardware.nfc).
  • VERSION è la versione main.minor separata da punti formato del pacchetto (ad es. 1.0).
  • UDT è il nome separato da un punto di una funzione HIDL UDT. Poiché HIDL supporta gli UDT nidificati e le interfacce HIDL possono contenere UDT (un tipo dichiarazione nidificata), i punti vengono utilizzati per accedere ai nomi.

Ad esempio, se la seguente dichiarazione nidificata è stata definita nel file di tipi 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 di Bar è android.hardware.example@1.0::Foo.Bar. Se, oltre a essere nel al pacchetto precedente, la dichiarazione nidificata era in un'interfaccia 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 di Bar è android.hardware.example@1.0::IQuux.Foo.Bar.

In entrambi i casi, Bar può essere definito solo come Bar nell'ambito della dichiarazione di Foo. Al pacco o a livello di interfaccia, devi fare riferimento a Bar tramite Foo: Foo.Bar, come nella dichiarazione del metodo doSomething in alto. 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 una funzione UDT è di tipo enum, ogni valore del tipo enum ha un nome completo che inizia con il nome completo del tipo enum, seguito dai due punti e dal nome del valore enum. Ad esempio: presupponi pacchetto android.hardware.nfc, versione 1.0 definisce un tipo di 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 è il esattamente lo stesso nome completo per il tipo di enum.
  • VALUE è il nome del valore.

Regole di inferenza automatica

Non è necessario specificare un nome UDT completo. Un nome UDT può omettere in sicurezza quanto segue:

  • Il pacchetto, ad esempio @1.0::IFoo.Type
  • Sia pacchetto che versione, ad es. IFoo.Type
di Gemini Advanced.

HIDL tenta di completare il nome utilizzando regole di interferenza automatica (regola inferiore rappresenta una priorità più alta).

Regola 1

Se non vengono forniti alcun pacchetto e versione, si cerca di eseguire una ricerca del nome locale. Esempio:

interface Nfc {
    typedef string NfcErrorMessage;
    send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m);
};

La ricerca di NfcErrorMessage viene eseguita a livello locale, mentre typedef sopra. NfcData viene cercato a livello locale, ma così com'è non definiti localmente, vengono utilizzate le regole 2 e 3. @1.0::NfcStatus fornisce una versione, pertanto la regola 1 non si applica.

Regola 2

Se la regola 1 non riesce e manca un componente del nome completo (pacchetto, versione o pacchetto e versione), il componente viene compilato automaticamente le informazioni del pacchetto corrente. Il compilatore HIDL cerca quindi il file corrente (e tutte le importazioni) per trovare il nome completo compilato automaticamente. Utilizzando l'esempio precedente, presupponi la dichiarazione ExtendedNfcData. è stata effettuata nello stesso pacchetto (android.hardware.nfc) con (1.0) come NfcData, come segue:

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

Il compilatore HIDL compila il nome del pacchetto e il nome della versione pacchetto attuale per produrre il nome UDT completo android.hardware.nfc@1.0::NfcData. Poiché il nome esiste nel pacchetto corrente (se importato correttamente), viene utilizzato per dichiarazione.

Un nome nel pacchetto corrente viene importato solo se uno dei seguenti è vero:

  • Viene importata in modo esplicito con un'istruzione import.
  • È definito in types.hal nel pacchetto attuale

La stessa procedura viene seguita se NfcData è stato qualificato solo il numero della versione:

struct ExtendedNfcData {
    // autofill the current package name (android.hardware.nfc)
    @1.0::NfcData base;
    // … additional members
};

Regola 3

Se la regola 2 non produce una corrispondenza (la funzione UDT non è definita nella ), il compilatore HIDL cerca una corrispondenza all'interno di tutti i pacchetti importati. Utilizzando l'esempio precedente, supponiamo che ExtendedNfcData sia dichiarato in versione 1.1 del pacchetto android.hardware.nfc, 1.1 importa 1.0 come dovrebbe (vedi estensioni a livello di pacchetto) e la definizione specifica solo il nome dell'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, con conseguente UDT completo di android.hardware.nfc@1.0::NfcData. Se viene trovata una corrispondenza per un determinato UDT parzialmente qualificato, il compilatore HIDL genera un errore.

Esempio

Utilizzando la regola 2, un tipo importato definito nel pacchetto corrente è 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 come android.hardware.bar@1.0::S e si trova in bar/1.0/types.hal (perché types.hal viene automaticamente importati).
  • IFooCallback viene interpolato come android.hardware.bar@1.0::IFooCallback utilizza la regola 2, ma impossibile trovare perché bar/1.0/IFooCallback.hal non è importato automaticamente (come è types.hal). Pertanto, la regola 3 lo risolve android.hardware.foo@1.0::IFooCallback, che è stata importata tramite import android.hardware.foo@1.0;).

type.hal

Ogni pacchetto HIDL contiene un file types.hal contenente gli UDT condivisi tra tutte le interfacce che fanno parte del pacchetto. Tipi HIDL sono sempre pubblici; a prescindere dalla dichiarazione di una funzione types.hal o all'interno di una dichiarazione dell'interfaccia, questi tipi sono accessibili al di fuori dell'ambito in cui sono definite. types.hal non ha lo scopo di descrivere l'API pubblica di un pacchetto, ma piuttosto di ospitare UDT utilizzata da tutte le interfacce all'interno del pacchetto. A causa della natura delle regole HIDL, tutti gli UDT fanno parte dell'interfaccia.

types.hal è composto da UDT e istruzioni import. Poiché types.hal è disponibile per ogni interfaccia del (è un'importazione implicita), queste istruzioni import vengono a livello di pacchetto per definizione. Gli UDT in types.hal possono anche incorporare gli UDT e le interfacce così importati.

Ad esempio, per 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 i seguenti dati:

  • android.hidl.base@1.0::IBase (implicitamente)
  • android.hardware.foo@1.0::types (implicitamente)
  • Tutto in android.hardware.bar@1.0 (inclusi tutti e relative types.hal)
  • types.hal da android.hardware.baz@1.0::types (le interfacce in android.hardware.baz@1.0 non vengono importate)
  • IQux.hal e types.hal da android.hardware.qux@1.0
  • Quuz da android.hardware.quuz@1.0 (supponendo Quuz è definito in types.hal, l'intero types.hal file viene analizzato, ma tipi diversi da Quuz non vengono importate).

Controllo delle versioni a livello di interfaccia

Ogni interfaccia all'interno di un pacchetto risiede in un proprio file. Il pacchetto a cui appartiene l'interfaccia è dichiarata nella parte superiore dell'interfaccia utilizzando package. Dopo la dichiarazione relativa al pacchetto, zero o più valori a livello di interfaccia (il pacchetto completo o parziale). Ad esempio:

package android.hardware.nfc@1.0;

Nelle interfacce HIDL, le interfacce possono ereditare da altre interfacce utilizzando extends parola chiave. Affinché un'interfaccia estenda un'altra interfaccia, devono avervi accesso tramite un'istruzione import. Il nome del dell'interfaccia utente estesa (l'interfaccia di base) segue le regole di type-name sulla qualifica descritta sopra. Un'interfaccia può ereditare una sola interfaccia; HIDL non supporta l'ereditarietà multipla.

Gli esempi di controllo delle versioni uprev riportati di seguito utilizzano il seguente pacchetto:

// 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 di uprev

Per definire un pacchetto package@major.minor, uno o tutti i componenti B deve essere vero:

Regola A "Is a start minor version" (È una versione secondaria iniziale): tutte le versioni secondarie precedenti, package@major.0, package@major.1, ..., Non è necessario definire package@major.(minor-1).
OPPURE
Regola B

Tutte le seguenti condizioni sono vere:

  1. "Versione secondaria precedente valida": package@major.(minor-1) devono essere definiti e seguire la stessa regola A (nessuno di Da package@major.0 a package@major.(minor-2) sono definiti) o la regola B (se è un aumento di entrate rispetto a @major.(minor-2));

    E

  2. "Eredita almeno un'interfaccia con lo stesso nome": esiste un'opzione un'interfaccia package@major.minor::IFoo che si estende package@major.(minor-1)::IFoo (se il pacchetto precedente ha un'interfaccia);

    E

  3. "Nessuna interfaccia ereditata con un nome diverso": non deve esistere package@major.minor::IBar che si estende package@major.(minor-1)::IBaz, dove IBar e IBaz sono due nomi diversi. Se esiste un'interfaccia con lo stesso nome, package@major.minor::IBar deve estendere package@major.(minor-k)::IBar in modo che non esistano IBar con un k più piccolo.

A causa della regola A:

  • Il pacchetto può iniziare con qualsiasi numero di versione secondario (ad esempio, android.hardware.biometrics.fingerprint inizia alle ore @2.1.
  • Il requisito "android.hardware.foo@1.0 non è definito" significa la directory hardware/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 su un versione principale diversa (ad esempio, android.hardware.camera.device ha sia @1.0 che @3.2 definito; @3.2 non deve interagire con @1.0. Pertanto, @3.2::IExtFoo può estendere @1.0::IFoo.

Se il nome del pacchetto è diverso, package@major.minor::IBar può estendersi da un'interfaccia con un un nome diverso (ad esempio, android.hardware.bar@1.0::IBar può estendi android.hardware.baz@2.2::IBaz). Se un'interfaccia non dichiarare esplicitamente un super tipo con la parola chiave extend, si estende android.hidl.base@1.0::IBase (tranne IBase ).

I punti B.2 e B.3 devono essere seguiti contemporaneamente. Ad esempio, anche se android.hardware.foo@1.1::IFoo si estende android.hardware.foo@1.0::IFoo per superare la regola B.2, se Si estende android.hardware.foo@1.1::IExtBar android.hardware.foo@1.0::IBar, questo valore non è ancora valido.

Interfacce Uprev

Per aumentare le entrate di android.hardware.example@1.0 (definita 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);
}

Questo è un import a livello di pacchetto della versione 1.0 di android.hardware.example a types.hal. Anche se non esistono nuovi Le funzioni UDT vengono aggiunte nella versione 1.1 del pacchetto, mentre i riferimenti alle funzioni definite dall'utente vengono aggiunti è ancora necessaria la versione 1.0, di conseguenza l'importazione a livello di pacchetto a types.hal. (Lo stesso effetto si sarebbe potuto ottenere con una importazione a livello di interfaccia in IQuux.hal.)

In extends @1.0::IQuux, nella dichiarazione di IQuux, abbiamo specificato la versione di IQuux attualmente ereditati (è necessaria la disambiguazione perché IQuux è utilizzato dichiarare un'interfaccia ed ereditare da un'interfaccia). Poiché le dichiarazioni sono semplicemente i nomi che ereditano tutti gli attributi di pacchetto e versione sul sito la disambiguazione deve essere indicata nel nome dell'interfaccia di base; noi avrebbe potuto utilizzare anche l'UDT completo, ma sarebbe stato così ridondante.

La nuova interfaccia IQuux non richiede una nuova dichiarazione del metodo fromFooToBar() che eredita da @1.0::IQuux; perché indica il nuovo metodo aggiunto fromBarToFoo(). In HIDL, ereditato metodi non possono essere dichiarati nuovamente nelle interfacce figlio, l'interfaccia IQuux non può dichiarare fromFooToBar() in modo esplicito.

Convenzioni di uprev

A volte i nomi di interfaccia devono rinominare l'interfaccia di estensione. I nostri suggerimenti che le estensioni, gli struct e le unioni enum hanno 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, in modo simile a quello 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 un nome alternativo.

Controllo delle versioni a livello di pacchetto

Il controllo delle versioni HIDL avviene a livello di pacchetto. dopo la pubblicazione di un pacchetto, è immutabile (il suo insieme di interfacce e UDT non può essere modificato). I pacchetti possono si relazionano tra loro in vari modi, che possono essere espressi mediante combinazione di ereditarietà a livello di interfaccia e creazione di funzioni definite dall'utente per composizione.

Tuttavia, un tipo di relazione è rigorosamente definito e deve essere applicato in modo forzato: Ereditarietà compatibile con le versioni precedenti a livello di pacchetto. In questo scenario, parent rappresenta il pacchetto ereditato da e Il pacchetto child è quello che estende il pacchetto principale. A livello di pacchetto le regole di ereditarietà compatibili con le versioni precedenti sono le seguenti:

  1. Tutte le interfacce di primo livello del pacchetto principale vengono ereditate dalle interfacce nel pacchetto secondario.
  2. È inoltre possibile aggiungere nuove interfacce al nuovo pacchetto (senza limitazioni su relazioni con altre interfacce di altri pacchetti).
  3. È possibile aggiungere nuovi tipi di dati anche per l'utilizzo con entrambi i nuovi metodi di aumento delle entrate interfacce esistenti o di nuove interfacce.

Queste regole possono essere implementate utilizzando l'ereditarietà a livello di interfaccia HIDL e le funzioni UDT composizione, ma sono necessarie conoscenze di meta-livello per conoscere queste relazioni rappresentano un'estensione del pacchetto compatibile con le versioni precedenti. Queste conoscenze vengono dedotte come segue:

Se un pacchetto soddisfa questo requisito, hidl-gen applica di compatibilità con le versioni precedenti.