HIDL 數據聲明生成 C++ 標準佈局數據結構。這些結構可以放置在任何感覺自然的地方(在堆棧上、在文件或全局範圍內,或在堆上),並且可以以相同的方式組合。客戶端代碼調用傳入 const 引用和原始類型的 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 |
以下部分更詳細地描述了數據類型。
枚舉
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::setToExternal()
函數初始化hidl_vec<T>
以指向T
類型的外部數據緩衝區。
除了在生成的 C++ 頭文件中適當地發出/插入結構之外,使用vec<T>
會生成一些方便的函數來轉換到/從std::vector
和裸T
指針。如果將vec<T>
用作參數,則使用它的函數將被重載(將生成兩個原型)以接受並傳遞 HIDL 結構和該參數的std::vector<T>
類型。
大批
hidl 中的常量數組由 libhidlbase 中的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
知道如何使用operator=
、隱式強制轉換和.c_str()
函數在std::string and char*
(C 樣式字符串)之間進行轉換。 HIDL 字符串結構具有適當的複制構造函數和賦值運算符,用於:
- 從
std::string
或 C 字符串加載 HIDL 字符串。 - 從 HIDL 字符串創建新的
std::string
string。
此外,HIDL 字符串具有轉換構造函數,因此 C 字符串 ( char *
) 和 C++ 字符串 ( std::string
) 可用於採用 HIDL 字符串的方法。
結構
struct
中的結構只能包含固定大小的數據類型,不能包含函數。 HIDL 結構定義直接映射到 C++ 中的標準佈局struct
,確保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*
對象的隱式和顯式轉換。 HIDL 中handle
類型的主要用途是通過 HIDL 接口傳遞文件描述符。因此,單個文件描述符由沒有int
s 和單個fd
的native_handle_t
表示。如果客戶端和服務器位於不同的進程中,RPC 實現將自動處理文件描述符,以確保兩個進程可以對同一個文件進行操作。
儘管進程在hidl_handle
中接收到的文件描述符在該進程中是有效的,但它不會在接收函數之外持續存在(一旦函數返回,它將被關閉)。想要保持對文件描述符的持久訪問的進程必須dup()
包含的文件描述符,或複制整個hidl_handle
對象。
記憶
HIDL memory
類型映射到libhidlbase
中的hidl_memory
類,它表示未映射的共享內存。這是必須在進程之間傳遞才能在 HIDL 中共享內存的對象。要使用共享內存:
- 獲取
IAllocator
的實例(目前只有實例“ashmem”可用)並使用它來分配共享內存。 -
IAllocator::allocate()
返回一個hidl_memory
對象,該對象可以通過 HIDL RPC 傳遞並使用libhidlmemory
的mapMemory
函數映射到進程中。 -
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<>
。