数据类型

HIDL 数据声明可生成 C++ 标准布局数据结构。您可以将这些结构放置在任何合适的位置(可以放在堆栈上,放在文件或全局范围内,也可以放在堆区上),而且这些结构能以相同的方式构成。客户端代码会调用传入常量引用和基元类型的 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

opaque

uint64_t

<stdint.h>

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 class Mode : uint8_t { WRITE = 1, READ = 2 };

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::vectorT 裸指针或从其进行转换。如果您将 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 字符串还有转换构造函数,因此 C 字符串 (char *) 和 C++ 字符串 (std::string) 可用于采用 HIDL 字符串的方法。

结构

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 接口传递文件描述符。因此,单个文件描述符由没有 intnative_handle_t 和单个 fd 表示。如果客户端和服务器在不同的进程中运行,则 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<>