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
。
向量<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
類別表示。 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
。
此外,HIDL 字串具有轉換建構函數,因此可以在採用 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
和單一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();
介面
接口可以作為物件傳遞。單字Interface可以用作android.hidl.base@1.0::IBase
類型的語法糖;此外,目前介面和任何匯入的介面都將被定義為類型。
保存介面的變數應該是強指標: sp<IName>
。接受介面參數的 HIDL 函數會將原始指針轉換為強指針,從而導致不直觀的行為(指針可能會被意外清除)。為了避免問題,請務必將 HIDL 介面儲存為sp<>
。