Объявления данных 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
в C++ представлен структурой hidl_handle
, которая представляет собой простую оболочку указателя на 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. Чтобы использовать общую память:
- Получите экземпляр
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();
интерфейс
Интерфейсы можно передавать как объекты. Слово интерфейс можно использовать как синтаксический сахар для типа android.hidl.base@1.0::IBase
; кроме того, текущий интерфейс и все импортированные интерфейсы определяются как тип.
Переменные, содержащие интерфейсы, должны быть сильными указателями: sp<IName>
. HIDL-функции, принимающие параметры интерфейса, преобразуют необработанные указатели в сильные указатели, что приводит к неинтуитивному поведению (указатель может быть неожиданно очищен). Чтобы избежать проблем, всегда сохраняйте интерфейсы HIDL как sp<>
.