HAL硬件抽象层
旧版 HAL
HAL 可定义一个标准接口以供硬件供应商实现,这可让 Android 忽略较低级别的驱动程序实现。借助 HAL,您可以顺利实现相关功能,而不会影响或更改更高级别的系统。本页面介绍了自 Android 8.0 开始已不再支持的旧版架构。对于 Android 8.0 及更高版本,请参阅 HAL 类型。

图 1. HAL 组件
您必须为您的产品所提供的特定硬件实现相应的 HAL(和驱动程序)。HAL 实现通常会内置在共享库模块(.so文件)中,但 Android 并不要求 HAL 实现与设备驱动程序之间进行标准交互,因此您可以视情况采取适当的做法。不过,要使 Android 系统能够与您的硬件正确互动,您必须遵守各个特定于硬件的 HAL 接口中定义的合同。
为了保证 HAL 具有可预测的结构,每个特定于硬件的 HAL 接口都要具有 hardware/libhardware/include/hardware/hardware.h 中定义的属性。这类接口可让 Android 系统以一致的方式加载 HAL 模块的正确版本。HAL 接口包含两个组件:模块和设备。
HAL 模块
模块表示被封装且存储为共享库 (.so file) 的 HAL 实现。hardware/libhardware/include/hardware/hardware.h 标头文件会定义一个表示模块的结构体 (hw_module_t),其中包含模块的版本、名称和作者等元数据。Android 会根据这些元数据来找到并正确加载 HAL 模块。
另外,hw_module_t 结构体还包含指向另一个结构体 hw_module_methods_t 的指针,后面这个结构体会包含一个指向相应模块的 open 函数的指针。此 open 函数用于与相关硬件(此 HAL 是其抽象形式)建立通信。每个特定于硬件的 HAL 通常都会使用附加信息为该特定硬件扩展通用的 hw_module_t 结构体。例如,在相机 HAL 中,camera_module_t 结构体会包含一个 hw_module_t 结构体以及其他特定于相机的函数指针:
typedef struct camera_module {
hw_module_t common;
int (*get_number_of_cameras)(void);
int (*get_camera_info)(int camera_id, struct camera_info *info);
} camera_module_t;
实现 HAL 并创建模块结构体时,您必须将其命名为 HAL_MODULE_INFO_SYM。以下是 Nexus 9 音频 HAL 的示例:
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "NVIDIA Tegra Audio HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
},
};
HAL 设备
设备是产品硬件的抽象表示。例如,一个音频模块可能包含主音频设备、USB 音频设备或蓝牙 A2DP 音频设备。
设备由 hw_device_t 结构体表示。与模块类似,每类设备都定义了一个通用 hw_device_t 的详细版本,其中包含指向硬件特定功能的函数指针。例如,audio_hw_device_t 结构体类型会包含指向音频设备操作的函数指针:
struct audio_hw_device {
struct hw_device_t common;
/**
* used by audio flinger to enumerate what devices are supported by
* each audio_hw_device implementation.
*
* Return value is a bitmask of 1 or more values of audio_devices_t
*/
uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
...
};
typedef struct audio_hw_device audio_hw_device_t;
除了这些标准属性之外,每个特定于硬件的 HAL 接口都可以定义更多的自有功能和要求。有关详情,请参阅 HAL 参考文档以及各 HAL 的单独说明。
编译 HAL 模块
HAL 实现会内置在模块 (.so) 文件中,并由 Android 适时地动态链接。您可以为每个 HAL 实现创建 Android.mk文件并指向源文件,从而编译模块。一般来说,您的共享库必须以特定格式命名,以方便找到并正确加载。各模块的命名方案略有不同,但它们都遵循以下通用模式:<module_type>.<device_name>。
HAL 类型
在 Android 8.0 及更高版本中,较低级别的层已重新编写以采用更加模块化的新架构。运行 Android 8.0 或更高版本的设备必须支持使用 HIDL 语言编写的 HAL,下面列出了一些例外情况。这些 HAL 可以是绑定式 HAL 也可以是直通式 HAL:
- 绑定式 HAL。 以 HAL 接口定义语言 (HIDL) 表示的 HAL。这些 HAL 取代了早期 Android 版本中使用的传统 HAL 和旧版 HAL。在绑定式 HAL 中,Android 框架和 HAL 之间通过 Binder 进程间通信 (IPC) 调用进行通信。所有在推出时即搭载了 Android 8.0 或更高版本的设备都必须只支持绑定式 HAL。
- 直通式 HAL。 以 HIDL 封装的传统 HAL 或旧版 HAL。这些 HAL 封装了现有的 HAL,可在绑定模式和 Same-Process(直通)模式下使用。升级到 Android 8.0 的设备可以使用直通式 HAL。
HAL 模式要求
| 设备 | 直通式 | 绑定式 |
|---|---|---|
| 搭载 Android 8.0 的设备 | 直通式 HAL 中列出的 HAL 必须为直通式。 | 所有其他 HAL 均为绑定式(包括作为供应商扩展程序的 HAL)。 |
| 升级到 Android 8.0 的设备 | 直通式 HAL 中列出的 HAL 必须为直通式。 | 绑定式 HAL 中列出的 HAL 必须为绑定式。 |
| 供应商映像提供的所有其他 HAL 既可以在直通模式下使用,也可以在绑定模式下使用。在完全符合 Treble 标准的设备中,所有 HAL 都必须为绑定式 HAL。 | ||
绑定式 HAL
Android 要求所有 Android 设备(无论是搭载 Android O 的设备还是升级到 Android O 的设备)上的下列 HAL 均为绑定式:
android.hardware.biometrics.fingerprint@2.1。取代 Android 8.0 中已不存在的fingerprintd。android.hardware.configstore@1.0。Android 8.0 中的新 HAL。android.hardware.dumpstate@1.0。此 HAL 提供的原始接口可能无法继续使用,并且已更改。因此,dumpstate_board必须在指定的设备上重新实现(这是一个可选的 HAL)。android.hardware.graphics.allocator@2.0。在 Android 8.0 中,此 HAL 必须为绑定式,因此无需在可信进程和不可信进程之间分享文件描述符。android.hardware.radio@1.0。取代由存活于自身进程中的rild提供的接口。android.hardware.usb@1.0。Android 8.0 中的新 HAL。android.hardware.wifi@1.0。Android 8.0 中的新 HAL,可取代此前加载到system_server的旧版 WLAN HAL 库。android.hardware.wifi.supplicant@1.0。在现有wpa_supplicant进程之上的 HIDL 接口
注意:Android 提供的以下 HIDL 接口将一律在绑定模式下使用:android.frameworks.*、android.system.* 和 android.hidl.*(不包括下文所述的 android.hidl.memory@1.0)。
直通式 HAL
Android 要求所有 Android 设备(无论是搭载 Android O 的设备还是升级到 Android O 的设备)上的下列 HAL 均在直通模式下使用:
android.hardware.graphics.mapper@1.0。将内存映射到其所属的进程中。android.hardware.renderscript@1.0。在同一进程中传递项(等同于openGL)。
上方未列出的所有 HAL 在搭载 Android O 的设备上都必须为绑定式。
Same-Process HAL
Same-Process HAL (SP-HAL) 一律在使用它们的进程中打开,其中包括未以 HIDL 表示的所有 HAL,以及那些非绑定式的 HAL。SP-HAL 集的成员只能由 Google 控制,这一点没有例外。
SP-HAL 包括以下 HAL:
openGLVulkanandroid.hidl.memory@1.0(由 Android 系统提供,一律为直通式)android.hardware.graphics.mapper@1.0。android.hardware.renderscript@1.0
传统 HAL 和旧版 HAL
传统 HAL(在 Android 8.0 中已弃用)是指与具有特定名称及版本号的应用二进制接口 (ABI) 标准相符的接口。大部分 Android 系统接口(相机、音频和传感器等)都采用传统 HAL 形式(已在 hardware/libhardware/include/hardware 下进行定义)。
旧版 HAL(也已在 Android 8.0 中弃用)是指早于传统 HAL 的接口。一些重要的子系统(WLAN、无线接口层和蓝牙)采用的就是旧版 HAL。虽然没有统一或标准化的方式来指明是否为旧版 HAL,但如果 HAL 早于 Android 8.0 而出现,那么这种 HAL 如果不是传统 HAL,就是旧版 HAL。有些旧版 HAL 的一部分包含在 libhardware_legacy中,而其他部分则分散在整个代码库中。
HIDL 框架向后兼容性验证
HIDL HAL 可保证 Android 核心系统(也称为 system.img 或框架)向后兼容。虽然供应商测试套件 (VTS) 测试可确保 HAL 按预期运行(例如,针对所有 1.2 实现运行 1.1 HAL 测试),但仍需要进行框架测试,以确保提供受支持的 HAL(1.0、1.1 或 1.2)时该框架适用于该 HAL。
要详细了解 HAL 接口定义语言 (HIDL),请参阅 HIDL、HIDL 版本控制和 HIDL HAL 弃用。
关于 HAL 升级
HAL 升级分为两类:主要和次要。大多数系统仅包含一个 HAL 实现,但支持多个实现。例如:
android.hardware.teleport@1.0 # initial interface android.hardware.teleport@1.1 # minor version upgrade android.hardware.teleport@1.2 # another minor version upgrade ... android.hardware.teleport@2.0 # major version upgrade ...
系统分区通常包含一个框架守护进程(如 teleportd),用于管理与特定 HAL 实现组进行的通信。作为一种替代方法,系统可能会包含一个用于实现便捷客户端行为的系统库(如 android.hardware.configstore-utils)。在上面的示例中,无论设备上安装了哪个版本的 HAL,teleportd 都必须能够正常运行。
Google 维护的版本
如果存在主要版本升级(1.0、2.0、3.0 等),则至少必须有一台 Google 维护的设备来维护各主要版本的实现,直到该版本弃用为止。如果 Google 维护的所有设备均未搭载特定主要版本,则 Google 会继续维护该主要版本的旧实现。
这种维护会增加一点额外的开销,因为创建新实现(如 2.0)时,旧实现(如 1.2)将保留且默认处于不使用的状态。
测试次要版本升级
如要测试框架中次要版本的向后兼容性,则需要一种自动生成次要版本实现的方法。鉴于 Google 维护的版本存在一定限制,hidl-gen 只会(且只能)生成采用 1.(x+n) 实现并提供 1.x 实现的适配器;它无法根据 2.0 实现生成 1.0 实现(以主要版本的定义为准)。
例如,要针对 1.2 实现运行 1.1 测试,则必须能够模拟具有 1.1 实现的情况。1.2 接口可自动用作 1.1 实现,但行为上存在一些细微差别(例如,框架会手动检查所属的具体版本或对自身调用 castFrom)。
基本做法如下所示:
- 在 Android 移动设备上安装 x.(y+n) 接口。
- 安装并启用目标为 x.y 的适配器。
- 测试设备,验证它能否按预期运行旧版次要版本。
这些适配器完全隐藏了如下事实:实现实际上由 1.2 接口提供支持,并且仅提供 1.1 接口(适配器采用 1.2 接口并让其看起来像 1.1 接口)。
工作流程示例
在此示例中,Android 设备运行 android.hardware.foo@1.1::IFoo/default。要确保客户端在 android.hardware.foo@1.0::IFoo/default 上正常运行,请执行以下操作:
- 在终端中,运行以下命令:
$ PACKAGE=android.hidl.allocator@1.0-adapter $ INTERFACE=IAllocator $ INSTANCE=ashmem $ THREAD_COUNT=1 # can see current thread use on `lshal -i -e` $ m -j $PACKAGE $ /data/nativetest64/$PACKAGE/$PACKAGE $INTERFACE $INSTANCE $THREAD_COUNT Trying to adapt down android.hidl.allocator@1.0-adapter/default Press any key to disassociate adapter.
- 使用
adb shell stop(或start)重启客户端,或者仅终止相应进程。 - 测试完成后,取消关联适配器。
- 通过重启设备或重启客户端来恢复系统状态。
其他目标
对于编译系统中使用 hidl_interface 指定的每个接口,hidl-gen 会自动为其适配器添加额外的编译目标。对于软件包 a.b.c@x.y,还需添加额外的 C++ 目标 a.b.c@x.y-adapter。
a.b.c@x.y 的适配器将一些实现 a.b.c@x.(y+n)::ISomething/instance-name 用作输入,并且必须注册 a.b.c@x.y::ISomething/instance-name,还必须取消注册 y+n 实现。
假设有如下示例接口:
// IFoo.hal
package a.b.c@1.0;
interface IFoo {
doFoo(int32_t a) generates (int64_t b);
doSubInterface() generates (IFoo a);
};
由 a.b.c@1.0-adapter 提供的代码与以下示例类似:
// autogenerated code
// in namespace a::b::c::V1_0::IFoo
struct MockFoo {
// takes some subclass of V1_0. May be V1_1, V1_2, etc...
MockFoo(V1_0::IFoo impl) mImpl(impl) {}
Return<int64_t> doFoo(int32_t a) {
return this->mImpl->doFoo(a);
}
Return<V1_0::ICallback> doSubInterface() {
// getMockForBinder returns MockCallback instance
// that corresponds to a particular binder object
// It can't return a new object every time or
// clients using interfacesSame will have
// divergent behavior when using the mock.
auto _hidl_out = this->mImpl->doSubInterface();
return getMockForBinder(_hidl_out);
}
};
数据值会精确地转发到自动生成的模拟类以及从中转出,子接口除外(它们会返回)。这些接口必须封装在相应的最新回调对象中。
动态可用的 HAL
实现动态关停涉及连接数据流以及执行动态进程,下文对此进行了详细介绍。
对 HAL 定义所做的更改
要实现动态关停,需要有关于哪些进程为哪些 HAL 接口提供服务的信息(此类信息之后在其他情况中也可能很有用),还需要确保在设备启动时不启动进程,而且在进程退出后,直到系统再次请求启动它们之前,都不重新启动它们。
# some init.rc script associated with the HAL
service vendor.some-service-name /vendor/bin/hw/some-binary-service
# init language extension, provides information of what service is served
# if multiple interfaces are served, they can be specified one on each line
interface android.hardware.light@2.0::ILight default
# restarted if hwservicemanager dies
# would also cause the hal to start early during boot if oneshot wasn't set
class hal
# will not be restarted if it exits until it is requested to be restarted
oneshot
# will only be started when requested
disabled
# ... other properties
对 init 和 hwservicemanager 所做的更改
要实现动态关停,还需要让 hwservicemanager 告知 init 启动所请求的服务。在 Android 9 中,init 包含三个额外的控制消息(例如,ctl.start):ctl.interface_start、ctl.interface_stop 和 ctl.interface_restart。这些消息可用于指示 init 打开或关闭特定硬件接口。如果系统请求使用某个服务但该服务未注册,则 hwservicemanager 会请求启动该服务。不过,动态 HAL 不需要使用以上任何消息。
确定 HAL 退出
在 Android 9 中,必须手动确定 HAL 退出。对于 Android 10 及更高版本,还可以使用自动生命周期确定 HAL 退出。
要实现动态关停,需要多个政策来决定何时启动和关停 HAL。如果 HAL 出于任何原因而决定退出,则当系统再次需要用到它时,它将使用以下信息和基础架构自动重新启动:HAL 定义中提供的信息,以及更改后的 init 和 hwservicemanager 提供的基础架构。 这可能会涉及多个不同的政策,包括:
- 如果有人对 HAL 调用关闭命令或类似的 API,则 HAL 可能会选择自行调用退出命令。此行为必须在相应的 HAL 接口中指定。
- HAL 可在任务完成后关停(记录在 HAL 文件中)。
自动生命周期
Android 10 为内核和 hwservicemanager 添加了更多支持,可让 HAL 在没有任何客户端时自动关闭。要使用此功能,请完成对 HAL 定义所做的更改部分的所有步骤,并执行以下操作:
- 使用
LazyServiceRegistrar而不是成员函数registerAsService在 C++ 中注册服务,例如:// only one instance of LazyServiceRegistrar per process
LazyServiceRegistrar registrar();
registrar.registerAsService(myHidlService /* , "default" */); - 验证 HAL 客户端是否仅在使用时保留对顶级 HAL(通过
hwservicemanager注册的接口)的引用。为了避免出现延迟,如果该引用在继续执行的 hwbinder 线程上被丢弃,则客户端还应该在丢弃引用后调用IPCThreadState::self()->flushCommands(),以确保 Binder 驱动程序在相关引用计数发生变化时收到通知。

浙公网安备 33010602011771号