HIDL 資料宣告會產生 C++ 標準版面配置資料結構。這些結構體可放置在任何自然位置 (堆疊、檔案或全域範圍,或堆積),並可以相同方式組合。用戶端程式碼會呼叫 HIDL 代理程式程式碼,並傳入 const 參照和原始類型,而 Stub 和代理程式程式碼會隱藏序列化的詳細資料。
注意:開發人員編寫的程式碼無須明確序列化或反序列化資料結構。
下表將 HIDL 原始類型對應至 C++ 資料類型:
HIDL 類型 | C++ 類型 | Header/library |
---|---|---|
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
情境。如果某個值在列舉中出現多次,則該值會多次出現在範圍中。
bitfield<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
類別表示。hidl_array<T, S1, S2, …,
SN>
代表 N 維固定大小陣列 T[S1][S2]…[SN]
。
string
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 介面傳遞檔案描述項。因此,單一檔案描述元會以 native_handle_t
表示,其中沒有 int
和單一 fd
。如果用戶端和伺服器位於不同的程序中,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 接收 mem
的一方。
// 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<>
。