Объявления данных HIDL генерируют структуры данных стандартной разметки C++. Эти структуры можно размещать в любом подходящем месте (в стеке, в файле, в глобальной области видимости или в куче) и составлять одинаковым образом. Клиентский код вызывает код прокси-сервера HIDL, передавая константные ссылки и примитивные типы, а код заглушки и прокси-сервера скрывает детали сериализации.
Примечание. Разработчику не нужно писать код для сериализации или десериализации структур данных.
В таблице ниже показано сопоставление примитивов HIDL с типами данных C++:
| Тип HIDL | Тип C++ | Заголовок/библиотека |
|---|---|---|
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 |
Подробная информация о типах данных приведена в разделах ниже.
enum : перечисление
Перечисление в 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.
vec<T>
Шаблон класса hidl_vec<T> является частью libhidlbase и может использоваться для передачи вектора любого типа HIDL произвольного размера. Сопоставимый контейнер фиксированного размера – hidl_array. Объект hidl_vec<T> также можно инициализировать, чтобы он указывал на внешний буфер данных типа T, с помощью функции hidl_vec::setToExternal().
Помимо того, что vec<T> правильно вставляет структуру в сгенерированный заголовок C++, этот тег также создает удобные функции для преобразования в std::vector и обычные указатели T и обратно. Если vec<T> используется в качестве параметра, функция, в которой он применяется, перегружается (создаются два прототипа), чтобы принимать и передавать как структуру HIDL, так и тип std::vector<T> для этого параметра.
массив
Постоянные массивы в HIDL представлены классом hidl_array в libhidlbase. hidl_array<T, S1, S2, …,
SN> – это многомерный массив фиксированного размера.
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
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.
память
Тип HIDL memory сопоставляется с классом hidl_memory в libhidlbase, который представляет несопоставленную общую память. Это объект, который должен передаваться между процессами для совместного использования памяти в HIDL. Чтобы использовать общую память:
- Получите экземпляр
IAllocator(в настоящее время доступен только экземпляр ashmem) и используйте его для выделения общей памяти. IAllocator::allocate()возвращает объектhidl_memory, который можно передать через HIDL RPC и сопоставить с процессом с помощью функцииmapMemoryобъектаlibhidlmemory.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();
интерфейс
Интерфейсы можно передавать как объекты. Слово interface можно использовать как синтаксический сахар для типа android.hidl.base@1.0::IBase. Кроме того, текущий интерфейс и любые импортированные интерфейсы определяются как тип.
Переменные, содержащие интерфейсы, должны быть сильными указателями:
sp<IName>. Функции HIDL, принимающие параметры интерфейса, преобразуют необработанные указатели в строгие указатели, что приводит к неинтуитивному поведению (указатель может быть неожиданно очищен). Чтобы избежать проблем, всегда храните интерфейсы HIDL как sp<>.