HIDL 简介
HIDL 即HAL interface definition language,在 Android Project Treble 中被起草,在 Android O 中被全面使用。
HIDL用于进程间通信(IPC)。对于c++和Java程序员来说,HIDL的语法看起来会很熟悉,即使它有一系列不同的关键字。HIDL还使用java风格的注解。
Project Treble
Project treble是Android O中引入的特性.
主要的目标是Android模块化,根据谷歌、SoC供应商和OEM的需求分离所有权和分配不同的模块。
Vendor 模块被分离到一个独立的可单独更新的image,从而改进了软件升级发布的过程
Treble由以下几部分组成:
Kernel modularization
HAL re-architecture (binderization)
Separation of the vendor and OEM modules from the system image
关于HIDL的设计
HIDL的设计目的是为了能够在不重新编译HALs的情况下能够替换framework。HALs将由供应商或SOC制造商构建,并放在设备的vendor下的分区中,而framework框架在它自己的分区中发挥作用,能够被OTA替换而不重新编译HALs。
HIDL的设计平衡了以下问题:
共用性
在进程间创建可靠的共用接口,而这些进程可能有不同的架构、工具链和构建配置。HIDL接口是版本化的,在发布后不能更改。
效率
HIDL会将复制的次数最小化。HIDL定义的数据以C++标准layout data structures的形式传递给c++代码,这种数据结构可以不打包的情况下使用。HIDL还提供共享内存接口,由于RPCs天生有点慢,HIDL无需使用RPC调用,而支持两种传输数据的方法:shared memory (共享内存)和 Fast Message Queue (FMQ).
直观
对于RPC,HIDL只使用参数(参见AIDL),避免了内存所有权的棘手问题;不能有效通过方法返回的值通过回调函数返回。既不将数据传递到HIDL,也不从HIDL接收数据,改变数据的所有权。数据只需要在被调用函数的持续时间内存在,并且可以在被调用函数返回后立即销毁。
使用 passthrough 模式
为了更新运行在Android系统早期版本的设备到Android O操作系统,你可以将传统的(和 legacy遗留的)HALs封装在新的HIDL接口中,这一接口以binderized 和same-process (passthrough)的模式服务于HAL。这种封装对HAL和Android框架都是透明的。
Passthrough模式仅适用于c++的客户端和实现。运行Android早期版本的设备没有Java编写的HALs,因此Java HALs必然使用binderized 的模式。
Passthrough header files
当一个.hal文件被编译后, hidl-gen除了用于binder通信的头文件外,还生成一个额外的 passthrough 头文件 BsFoo.h ;这个头文件定义了dlopened的函数. 由于passthrough HALs 运行在调用它们的相同的进程中,大多数情况下 passthrough 方法被直接通过函数调用 (相同线程)。oneway 方法在他们自己的线程中运行,因为它们并不打算等待HAL来处理它们(这意味着任何在passthrough模式中使用oneway方法的HAL必须是线程安全的)。
给定一个IFoo.hal文件,BsFoo.h封装了hidl生成的方法以提供额外的特性(比如在另一个线程中运行oneway事务)。这个文件类似于BpFoo。但是,不是通过binder进行IPC调用,而是直接调用所需的函数。未来HALs可能提供多种实现,例如FooFast HAL和FooAccurate HAL。在这种情况下,将创建每个额外实现的文件(例如:PTFooFast.cpp和PTFooAccurate.cpp)。
Binderizing passthrough HALs
支持passthrough 模式的HAL实现可以binderized 。对于一个HAL 接口a.b.c.d@M.N::IFoo,需要创建两个包:
- a.b.c.d@M.N::IFoo-impl. 包含HAL的实现,并暴露函数IFoo * HIDL_FETCH_IFoo(const char * name)。在legacy hal设备上,这个包是dlopened 的,实现是使用HIDL_FETCH_IFoo实例化的。 您可以使用hidl - gen和- lc + + - impl和- landroidbp - impl生成基本代码。
- a.b.c.d@M.N::IFoo-service. 打开passthrough HAL并将其注册为一个binder化服务,使相同的HAL实现被用作passthrough 和binderized。
给定类型IFoo,您可以调用sp < IFoo > IFoo::getService(string name, bool getStub)来获取IFoo的实例。
如果getStub值是true,getService尝试只以passthrough模式打开HAL。如果getStub为false,则getService尝试查找到一个binderized 服务;如果失败,则尝试找到passthrough服务。getStub参数除了defaultPassthroughServiceImplementation不应使用。(使用Android O的设备是完全binderized 的设备,因此不允许以passthrough模式打开一个服务。)
HIDL 语法
HIDL语言类似于C(但不使用C预处理器)
- /* / 表示文档注释.
- /* */ 表示文档多行注释.
- // 表示在一行结束后注释
- [empty] 表明当前项的值为空
- ? 放置在项前,表明该项为可选项.
- … 表明该序列包含0个或多个如前述使用分隔符隔开的项
- , 逗号用于分隔序列中的元素
- ; 分号用于标记每个元素的结束位置.
- @entry 当前HAL模块被使用时应当被最先调用的接口
- @exit 当前HAL模块被调用时应当被最后调用的接口
- @callflow(next={“name_a”, “name_b”, “name_c”}) 当前接口被调用后可能被调用的接口列表。其中name_a接口被调用的概率最大,name_c接口被调用的概率最小。如果只存在1个可能被调用的接口,那么花括号{ }可以省略不写。如果给定的接口名无效,则会导致VTS编译失败。
- @callflow(next={“*”}) 当前接口被调用后可能会调用任意接口
Example:
ROOT =
PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal
PREAMBLE = interface identifier EXTENDS
| PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions
ITEM =
ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
| 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 have been previously typedef'd
// or defined with struct, union, enum, or import
| 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
| struct identifier { FIELD; FIELD; ...} identifier;
| union identifier { FIELD; FIELD; ...} identifier;
SFIELD =
TYPE identifier
| struct identifier { FIELD; FIELD; ...};
| union identifier { FIELD; FIELD; ...};
| struct identifier { FIELD; FIELD; ...} identifier;
| union identifier { FIELD; FIELD; ...} identifier;
SIZE = // Must be greater than zero
constexpr
ANNOTATIONS =
[empty]
| ANNOTATIONS ANNOTATION
ANNOTATION =
| @identifier
| @identifier(VALUE)
| @identifier(ANNO_ENTRY, ANNO_ENTRY ...)
ANNO_ENTRY =
identifier=VALUE
VALUE =
"any text including \" and other escapes"
| constexpr
| {VALUE, VALUE ...} // only in annotations
ENUM_ENTRY =
identifier
| identifier = constexpr
(未完 待续)