传感器 HAL 2.0

Sensors 硬件抽象层 (HAL) 是 Android 传感器框架与设备传感器(如加速度计或陀螺仪)之间的接口。Sensors HAL 定义了一系列函数,要使传感器框架能够控制传感器,必须先实现这些函数。

Sensors HAL 2.0 适用于搭载 Android 10 及更高版本的新设备和升级设备。Sensors HAL 2.0 基于 Sensors HAL 1.0 构建,但与 1.0 版有几个关键的区别,这使得它无法向后兼容。Sensors HAL 2.0 使用快速消息队列 (FMQ) 将传感器事件从 HAL 发送到 Android 传感器框架。

Sensors HAL 2.1 适用于搭载 Android 11 及更高版本的新设备和升级设备。Sensors HAL 2.1 是 Sensors HAL 2.0 的迭代升级版本,公开了 HINGE_ANGLE 传感器类型,并更新了各种方法来接受 HINGE_ANGLE 类型。

HAL 2.1 接口

Sensors HAL 2.1 文档的主要来源位于 HAL 定义内,该定义位于:hardware/interfaces/sensors/2.1/ISensors.hal。如果本页中的要求与 ISensors.hal 中的要求存在冲突,请遵循 ISensors.hal 中的要求。

HAL 2.0 接口

Sensors HAL 2.0 文档的主要来源位于 HAL 定义内,该定义位于:hardware/interfaces/sensors/2.0/ISensors.hal。如果本页中的要求与 ISensors.hal 中的要求存在冲突,请遵循 ISensors.hal 中的要求。

实现 Sensors HAL 2.0 和 HAL 2.1

要实现 Sensors HAL 2.0 或 2.1,则必须有一个对象来扩展 ISensors 接口并实现 2.0/ISensors.hal2.1/ISensors.hal 中定义的所有函数。

初始化 HAL

Sensors HAL 必须先由 Android 传感器框架初始化,然后才能使用。该框架为 HAL 2.0 调用 initialize() 函数,为 HAL 2.1 调用 initialize_2_1() 函数,从而为 Sensors HAL 提供三个参数:两个 FMQ 描述符和一个指向 ISensorsCallback 对象的指针。

HAL 使用第一个描述符来创建事件 FMQ,该 FMQ 将用于向框架写入传感器事件。HAL 使用第二个描述符来创建唤醒锁 FMQ,该 FMQ 将在 HAL 为 WAKE_UP 传感器事件解锁其唤醒锁时,用于执行同步。HAL 必须保存指向 ISensorsCallback 对象的指针,以便可以调用任何必要的回调函数。

initialize()initialize_2_1() 函数必须是初始化 Sensors HAL 时调用的第一个函数。

公开可用的传感器

如需获取设备中所有可用的静态传感器的列表,请使用有关 HAL 2.0 的 getSensorsList() 函数和有关 HAL 2.1 的 getSensorsList_2_1() 函数。此函数会返回传感器的列表,其中的每个传感器由其句柄唯一标识。当托管 Sensors HAL 的进程重启时,指定传感器的句柄不得发生改变。然而,在设备和系统服务器重新启动时,句柄可能会发生改变。

如果多个传感器具有相同的传感器类型和唤醒属性,则列表中的第一个传感器就叫做“默认”传感器,该传感器会返回给使用 getDefaultSensor(int sensorType, bool wakeUp) 函数的应用。

传感器列表的稳定性

在 Sensors HAL 重启后,如果 getSensorsList()getSensorsList_2_1() 返回的数据表明与重启之前检索到的传感器列表相比发生了明显变化,框架会触发重启 Android 运行时。传感器列表的明显变化包括以下这些情况:具有指定句柄的传感器缺失或属性发生变化、引入了新传感器等等。虽然重启 Android Runtime 会对用户造成干扰,但是必须这样做,因为 Android 框架可能不再符合 Android API 合同要求,即静态(非动态)传感器在应用的生命周期内保持不变。这还可能使框架无法重建应用提出的活跃传感器请求。因此,建议 HAL 供应商尽可能避免让传感器列表发生变化。

为了确保传感器句柄的稳定性,HAL 必须明确将设备中的指定物理传感器映射到其句柄。尽管传感器 HAL 接口没有规定具体的实现方式,但开发者可以通过多种方式来满足此要求。

例如,可以使用每个传感器的固定属性(例如供应商、型号和传感器类型)的组合对传感器列表进行排序。另一种方法就是要利用设备的那组静态传感器在硬件中固定不变这一事实,因此 HAL 需要知道所需的全部传感器在从 getSensorsList()getSensorsList_2_1() 返回之前何时已完成初始化。该所需传感器的列表可以编译为 HAL 二进制文件或存储在文件系统中的配置文件中,并且可以根据列表中传感器的显示顺序确定哪些句柄是稳定的。虽然最佳解决方案取决于 HAL 的具体实现细节,但关键要求是传感器句柄在 HAL 重启期间不会发生改变。

配置传感器

激活传感器之前,必须先使用 batch() 函数为传感器配置采样周期和最大报告延迟时间。

必须能够使用 batch() 随时重新配置传感器,且不会丢失传感器数据。

采样周期

所配置的传感器类型不同,采样周期也会具有不同的含义:

  • 连续模式:以连续的速率生成传感器事件。
  • 变化时触发模式:生成事件的速率不会高于采样周期;如果测量值没有变化,则可能会以低于采样周期的频率生成事件。
  • 单次模式:采样周期会被忽略。
  • 特殊模式:如需了解详情,请参阅传感器类型

如需了解采样周期与传感器报告模式之间的交互,请参阅报告模式

最大报告延迟

最大报告延迟设置了一个最长时间值(以纳秒为单位),该值是指在 SoC 唤醒期间,通过 HAL 将事件写入事件 FMQ 之前,事件可以延迟并存储在硬件 FIFO 中的最长时间。

值为零表示事件必须在测量完成后立即报告,即完全跳过 FIFO,或者在传感器发出的事件出现在 FIFO 中时立即清空 FIFO。

例如,以 50 Hz 激活的加速度计,如果最大报告延迟为零,则在 SoC 唤醒期间,每秒会触发 50 次中断。

当最大报告延迟大于零时,则在检测到传感器事件后无需立即报告。只要所有事件的延迟均未超过最大报告延迟,就可以将事件暂时存储在硬件 FIFO 中并以批量方式进行报告。自上一批报告的事件之后产生的所有事件,都将一次性记录并返回。这样就可以减少发送到 SoC 的中断数,并且在传感器捕获数据并进行批量处理时,SoC 可以切换到低耗电模式。

每个事件都具有相关联的时间戳。推迟事件的报告时间不得影响事件的时间戳。时间戳必须准确,并与事件的实际发生时间(而非报告时间)相对应。

如需详细了解如何在最大报告延迟不为零的情况下报告传感器事件以及相关的要求,请参阅批处理

激活传感器

框架使用 activate() 函数启用和停用传感器。在激活传感器之前,框架必须先使用 batch() 配置传感器。

传感器被停用后,其发出的其他传感器事件就不能再写入到事件 FMQ 中。

刷写传感器

如果某个传感器配置为批处理传感器数据,则框架可以通过调用 flush() 强制立即刷写经过批处理的传感器事件。这会使指定传感器句柄的经过批处理的传感器事件立即写入事件 FMQ 中。Sensors HAL 必须将刷写完成事件作为调用 flush() 的结果附加到写入的传感器事件的末尾。

刷写会异步执行(即该函数必须立即返回)。如果实现将一个 FIFO 用于多个传感器,则刷写该 FIFO,并且仅为指定传感器添加刷写完成事件。

如果指定传感器没有 FIFO(无法缓冲),或者 FIFO 在调用时为空,则 flush() 仍会成功并且会针对该传感器发送刷写完成事件。这适用于除单次传感器以外的所有传感器。

如果针对单次传感器调用 flush(),则 flush() 必须返回 BAD_VALUE 并且不会生成刷写完成事件。

将传感器事件写入 FMQ

Sensors HAL 使用事件 FMQ 将传感器事件推送给 Android 传感器框架。

事件 FMQ 是同步 FMQ,这意味着如果尝试向 FMQ 写入超出可用空间容量的事件,则会导致写入失败。在这种情况下,HAL 需要决定是将当前这组事件分成两个较小的组进行写入,还是等到有足够的可用空间时将所有事件一起写入。

当 Sensors HAL 将所需数量的传感器事件写入事件 FMQ 后,Sensors HAL 必须将 EventQueueFlagBits::READ_AND_PROCESS 位写入事件 FMQ 的 EventFlag::wake 函数,以通知框架这些事件已准备就绪。可以使用 EventFlag::createEventFlag 和事件 FMQ 的 getEventFlagWord() 函数从事件 FMQ 创建 EventFlag。

Sensors HAL 2.0/2.1 支持对事件 FMQ 使用 writewriteBlocking。默认实现提供一个可供使用 write 的引用。如果使用 writeBlocking 函数,则必须将 readNotification 标志设置为 EventQueueFlagBits::EVENTS_READ,该标志由框架在从事件 FMQ 中读取事件时设置。写入通知标志必须设置为 EventQueueFlagBits::READ_AND_PROCESS,以通知框架已将事件写入事件 FMQ。

WAKE_UP 事件

WAKE_UP 事件是这样一类传感器事件,它们会立即唤醒应用处理器 (AP) 来处理事件。每次将 WAKE_UP 事件写入事件 FMQ 时,Sensors HAL 都必须锁住唤醒锁,以确保在框架能够处理事件之前系统不会锁定屏幕。收到 WAKE_UP 事件后,框架会锁住自己的唤醒锁,以允许 Sensors HAL 解锁其唤醒锁。如需在 Sensors HAL 解锁其唤醒锁时执行同步,请使用唤醒锁 FMQ。

Sensors HAL 必须读取唤醒锁 FMQ 来确定框架已处理的 WAKE_UP 事件数量。只有当未处理的 WAKE_UP 事件的总数为零时,HAL 才应针对 WAKE_UP 事件解锁其唤醒锁。处理传感器事件后,框架会统计标记为 WAKE_UP 事件的事件数量,并将此数量写回唤醒锁 FMQ。

每次将数据写入唤醒锁 FMQ 时,框架都会在唤醒锁 FMQ 上设置 WakeLockQueueFlagBits::DATA_WRITTEN 写入通知。

动态传感器

动态传感器实际上不是设备的一部分,但可以作为设备的输入,例如带有加速度计的游戏手柄。

在连接动态传感器时,必须从 Sensors HAL 调用 ISensorsCallback 中的 onDynamicSensorConnected 函数。这会通知框架连接了新的动态传感器,从而允许通过框架控制传感器,并让客户端能够使用传感器的事件。

同样地,当动态传感器断开连接时,必须调用 ISensorsCallback 中的 onDynamicSensorDisconnected 函数,以便框架能够移除已不再可用的传感器。

直接通道

直接通道是一种操作方法,这种方法会绕过 Android 传感器框架,将传感器事件写入特定的内存而不是事件 FMQ 中。注册直接通道的客户端,必须直接从用于创建直接通道的内存中读取传感器事件,而不是通过框架接收传感器事件。就一般操作而言,configDirectReport() 函数与 batch() 类似,但它会配置直接报告通道。

registerDirectChannel()unregisterDirectChannel() 函数分别用于创建和销毁新的直接通道。

操作模式

框架可以使用 setOperationMode() 函数来配置传感器,以便能够将传感器数据注入传感器。这对于测试非常有用,尤其是对于框架下存在的算法。

HAL 2.0 中的 injectSensorData() 函数和 HAL 2.0 中的 injectSensorsData_2_1() 函数通常用于将操作参数推送到 Sensors HAL 中。该函数还可用于将传感器事件注入特定传感器。

验证

要验证 Sensors HAL 的实现,请运行传感器 CTS 和 VTS 测试。

CTS 测试

传感器 CTS 测试存在于自动化 CTS 测试和手动 CTS 验证程序应用中。

自动化测试位于 cts/tests/sensor/src/android/hardware/cts。 这些测试用于验证传感器的标准功能,例如激活传感器、批处理和传感器事件频率。

CTS 验证程序测试位于 cts/apps/CtsVerifier/src/com/android/cts/verifier/sensors。 这些测试需要测试操作员手动输入,以确保传感器报告准确的值。

通过 CTS 测试是确保受测设备满足所有 CDD 要求的关键所在。

VTS 测试

适用于 Sensors HAL 2.0 的 VTS 测试位于 hardware/interfaces/sensors/2.0/vts。适用于 Sensors HAL 2.1 的 VTS 测试位于 hardware/interfaces/sensors/2.1/vts。 这些测试可确保能够正确实现 Sensors HAL,并完全满足 ISensors.halISensorsCallback.hal 的所有要求。

从 Sensors HAL 2.0 升级到 2.1

从 Sensors HAL 2.0 升级到 2.1 时,您的 HAL 实现必须包含 initialize_2_1()getSensorsList_2_1()injectSensorsData_2_1() 方法,以及 HAL 2.1 类型。这些方法必须符合上述针对 HAL 2.0 的要求。

由于次要版本 HAL 必须支持之前 HAL 中的所有函数,因此 2.1 HAL 必须支持初始化为 2.0 HAL。为了避免同时支持这两种 HAL 版本所造成的复杂性,我们强烈建议您使用 Multi-HAL 2.1。

有关如何实现您自己的 Sensors HAL 2.1 的示例,请参阅 Sensors.h

从 Sensors HAL 1.0 升级到 2.0

从传感器 HAL 1.0 升级到 2.0 时,请确保您的 HAL 实现满足以下要求。

初始化 HAL

为了在框架和 HAL 之间建立 FMQ,必须支持 initialize() 函数。

公开可用的传感器

在 Sensors HAL 2.0 中,getSensorsList() 函数在单次设备启动期间必须返回相同的值,即使 Sensors HAL 在此期间进行了重启也是如此。getSensorsList() 函数的一项新要求就是,它在单次设备启动期间必须返回相同的值,即使 Sensors HAL 在此期间进行了重启也是如此。这样一来,框架就可以在系统服务器重启时尝试重建传感器连接。在设备重新启动后,getSensorsList() 返回的值可以更改。

将传感器事件写入 FMQ

在 Sensors HAL 2.0 中,Sensors HAL 必须在有可用的传感器事件时主动将传感器事件写入事件 FMQ,而不是等待调用 poll()。HAL 还需要将正确的位写入 EventFlag,以在框架内触发 FMQ 读取。

WAKE_UP 事件

在 Sensors HAL 1.0 中,当 WAKE_UP 发布到 poll() 后,HAL 能够在随后的任何一次 poll() 调用中,针对任何 WAKE_UP 事件解锁其唤醒锁,因为这表明框架已经处理了所有传感器事件并且已经获得了唤醒锁(如有必要)。在 Sensors HAL 2.0 中,HAL 不会再获知框架是否已处理了写入 FMQ 的事件,框架在处理 WAKE_UP 事件后,可以通过唤醒锁 FMQ 通知 HAL。

在 Sensors HAL 2.0 中,由 Sensors HAL 针对 WAKE_UP 事件锁住的唤醒锁必须以 SensorsHAL_WAKEUP 开头。

动态传感器

在 Sensors HAL 1.0 中,动态传感器使用 poll() 函数返回。 Sensors HAL 2.0 要求,每次动态传感器连接发生变化时,都必须调用 ISensorsCallback 中的 onDynamicSensorsConnectedonDynamicSensorsDisconnected。这些回调作为 ISensorsCallback 指针(通过 initialize() 函数提供)的一部分提供。

操作模式

传感器 HAL 2.0 必须支持 WAKE_UP 传感器的 DATA_INJECTION 模式。

Multi-HAL 支持

Sensors HAL 2.0 和 2.1 支持使用 Sensors Multi-HAL 框架的 multi-HAL。如需了解实现详情,请参阅从 Sensors HAL 1.0 移植