Las declaraciones de datos HIDL generan estructuras de datos de diseño estándar de C++. Estas estructuras se pueden colocar en cualquier lugar que parezca natural (en la pila, en el archivo o en el ámbito global, o en el montón) y se pueden componer de la misma manera. El código del cliente llama al código proxy HIDL pasando referencias constantes y tipos primitivos, mientras que el código auxiliar y el código proxy ocultan los detalles de la serialización.
Nota: En ningún momento se requiere código escrito por el desarrollador para serializar o deserializar explícitamente estructuras de datos.
La siguiente tabla asigna primitivas HIDL a tipos de datos C++:
Tipo HIDL | Tipo C++ | Encabezado/Biblioteca |
---|---|---|
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 |
Las secciones siguientes describen los tipos de datos con más detalle.
enumeración
Una enumeración en HIDL se convierte en una enumeración en C++. Por ejemplo:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… se convierte en:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
A partir de Android 10, se puede iterar una enumeración usando ::android::hardware::hidl_enum_range
. Este rango incluye cada enumerador en el orden en que aparece en el código fuente HIDL, desde la enumeración principal hasta el último hijo. Por ejemplo, este código itera sobre WRITE
, READ
, NONE
y COMPARE
en ese orden. Dado SpecialMode
anterior:
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
también implementa iteradores inversos y puede usarse en contextos constexpr
. Si un valor aparece en una enumeración varias veces, el valor aparece en el rango varias veces.
campo de bits<T>
bitfield<T>
(donde T
es una enumeración definida por el usuario) se convierte en el tipo subyacente de esa enumeración en C++. En el ejemplo anterior, bitfield<Mode>
se convierte en uint8_t
.
vec<T>
La plantilla de clase hidl_vec<T>
es parte de libhidlbase
y puede usarse para pasar un vector de cualquier tipo HIDL con un tamaño arbitrario. El contenedor de tamaño fijo comparable es hidl_array
. Un hidl_vec<T>
también se puede inicializar para que apunte a un búfer de datos externo de tipo T
, usando la función hidl_vec::setToExternal()
.
Además de emitir/insertar la estructura apropiadamente en el encabezado C++ generado, el uso de vec<T>
genera algunas funciones convenientes para traducir hacia/desde std::vector
y punteros T
desnudos. Si vec<T>
se usa como parámetro, la función que lo usa se sobrecargará (se generarán dos prototipos) para aceptar y pasar tanto la estructura HIDL como un tipo std::vector<T>
para ese parámetro.
formación
Las matrices constantes en hidl están representadas por la clase hidl_array
en libhidlbase
. Un hidl_array<T, S1, S2, …, SN>
representa una matriz de tamaño fijo N dimensional T[S1][S2]…[SN]
.
cadena
La clase hidl_string
(parte de libhidlbase
) se puede utilizar para pasar cadenas a través de interfaces HIDL y está definida en /system/libhidl/base/include/hidl/HidlSupport.h
. La primera ubicación de almacenamiento de la clase es un puntero a su búfer de caracteres.
hidl_string
sabe cómo convertir hacia y desde std::string and char*
(cadena estilo C) usando operator=
, conversiones implícitas y la función .c_str()
. Las estructuras de cadenas HIDL tienen los constructores de copia y operadores de asignación adecuados para:
- Cargue la cadena HIDL desde una
std::string
o una cadena C. - Cree una nueva
std::string
a partir de una cadena HIDL.
Además, las cadenas HIDL tienen constructores de conversión, por lo que las cadenas C ( char *
) y C++ ( std::string
) se pueden usar en métodos que toman una cadena HIDL.
estructura
Una struct
en HIDL solo puede contener tipos de datos de tamaño fijo y ninguna función. Las definiciones de estructuras HIDL se asignan directamente a struct
de diseño estándar en C++, lo que garantiza que struct
tengan un diseño de memoria consistente. Una estructura puede incluir tipos HIDL, incluidos handle
, string
y vec<T>
, que apuntan a búferes de longitud variable separados.
manejar
ADVERTENCIA: Las direcciones de cualquier tipo (incluso direcciones de dispositivos físicos) nunca deben formar parte de un identificador nativo. Pasar esta información entre procesos es peligroso y los hace susceptibles a ataques. Cualquier valor pasado entre procesos debe validarse antes de usarse para buscar la memoria asignada dentro de un proceso. De lo contrario, los malos manejos pueden causar un mal acceso a la memoria o corrupción de la misma.
El tipo handle
está representado por la estructura hidl_handle
en C++, que es un contenedor simple alrededor de un puntero a un objeto const native_handle_t
(esto ha estado presente en Android durante mucho tiempo).
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;
De forma predeterminada, hidl_handle
no toma posesión del puntero native_handle_t
que envuelve. Simplemente existe para almacenar de forma segura un puntero a un native_handle_t
de modo que pueda usarse en procesos de 32 y 64 bits.
Los escenarios en los que hidl_handle
posee sus descriptores de archivos adjuntos incluyen:
- Después de una llamada al método
setTo(native_handle_t* handle, bool shouldOwn)
con el parámetroshouldOwn
establecido entrue
- Cuando el objeto
hidl_handle
se crea copiando la construcción de otro objetohidl_handle
- Cuando el objeto
hidl_handle
se copia y asigna desde otro objetohidl_handle
hidl_handle
proporciona conversiones implícitas y explícitas hacia/desde objetos native_handle_t*
. El uso principal del tipo handle
en HIDL es pasar descriptores de archivos a través de interfaces HIDL. Por lo tanto, un único descriptor de archivo está representado por un native_handle_t
sin int
sy un solo fd
. Si el cliente y el servidor viven en un proceso diferente, la implementación de RPC se encargará automáticamente del descriptor del archivo para garantizar que ambos procesos puedan operar en el mismo archivo.
Aunque un descriptor de archivo recibido en hidl_handle
por un proceso será válido en ese proceso, no persistirá más allá de la función receptora (se cerrará una vez que la función regrese). Un proceso que quiera retener el acceso persistente al descriptor de archivo debe dup()
los descriptores de archivo adjuntos o copiar todo el objeto hidl_handle
.
memoria
El tipo memory
HIDL se asigna a la clase hidl_memory
en libhidlbase
, que representa la memoria compartida no asignada. Este es el objeto que se debe pasar entre procesos para compartir memoria en HIDL. Para utilizar la memoria compartida:
- Obtenga una instancia de
IAllocator
(actualmente sólo está disponible la instancia "ashmem") y úsela para asignar memoria compartida. -
IAllocator::allocate()
devuelve un objetohidl_memory
que puede pasarse a través de HIDL RPC y asignarse a un proceso utilizando la funciónmapMemory
delibhidlmemory
. -
mapMemory
devuelve una referencia a un objetosp<IMemory>
que se puede utilizar para acceder a la memoria. (IMemory
eIAllocator
se definen enandroid.hidl.memory@1.0
).
Se puede utilizar una instancia de IAllocator
para asignar 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 }));
Los cambios reales en la memoria se deben realizar a través de un objeto IMemory
, ya sea en el lado que creó mem
o en el lado que la recibe a través de 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();
interfaz
Las interfaces se pueden pasar como objetos. La palabra interfaz se puede utilizar como azúcar sintáctico para el tipo android.hidl.base@1.0::IBase
; Además, la interfaz actual y cualquier interfaz importada se definirán como un tipo.
Las variables que contienen interfaces deben ser indicadores sólidos: sp<IName>
. Las funciones HIDL que toman parámetros de interfaz convertirán punteros sin formato en punteros potentes, lo que provocará un comportamiento no intuitivo (el puntero se puede borrar inesperadamente). Para evitar problemas, almacene siempre las interfaces HIDL como sp<>
.