HIDL
HIDL
HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。
HIDL 旨在用于进程间通信 (IPC)。进程之间的通信经过 Binder 化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。
HIDL 可指定数据结构和方法签名,这些内容会整理归类到接口(与类相似)中,而接口会汇集到软件包中。尽管 HIDL 具有一系列不同的关键字,但 C++ 和 Java 程序员对 HIDL 的语法并不陌生。此外,HIDL 还使用 Java 样式的注释。
HIDL 设计
HIDL 的目标是,框架可以在无需重新构建 HAL 的情况下进行替换。HAL 将由供应商或 SOC 制造商构建,放置在设备的 /vendor
分区中,这样一来,框架就可以在其自己的分区中通过 OTA 进行替换,而无需重新编译 HAL。
HIDL 设计在以下方面之间保持了平衡:
- 互操作性。在可以使用各种架构、工具链和编译配置来编译的进程之间创建可互操作的可靠接口。HIDL 接口是分版本的,发布后不得再进行更改。
- 效率。HIDL 会尝试尽可能减少复制操作的次数。HIDL 定义的数据以 C++ 标准布局数据结构传递至 C++ 代码,无需解压,可直接使用。此外,HIDL 还提供共享内存接口;由于 RPC 本身有点慢,因此 HIDL 支持两种无需使用 RPC 调用的数据传输方法:共享内存和快速消息队列 (FMQ)。
- 直观。通过仅针对 RPC 使用
in
参数,HIDL 避开了内存所有权这一棘手问题(请参阅 Android 接口定义语言 (AIDL));无法从方法高效返回的值将通过回调函数返回。无论是将数据传递到 HIDL 中以进行传输,还是从 HIDL 接收数据,都不会改变数据的所有权,也就是说,数据所有权始终属于调用函数。数据仅需要在函数被调用期间保留,可在被调用的函数返回数据后立即清除。
使用直通模式
要将运行早期版本的 Android 的设备更新为使用 Android O,您可以将惯用的(和旧版)HAL 封装在一个新 HIDL 接口中,该接口将在绑定式模式和同进程(直通)模式提供 HAL。这种封装对于 HAL 和 Android 框架来说都是透明的。
直通模式仅适用于 C++ 客户端和实现。运行早期版本的 Android 的设备没有用 Java 编写的 HAL,因此 Java HAL 自然而然经过 Binder 化。
直通式标头文件
编译 .hal
文件时,除了用于 Binder 通信的标头之外,hidl-gen
还会生成一个额外的直通标头文件 BsFoo.h
;此标头定义了会被执行 dlopen
操作的函数。由于直通式 HAL 在它们被调用的同一进程中运行,因此在大多数情况下,直通方法由直接函数调用(同一线程)来调用。oneway
方法在各自的线程中运行,因为它们不需要等待 HAL 来处理它们(这意味着,在直通模式下使用 oneway
方法的所有 HAL 对于线程必须是安全的)。
如果有一个 IFoo.hal
,BsFoo.h
会封装 HIDL 生成的方法,以提供额外的功能(例如使 oneway
事务在其他线程中运行)。该文件类似于 BpFoo.h
,不过,所需函数是直接调用的,并未使用 Binder 传递调用 IPC。未来,HAL 的实现可能提供多种实现结果,例如 FooFast HAL 和 FooAccurate HAL。在这种情况下,系统会针对每个额外的实现结果创建一个文件(例如 PTFooFast.cpp
和 PTFooAccurate.cpp
)。
Binder 化直通式 HAL
您可以将支持直通模式的 HAL 实现 Binder 化。如果有一个 HAL 接口 a.b.c.d@M.N::IFoo
,系统会创建两个软件包:
a.b.c.d@M.N::IFoo-impl
。包含 HAL 的实现,并暴露函数IFoo* HIDL_FETCH_IFoo(const char* name)
。在旧版设备上,此软件包经过dlopen
处理,且实现使用HIDL_FETCH_IFoo
进行了实例化。您可以使用hidl-gen
和-Lc++-impl
以及-Landroidbp-impl
来生成基础代码。a.b.c.d@M.N::IFoo-service
。打开直通式 HAL,并将其自身注册为 Binder 化服务,从而使同一 HAL 实现能够同时以直通模式和 Binder 化模式使用。
如果有一个 IFoo
,您可以调用 sp<IFoo> IFoo::getService(string name, bool getStub)
,以获取对 IFoo
实例的访问权限。如果 getStub
为 True,则 getService
会尝试仅在直通模式下打开 HAL。如果 getStub
为 False,则 getService
会尝试找到 Binder 化服务;如果未找到,则它会尝试找到直通式服务。除了在 defaultPassthroughServiceImplementation
中,其余情况一律不得使用 getStub
参数。(搭载 Android O 的设备是完全 Binder 化的设备,因此不得在直通模式下打开服务。)
HIDL 语法
根据设计,HIDL 语言与 C 语言类似(但前者不使用 C 预处理器)。下面未描述的所有标点符号(用途明显的 =
和 |
除外)都是语法的一部分。
注意:有关 HIDL 代码样式的详细信息,请参阅代码样式指南。
/** */
表示文档注释。此样式只能应用于类型、方法、字段和枚举值声明。/* */
表示多行注释。//
表示注释一直持续到行结束。除了//
,换行符与任何其他空白一样。- 在以下示例语法中,从
//
到行结束的文本不是语法的一部分,而是对语法的注释。 [empty]
表示该字词可能为空。?
跟在文本或字词后,表示它是可选的。...
表示包含零个或多个项、用指定的分隔符号分隔的序列。HIDL 中不含可变参数。- 逗号用于分隔序列元素。
- 分号用于终止各个元素,包括最后的元素。
- 大写字母是非终止符。
italics
是一个令牌系列,如integer
或identifier
(标准 C 解析规则)。constexpr
是 C 样式的常量表达式(例如1 + 1
和1L << 3
)。import_name
是软件包或接口名称,按 HIDL 版本编号中所述的方式加以限定。- 小写
words
是文本令牌。
例如:
ROOT =
PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal
| PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions
ITEM =
ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
| safe_union identifier { UFIELD; UFIELD; ...};
| struct identifier { SFIELD; SFIELD; ...}; // Note - no forward declarations
| union identifier { UFIELD; UFIELD; ...};
| enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
| typedef TYPE identifier;
VERSION = integer.integer;
PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;
PREAMBLE = interface identifier EXTENDS
EXTENDS = <empty> | extends import_name // must be interface, not package
GENERATES = generates (FIELD, FIELD ...)
// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
[empty]
| IMPORTS import import_name;
TYPE =
uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
float | double | bool | string
| identifier // must be defined as a typedef, struct, union, enum or import
// including those defined later in the file
| memory
| pointer
| vec<TYPE>
| bitfield<TYPE> // TYPE is user-defined enum
| fmq_sync<TYPE>
| fmq_unsync<TYPE>
| TYPE[SIZE]
FIELD =
TYPE identifier
UFIELD =
TYPE identifier
| safe_union identifier { FIELD; FIELD; ...} identifier;
| struct identifier { FIELD;