Controllo delle versioni dell'interfaccia

HIDL richiede che ogni interfaccia scritta in HIDL sia versione. Una volta pubblicata, un'interfaccia HAL viene bloccata e qualsiasi modifica successiva deve essere apportata a una nuova versione dell'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 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 (comuni a tutte le interfacce) o localmente in un'interfaccia.
  • Interfacce. Come componente di base di HIDL, un'interfaccia è costituita da dichiarazioni di metodi e UDT. 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 denominato types.hal.
    • Zero o più interfacce, ciascuna in un proprio file .hal.

Il file di definizione del tipo di dati types.hal contiene solo UDT (tutti gli UDT a livello di pacchetto vengono 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 (ad esempio android.hardware.nfc), dopo essere stato pubblicato per una determinata versione (ad esempio 1.0), è immutabile e non può essere modificato. Le modifiche alle interfacce del pacchetto o alle sue UDT possono essere apportate 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 rispettano il controllo delle versioni semantiche senza il livello della patch e i componenti dei metadati di compilazione. All'interno di un determinato pacchetto, un aggiornamento di versione minore implica che la nuova versione del pacchetto è compatibile con le versioni precedenti, mentre un aggiornamento di versione maggiore implica che la nuova versione del pacchetto non è compatibile con le versioni precedenti.

A livello concettuale, un pacchetto può essere correlato a un altro pacchetto in uno dei seguenti modi:

  • Assolutamente no.
  • Estensione compatibile con le versioni precedenti a livello di pacchetto. Questo accade per le nuove revisioni con incremento della versione secondaria (revisione successiva incrementata) di un pacchetto. Il nuovo pacchetto ha lo stesso nome e la stessa versione principale del vecchio pacchetto, ma una versione secondaria superiore. Dal punto di vista funzionale, il nuovo pacchetto è un superset del vecchio pacchetto, ovvero:
    • Le interfacce di primo livello del pacchetto principale sono presenti nel nuovo pacchetto, anche se le interfacce potrebbero 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 possono essere aggiunte anche nuove interfacce.
    • Tutti i tipi di dati del pacchetto principale sono presenti nel nuovo pacchetto e possono essere gestiti dai metodi (eventualmente implementati di nuovo) del vecchio pacchetto.
    • È anche possibile aggiungere nuovi tipi di dati da utilizzare con i nuovi metodi delle interfacce esistenti aggiornate o con nuove interfacce.
  • Estensione compatibile con le versioni precedenti a livello di interfaccia. Il nuovo pacchetto può inoltre estendere il pacchetto originale mediante interfacce logicamente separate che forniscono semplicemente funzionalità aggiuntive, non quella principale. A questo scopo, potrebbe essere opportuno:
    • Le interfacce nel nuovo pacchetto devono fare ricorso ai tipi di dati del vecchio pacchetto.
    • Le interfacce nel nuovo pacchetto possono estendere le interfacce di uno o più vecchi pacchetti.
  • Estendere l'incompatibilità con le versioni precedenti originale. Si tratta di una versione precedente del pacchetto con una versione principale e non è necessaria alcuna correlazione tra le due. Se esiste, può essere espresso con una combinazione di tipi della versione precedente del pacchetto e l'eredità di un sottoinsieme di interfacce del pacchetto precedente.

Strutturazione dell'interfaccia

Per un'interfaccia ben strutturata, l'aggiunta di nuovi tipi di funzionalità che non fanno parte del design 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, l'interfaccia non è strutturata.

Treble supporta componenti di sistema e del fornitore compilati separatamente in cui il vendor.img su un dispositivo e il 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. Sono incluse molte interfacce API, ma una delle più importanti è il meccanismo IPC utilizzato da HIDL per la comunicazione tra processi al confine system.img/vendor.img.

Requisiti

Tutti i dati passati tramite HIDL devono essere definiti in modo esplicito. Per garantire che un'implementazione e un client possano continuare a funzionare insieme anche quando vengono compilati separatamente o sviluppati in modo indipendente, i dati devono soddisfare i seguenti requisiti:

  • Possono essere descritti direttamente in HIDL (utilizzando struct, enum e così via) con nomi e significato semantici.
  • Può essere descritto da uno standard pubblico come ISO/IEC 7816.
  • Può essere descritto da uno standard hardware o dal layout fisico dell'hardware.
  • Se necessario, possono essere dati opachi (ad esempio chiavi pubbliche, ID e così via).

Se vengono utilizzati dati opachi, questi 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>, questi dati non possono essere analizzati dal system.img stesso; possono essere nuovamente passati a vendor.img solo per l'interpretazione. Quando si passa un valore da vendor.img al codice del fornitore su system.img o a un altro dispositivo, il formato dei dati e la relativa interpretazione 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 utilizzando solo i file .hal (ovvero non dovresti dover esaminare il codice sorgente di Android o gli standard pubblici). Ti consigliamo di specificare il comportamento esatto richiesto. Dichiarazioni come "un'implementazione potrebbe eseguire A o B" incoraggiano le implementazioni a essere intrecciate con i client con cui vengono sviluppate.

Layout del codice HIDL

HIDL include pacchetti di base e del fornitore.

Le interfacce HIDL di base 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 della videocamera è android.hardware.camera. In genere, un pacchetto principale ha il nome android.hardware.[name1].[name2]…. I pacchetti HIDL hanno una versione oltre al nome. Ad esempio, il pacchettoandroid.hardware.camera potrebbe essere nella versione 3.4. Questo è importante, poiché la versione di un pacchetto influisce sul suo posizionamento nella struttura ad albero delle origini.

Tutti i pacchetti di base 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 nella versione 3.4 si trova nella directory hardware/interfaces/camera/3.4/. Esiste un mapping hardcoded tra il prefisso del pacchetto android.hardware. e il percorso hardware/interfaces/.

I pacchetti non di base (del fornitore) sono quelli prodotti dal fornitore del SoC o dall'ODM. Il prefisso per i pacchetti non di base è vendor.$(VENDOR).hardware., dove $(VENDOR)si riferisce a un fornitore di SoC o a un OEM/ODM. Questo corrisponde al percorsovendor/$(VENDOR)/interfaces nell'albero (anche questa mappatura è hardcoded).

Nomi di tipi definiti dall'utente completi

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 completamente qualificato viene utilizzato solo quando vengono dichiarate le istanze del tipo e non dove è definito il tipo stesso. Ad esempio, supponiamo che la versione 1.0 del pacchetto android.hardware.nfc, definisce una struct denominata NfcData. Nel sito della dichiarazione (in 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 di 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 es. android.hardware.nfc).
  • VERSION è il formato del pacchetto con la versione principale.secondaria separata da un punto (ad es. 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 common types del 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 trovarsi nel pacchetto sopra indicato, la dichiarazione nidificata fosse in un'interfaccia denominata 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, a Bar si può fare riferimento come a 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, puoi 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 di enum, ogni valore del tipo di enum ha un nome completamente qualificato che inizia con il nome completamente qualificato del tipo di enum, seguito da due punti e dal nome del valore dell'enum. Ad esempio, assume che la versione 1.0 del pacchetto android.hardware.nfc, definisce un tipo di enum NfcStatus:

enum NfcStatus {
    STATUS_OK,
    STATUS_FAILED
};

Quando fai 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 è lo stesso nome completo del tipo di enum.
  • VALUE è il nome del valore.

Regole di deduzione automatica

Non è necessario specificare un nome UDT completo. Il nome di un 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 inferiore indica una priorità più elevata).

Regola 1

Se non vengono forniti il pacchetto e la 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 typedef sopra. NfcData viene cercato anche localmente, ma poiché non è definito 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 va a buon fine e manca un componente del nome completo (pacchetto, versione o pacchetto e versione), il componente viene completato 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, supponiamo che la dichiarazione di ExtendedNfcData sia stata effettuata nello stesso pacchetto (android.hardware.nfc) e 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 del pacchetto corrente per produrre il nome completo dell'UDTandroid.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 è vero uno dei seguenti requisiti:

  • Viene importato esplicitamente con un'istruzione import.
  • È definito in types.hal nel pacchetto corrente

Viene seguito lo stesso procedimento 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 genera una corrispondenza (l'UDT non è definito nel pacchetto corrente), il compilatore HIDL cerca una corrispondenza in tutti i pacchetti importati. Nell'esempio precedente, supponiamo che ExtendedNfcData sia dichiarato nella versione 1.1 del pacchetto android.hardware.nfc, che 1.1 importi 1.0 come dovrebbe (vedi Estensioni a livello di pacchetto) e che la definizione specifichi 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 nella versione 1.0, generando un UDT completamente qualificato 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

Se utilizzi 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 come android.hardware.bar@1.0::S e si trova in bar/1.0/types.hal (poiché types.hal viene automaticamente importato).
  • IFooCallback viene 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 types.hal). Pertanto, la regola 3 lo risolve in android.hardware.foo@1.0::IFooCallback, che viene importato tramite import android.hardware.foo@1.0;.

types.hal

Ogni pacchetto HIDL contiene un file types.hal contenente UDT condivisi tra tutte le interfacce che partecipano al 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 per ospitare 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. Poiché types.hal è reso disponibile a ogni interfaccia del package (è un'importazione implicita), queste dichiarazioni import sono a livello di package per definizione. Le 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 i seguenti elementi:

  • android.hidl.base@1.0::IBase (implicitamente)
  • android.hardware.foo@1.0::types (implicitamente)
  • Tutto ciò che è in android.hardware.bar@1.0 (incluse tutte le interfacce e il relativo 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 che Quuz sia definito in 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 si trova nel proprio file. Il pacchetto a cui appartiene l'interfaccia è dichiarato nella parte superiore dell'interfaccia utilizzando l'istruzione package. Dopo la dichiarazione del pacchetto, potrebbero essere elencate zero o più importazioni a livello di interfaccia (parziali o dell'intero pacchetto). Ad 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, deve avere accesso tramite un'istruzione import. Il nome dell'interfaccia estesa (l'interfaccia di base) segue le regole per la qualificazione del nome del tipo spiegate sopra. Un'interfaccia può ereditare solo da un'altra interfaccia; HIDL non supporta l'eredità multipla.

Gli esempi di versionamento 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 Uprev

Per definire un pacchetto package@major.minor, deve essere vera A o tutta la condizione B:

Regola A "È una versione secondaria iniziale": tutte le versioni secondarie precedenti, package@major.0, package@major.1, …, package@major.(minor-1) non devono essere definite.
OPPURE
Regola B

Tutte le seguenti condizioni sono vere:

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

    E

  2. "Eredità di 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 che non esista un'interfaccia IBar con un valore k inferiore.

A causa della regola A:

  • Il pacchetto può iniziare con qualsiasi numero di versione minore (ad esempio,android.hardware.biometrics.fingerprint inizia da@2.1).
  • Il requisito "android.hardware.foo@1.0 non è definito" significa che la directory hardware/interfaces/foo/1.0 non deve 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 definito sia @1.0 che @3.2; @3.2 non deve interagire con @1.0). Pertanto, @3.2::IExtFoo può estendere @1.0::IFoo.

A condizione che il nome del pacchetto sia diverso, package@major.minor::IBar può estendere 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 super tipo con la parola chiave extend, espande android.hidl.base@1.0::IBase (tranne IBase stessa).

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 soddisfare la regola B.2, se un android.hardware.foo@1.1::IExtBar estende android.hardware.foo@1.0::IBar, non si tratta ancora di un uprev valido.

Interfacce Uprev

Per eseguire l'upgrade di 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);
}

Si tratta di un 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, 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 poteva 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 (è necessaria una disambiguazione perché IQuux viene utilizzato per dichiarare un'interfaccia e per 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 completamente qualificato, ma sarebbe stato ridondante.

La nuova interfaccia IQuux non dichiara di nuovo il metodofromFooToBar() ereditato da @1.0::IQuux, ma elenca semplicemente il nuovo metodo aggiunto fromBarToFoo(). In HIDL, i metodi ereditati non possono essere dichiarati di nuovo nelle interfacce secondarie, pertanto l'interfaccia IQuux non può dichiarare esplicitamente il metodo fromFooToBar().

Convenzioni di Uprev

A volte i nomi delle interfacce devono rinominare l'interfaccia estesa. Ti consigliamo di assegnare a enum, struct e unioni di estensioni 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 esempiofooWithLocation), è preferibile. In caso contrario, deve essere nominato in modo simile a ciò che estende. 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 versioni a livello di pacchetto

La gestione delle versioni HIDL avviene a livello di pacchetto. Una volta pubblicato, un pacchetto è immutabile (il relativo 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 creazione di UDT per composizione.

Tuttavia, un tipo di relazione è definito in modo rigoroso e deve essere applicato: eredità compatibile con le versioni precedenti a livello di pacchetto. In questo scenario, il pacchetto principale è il pacchetto da cui viene ereditato e il pacchetto secondario è quello che estende il pacchetto principale. Le regole di ereditarietà compatibili con le versioni precedenti a livello di pacchetto sono le seguenti:

  1. Tutte le interfacce di primo livello del pacchetto principale vengono ereditate dalle interfacce nel pacchetto figlio.
  2. È anche possibile aggiungere nuove interfacce al nuovo pacchetto (senza limitazioni relative alle relazioni con altre interfacce in altri pacchetti).
  3. È anche possibile aggiungere nuovi tipi di dati da utilizzare con i nuovi metodi delle interfacce esistenti aggiornate 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 di meta per sapere che queste relazioni costituiscono un'estensione del pacchetto compatibile con le versioni precedenti. Queste informazioni vengono dedotte come segue:

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