As declarações de dados HIDL geram estruturas de dados de layout padrão em C++. Essas estruturas podem ser colocadas em qualquer lugar que pareça natural (na pilha, no arquivo ou no escopo global ou na pilha) e podem ser compostas da mesma maneira. O código do cliente chama o código de proxy HIDL transmitindo referências const e tipos primitivos, enquanto o código de stub e proxy oculta os detalhes da serialização.
Observação:em nenhum momento é necessário que o código escrito pelo desenvolvedor seja serializado ou desserializado explicitamente.
A tabela abaixo mapeia primitivas 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 em 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 };
… se torna:
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 tipo enumerado pode ser iterado
usando ::android::hardware::hidl_enum_range
. Esse intervalo
inclui todos os enumeradores na ordem em que aparecem no código-fonte HIDL, começando
pelo tipo enumerado pai até o último filho. Por exemplo, este código itera
sobre WRITE
, READ
, NONE
e
COMPARE
nessa ordem. Considerando 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, ele vai aparecer no intervalo várias 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 pode ser usado para transmitir um vetor de qualquer tipo de 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 corretamente no cabeçalho
C++ gerado, o uso de vec<T>
gera algumas funções
de conveniência para traduzir para/de std::vector
e ponteiros T
simples. Se o vec<T>
for usado como um parâmetro, a função
que o usa será sobrecarregada (dois protótipos são gerados) para aceitar e
transmitir o struct HIDL e um tipo std::vector<T>
para esse
parâmetro.
matriz
Matrizes constantes no hidl são representadas 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]
.
string
A classe hidl_string
(parte de libhidlbase
) pode ser
usada para transmitir strings por interfaces HIDL e é definida em
/system/libhidl/base/include/hidl/HidlSupport.h
. O primeiro local de armazenamento
na classe é um ponteiro para o buffer de caracteres.
hidl_string
sabe como converter de e para
std::string and char*
(string no estilo C) usando
operator=
, casts implícitos e a função .c_str()
.
As estruturas de string de HIDL têm os construtores de cópia e operadores de
atribuição apropriados para:
- Carregue a string HIDL de uma
std::string
ou uma string C. - Crie uma nova
std::string
a partir de uma string HIDL.
Além disso, as strings HIDL têm construtores de conversão para que as strings C
(char *
) e C++ (std::string
) possam ser usadas em
métodos que recebem uma string HIDL.
struct
Uma struct
no HIDL pode conter apenas tipos de dados de tamanho fixo e nenhuma
função. As definições de estrutura HIDL são mapeadas diretamente para struct
s
de layout padrão em C++, garantindo que as struct
s 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.
identificador
AVISO:endereços de qualquer tipo (até mesmo endereços de dispositivos físicos) nunca podem fazer parte de um identificador nativo. Transmitir essas 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 inválidos 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
(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 envolve. Ele existe apenas para armazenar com segurança
um ponteiro para um native_handle_t
, de modo que ele possa ser usado em
processos de 32 e 64 bits.
Os cenários em que o hidl_handle
é proprietário dos descritores de arquivo
anexados incluem:
- Após uma chamada para o método
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
para/de objetos native_handle_t*
. O uso principal do
tipo handle
no HIDL é transmitir descritores de arquivo por interfaces
HIDL. Portanto, um único descritor de arquivo é 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
do 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 persiste além da função
recebedora (ele é fechado quando a função retorna). Um processo que quer
manter o acesso persistente ao descritor de arquivo precisa dup()
os
descritores de arquivo incluídos ou copiar todo o objeto hidl_handle
.
memória
O tipo memory
do HIDL é mapeado para a classe hidl_memory
em libhidlbase
, que representa a memória compartilhada não mapeada. Esse é
o objeto que precisa ser transmitido entre os processos para compartilhar memória no HIDL. Para
usar a memória compartilhada:
- Receba uma instância de
IAllocator
(atualmente, apenas a instância "ashmem" está disponível) e use-a para alocar a memória compartilhada. IAllocator::allocate()
retorna um objetohidl_memory
que pode ser transmitido pelo RPC HIDL e mapeado em um processo usando a funçãomapMemory
dolibhidlmemory
.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 mudanças reais na memória precisam ser feitas por um objeto IMemory
, seja no lado que criou mem
ou no lado que
o 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 todas as interfaces importadas são definidas
como um tipo.
As variáveis que contêm interfaces precisam ser ponteiros fortes:
sp<IName>
. As funções HIDL que usam parâmetros de interface
convertem ponteiros brutos em ponteiros fortes, causando um comportamento não intuitivo
(o ponteiro pode ser limpo inesperadamente). Para evitar problemas, sempre armazene interfaces
HIDL como um sp<>
.