Controllo delle versioni

HIDL richiede che ogni interfaccia scritta in HIDL abbia la versione. Dopo la pubblicazione, l'interfaccia HAL viene bloccata e qualsiasi ulteriore modifica dovrà essere apportata a una nuova versione dell'interfaccia. Sebbene una determinata interfaccia pubblicata non possa essere modificata, può essere estesa con 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 un insieme 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 da una versione e include quanto segue:
    • File di definizione del tipo di dati types.hal .
    • Zero o più interfacce, ciascuna nel proprio file .hal .

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 del pacchetto.

Filosofia del controllo delle versioni

Un pacchetto HIDL (come android.hardware.nfc ), dopo essere stato pubblicato per una determinata versione (come 1.0 ), è immutabile; non può essere cambiato. Modifiche alle interfacce del pacchetto o eventuali modifiche ai suoi 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 del pacchetto seguono il controllo delle versioni semantico senza il livello di patch e i componenti dei metadati di build. All'interno di un dato pacchetto, un aumento di versione minore implica che la nuova versione del pacchetto è compatibile con il vecchio pacchetto e un aumento di versione maggiore implica che la nuova versione del pacchetto non è compatibile con il vecchio pacchetto.

Concettualmente, un pacchetto può relazionarsi a un altro pacchetto in diversi modi:

  • Affatto .
  • Estensibilità compatibile con le versioni precedenti a livello di pacchetto . Ciò si verifica per i nuovi uprev della versione minore (prossima revisione incrementata) di un pacchetto; il nuovo pacchetto ha lo stesso nome e versione principale del vecchio pacchetto, ma una versione secondaria più elevata. Funzionalmente, il nuovo pacchetto è un superset del vecchio pacchetto, ovvero:
    • Le interfacce di livello superiore del pacchetto genitore 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 .
    • Al nuovo pacchetto potrebbero essere aggiunte anche nuove interfacce.
    • Tutti i tipi di dati del pacchetto genitore sono presenti nel nuovo pacchetto e possono essere gestiti dai metodi (eventualmente reimplementati) del vecchio pacchetto.
    • È inoltre possibile aggiungere nuovi tipi di dati da utilizzare con nuovi metodi di aggiornamento delle interfacce esistenti o con nuove interfacce.
  • 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 questo scopo può essere auspicabile quanto segue:
    • Le interfacce nel nuovo pacchetto necessitano del 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'incompatibilità con le versioni precedenti originale . Si tratta di una versione principale del pacchetto e non è necessaria alcuna correlazione tra i due. Nella misura in cui esiste, può essere espresso con una combinazione di tipi della versione precedente del pacchetto e con 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 prevedi di apportare una modifica su entrambi i lati dell'interfaccia che introduca nuove funzionalità senza modificare l'interfaccia stessa, allora 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 esplicitamente. Per garantire che un'implementazione e un cliente possano continuare a lavorare insieme anche se compilati separatamente o sviluppati in modo indipendente, i dati devono rispettare i seguenti requisiti:

  • Può essere descritto direttamente in HIDL (utilizzando struct enum, 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.
  • Se necessario, possono essere dati opachi (come chiavi pubbliche, ID, ecc.).

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 su system.img un messaggio di stringa o dati vec<uint8_t> , tali dati non possono essere analizzati da system.img stesso; può essere restituito solo a vendor.img per l'interpretazione. Quando si passa un valore da vendor.img al codice del fornitore su system.img o ad un altro dispositivo, il formato dei dati e come deve essere interpretato deve essere descritto esattamente e fa ancora parte dell'interfaccia .

Linee guida

Dovresti essere in grado di scrivere un'implementazione o un client di un HAL utilizzando solo i file .hal (ovvero non dovresti aver bisogno di consultare il codice sorgente Android o gli standard pubblici). Si consiglia di specificare l'esatto comportamento richiesto. Affermazioni come "un'implementazione può fare A o B" incoraggiano le implementazioni a intrecciarsi con i clienti con cui sono sviluppate.

Disposizione del codice HIDL

HIDL include pacchetti core e fornitori.

Le interfacce HIDL principali sono quelle specificate da Google. I pacchetti a cui appartengono iniziano 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 il pacchetto fotocamera è android.hardware.camera . In generale, un pacchetto core ha il nome android.hardware. [ name1 ].[ name2 ]…. I pacchetti HIDL hanno una versione oltre al nome. Ad esempio, il pacchetto android.hardware.camera potrebbe essere alla versione 3.4 ; questo è importante, poiché la versione di un pacchetto influenza la sua posizione nell'albero dei sorgenti.

Tutti i pacchetti principali sono posizionati in hardware/interfaces/ nel sistema di compilazione. Il pacchetto android.hardware. [ name1 ].[ name2 ]… nella versione $m.$n si trova 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 (fornitore) sono quelli prodotti dal fornitore del 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 (anche questa mappatura è codificata).

Nomi di tipo completo definiti dall'utente

In HIDL, ogni UDT ha un nome completo costituito dal nome dell'UDT, dal nome del pacchetto in cui è definito l'UDT e dalla versione del pacchetto. Il nome completo viene utilizzato solo quando vengono dichiarate istanze del tipo e non dove viene definito il tipo stesso. Ad esempio, supponiamo che il pacchetto android.hardware.nfc, 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 dichiari un'istanza di questo tipo (all'interno di una struttura dati o come parametro di un metodo), utilizza 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 del pacchetto separato da punti (ad esempio, 1.0 ).
  • UDT è il nome separato da punti di un UDT HIDL. Poiché HIDL supporta UDT annidati e le interfacce HIDL possono contenere UDT (un tipo di dichiarazione annidata), per accedere ai nomi vengono utilizzati i punti.

Ad esempio, se la seguente dichiarazione nidificata è stata definita nel file dei 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 nidificata 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 di 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 tipo definito dall'utente è 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 e 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 auto-interferenza (un numero di regola più basso indica una priorità più alta).

Regola 1

Se non vengono forniti alcun 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 cercato localmente e viene trovato il typedef sopra di esso. Anche NfcData viene cercato 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 fallisce 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 precedente, presupponiamo 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 inserisce il nome del pacchetto e il nome della versione del pacchetto corrente per produrre il nome UDT completo android.hardware.nfc@1.0::NfcData . Poiché il nome esiste nel pacchetto corrente (assumendo 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 esplicitamente 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 cerca una corrispondenza all'interno di tutti i pacchetti importati. Utilizzando l'esempio precedente, presupponiamo che ExtendedNfcData sia dichiarato nella versione 1.1 del pacchetto android.hardware.nfc , 1.1 importi 1.0 come dovrebbe (vedere 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

Utilizzando la regola 2, un tipo importato definito nel pacchetto corrente viene favorito 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 è interpolato come android.hardware.bar@1.0::S e si trova in bar/1.0/types.hal ( types.hal viene importato automaticamente).
  • IFooCallback è interpolato come android.hardware.bar@1.0::IFooCallback utilizzando 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 invece in android.hardware.foo@1.0::IFooCallback , che viene importato tramite import android.hardware.foo@1.0; ).

tipi.hal

Ogni pacchetto HIDL contiene un file types.hal contenente gli 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 è pensato per descrivere l'API pubblica di un pacchetto, ma piuttosto per 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 import . types.hal è reso disponibile per ogni interfaccia del pacchetto (è un'importazione implicita), queste istruzioni import sono per definizione a livello di pacchetto. Gli UDT in types.hal possono anche incorporare UDT e 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 (incluse tutte le interfacce e i suoi types.hal )
  • types.hal da android.hardware.baz@1.0::types (le interfacce in android.hardware.baz@1.0 non vengono importate)
  • IQux.hal types.hal da android.hardware.qux@1.0
  • Quuz da android.hardware.quuz@1.0 (supponendo che Quuz sia definito types.hal , l'intero file types.hal viene analizzato, ma i tipi diversi da Quuz non vengono importati).

Controllo delle 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 possa estendere un'altra interfaccia, deve avere accesso ad essa 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 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 Upv

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.
O
Regola B

È vero tutto quanto segue:

  1. "La versione minore precedente è valida": package@major.(minor-1) deve essere definito e seguire la stessa regola A (nessuno da package@major.0 a package@major.(minor-2) è definito) o regola B (se è un uprev da @major.(minor-2) );

    E

  2. "Eredita almeno un'interfaccia con lo stesso nome": esiste un'interfaccia package@major.minor::IFoo che 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 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 tale che non esista alcuna IBar con un k più piccolo.

A causa della regola A:

  • Il pacchetto può iniziare con qualsiasi numero di versione minore (ad esempio, android.hardware.biometrics.fingerprint inizia con @2.1 .)
  • Il requisito " android.hardware.foo@1.0 non è definito" significa che la directory hardware/interfaces/foo/1.0 non dovrebbe nemmeno esistere.

Tuttavia, la regola A non influisce su un pacchetto con lo stesso nome ma con 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 supertipo con la parola chiave extend , estenderà android.hidl.base@1.0::IBase (eccetto 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 passare 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.

Migliorare le 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 . Anche se nella versione 1.1 del pacchetto non vengono aggiunti nuovi UDT, i riferimenti agli UDT nella versione 1.0 sono ancora necessari, da qui 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 (è necessario chiarire le ambiguità perché IQuux viene utilizzato per dichiarare un'interfaccia ed ereditare da un'interfaccia). Dato che 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 completo, ma sarebbe stato ridondante.

La nuova interfaccia IQuux non dichiara nuovamente il metodo fromFooToBar() che 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 figlie, quindi l'interfaccia IQuux non può dichiarare esplicitamente il metodo fromFooToBar() .

Convenzioni uprev

A volte i nomi delle interfacce devono rinominare l'interfaccia estesa. Consigliamo che le estensioni, le strutture e le unioni di enumerazione abbiano lo stesso nome di ciò che estendono, a meno che non siano sufficientemente diverse 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 ), è preferibile. Altrimenti, 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.

Controllo delle versioni a livello di pacchetto

Il controllo delle versioni HIDL avviene 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 relazionarsi tra loro in diversi modi, tutti esprimibili tramite una combinazione di ereditarietà a livello di interfaccia e creazione di UDT per composizione.

Tuttavia, un tipo di relazione è rigorosamente definita e deve essere applicata: l'ereditarietà compatibile con le versioni precedenti a livello di pacchetto . In questo scenario, il pacchetto genitore è il pacchetto da cui si eredita e il pacchetto figlio è quello che estende il genitore. Le regole di ereditarietà compatibili con le versioni precedenti a livello di pacchetto sono le seguenti:

  1. Tutte le interfacce di livello superiore del pacchetto padre vengono ereditate dalle interfacce del pacchetto figlio.
  2. È possibile aggiungere anche nuove interfacce al nuovo pacchetto (nessuna restrizione sulle relazioni con altre interfacce in altri pacchetti).
  3. È inoltre possibile aggiungere nuovi tipi di dati da utilizzare con nuovi metodi di aggiornamento delle interfacce esistenti o con nuove interfacce.

Queste regole possono essere implementate utilizzando l'ereditarietà a livello di interfaccia HIDL e la composizione UDT, ma richiedono conoscenze a livello meta per sapere che queste relazioni costituiscono un'estensione del pacchetto compatibile con le versioni precedenti. Questa conoscenza si deduce come segue:

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