Tipos de dados

As declarações de dados HIDL geram estruturas de dados no layout padrão do C++. Esses estruturas podem ser colocadas em qualquer lugar que pareça natural (na pilha, no arquivo ou escopo global ou na heap) e podem ser compostas da mesma maneira. Cliente chama o código de proxy HIDL passando em referências de constantes e tipos primitivos, enquanto o stub e o código proxy ocultam os detalhes da serialização.

Observação:em nenhum momento código escrito pelo desenvolvedor para serializar ou desserializar explicitamente as estruturas de dados.

A tabela abaixo mapeia primitivos HIDL para tipos de dados C++:

Tipo de HIDL Tipo C++ Cabeçalho/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

As seções abaixo descrevem os tipos de dados em mais detalhes.

enum

Um tipo enumerado no HIDL se torna um tipo enumerado em C++. Por exemplo:

enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 };
enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };

... passa a ser:

enum class Mode : uint8_t { WRITE = 1, READ = 2 };
enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };

No Android 10 e versões mais recentes, um tipo enumerado pode ser iterado. acima do uso de ::android::hardware::hidl_enum_range. Esta faixa inclui cada enumerador na ordem em que aparece no código-fonte de HIDL, começando desde o tipo enumerado pai até o último filho. Por exemplo, esse código itera mais de WRITE, READ, NONE e COMPARE nessa ordem. SpecialMode informado acima:

template <typename T>
using hidl_enum_range = ::android::hardware::hidl_enum_range<T>

for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}

hidl_enum_range também implementa iteradores reversos e pode ser usada em contextos constexpr. Se um valor aparecer em uma enumeração várias vezes, o valor aparece no intervalo diversas vezes.

bitfield<T>

bitfield<T> (em que T é um tipo enumerado definido pelo usuário) se torna o tipo subjacente desse tipo enumerado em C++. No exemplo acima, bitfield<Mode> se torna uint8_t.

vec<T>

O modelo de classe hidl_vec<T> faz parte de libhidlbase e podem ser usadas para transmitir um vetor de qualquer tipo HIDL com um tamanho arbitrário. O contêiner de tamanho fixo comparável hidl_array: Um hidl_vec<T> também pode ser inicializado para apontar para um buffer de dados externo do tipo T, usando a função hidl_vec::setToExternal().

Além de emitir/inserir o struct adequadamente no cabeçalho C++, o uso de vec<T> gera alguma conveniência funções para traduzir de/para std::vector e T nua ponteiros. Se o vec<T> for usado como um parâmetro, a função que está sobrecarregado (são gerados dois protótipos) para aceitar e transmitir o struct HIDL e um tipo std::vector<T> para isso. .

matriz

Matrizes constantes no hidl são representadas pela classe hidl_array em libhidlbase. Um hidl_array<T, S1, S2, …, SN> representa uma matriz n de tamanho fixo de dimensão. T[S1][S2]…[SN].

string

A classe hidl_string (parte de libhidlbase) pode ser usada para passar strings por interfaces HIDL e é definido em /system/libhidl/base/include/hidl/HidlSupport.h O primeiro armazenamento local na classe é um ponteiro para o buffer de caracteres dela.

O hidl_string sabe como converter de e para std::string and char* (string em estilo C) usando operator=, casts implícitos e função .c_str(). Os structs de string HIDL têm os construtores de cópia e atribuição apropriados operadores para:

  • Carregue a string HIDL de uma string std::string ou C.
  • Cria um novo std::string com base em uma string HIDL.

Além disso, as strings HIDL têm construtores de conversão. Portanto, as strings C (char *) e as strings C++ (std::string) podem ser usadas em métodos que usam uma string HIDL.

estrutura

Um struct em HIDL pode conter apenas tipos de dados de tamanho fixo e nenhum . As definições de struct HIDL são mapeadas diretamente para o layout padrão structs em C++, garantindo que structs tenham uma e um layout de memória consistente. Um struct pode incluir tipos de HIDL, incluindo handle, string e vec<T>, que para separar buffers de comprimento variável.

identificador

AVISO: endereços de qualquer tipo (mesmo físicos endereços de dispositivo) nunca podem fazer parte de um identificador nativo. Ao passar informações entre processos é perigoso e os torna suscetíveis a ataques. Todos os valores transmitidos entre processos precisam ser validados antes de serem usados para procurar a memória alocada em um processo. Caso contrário, identificadores incorretos podem causar acesso ou corrupção de memória.

O tipo handle é representado por hidl_handle. em C++, que é um wrapper simples em torno de um ponteiro para um Objeto const native_handle_t, que está presente no Android há muito 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;

Por padrão, hidl_handle não assume a propriedade do ponteiro native_handle_t que ele une. Ele existe apenas para proteger armazene um ponteiro para um native_handle_t para que ele possa ser usado em processos de 32 e 64 bits.

Cenários em que hidl_handle é o proprietário do arquivo incluído os descritores incluem:

  • Após uma chamada para o método setTo(native_handle_t* handle, bool shouldOwn) com o parâmetro shouldOwn definido como true
  • Quando o objeto hidl_handle é criado pela construção de cópia. de outro objeto hidl_handle
  • Quando o objeto hidl_handle é atribuído por cópia de outro hidl_handle objeto

hidl_handle oferece conversões implícitas e explícitas. de/para objetos native_handle_t* . O principal uso da O tipo handle em HIDL é transmitir descritores de arquivos pelo HIDL do Google Cloud. Portanto, um único descritor de arquivo é representado por uma native_handle_t sem ints e uma única fd Se o cliente e o servidor estiverem em um processo diferente, o RPC implementação cuida automaticamente do descritor de arquivo para garantir os dois processos podem operar no mesmo arquivo.

Embora um descritor de arquivo recebido em um hidl_handle por um ou processo é válido nesse processo, ele não persiste além do função (ela é fechada quando a função retorna). Um processo que quer reter o acesso persistente ao descritor de arquivo deverá dup() o descritores de arquivo incluídos ou copie todo o objeto hidl_handle.

memória

O tipo memory de HIDL é mapeado para a classe hidl_memory em libhidlbase, que representa a memória compartilhada não mapeada. Isso é o objeto que precisa ser transmitido entre processos para compartilhar a memória no HIDL. Para usar memória compartilhada:

  1. Consiga uma instância de IAllocator (atualmente, apenas a instância "ashmem" estiver disponível) e usá-lo para alocar memória compartilhada.
  2. IAllocator::allocate() retorna um hidl_memory. que pode ser transmitido pelo RPC HIDL e mapeado para um processo usando Função mapMemory de libhidlmemory.
  3. mapMemory retorna uma referência a um Objeto sp<IMemory> que pode ser usado para acessar a memória. (IMemory e IAllocator são definidos nas android.hidl.memory@1.0).

Uma instância de IAllocator pode ser usada para alocar memória:

#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
  }));

Mudanças reais na memória precisam ser feitas por uma IMemory do objeto, seja no lado que criou mem ou no lado que recebe por 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();

interface

As interfaces podem ser transmitidas como objetos. A palavra interface pode ser usada como açúcar sintático para o tipo android.hidl.base@1.0::IBase. Além disso, a interface atual e quaisquer interfaces importadas são definidas como um tipo.

As variáveis que contêm interfaces precisam ser indicadores fortes: sp<IName>: Funções HIDL que usam parâmetros de interface converter ponteiros brutos em ponteiros fortes, causando um comportamento não intuitivo (o ponteiro pode ser apagado inesperadamente). Para evitar problemas, sempre armazene o HIDL como um sp<>.