HIDL

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

HAL 接口定义语言或 HIDL 是一种接口描述语言 (IDL),用于指定 HAL 与其用户之间的接口。 HIDL 允许指定类型和方法调用,收集到接口和包中。更广泛地说,HIDL 是一种用于在可以独立编译的代码库之间进行通信的系统。从 Android 10 开始,HIDL 已被弃用,Android 正在迁移以在任何地方使用AIDL

HIDL 旨在用于进程间通信 (IPC)。使用 HDL 创建的 HALS 称为绑定 HAL,因为它们可以使用绑定程序进程间通信 (IPC) 调用与其他架构层进行通信。 Binderized HAL 在与使用它们的客户端不同的进程中运行。对于必须链接到进程的库,也可以使用直通模式(Java 中不支持)。

HIDL 指定数据结构和方法签名,这些数据结构和方法签名以接口(类似于类)进行组织,这些接口被收集到包中。 HIDL 的语法对于 C++ 和 Java 程序员来说看起来很熟悉,但使用了一组不同的关键字。 HIDL 还使用 Java 样式的注释。

术语

本部分使用以下 HIDL 相关术语:

粘合的指示 HIDL 用于进程之间的远程过程调用,通过类似 Binder 的机制实现。另请参阅直通
回调,异步由 HAL 用户提供服务的接口,传递给 HAL(使用 HIDL 方法),并由 HAL 调用以随时返回数据。
回调,同步将数据从服务器的 HIDL 方法实现返回到客户端。不用于返回 void 或单个原始值的方法。
客户调用特定接口的方法的进程。 HAL 或 Android 框架进程可能是一个接口的客户端和另一个接口的服务器。另请参阅直通
延伸表示将方法和/或类型添加到另一个接口的接口。一个接口只能扩展另一个接口。可用于相同包名称中的次要版本增量或用于新包(例如供应商扩展)以在旧包上构建。
生成指示向客户端返回值的接口方法。要返回一个非原始值或多个值,会生成一个同步回调函数。
界面方法和类型的集合。翻译成 C++ 或 Java 中的类。接口中的所有方法都以相同的方向调用:客户端进程调用由服务器进程实现的方法。
单程应用于 HIDL 方法时,指示该方法不返回任何值且不阻塞。
包裹共享一个版本的接口和数据类型的集合。
直通HIDL 模式,其中服务器是共享库,由客户端dlopen编辑。在直通模式下,客户端和服务器是相同的进程,但代码库不同。仅用于将旧代码库引入 HIDL 模型。另请参阅绑定
服务器实现接口方法的进程。另请参阅直通
运输在服务器和客户端之间移动数据的 HIDL 基础架构。
版本包的版本。由两个整数组成,主要的和次要的。次要版本增量可能会添加(但不会更改)类型和方法。

HIDL 设计

HIDL 的目标是无需重新构建 HAL 即可替换 Android 框架。 HAL 将由供应商或 SOC 制造商构建,并放在设备上的/vendor分区中,从而使 Android 框架在其自己的分区中无需重新编译 HAL 即可替换为 OTA。

HIDL 设计平衡了以下问题:

  • 互操作性。在可以使用各种架构、工具链和构建配置编译的进程之间创建可靠的可互操作接口。 HIDL 接口是版本化的,发布后无法更改。
  • 效率。 HIDL 会尽量减少复制操作的数量。 HIDL 定义的数据以 C++ 标准布局数据结构交付给 C++ 代码,无需解包即可使用。 HIDL 还提供共享内存接口,并且由于 RPC 本身有些慢,HIDL 支持两种无需使用 RPC 调用即可传输数据的方法:共享内存和快速消息队列 (FMQ)。
  • 直观。 HIDL 通过仅in参数中使用 RPC 来避免内存所有权的棘手问题(请参阅Android 接口定义语言 (AIDL) );无法从方法有效返回的值通过回调函数返回。将数据传递到 HIDL 进行传输或从 HIDL 接收数据都不会更改数据的所有权 - 所有权始终由调用函数保留。数据只需要在被调用函数的持续时间内持续存在,并且可以在被调用函数返回后立即销毁。

使用直通模式

要将运行早期版本 Android 的设备更新到 Android O,您可以将传统(和旧版)HAL 包装在一个新的 HIDL 接口中,该接口以绑定和相同进程(直通)模式为 HAL 提供服务。这种包装对 HAL 和 Android 框架都是透明的。

直通模式仅适用于 C++ 客户端和实现。运行早期 Android 版本的设备没有用 Java 编写的 HAL,因此 Java HAL 本质上是绑定的。

编译.hal文件时, hidl-gen除了用于 binder 通信的头文件外,还会产生一个额外的 passthrough 头文件BsFoo.h ;此标头定义要dlopen编辑的函数。由于直通 HAL 在调用它们的同一进程中运行,因此在大多数情况下,直通方法由直接函数调用(同一线程)调用。 oneway方法在它们自己的线程中运行,因为它们不打算等待 HAL 处理它们(这意味着在直通模式下使用oneway方法的任何 HAL 都必须是线程安全的)。

给定IFoo.halBsFoo.h包装 HIDL 生成的方法以提供附加功能(例如使oneway事务在另一个线程中运行)。该文件类似于BpFoo.h ,但不是使用 binder 传递调用 IPC,而是直接调用所需的函数。 HAL 的未来实现可能会提供多种实现,例如 FooFast HAL 和 FooAccurate HAL。在这种情况下,将为每个附加实现创建一个文件(例如, PTFooFast.cppPTFooAccurate.cpp )。

绑定直通 HAL

您可以绑定支持直通模式的 HAL 实现。给定一个 HAL 接口abcd@MN::IFoo ,会创建两个包:

  • abcd@MN::IFoo-impl 。包含 HAL 的实现并公开函数IFoo* HIDL_FETCH_IFoo(const char* name) 。在旧设备上,此包是dlopen ed,并使用HIDL_FETCH_IFoo实例化实现。您可以使用hidl-gen-Lc++-impl-Landroidbp-impl生成基本代码。
  • abcd@MN::IFoo-service 。打开直通 HAL 并将其自身注册为绑定服务,使相同的 HAL 实现既可用作直通,也可用作绑定服务。

给定类型IFoo ,您可以调用sp<IFoo> IFoo::getService(string name, bool getStub)来访问IFoo的实例。如果getStub为 true,则getService仅尝试在直通模式下打开 HAL。如果getStub为 false, getService会尝试查找绑定服务;如果失败,它会尝试查找直通服务。除非在defaultPassthroughServiceImplementation中,否则永远不应使用getStub参数。 (使用 Android O 启动的设备是完全绑定的设备,因此不允许以直通模式打开服务。)

HIDL 语法

按照设计,HIDL 语言类似于 C(但不使用 C 预处理器)。下面未描述的所有标点符号(除了明显使用=| )都是语法的一部分。

注意:有关 HIDL 代码样式的详细信息,请参阅代码样式指南

  • /** */表示文档注释。这些只能应用于类型、方法、字段和枚举值声明。
  • /* */表示多行注释。
  • //表示对行尾的注释。除了// ,换行符与任何其他空格相同。
  • 在下面的示例语法中,从//到行尾的文本不是语法的一部分,而是对语法的注释。
  • [empty]表示该术语可能为空。
  • ?跟随文字或术语意味着它是可选的。
  • ...表示包含零个或多个项目的序列,其中标点符号如所示。 HIDL 中没有可变参数。
  • 逗号分隔序列元素。
  • 分号终止每个元素,包括最后一个元素。
  • UPPERCASE 是非终结符。
  • italics是一个令牌族,例如integeridentifier (标准 C 解析规则)。
  • constexpr是 C 风格的常量表达式(例如1 + 11L << 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; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  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