Tipos de dados

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 na pilha) 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.

Nota: Em nenhum momento o código escrito pelo desenvolvedor é necessário para serializar ou desserializar explicitamente 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.

enumeração

Um enum em HIDL se torna um enum 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 aparece no código-fonte HIDL, começando do enum pai até o último filho. Por exemplo, este código itera 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 várias vezes em uma enumeração, 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> torna-se 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 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 a estrutura apropriadamente no cabeçalho C++ gerado, o uso de vec<T> gera algumas funções convenientes para traduzir de/para std::vector e ponteiros T simples. Se vec<T> for usado como parâmetro, a função que o utiliza será sobrecarregada (dois protótipos serão gerados) para aceitar e passar a estrutura HIDL e um tipo std::vector<T> para esse parâmetro.

variedade

Matrizes constantes em 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] .

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 estilo C) usando operator= , conversões implícitas e a função .c_str() . As estruturas de string HIDL possuem os construtores de cópia e operadores de atribuição apropriados para:

  • Carregue a string HIDL de uma std::string ou C.
  • Crie um novo std::string a partir de uma string HIDL.

Além disso, as strings HIDL possuem 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 estrutura HIDL são mapeadas diretamente para struct s de layout padrão em C++, garantindo que struct s tenham um layout de memória consistente. Uma estrutura pode incluir tipos HIDL, incluindo handle , string e vec<T> , que apontam para buffers separados de comprimento variável.

lidar

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 consultar a memória alocada em um processo. Caso contrário, identificadores incorretos poderão 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.

Os cenários em que o hidl_handle possui seus descritores de arquivo incluídos incluem:

  • Após uma chamada para o 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 objeto hidl_handle

hidl_handle fornece conversões implícitas e explícitas de/para objetos native_handle_t* . O principal uso do tipo 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 residirem em um processo diferente, a implementação 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 assim que a função retornar). Um processo que deseja reter 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 processos para compartilhar memória em HIDL. Para usar memória compartilhada:

  1. Obtenha uma instância do IAllocator (atualmente apenas a instância "ashmem" está disponível) e use-a para alocar memória compartilhada.
  2. IAllocator::allocate() retorna um objeto hidl_memory que pode ser passado através do HIDL RPC e mapeado em um processo usando a 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 em 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
  }));

As alterações reais na memória devem ser feitas por meio de um objeto IMemory , seja no lado que criou mem ou no lado que o recebe por meio 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();

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 indicadores 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 interfaces HIDL como sp<> .