HAL硬件抽象层
旧版 HAL
HAL 可定义一个标准接口以供硬件供应商实现,这可让 Android 忽略较低级别的驱动程序实现。借助 HAL,您可以顺利实现相关功能,而不会影响或更改更高级别的系统。本页面介绍了自 Android 8.0 开始已不再支持的旧版架构。对于 Android 8.0 及更高版本,请参阅 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:
openGL
Vulkan
android.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 驱动程序在相关引用计数发生变化时收到通知。