Tipi di dati

Questa sezione descrive i tipi di dati HIDL. Per i dettagli di implementazione, consulta HIDL C++ (per C++ implementazioni) o HIDL Java (per le implementazioni Java).

Le analogie con C++ includono:

  • structs utilizza la sintassi C++; unions supporta la sintassi C++ per impostazione predefinita. Entrambi devono essere denominati; struct e unioni anonimi non sono supportati.
  • I valori Typedef sono consentiti nell'HIDL (come lo sono in C++).
  • I commenti in stile C++ sono consentiti e vengono copiati nel file di intestazione generato.

Le analogie con Java includono:

  • Per ciascun file, HIDL definisce uno spazio dei nomi in stile Java che deve iniziare con android.hardware.. Lo spazio dei nomi C++ generato è ::android::hardware::….
  • Tutte le definizioni del file sono contenute in uno schema di Wrapper interface.
  • Le dichiarazioni degli array HIDL seguono lo stile Java, non lo stile C++. Esempio:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • I commenti sono simili al formato javadoc.

Rappresentazione dei dati

Un elemento struct o union composto da Layout standard (un sottoinsieme del requisito dei tipi di dati normali) ha una memoria coerente layout nel codice C++ generato, applicato con attributi di allineamento espliciti attivati struct e union membri.

Tipi HIDL primitivi, nonché enum e bitfield (che derivano sempre dai tipi primitivi), vengono mappati ai tipi C++ standard come std::uint32_t da cstdint.

Poiché Java non supporta i tipi non firmati, i tipi HIDL non firmati vengono mappati a il tipo Java firmato corrispondente. Gli strutti sono mappati a classi Java. gli array vengono mappati ad array Java; unions non sono al momento supportati in Java. Le stringhe vengono memorizzate internamente come UTF8. Poiché Java supporta solo le stringhe UTF16, i valori delle stringhe inviati a o da un'implementazione Java vengono e potrebbero non essere identici nella ritraduzione perché i set di caratteri mappano sempre in modo uniforme.

I dati ricevuti su IPC in C++ sono contrassegnati come const e sono in memoria di sola lettura che persiste solo per la durata della chiamata di funzione. Dati ricevuti tramite IPC in Java sono già stati copiati in oggetti Java, quindi può devono essere conservati senza ulteriori copie (e possono essere modificati).

Annotazioni

È possibile aggiungere annotazioni in stile Java alle dichiarazioni di tipo. Le annotazioni sono analizzato dal backend Vendor Test Suite (VTS) del compilatore HIDL, ma nessuno dei due tali annotazioni analizzate vengono effettivamente comprese dal compilatore HIDL. Invece, Le annotazioni VTS analizzate vengono gestite dal compilatore VTS (VTSC).

Le annotazioni usano la sintassi Java: @annotation o @annotation(value) o @annotation(id=value, id=value…) dove valore può essere un'espressione costante, una stringa o un elenco di valori all'interno di {}, proprio come in Java. Più annotazioni con lo stesso nome può essere collegato allo stesso elemento.

Dichiarazioni di inoltro

In HIDL, gli struct potrebbero non essere dichiarati, perciò tipi di dati autoreferenziali impossibili (ad esempio, non puoi descrivere un elenco collegato o un albero in HIDL). La maggior parte degli HAL (precedenti ad Android 8.x) ha un uso limitato di dichiarazioni di inoltro, che possono essere rimosse riorganizzando la struttura dei dati dichiarazioni.

Questa restrizione consente di copiare le strutture di dati in base al valore con una semplice deep-copy, invece di tenere traccia dei valori dei cursori che potrebbero verificarsi in una struttura di dati autoreferenziale. Se gli stessi dati vengono trasmessi due volte, ad esempio con due parametri di metodo o vec<T> che puntano a gli stessi dati, vengono create e consegnate due copie separate.

Dichiarazioni nidificate

HIDL supporta dichiarazioni nidificate per tutti i livelli desiderati (con una indicata di seguito). Ad esempio:

interface IFoo {
    uint32_t[3][4][5][6] multidimArray;

    vec<vec<vec<int8_t>>> multidimVector;

    vec<bool[4]> arrayVec;

    struct foo {
        struct bar {
            uint32_t val;
        };
        bar b;
    }
    struct baz {
        foo f;
        foo.bar fb; // HIDL uses dots to access nested type names
    }
    …

L'eccezione è che i tipi di interfaccia possono essere incorporati solo vec<T> e solo un livello di profondità (no vec<vec<IFoo>>).

Sintassi del puntatore non elaborato

La lingua HIDL non utilizza * e non supporta i completa flessibilità dei cursori non elaborati C/C++. Per dettagli su come HIDL incapsula puntatori e matrici/vettori, vedi vec<T> modello.

Interfacce

La parola chiave interface è utilizzabile in due modi.

  • Apre la definizione di un'interfaccia in un file .hal.
  • Può essere usato come tipo speciale in campi struct/union, parametri di metodo, e resi. È vista come un'interfaccia generale e sinonimo di android.hidl.base@1.0::IBase.

Ad esempio, IServiceManager prevede il seguente metodo:

get(string fqName, string name) generates (interface service);

Il metodo promette di cercare un'interfaccia per nome. È inoltre possibile identico per sostituire l'interfaccia con android.hidl.base@1.0::IBase.

Le interfacce possono essere passate solo in due modi: come parametri di primo livello o come membri di un vec<IMyInterface>. Non possono essere membri di kubectl, struct, array o unioni nidificati.

MQDescriptorSync e MQDescriptorUnsync

I tipi MQDescriptorSync e MQDescriptorUnsync passare un descrittori FMQ (Fast Message Queue) sincronizzati o non sincronizzati attraverso un'interfaccia HIDL. Per maggiori dettagli, vedi HIDL C++ (gli FMQ non sono supportati in Java).

tipo di memoria

Il tipo memory viene utilizzato per rappresentare la memoria condivisa non mappata in HIDL È supportato solo in C++. Un valore di questo tipo può essere utilizzato nella ricevente per inizializzare un oggetto IMemory, mappando la memoria e renderla fruibile. Per maggiori dettagli, vedi HIDL C++.

Avviso: dati strutturati inseriti in elementi condivisi la memoria DEVE essere di tipo il cui formato non cambia mai per tutta la durata dell'elemento dell'interfaccia utente che trasmette memory. Altrimenti, gli HAL possono risentirne problemi di compatibilità irreversibili.

tipo di puntatore

Il tipo pointer è solo per uso interno HIDL.

bitfield<T> tipo di modello

bitfield<T> in cui T è un L'enumerazione definita dall'utente suggerisce che il valore è un bit a bit-OR del valori enum definiti in T. Nel codice generato, bitfield<T> compare come tipo di T sottostante. Per esempio:

enum Flag : uint8_t {
    HAS_FOO = 1 << 0,
    HAS_BAR = 1 << 1,
    HAS_BAZ = 1 << 2
};
typedef bitfield<Flag> Flags;
setFlags(Flags flags) generates (bool success);

Il compilatore gestisce i flag di tipo allo stesso modo di uint8_t.

Perché non utilizzarlo (u)int8_t/(u)int16_t/(u)int32_t/(u)int64_t? L'uso di bitfield fornisce al lettore informazioni HAL aggiuntive, che ora sa che setFlags prende un valore OR a bit per il flag (ad es. sa che la chiamata a setFlags con 16 non è valida). Senza bitfield, queste informazioni vengono fornite solo tramite documentazione. Nella Inoltre, VTS può effettivamente verificare se il valore dei flag è un operatore di bit a bit del flag.

Handle del tipo primitivo

ATTENZIONE: indirizzi di qualsiasi tipo (anche fisico) indirizzi del dispositivo) non devono mai far parte di un handle nativo. Superato le informazioni tra i processi sono pericolose e suscettibili di attacchi. Tutti i valori passati tra i processi devono essere convalidati prima di essere utilizzati per cercare la memoria allocata all'interno di un processo. In caso contrario, gli handle non validi potrebbero l'accesso alla memoria o il danneggiamento della memoria.

La semantica HIDL è copia per valore, il che implica che i parametri vengono copiati. Eventuali dati di grandi dimensioni o dati che devono essere condivisi tra i processi (come un recinto di sincronizzazione), vengono gestiti facendo passare i descrittori dei file, a oggetti permanenti: ashmem per memoria condivisa, file effettivi o qualsiasi altra cosa che possa nascondersi dietro un descrittore del file. Il raccoglitore duplica il descrittore del file nell'altro processo.

handle_t_nativo

Android supporta native_handle_t, un concetto di handle generale definita in libcutils.

typedef struct native_handle
{
  int version;        /* sizeof(native_handle_t) */
  int numFds;         /* number of file-descriptors at &data[0] */
  int numInts;        /* number of ints at &data[numFds] */
  int data[0];        /* numFds + numInts ints */
} native_handle_t;

Un handle nativo è una raccolta di int e descrittori dei file che vengono trasmessi intorno per valore. È possibile archiviare un singolo descrittore di file in un handle nativo senza int e con un singolo descrittore di file. Handle di passaggio con handle nativi incapsulato con il tipo primitivo handle, garantisce che gli asset nativi sono inclusi direttamente nell'elenco HIDL.

Poiché un elemento native_handle_t ha una dimensione variabile, non può essere incluso direttamente in uno struct. Un campo handle genera un puntatore a un native_handle_t allocato.

Nelle versioni precedenti di Android, gli handle nativi venivano creati utilizzando lo stesso di funzioni presenti in libcutils. In Android 8.0 e versioni successive, queste funzioni vengono ora copiate android::hardware::hidl o spostato nell'NDK. HIDL il codice generato automaticamente serializza e deserializza automaticamente queste funzioni, senza coinvolgere il codice scritto dall'utente.

Gestire e gestire la proprietà del descrittore dei file

Quando chiami un metodo di interfaccia HIDL che trasmette (o restituisce) un oggetto hidl_handle (di primo livello o parte di un tipo composto), la proprietà dei descrittori dei file in essi contenuti è la seguente:

  • Il caller che passa un oggetto hidl_handle come un conserva la proprietà dei descrittori dei file contenuti nell’argomento native_handle_t avvolge; il chiamante deve chiudere il file descrittori quando hai finito di utilizzarli.
  • La procedura che restituisce un hidl_handle (passandolo in una funzione _cb) mantiene la proprietà dei i descrittori del file contenuti in native_handle_t avvolti dal object; il processo deve chiudere questi descrittori del file al termine dell'operazione.
  • Un trasporto che riceve un hidl_handle ha la proprietà dei descrittori del file all'interno dell'elemento native_handle_t aggregato dall'oggetto; il destinatario può utilizzare questi descrittori dei file così come sono il callback della transazione, ma devi clonare l'handle nativo per utilizzare il file oltre il callback. Il trasporto chiama automaticamente close() per i descrittori del file al termine della transazione.

HIDL non supporta gli handle in Java (in quanto Java non supporta gli handle tutti).

Array di dimensioni

Per array con dimensioni negli struct HIDL, i loro elementi possono essere di qualsiasi tipo può contenere:

struct foo {
uint32_t[3] x; // array is contained in foo
};

Stringhe

Le stringhe vengono visualizzate in modo diverso in C++ e Java, ma la struttura è una struttura C++. Per maggiori dettagli, vedi Tipi di dati HIDL C++ o Tipi di dati Java HIDL.

Nota: il passaggio di una stringa da o verso Java attraverso una L'interfaccia HIDL (compreso Java a Java) causa la conversione del set di caratteri che potrebbero non conservare la codifica originale.

vec<T> tipo di modello

Il modello vec<T> rappresenta un buffer di dimensioni variabili che contengono istanze di T.

T può essere uno dei seguenti:

  • Tipi primitivi (ad es. uint32_t)
  • Stringhe
  • Enum definite dall'utente
  • struct definiti dall'utente
  • Interfacce o la parola chiave interface (vec<IFoo>, vec<interface> è supportato solo come parametro di primo livello)
  • Handle
  • bitfield<U>
  • vec<U>, dove U è presente in questo elenco ad eccezione dell'interfaccia (ad es. vec<vec<IFoo>> non è supportato)
  • U[] (array dimensionale di U), dove U è in questo elenco tranne che nell'interfaccia

Tipi definiti dall'utente

Questa sezione descrive i tipi definiti dall'utente.

Enum

HIDL non supporta le enumerazioni anonime. In caso contrario, le enumerazioni in HIDL sono simili a C++11:

enum name : type { enumerator , enumerator = constexpr , …  }

Un'enumerazione di base è definita in termini di uno dei tipi di numeri interi in HIDL. In caso contrario viene specificato per il primo enumeratore di un'enumerazione in base a un numero intero il valore predefinito è 0. Se non viene specificato alcun valore per un enumeratore successivo, Il valore predefinito corrisponde al valore precedente più uno. Ad esempio:

// RED == 0
// BLUE == 4 (GREEN + 1)
enum Color : uint32_t { RED, GREEN = 3, BLUE }

Un'enumerazione può anche ereditare da un'enumerazione definita in precedenza. Se nessun valore è specificato per il primo enumeratore di un'enumerazione figlio (in questo caso FullSpectrumColor), il valore predefinito è l'ultimo enumeratore dell'enumerazione principale più uno. Ad esempio:

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

Avviso: l'ereditarietà enum funziona a ritroso dalla maggior parte degli altri tipi di ereditarietà. Un valore enum figlio non può essere utilizzato come enum principale. Questo perché un'enumerazione secondaria include più valori rispetto a principale. Tuttavia, un valore enum padre può essere utilizzato tranquillamente come valore enum figlio perché i valori enum secondari sono per definizione un soprainsieme di valori enum principali. Tienilo a mente quando progetti le interfacce, in quanto significa che i tipi che fanno riferimento enum principali non possono fare riferimento a enumerazioni secondarie nelle iterazioni successive della a riga di comando.

I valori delle enum sono indicati con la sintassi dei due punti (non con la sintassi dot come tipi nidificati). La sintassi è Type:VALUE_NAME. Non è necessario specificare se al valore viene fatto riferimento nello stesso tipo enum o nello stesso tipo figlio. Esempio:

enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 };
enum Color : Grayscale { RED = WHITE + 1 };
enum Unrelated : uint32_t { FOO = Color:RED + 1 };

A partire da Android 10, le enum hanno un Attributo len che può essere utilizzato in espressioni costanti. MyEnum::len è il numero totale di voci in quell'enumerazione. Questo valore è diverso dal numero totale di valori, che potrebbe essere inferiore quando sono duplicati.

Struttura

HIDL non supporta gli struct anonimi. Altrimenti, gli struct in HIDL sono molto simile a C.

HIDL non supporta le strutture di dati a lunghezza variabile contenute interamente uno struct. Include l'array di durata indefinita che a volte viene utilizzato come l'ultimo campo di uno struct in C/C++ (a volte visto con una dimensione [0]). HIDL vec<T> rappresenta le dimensioni dinamiche array con i dati archiviati in un buffer separato; queste istanze sono rappresentate con un'istanza di vec<T> in struct.

Analogamente, string può essere contenuto in un file struct (i buffer associati sono separati). Nel C++ generato, le istanze dell'HIDL sono rappresentati tramite un puntatore all'handle nativo effettivo delle istanze del tipo di dati sottostante sono di lunghezza variabile.

Union

HIDL non supporta i unioni anonimi. Altrimenti, i unioni sono simili a C.

Le unioni non possono contenere tipi di correzione (come puntatori, descrittori di file, binder) oggetti). Non richiedono campi speciali o tipi associati e sono semplicemente copiati utilizzando memcpy() o un'altra equivalente. Un sindacato potrebbe non direttamente contengono (o contengono usando altre strutture dati) qualsiasi cosa che richieda l'impostazione gli offset del binder (ovvero i riferimenti handle o binder-interface). Ad esempio:

union UnionType {
uint32_t a;
//  vec<uint32_t> r;  // Error: can't contain a vec<T>
uint8_t b;1
};
fun8(UnionType info); // Legal

Le unioni possono essere dichiarate anche all'interno degli struct. Ad esempio:

struct MyStruct {
    union MyUnion {
      uint32_t a;
      uint8_t b;
    }; // declares type but not member

    union MyUnion2 {
      uint32_t a;
      uint8_t b;
    } data; // declares type but not member
  }