As declarações de dados HIDL geram estruturas de dados de layout padrão C++. Essas estruturas podem ser colocadas em qualquer lugar que pareça natural (na pilha, no arquivo ou no escopo global ou no heap) e podem ser compostas da mesma maneira. O código do cliente chama o código proxy HIDL passando referências const e tipos primitivos, enquanto o código stub e proxy oculta os detalhes da serialização.
Observação: em nenhum momento o código escrito pelo desenvolvedor é necessário para serializar ou desserializar explicitamente as estruturas de dados.
A tabela abaixo mapeia primitivas HIDL para tipos de dados C++:
Tipo 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 com mais detalhes.
enumerar
Uma enumeração em HIDL torna-se uma enumeração em C++. Por exemplo:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… torna-se:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
A partir do Android 10, um enum pode ser iterado usando ::android::hardware::hidl_enum_range
. Esse intervalo inclui cada enumerador na ordem em que aparecem no código-fonte HIDL, começando da enumeração pai até o último filho. Por exemplo, esse código itera sobre WRITE
, READ
, NONE
e COMPARE
nessa ordem. Dado SpecialMode
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 usado em contextos constexpr
. Se um valor aparecer em uma enumeração várias vezes, o valor aparecerá no intervalo várias vezes.
campo de bits<T>
bitfield<T>
(onde T
é uma enumeração definida pelo usuário) torna-se o tipo subjacente dessa enumeração em C++. No exemplo acima, bitfield<Mode>
se torna uint8_t
.
vec<T>
O modelo de classe hidl_vec<T>
faz parte da libhidlbase
e pode ser usado para passar 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 hidl_vec::setToExternal()
.
Além de emitir/inserir a struct apropriadamente no cabeçalho C++ gerado, o uso de vec<T>
gera algumas funções de conveniência para traduzir de/para std::vector
e ponteiros T
nus. Se o vec<T>
for usado como parâmetro, a função que o usar será sobrecarregada (dois protótipos serão gerados) para aceitar e passar tanto a estrutura HIDL quanto um tipo std::vector<T>
para esse parâmetro.
variedade
Arrays constantes em hidl são representados pela classe hidl_array
em libhidlbase
. Um hidl_array<T, S1, S2, …, SN>
representa uma matriz de tamanho fixo N dimensional T[S1][S2]…[SN]
.
corda
A classe hidl_string
(parte de libhidlbase
) pode ser usada para passar strings por interfaces HIDL e é definida em /system/libhidl/base/include/hidl/HidlSupport.h
. O primeiro local de armazenamento na classe é um ponteiro para seu buffer de caracteres.
hidl_string
sabe como converter de e para std::string and char*
(string de estilo C) usando operator=
, conversões implícitas e a função .c_str()
. As estruturas de string HIDL têm os construtores de cópia e operadores de atribuição apropriados para:
- Carregue a string HIDL de um
std::string
ou uma string C. - Crie um novo
std::string
de uma string HIDL.
Além disso, as strings HIDL têm construtores de conversão, portanto, strings C ( char *
) e strings C++ ( std::string
) podem ser usadas em métodos que usam uma string HIDL.
estrutura
Uma struct
em HIDL pode conter apenas tipos de dados de tamanho fixo e nenhuma função. As definições de struct
HIDL são mapeadas diretamente para structs de layout padrão em C++, garantindo que struct
tenham um layout de memória consistente. Um struct pode incluir tipos HIDL, incluindo handle
, string
e vec<T>
, que apontam para buffers de comprimento variável separados.
lidar com
AVISO: Endereços de qualquer tipo (mesmo endereços de dispositivos físicos) nunca devem fazer parte de um identificador nativo. Passar essas informações entre processos é perigoso e os torna suscetíveis a ataques. Quaisquer valores passados entre processos devem ser validados antes de serem usados para pesquisar a memória alocada em um processo. Caso contrário, identificadores incorretos podem causar acesso incorreto à memória ou corrupção de memória.
O tipo handle
é representado pela estrutura hidl_handle
em C++, que é um wrapper simples em torno de um ponteiro para um objeto const native_handle_t
(isso 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 se apropria do ponteiro native_handle_t
que ele envolve. Ele existe apenas para armazenar com segurança um ponteiro para um native_handle_t
de modo que possa ser usado em processos de 32 e 64 bits.
Cenários em que o hidl_handle
possui seus descritores de arquivo incluídos incluem:
- Após uma chamada ao
setTo(native_handle_t* handle, bool shouldOwn)
com o parâmetroshouldOwn
definido comotrue
- Quando o objeto
hidl_handle
é criado pela construção de cópia de outro objetohidl_handle
- Quando o objeto
hidl_handle
é atribuído por cópia de outro objetohidl_handle
hidl_handle
fornece conversões implícitas e explícitas de/para objetos native_handle_t*
. O principal uso para o tipo de handle
em HIDL é passar descritores de arquivo por interfaces HIDL. Um único descritor de arquivo é, portanto, representado por um native_handle_t
sem int
s e um único fd
. Se o cliente e o servidor estiverem em um processo diferente, a implementação de RPC cuidará automaticamente do descritor de arquivo para garantir que ambos os processos possam operar no mesmo arquivo.
Embora um descritor de arquivo recebido em um hidl_handle
por um processo seja válido nesse processo, ele não persistirá além da função receptora (será fechado quando a função retornar). Um processo que deseja manter o acesso persistente ao descritor de arquivo deve dup()
os descritores de arquivo incluídos ou copiar todo o objeto hidl_handle
.
memória
O tipo de memory
HIDL é mapeado para a classe hidl_memory
em libhidlbase
, que representa a memória compartilhada não mapeada. Este é o objeto que deve ser passado entre os processos para compartilhar memória em HIDL. Para usar a memória compartilhada:
- Obtenha uma instância de
IAllocator
(atualmente apenas a instância "ashmem" está disponível) e use-a para alocar memória compartilhada. -
IAllocator::allocate()
retorna um objetohidl_memory
que pode ser passado através de HIDL RPC e ser mapeado em um processo usando a funçãolibhidlmemory
damapMemory
. -
mapMemory
retorna uma referência a um objetosp<IMemory>
que pode ser usado para acessar a memória. (IMemory
eIAllocator
são definidos emandroid.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 }));
As alterações reais na memória devem ser feitas por meio de um objeto IMemory
, seja no lado que criou o mem
ou no lado que o recebe por 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();
interface
Interfaces podem ser passadas 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 serão definidas como um tipo.
Variáveis que contêm Interfaces devem ser ponteiros fortes: sp<IName>
. As funções HIDL que usam parâmetros de interface converterão ponteiros brutos em ponteiros fortes, causando um comportamento não intuitivo (o ponteiro pode ser apagado inesperadamente). Para evitar problemas, sempre armazene as interfaces HIDL como um sp<>
.