Tipos de datos

Las declaraciones de datos HIDL generan estructuras de datos de diseño estándar C++. Estos Las estructuras se pueden colocar en cualquier lugar que se sienta natural (en la pila, en el archivo o alcance global o en el montón) y se pueden componer de la misma manera. Cliente el código llama al código proxy HIDL pasando referencias constantes y tipos primitivos, mientras que el código stub y proxy oculta los detalles de la serialización.

Nota: En ningún momento se encuentra código escrito por el desarrollador. necesarias para serializar o deserializar explícitamente las estructuras de datos.

En la siguiente tabla, se asignan primitivas de HIDL a tipos de datos C++:

Tipo de HIDL Tipo de 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

En las siguientes secciones, se describen los tipos de datos en más detalle.

enum

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 por encima del uso de ::android::hardware::hidl_enum_range. Este rango incluye a cada enumerador en el orden en que aparece en el código fuente HIDL, a partir de desde la enumeración principal hasta la secundaria. 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 usado en contextos constexpr. Si aparece un valor 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 la clase hidl_vec<T> forma parte de libhidlbase y se puede usar para pasar un vector de cualquier tipo HIDL con un tamaño arbitrario. El contenedor de tamaño fijo comparable hidl_array. Un hidl_vec<T> también puede ser inicializada para apuntar a un búfer de datos externo de tipo T, con la función hidl_vec::setToExternal().

Además de emitir o insertar el struct de forma adecuada en los encabezado C++, el uso de vec<T> genera algunos beneficios. funciones para traducir desde y hacia std::vector y T vacío de seguridad. Si se usa vec<T> como parámetro, la función está sobrecargado (se generan dos prototipos) para aceptar y pasar el struct HIDL y un tipo std::vector<T> para eso parámetro.

matriz

Los arrays constantes en hidl se representan con la clase hidl_array en libhidlbase. Una hidl_array<T, S1, S2, …, SN> representa un array de tamaño fijo de N dimensiones. T[S1][S2]…[SN]

string

La clase hidl_string (parte de libhidlbase) puede ser que se usa para pasar cadenas a través de interfaces HIDL y se define en /system/libhidl/base/include/hidl/HidlSupport.h El primer almacenamiento la ubicación en la clase es un puntero a su búfer de caracteres.

hidl_string sabe cómo convertir y viceversa. std::string and char* (cadena de estilo C) con operator=, conversiones implícitas y función .c_str(). Las estructuras de cadena de HIDL tienen los constructores de copia y la asignación adecuados operadores para lo siguiente:

  • Carga la cadena HIDL desde una cadena std::string o C.
  • Crea un std::string nuevo a partir de una cadena HIDL.

Además, las cadenas HIDL tienen constructores de conversión, por lo que las cadenas C (char *) y las cadenas C++ (std::string) se pueden usar en que toman una cadena HIDL.

estructura

Una struct en HIDL solo puede contener tipos de datos de tamaño fijo y no funciones. Las definiciones de la estructura HIDL se asignan directamente al diseño estándar struct en C++, lo que garantiza que los struct tengan un un diseño de memoria coherente. Una struct puede incluir tipos de HIDL, como handle, string y vec<T>, que apuntan a búferes de longitud variable separados.

identificador

ADVERTENCIA: Direcciones de cualquier tipo (incluso físicas del dispositivo) nunca deben ser parte de un identificador nativo. Pasando por alto esto información entre procesos es peligrosa y los hace susceptibles de sufrir ataques. Todos los valores que se pasen entre procesos se deben validar antes de usarlos para buscar la memoria asignada dentro de un proceso. De lo contrario, los identificadores incorrectos pueden causar el acceso a la memoria o los daños en la memoria.

El tipo handle se representa con hidl_handle. en C++, que es un wrapper sencillo alrededor de un puntero hacia una const native_handle_t (esto está 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 asume la propiedad. del puntero native_handle_t que une. Simplemente existe para proteger almacenar un puntero para un elemento native_handle_t, de modo que pueda usarse en en procesos de 32 y 64 bits.

Situaciones en las que hidl_handle posee su archivo adjunto los descriptores incluyen:

  • Luego de una llamada al método setTo(native_handle_t* handle, bool shouldOwn) con el parámetro shouldOwn configurado como true
  • Cuando se crea el objeto hidl_handle mediante construcción de copia de otro objeto hidl_handle
  • Cuando el objeto hidl_handle se asigna por copia desde otro hidl_handle objeto

hidl_handle proporciona conversiones implícitas y explícitas desde y hacia native_handle_t* objetos. El uso principal del El tipo handle en HIDL consiste en pasar los descriptores de archivos a través del HIDL interfaces. Por lo tanto, un solo descriptor de archivo está representado por un native_handle_t sin int y un solo fd Si el cliente y el servidor se encuentran en un proceso diferente, la RPC implementación se encarga automáticamente del descriptor de archivo para garantizar ambos procesos pueden operar en el mismo archivo.

Aunque un descriptor de archivos recibido en un hidl_handle por un proceso es válido en ese proceso, no persiste más allá de la recepción (se cierra cuando la función muestra un resultado). Un proceso en el que se intenta retener el acceso persistente al descriptor de archivo debe dup() el descriptores de archivos delimitados o copiar el objeto hidl_handle completo.

memoria

El tipo memory de HIDL se asigna a la clase hidl_memory en libhidlbase, que representa la memoria compartida sin asignar. Este es el objeto que se debe pasar entre procesos para compartir memoria en HIDL. Para usar memoria compartida:

  1. Obtén una instancia de IAllocator (por el momento, la única instancia “ashmem” está disponible) y lo usa para asignar memoria compartida.
  2. IAllocator::allocate() muestra un hidl_memory. que se puede pasar a través de la RPC de HIDL y asignarse a un proceso mediante Función mapMemory de libhidlmemory.
  3. mapMemory devuelve una referencia a un Es el objeto sp<IMemory> que se puede usar para acceder a la memoria. (IMemory y IAllocator se definen en android.hidl.memory@1.0).

Se puede usar 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 deben realizarse mediante un IMemory. objeto, ya sea en el lado que creó mem o en el lado que los recibe a través de RPC HIDL.

// 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. Puedes usar la palabra interfaz. como sintaxis edulcorada para el tipo android.hidl.base@1.0::IBase; además, se definen la interfaz actual y cualquier interfaz importada como un tipo.

Las variables que contienen interfaces deben ser punteros fuertes: sp<IName>. Funciones HIDL que toman parámetros de interfaz Convierten punteros sin procesar en punteros fuertes, lo que provoca un comportamiento no intuitivo (el puntero se puede borrar de forma inesperada). Para evitar problemas, almacena siempre el HIDL interfaces como un sp<>.