Le dichiarazioni di dati HIDL generano strutture di dati con layout standard C++. Queste strutture possono essere posizionate in qualsiasi punto (nello stack, a livello di file o di ambito globale o nell'heap) e possono essere composte nello stesso modo. Il codice del client chiama il codice proxy HIDL passando riferimenti const e tipi primitivi, mentre il codice stub e proxy nasconde i dettagli della serializzazione.
Nota:in nessun momento il codice scritto dagli sviluppatori è obbligatorio per eseguire la serializzazione o la deserializzazione esplicita delle strutture di dati.
La tabella seguente mappa le primitive HIDL ai tipi di dati C++:
Tipo HIDL | Tipo C++ | Intestazione/libreria |
---|---|---|
enum |
enum class |
|
uint8_t..uint64_t |
uint8_t..uint64_t |
<stdint.h> |
int8_t..int64_t |
int8_t..int64_t |
<stdint.h> |
float |
float |
|
double |
double |
|
vec<T> |
hidl_vec<T> |
libhidlbase |
T[S1][S2]...[SN] |
T[S1][S2]...[SN] |
|
string |
hidl_string |
libhidlbase |
handle |
hidl_handle |
libhidlbase |
safe_union |
(custom) struct |
|
struct |
struct |
|
union |
union |
|
fmq_sync |
MQDescriptorSync |
libhidlbase |
fmq_unsync |
MQDescriptorUnsync |
libhidlbase |
Le sezioni seguenti descrivono i tipi di dati in modo più dettagliato.
enum
Un enum in HIDL diventa un enum in C++. Ad esempio:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… diventa:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
A partire da Android 10, è possibile eseguire un'iterazione su un enum utilizzando ::android::hardware::hidl_enum_range
. Questo intervallo include ogni enumeratore nell'ordine in cui appare nel codice sorgente HIDL, dall'enum principale all'ultimo secondario. Ad esempio, questo codice esegue un'iterazione su WRITE
, READ
, NONE
e COMPARE
in quest'ordine. Dato SpecialMode
sopra:
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
implementa anche gli iteratori inversi e può essere utilizzato in contesti constexpr
. Se un valore compare in un'enumerazione più volte, viene visualizzato nell'intervallo più volte.
bitfield<T>
bitfield<T>
(dove T
è un enum definito dall'utente) diventa il tipo sottostante di quell'enum in C++. Nell'esempio precedente, bitfield<Mode>
diventa uint8_t
.
vec<T>
Il modello di classe hidl_vec<T>
fa parte di
libhidlbase
e può essere utilizzato per passare un vettore di qualsiasi tipo HIDL con
una dimensione arbitraria. Il contenitore con dimensioni fisse paragonabile è
hidl_array
. Un hidl_vec<T>
può anche essere inizializzato in modo da puntare a un buffer di dati esterno di tipo T
utilizzando la funzione hidl_vec::setToExternal()
.
Oltre a emettere/inserire la struttura in modo appropriato nell'intestazione C++ generata, l'uso di vec<T>
genera alcune funzioni di utilità per tradurre a/da std::vector
e puntatori T
bare. Se vec<T>
viene utilizzato come parametro, la funzione che lo utilizza è sovraccaricata (vengono generati due prototipi) per accettare e passare sia la struttura HIDL sia un tipo std::vector<T>
per quel parametro.
array
Gli array costanti in HIDL sono rappresentati dalla classe hidl_array
in libhidlbase
. Un hidl_array<T, S1, S2, …,
SN>
rappresenta un array di dimensioni fisse N-dimensionale
T[S1][S2]…[SN]
.
stringa
La classe hidl_string
(parte di libhidlbase
) può essere utilizzata per trasmettere stringhe tramite le interfacce HIDL ed è definita in /system/libhidl/base/include/hidl/HidlSupport.h
. La prima posizione di archiviazione nel corso è un puntatore al relativo buffer di caratteri.
hidl_string
sa come convertire da e verso
std::string and char*
(stringa in stile C) utilizzando
operator=
, i tipi di conversione impliciti e la funzione .c_str()
.
Le strutture di stringhe HIDL hanno i costruttori di copia e gli operatori di assegnazione appropriati per:
- Carica la stringa HIDL da una stringa
std::string
o C. - Crea un nuovo
std::string
da una stringa HIDL.
Inoltre, le stringhe HIDL hanno costruttori di conversione, pertanto le stringhe C (char *
) e C++ (std::string
) possono essere utilizzate nei metodi che accettano una stringa HIDL.
struct
Un struct
in HIDL può contenere solo tipi di dati di dimensioni fisse e nessuna funzione. Le definizioni di struct HIDL mappano direttamente a struct
con layout standard in C++, garantendo che i struct
abbiano un layout della memoria coerente. Una struct può includere tipi HIDL, tra cui handle
, string
e vec<T>
, che fanno riferimento a buffer separati di lunghezza variabile.
handle
ATTENZIONE: gli indirizzi di qualsiasi tipo (anche quelli fisici dei dispositivi) non devono mai far parte di un handle nativo. Il passaggio di queste informazioni tra i processi è pericoloso e li rende suscettibili di attacchi. Tutti i valori trasmessi 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 errati possono causare accesso alla memoria o danneggiamento della memoria.
Il tipo handle
è rappresentato dalla struttura hidl_handle
in C++, che è un semplice wrapper attorno a un puntatore a un oggetto const native_handle_t
(è presente in Android da molto tempo).
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;
Per impostazione predefinita, hidl_handle
non acquisisce la proprietà del cursore native_handle_t
in cui è racchiuso. Esiste solo per memorizzare in modo sicuro un puntatore a un native_handle_t
in modo che possa essere utilizzato sia nei processi a 32 che a 64 bit.
Gli scenari in cui hidl_handle
possiede i descrittori dei file inclusi includono:
- Dopo una chiamata al metodo
setTo(native_handle_t* handle, bool shouldOwn)
con il parametroshouldOwn
impostato sutrue
- Quando l'oggetto
hidl_handle
viene creato mediante la costruzione tramite copia da un altro oggettohidl_handle
- Quando l'oggetto
hidl_handle
viene assegnato tramite copia da un altro oggettohidl_handle
hidl_handle
fornisce conversioni implicite ed esplicite
a/da oggetti native_handle_t*
. L'utilizzo principale del tipo handle
in HIDL è trasmettere descrittori file tramite le interfacce HIDL. Un singolo descrittore file è quindi rappresentato da un native_handle_t
senza int
e da un singolo fd
. Se il client e il server si trovano in un processo diverso, l'implementazione RPC si occupa automaticamente del descrittore file per garantire che entrambi i processi possano operare sullo stesso file.
Sebbene un descrittore di file ricevuto in un hidl_handle
da un
procedura sia valido in quella procedura, non persiste oltre la funzione di ricezione (viene chiuso quando la funzione restituisce). Un processo che vuole mantenere l'accesso permanente al descrittore file deve dup()
i descrittori file racchiusi o copiare l'intero oggetto hidl_handle
.
ricordo
Il tipo HIDL memory
viene mappato alla classe hidl_memory
in libhidlbase
, che rappresenta la memoria condivisa non mappata. Si tratta
dell'oggetto che deve essere passato tra i processi per condividere la memoria in HIDL. Per
utilizzare la memoria condivisa:
- Ottieni un'istanza di
IAllocator
(attualmente è disponibile solo l'istanza "ashmem") e utilizzala per allocare la memoria condivisa. IAllocator::allocate()
restituisce un oggettohidl_memory
che può essere passato tramite l'RPC HIDL ed essere mappato in un processo utilizzando la funzionemapMemory
dilibhidlmemory
.mapMemory
restituisce un riferimento a unsp<IMemory>
oggetto che può essere utilizzato per accedere alla memoria. (IMemory
eIAllocator
sono definiti inandroid.hidl.memory@1.0
.)
Un'istanza di IAllocator
può essere utilizzata per allocare memoria:
#include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> #include <hidlmemory/mapping.h> using ::android::hidl::allocator::V1_0::IAllocator; using ::android::hidl::memory::V1_0::IMemory; using ::android::hardware::hidl_memory; .... sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem"); ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) { if (!success) { /* error */ } // now you can use the hidl_memory object 'mem' or pass it around }));
Le modifiche effettive alla memoria devono essere apportate tramite un oggetto IMemory
, sul lato che ha creato mem
o su quello che lo riceve tramite HIDL RPC.
// Same includes as above sp<IMemory> memory = mapMemory(mem); void* data = memory->getPointer(); memory->update(); // update memory however you wish after calling update and before calling commit data[0] = 42; memory->commit(); // … memory->update(); // the same memory can be updated multiple times // … memory->commit();
interfaccia
Le interfacce possono essere passate come oggetti. La parola interface può essere utilizzata come sintassi per il tipo android.hidl.base@1.0::IBase
. Inoltre, l'interfaccia corrente e le eventuali interfacce importate sono definite come tipo.
Le variabili che contengono interfacce devono essere puntatori forti:
sp<IName>
. Le funzioni HIDL che accettano parametri di interfaccia
convertono i puntatori non elaborati in puntatori sicuri, provocando un comportamento non intuitivo
(il puntatore può essere cancellato in modo imprevisto). Per evitare problemi, memorizza sempre le interfacce HIDL come sp<>
.