Tipi di dati

Le dichiarazioni dei dati HIDL generano strutture di dati con layout standard C++. Questi di sistema possono essere posizionate in qualsiasi punto ti sembri naturale (sulla pila, su un file in un ambito globale o sull'heap) e possono essere create nello stesso modo. Cliente chiama codice proxy HIDL che passa in riferimenti const e tipi primitivi, mentre il codice stub e proxy nasconde i dettagli della serializzazione.

Nota: in nessun momento viene creato codice scritto dallo sviluppatore. necessari per serializzare o deserializzare in modo esplicito le 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 in maggiore dettaglio i tipi di dati.

enum

Un'enumerazione in HIDL diventa 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 ripetere un'enumerazione rispetto all'uso di ::android::hardware::hidl_enum_range. Questo intervallo include ogni enumeratore nell'ordine in cui appare nel codice sorgente HIDL, a partire da dall'enumerazione padre all'ultimo figlio. Ad esempio, questo codice esegue l'iterazione più di WRITE, READ, NONE e COMPARE in quell'ordine. Dati di 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 iteratori inversi e può essere utilizzata in constexpr contesti. Se un valore appare in un'enumerazione più volte, il valore appare nell'intervallo più volte.

bitfield<T>

bitfield<T> (dove T è un enum definito dall'utente) diventa il tipo sottostante di quell'enumerazione in C++. Nell'esempio precedente, bitfield<Mode> diventa uint8_t.

vec<T>

Il modello del corso hidl_vec<T> fa parte di libhidlbase e può essere utilizzato per passare un vettore di qualsiasi tipo HIDL con una dimensione arbitraria. Il container comparabile a dimensione fissa 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 correttamente lo struct nel codice Intestazione C++, l'uso di vec<T> genera una certa praticità funzioni per la traduzione da/verso std::vector e T semplice i cursori. Se vec<T> viene utilizzato come parametro, la funzione il suo utilizzo è sovraccarico (vengono generati due prototipi) per accettare e passare sia lo struct HIDL sia un tipo std::vector<T> per quello .

array

Gli array costanti in hidl sono rappresentati dalla classe hidl_array nel mese di libhidlbase. Un hidl_array<T, S1, S2, …, SN> rappresenta un array di dimensioni fisse N dimensionale T[S1][S2]…[SN].

string

La classe hidl_string (parte di libhidlbase) può essere utilizzato per passare stringhe su interfacce HIDL ed è definito in /system/libhidl/base/include/hidl/HidlSupport.h. Il primo spazio di archiviazione nella classe è un puntatore al suo buffer di caratteri.

hidl_string sa come effettuare conversioni da e verso std::string and char* (stringa di tipo C) utilizzando operator=, trasmissioni implicite e funzione .c_str(). Gli struct stringa HIDL hanno i costruttori e le assegnazioni di copia appropriati operatori 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, quindi le stringhe C. (char *) e le stringhe C++ (std::string) possono essere utilizzate che accettano una stringa HIDL.

struct

Un elemento struct in HIDL può contenere solo tipi di dati a dimensione fissa e nessun tipo funzioni. Le definizioni degli struct HIDL vengono mappate direttamente al layout standard struct in C++, per fare in modo che struct abbia un un layout di memoria coerente. Uno struct può includere tipi HIDL, inclusi handle, string e vec<T>, che punto per separare i buffer a lunghezza variabile.

handle

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 memoria allocata all'interno di un processo. In caso contrario, gli handle non validi possono causare errori l'accesso alla memoria o il danneggiamento della memoria.

Il tipo handle è rappresentato dal valore hidl_handle struttura in C++, che è un semplice wrapper attorno a un puntatore a const native_handle_t oggetto (disponibile in Android per 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 puntatore native_handle_t che avvolge. Esiste solo per proteggere un puntatore a native_handle_t in modo che possa essere utilizzato sia a 32 che a 64 bit.

Scenari in cui hidl_handle possiede il file incluso i descrittori includono:

  • A seguito di una chiamata al metodo setTo(native_handle_t* handle, bool shouldOwn) con il parametro shouldOwn impostato su true
  • Quando l'oggetto hidl_handle viene creato con una copia da un altro oggetto hidl_handle
  • Quando l'oggetto hidl_handle viene copiato da un altro oggetto hidl_handle oggetto

hidl_handle fornisce conversioni sia implicite che esplicite da/verso native_handle_t* oggetti. L'utilizzo principale Il tipo handle in HIDL prevede il trasferimento dei descrittori dei file tramite HIDL interfacce. Un descrittore di file singolo è quindi rappresentato da un native_handle_t senza int e un singolo fd. Se il client e il server risiedono in un processo diverso, l'RPC l'implementazione si occupa automaticamente del descrittore del file per garantire entrambi i processi possono operare sullo stesso file.

Anche se un descrittore di file ricevuto in un elemento hidl_handle da un processo è valido durante questo processo, non persiste oltre il destinatario (è chiusa quando restituisce la funzione). Un processo che vuole mantenga l'accesso permanente al descrittore del file deve dup() descrittori di file racchiusi o copia 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. Questo è l'oggetto che deve essere passato tra i processi per condividere la memoria in HIDL. A usa memoria condivisa:

  1. Ottieni un'istanza di IAllocator (al momento solo l'istanza "ashmem" è disponibile) e utilizzarla per allocare la memoria condivisa.
  2. IAllocator::allocate() restituisce un hidl_memory che può essere passato attraverso HIDL RPC e mappato in un processo utilizzando Funzione mapMemory di libhidlmemory.
  3. mapMemory restituisce un riferimento a un Oggetto sp<IMemory> che può essere utilizzato per accedere alla memoria. (IMemory e IAllocator sono definiti in android.hidl.memory@1.0.

È possibile utilizzare un'istanza di IAllocator per allocare la 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 IMemory sia sul lato che ha creato mem sia sul lato la 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. Si può usare la parola interface come zucchero sintattico per il tipo android.hidl.base@1.0::IBase; Inoltre, vengono definite l'interfaccia corrente e le eventuali interfacce importate come tipo.

Le variabili che contengono le interfacce dovrebbero essere puntatori efficaci: sp<IName>. Funzioni HIDL che accettano i parametri dell'interfaccia convertire i puntatori non elaborati in puntatori forti, con un comportamento non intuitivo (il puntatore può essere cancellato in modo imprevisto). Per evitare problemi, memorizza sempre i dati HIDL si interfaccia come sp<>.