Типы данных

Объявления данных HIDL генерируют структуры данных стандартного формата C++. Эти структуры можно размещать где угодно (в стеке, в файловой или глобальной области видимости, в куче) и составлять их одинаковым образом. Клиентский код вызывает прокси-код HIDL, передавая константные ссылки и примитивные типы, тогда как код-заглушка и прокси-сервер скрывают детали сериализации.

Примечание. Ни в коем случае не требуется написанный разработчиком код для явной сериализации или десериализации структур данных.

В таблице ниже примитивы HIDL сопоставляются с типами данных C++:

Тип HIDL Тип С++ Заголовок/Библиотека
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

В разделах ниже типы данных описываются более подробно.

перечисление

Перечисление в HIDL становится перечислением в C++. Например:

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

… становится:

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

Начиная с Android 10, перечисление можно перебирать с помощью ::android::hardware::hidl_enum_range . Этот диапазон включает в себя все перечислители в том порядке, в котором они появляются в исходном коде HIDL, начиная с родительского перечисления и заканчивая последним дочерним элементом. Например, этот код перебирает WRITE , READ , NONE и COMPARE в указанном порядке. Учитывая SpecialMode выше:

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

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

hidl_enum_range также реализует обратные итераторы и может использоваться в контекстах constexpr . Если значение появляется в перечислении несколько раз, оно появляется в диапазоне несколько раз.

битовое поле<T>

bitfield<T> (где T — определяемое пользователем перечисление) становится базовым типом этого перечисления в C++. В приведенном выше примере bitfield<Mode> становится uint8_t .

век<T>

Шаблон класса hidl_vec<T> является частью libhidlbase и может использоваться для передачи вектора любого типа HIDL произвольного размера. Сопоставимый контейнер фиксированного размера — hidl_array . hidl_vec<T> также можно инициализировать, чтобы указать на внешний буфер данных типа T , используя функцию hidl_vec::setToExternal() .

Помимо создания/вставки структуры в сгенерированный заголовок C++, использование vec<T> генерирует некоторые удобные функции для преобразования в/из указателей std::vector и голых T Если vec<T> используется в качестве параметра, функция, использующая его, будет перегружена (будет создано два прототипа), чтобы принять и передать как структуру HIDL, так и тип std::vector<T> для этого параметра.

множество

Массивы констант в hidl представлены классом hidl_array в libhidlbase . hidl_array<T, S1, S2, …, SN> представляет N-мерный массив фиксированного размера T[S1][S2]…[SN] .

нить

Класс hidl_string (часть libhidlbase ) может использоваться для передачи строк через интерфейсы HIDL и определен в /system/libhidl/base/include/hidl/HidlSupport.h . Первое место хранения в классе — это указатель на его буфер символов.

hidl_string знает, как преобразовывать в std::string and char* (строку в стиле C) и обратно, используя operator= , неявное приведение типов и функцию .c_str() . Строковые структуры HIDL имеют соответствующие конструкторы копирования и операторы присваивания для:

  • Загрузите строку HIDL из std::string или строки C.
  • Создайте новую std::string из строки HIDL.

Кроме того, строки HIDL имеют конструкторы преобразования, поэтому строки C ( char * ) и строки C++ ( std::string ) можно использовать в методах, которые принимают строку HIDL.

структура

struct в HIDL может содержать только типы данных фиксированного размера и не содержать функций. Определения структур HIDL напрямую сопоставляются со struct стандартной компоновки в C++, гарантируя, что struct имеют единообразную структуру памяти. Структура может включать типы HIDL, включая handle , string и vec<T> , которые указывают на отдельные буферы переменной длины.

ручка

ВНИМАНИЕ: адреса любого типа (даже адреса физических устройств) никогда не должны быть частью собственного дескриптора. Передача этой информации между процессами опасна и делает их уязвимыми для атак. Любые значения, передаваемые между процессами, должны быть проверены перед использованием для поиска выделенной памяти внутри процесса. В противном случае неправильные дескрипторы могут привести к плохому доступу к памяти или повреждению памяти.

Тип handle представлен структурой hidl_handle в C++, которая представляет собой простую оболочку указателя на const native_handle_t (в Android это уже давно присутствует).

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;

По умолчанию hidl_handle не становится владельцем указателя native_handle_t , который он обертывает. Он существует просто для безопасного хранения указателя на native_handle_t , чтобы его можно было использовать как в 32-, так и в 64-битных процессах.

Сценарии, в которых hidl_handle владеет вложенными дескрипторами файлов, включают:

  • После вызова метода setTo(native_handle_t* handle, bool shouldOwn) с параметром shouldOwn , установленным в true
  • Когда объект hidl_handle создается путем копирования из другого объекта hidl_handle .
  • Когда объект hidl_handle копируется из другого объекта hidl_handle

hidl_handle обеспечивает как неявные, так и явные преобразования в/из объектов native_handle_t* . Основное использование типа handle в HIDL — передача дескрипторов файлов через интерфейсы HIDL. Таким образом, один файловый дескриптор представлен native_handle_t без int и одним fd . Если клиент и сервер находятся в разных процессах, реализация RPC автоматически позаботится о файловом дескрипторе, чтобы оба процесса могли работать с одним и тем же файлом.

Хотя дескриптор файла, полученный процессом в hidl_handle , будет действительным в этом процессе, он не будет сохраняться за пределами принимающей функции (он будет закрыт после завершения функции). Процесс, который хочет сохранить постоянный доступ к файловому дескриптору, должен dup() вложенных файловых дескрипторов или скопировать весь объект hidl_handle .

Память

Тип memory HIDL сопоставляется с классом hidl_memory в libhidlbase , который представляет неотображенную общую память. Это объект, который необходимо передавать между процессами для совместного использования памяти в HIDL. Чтобы использовать общую память:

  1. Получите экземпляр IAllocator (на данный момент доступен только экземпляр «ashmem») и используйте его для выделения общей памяти.
  2. IAllocator::allocate() возвращает объект hidl_memory , который можно передать через HIDL RPC и отобразить в процесс с помощью функции mapMemory libhidlmemory .
  3. mapMemory возвращает ссылку на объект sp<IMemory> , который можно использовать для доступа к памяти. ( IMemory и IAllocator определены в android.hidl.memory@1.0 .)

Экземпляр IAllocator можно использовать для выделения памяти:

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

Фактические изменения в памяти должны выполняться через объект IMemory либо на стороне, создавшей mem , либо на стороне, которая получает ее через 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();

интерфейс

Интерфейсы можно передавать как объекты. Слово интерфейс можно использовать как синтаксический сахар для типа android.hidl.base@1.0::IBase ; кроме того, текущий интерфейс и все импортированные интерфейсы будут определены как тип.

Переменные, содержащие интерфейсы, должны быть сильными указателями: sp<IName> . HIDL-функции, принимающие параметры интерфейса, преобразуют необработанные указатели в сильные указатели, что приводит к неинтуитивному поведению (указатель может быть неожиданно очищен). Чтобы избежать проблем, всегда сохраняйте интерфейсы HIDL как sp<> .