HIDL 围绕接口构建,接口是面向对象语言中用于定义行为的抽象类型。每个接口都是软件包的一部分。
软件包
软件包名称可以有子级别,例如 package.subpackage
。已发布的 HIDL 软件包的根目录是 hardware/interfaces
或 vendor/vendorName
(例如,Pixel 设备的 vendor/google
)。软件包名称在根目录下形成一个或多个子目录;定义软件包的所有文件都在同一目录中。例如,package android.hardware.example.extension.light@2.0
可以在 hardware/interfaces/example/extension/light/2.0
下找到。
下表列出了软件包前缀和位置
软件包前缀 | 位置 | 接口类型 |
---|---|---|
|
|
HAL |
|
|
frameworks/ 相关 |
|
|
system/ 相关 |
|
|
核心 |
软件包目录包含扩展名为 .hal
的文件。每个文件都必须包含一个 package
语句,用于命名文件所属的软件包和版本。文件 types.hal
(如果存在)不定义接口,而是定义软件包中每个接口都可访问的数据类型。
接口定义
除了 types.hal
之外,其他每个 .hal
文件都定义一个接口。接口通常定义如下
interface IBar extends IFoo { // IFoo is another interface // embedded types struct MyStruct {/*...*/}; // interface methods create(int32_t id) generates (MyStruct s); close(); };
没有显式 extends
声明的接口隐式地从 android.hidl.base@1.0::IBase
扩展(类似于 Java 中的 java.lang.Object
)。隐式导入的 IBase 接口声明了几个保留方法,这些方法不应也不能在用户定义的接口中重新声明或以其他方式使用。这些方法包括
ping
interfaceChain
interfaceDescriptor
notifySyspropsChanged
linkToDeath
unlinkToDeath
setHALInstrumentation
getDebugInfo
debug
getHashChain
导入过程
import
语句是 HIDL 机制,用于访问另一个软件包中的软件包接口和类型。import
语句关注两个实体
- 导入实体,可以是软件包或接口
- 被导入实体,可以是软件包或接口
导入实体由 import
语句的位置确定。当语句位于软件包的 types.hal
中时,导入的内容对整个软件包可见;这是软件包级导入。当语句位于接口文件中时,导入实体是接口本身;这是接口级导入。
被导入实体由 import
关键字后的值确定。该值不必是完全限定的名称;如果省略了组件,则会自动用当前软件包中的信息填充。对于完全限定的值,支持以下导入情况
- 全软件包导入。如果该值是软件包名称和版本(语法如下所述),则整个软件包将导入到导入实体中。
- 部分导入。如果该值是
- 接口,软件包的
types.hal
和该接口被导入到导入实体中。 types.hal
中定义的 UDT,则只有该 UDT 被导入到导入实体中(types.hal
中的其他类型不会被导入)。
- 接口,软件包的
- 仅类型导入。 如果值使用上述部分导入的语法,但关键字是
types
而不是接口名称,则仅导入指定软件包types.hal
中的 UDT。
导入实体可以访问以下各项的组合:
- 导入软件包的
types.hal
中定义的通用 UDT; - 导入软件包的接口(用于完整软件包导入)或指定的接口(用于部分导入),目的是调用它们、向它们传递句柄和/或从它们继承。
import 语句使用完全限定类型名称语法来提供要导入的软件包或接口的名称和版本
import android.hardware.nfc@1.0; // import a whole package import android.hardware.example@1.0::IQuux; // import an interface and types.hal import android.hardware.example@1.0::types; // import just types.hal
接口继承
接口可以是先前定义的接口的扩展。扩展可以是以下三种类型之一
- 接口可以向另一个接口添加功能,并保持其 API 不变。
- 软件包可以向另一个软件包添加功能,并保持其 API 不变。
- 接口可以从软件包或特定接口导入类型。
一个接口只能扩展另一个接口(没有多重继承)。软件包中次版本号非零的每个接口都必须扩展软件包先前版本中的接口。例如,如果软件包 derivative
的 4.0 版本中的接口 IBar
基于(扩展)软件包 original
的 1.2 版本中的接口 IFoo
,并且创建了软件包 original
的 1.3 版本,则 IBar
版本 4.1 不能扩展 IFoo
的 1.3 版本。相反,IBar
版本 4.1 必须扩展 IBar
版本 4.0,后者与 IFoo
版本 1.2 绑定。如果需要,IBar
版本 5.0 可以扩展 IFoo
版本 1.3。
接口扩展并不意味着库依赖性或生成的代码中包含跨 HAL 的内容——它们只是在 HIDL 级别导入数据结构和方法定义。HAL 中的每个方法都必须在该 HAL 中实现。
供应商扩展
在某些情况下,供应商扩展被实现为表示它们扩展的核心接口的基对象的子类。同一对象在基本 HAL 名称和版本以及扩展(供应商)HAL 名称和版本下注册。
版本控制
软件包是版本化的,接口具有其软件包的版本。版本用两个整数表示,主版本号.次版本号。
- 主版本号不向后兼容。递增主版本号会将次版本号重置为 0。
- 次版本号向后兼容。递增次版本号表示较新版本与先前版本完全向后兼容。可以添加新的数据结构和方法,但不得更改现有的数据结构或方法签名。
设备上可以同时存在 HAL 的多个主版本号或次版本号。但是,次版本号应优先于主版本号,因为适用于先前次版本号接口的客户端代码也适用于同一接口的更高次版本号。有关版本控制和供应商扩展的更多详细信息,请参阅 HIDL 版本控制。
接口布局摘要
本节概述了如何管理 HIDL 接口软件包(例如 hardware/interfaces
),并整合了整个 HIDL 部分中提供的信息。在阅读之前,请确保您熟悉 HIDL 版本控制、使用 hidl-gen 进行哈希处理的概念、通常使用 HIDL 的详细信息以及以下定义
术语 | 定义 |
---|---|
应用程序二进制接口 (ABI) | 应用程序编程接口以及任何必需的二进制链接。 |
完全限定名称 (fqName) | 用于区分 hidl 类型的名称。示例:android.hardware.foo@1.0::IFoo 。 |
软件包 | 包含 HIDL 接口和类型的软件包。示例:android.hardware.foo@1.0 。 |
软件包根目录 | 包含 HIDL 接口的根软件包。示例:HIDL 接口 android.hardware 位于软件包根目录 android.hardware.foo@1.0 中。 |
软件包根路径 | Android 源代码树中软件包根目录映射到的位置。 |
有关更多定义,请参阅 HIDL 术语表。
每个文件都可以从软件包根目录映射及其完全限定名称中找到
软件包根目录在 hidl-gen
中指定为参数 -r android.hardware:hardware/interfaces
。例如,如果软件包是 vendor.awesome.foo@1.0::IFoo
并且 hidl-gen
发送了 -r vendor.awesome:some/device/independent/path/interfaces
,则接口文件应位于 $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal
中。
在实践中,建议名为 awesome
的供应商或 OEM 将其标准接口放在 vendor.awesome
中。选择软件包路径后,不得更改它,因为这已 baked into 接口的 ABI 中。
软件包路径映射应该是唯一的
例如,如果您有 -rsome.package:$PATH_A
和 -rsome.package:$PATH_B
,$PATH_A
必须等于 $PATH_B
才能获得一致的接口目录(这也使版本控制接口变得更加容易)。
软件包根目录必须具有版本控制文件
如果您创建了一个软件包路径,例如 -r vendor.awesome:vendor/awesome/interfaces
,您还应该创建文件 $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt
,该文件应包含使用 hidl-gen
中的 -Lhash
选项生成的接口的哈希值(这在 使用 hidl-gen 进行哈希处理 中进行了广泛讨论)。
接口位于设备无关的位置
在实践中,我们建议在分支之间共享接口。这样可以最大限度地重用代码,并最大限度地跨不同设备和用例测试代码。