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’argomentonative_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 innative_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'elementonative_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 automaticamenteclose()
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 }