數據類型

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

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_arraylibhidlbasehidl_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 類型,包括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 s 和單個fdnative_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();

界面

接口可以作為對像傳遞。接口一詞可用作android.hidl.base@1.0::IBase類型的語法糖;此外,當前接口和任何導入的接口都將被定義為一個類型。

保存接口的變量應該是強指針: sp<IName> 。採用接口參數的 HIDL 函數會將原始指針轉換為強指針,從而導致不直觀的行為(指針可能會被意外清除)。為避免出現問題,請始終將 HIDL 接口存儲為sp<>