資料類型

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 原始碼中出現的順序排列的每個枚舉器,從父枚舉開始一直到最後一個子枚舉。例如,此程式碼依序迭代WRITEREADNONECOMPARE 。鑑於上面的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 類型,包括handlestringvec<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 記憶體的物件。使用共享記憶體:

  1. 取得IAllocator的實例(目前只有實例「ashmem」可用)並使用它來分配共享記憶體。
  2. IAllocator::allocate()傳回一個hidl_memory對象,該物件可以透過 HIDL RPC 傳遞,並使用libhidlmemorymapMemory函數映射到進程中。
  3. mapMemory傳回可用於存取記憶體的sp<IMemory>物件的參考。 ( IMemoryIAllocatorandroid.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<>