第二十一章:硬件抽象层(HAL)探秘:Android传感器HAL接口、如何查看设备传感器HAL实现
说到Android的传感器框架,很多人会盯着上层的应用开发,或者研究Framework层的代码流转。但我个人觉得,真正有意思的地方,其实是硬件抽象层——也就是HAL。为什么?因为它是连接Android系统和底层硬件的那座桥。桥修得好不好,直接决定了你的距离传感器灵不灵、省不省电。
这一章,我们就来扒一扒传感器HAL的底裤。我会带你看看它的接口定义,再手把手教你如何查看自己手机上的HAL实现。
HAL在传感器架构中的位置
先看一张图,你就明白HAL到底站在哪了。
你看,HAL层夹在Framework和内核驱动之间。Framework通过JNI调用到HAL,HAL再通过ioctl或者sysfs跟内核驱动打交道。说白了,HAL就是Android系统给硬件厂商留的一个“标准化接口”。
核心观点:HAL的存在,让Google不用关心你用的是哪家的传感器芯片。只要你的HAL实现了规定的接口,Android系统就能正常使用你的传感器。
传感器HAL接口长什么样?
Android的传感器HAL接口定义在 hardware/libhardware/include/hardware/sensors.h 这个头文件里。我当年第一次打开这个文件的时候,说实话有点懵——结构体套结构体,回调函数满天飞。但别怕,我们拆开来看。
核心结构体:sensors_module_t
每个HAL模块都要暴露一个 sensors_module_t 结构体。它长这样:
typedef struct sensors_module_t {
struct hw_module_t common;
/* 获取传感器列表 */
int (*get_sensors_list)(struct sensors_module_t* module,
struct sensor_t const** list);
} sensors_module_t;
嗯,代码不多,但信息量很大。这个结构体里最重要的就是 get_sensors_list 函数指针。它返回一个 sensor_t 数组,里面描述了设备上所有可用的传感器信息。
我的经验:我在调试一款国产手机的距离传感器时,发现get_sensors_list返回的列表里居然没有距离传感器。查了半天,原来是HAL实现里把距离传感器类型写成了“PROXIMITY_RAW”,而不是标准的“SENSOR_TYPE_PROXIMITY”。这种坑,你不看HAL代码根本发现不了。
核心结构体:sensor_t
每个传感器的描述信息,都放在 sensor_t 里:
typedef struct sensor_t {
const char* name; // 传感器名称
const char* vendor; // 供应商
int version; // 版本
int handle; // 句柄,用于标识传感器
int type; // 类型,比如距离传感器是8
float maxRange; // 最大量程
float resolution; // 分辨率
float power; // 功耗,单位mA
int32_t minDelay; // 最小采样周期,单位微秒
uint32_t fifoReservedEventCount; // FIFO保留事件数
uint32_t fifoMaxEventCount; // FIFO最大事件数
const char* stringType; // 字符串类型
const char* requiredPermission; // 所需权限
int32_t maxDelay; // 最大采样周期
uint32_t flags; // 标志位
void* reserved[2];// 保留字段
} sensor_t;
这里面的字段,每一个都对应着上层 Sensor 类的属性。比如 maxRange 对应距离传感器的最大检测距离,minDelay 对应最快能多快上报数据。
核心结构体:sensors_poll_device_t
有了传感器列表,接下来就是怎么操作传感器了。这个操作接口定义在 sensors_poll_device_t 里:
typedef struct sensors_poll_device_t {
struct hw_device_t common;
/* 激活/去激活传感器 */
int (*activate)(struct sensors_poll_device_t* dev,
int handle, int enabled);
/* 设置传感器延迟 */
int (*setDelay)(struct sensors_poll_device_t* dev,
int handle, int64_t period_ns);
/* 轮询传感器数据 */
int (*poll)(struct sensors_poll_device_t* dev,
sensors_event_t* data, int count);
} sensors_poll_device_t;
这三个函数,就是HAL的灵魂:
- activate:打开或关闭某个传感器。传入handle和enable标志。
- setDelay:设置采样频率。注意单位是纳秒。
- poll:阻塞式读取传感器数据。Framework会开一个线程不断调用它。
注意:poll函数是阻塞的。如果传感器没有新数据,它会一直等在那里。我曾经见过一个实现,poll函数里直接死循环了,导致整个传感器服务卡死。嗯,这种bug排查起来特别痛苦。
如何查看自己设备的传感器HAL实现?
理论说完了,我们来点实操。怎么看到你手机上的传感器HAL代码?
首先,你要明白一件事:HAL是编译成动态库的。在Android系统里,传感器HAL的so文件通常叫 sensors.<设备名>.so。比如:
sensors.msm8998.so— 高通平台sensors.exynos9810.so— 三星平台sensors.mt6765.so— 联发科平台
这些so文件放在 /vendor/lib/hw/ 或者 /system/lib/hw/ 目录下。你可以用adb连上手机,直接查看:
adb shell
ls -l /vendor/lib/hw/sensors.*.so
如果能看到文件,恭喜你,你的设备有传感器HAL实现。如果看不到……嗯,可能是厂商把传感器做进了别的模块里,或者你的手机太老了。
反编译HAL库
光看文件名不够,我们得看看里面到底实现了什么。用 readelf 可以查看导出的符号:
adb shell
readelf -s /vendor/lib/hw/sensors.xxx.so | grep -E "sensors_module|activate|poll|setDelay"
你会看到类似这样的输出:
12: 0000a3e0 132 FUNC GLOBAL DEFAULT 8 sensors_module
45: 0000b210 56 FUNC GLOBAL DEFAULT 8 activate
67: 0000b2a0 88 FUNC GLOBAL DEFAULT 8 poll
89: 0000b1c0 44 FUNC GLOBAL DEFAULT 8 setDelay
看到这些符号,说明HAL实现了标准的接口。如果缺少某个符号,那就有问题了。
我的习惯:拿到一台新设备,我第一件事就是跑这个命令。如果发现poll函数没有导出,那这个设备要么是用了旧的HAL版本,要么就是厂商自己改了接口。这两种情况,上层应用都可能遇到奇怪的问题。
查看HAL源码
如果你有设备的源码(比如AOSP或者厂商发布的kernel源码),那就可以直接看源代码了。传感器HAL的源码通常放在:
hardware/<厂商>/sensors/
或者
device/<厂商>/<设备名>/sensors/
举个例子,Pixel设备的传感器HAL源码在:
hardware/google/pixel/sensors/
里面一般会有 SensorBase.cpp、SensorDevice.cpp 这样的文件。打开看看,你会发现它们实现了我们上面说的那些接口。
一个典型的HAL实现流程
我画个流程图,帮你理清HAL内部的工作逻辑:
流程其实不复杂:Framework先调用activate打开传感器,然后不断调用poll获取数据。数据以 sensors_event_t 结构体返回,里面包含了距离值、时间戳等信息。
距离传感器的HAL实现细节
具体到距离传感器,HAL实现里有个关键点:近程/远程状态的判断。距离传感器通常返回两个值:距离(厘米)和最大量程。但上层应用更关心的是“靠近”还是“远离”。
这个判断逻辑,其实是在HAL里做的。看一段伪代码:
// 距离传感器HAL实现片段
int proximity_poll(sensors_event_t* data) {
// 从驱动读取原始值
int raw_value = read_from_driver();
// 判断近程/远程
if (raw_value < PROXIMITY_THRESHOLD) {
data->distance = 0.0f; // 靠近
} else {
data->distance = maxRange; // 远离
}
data->type = SENSOR_TYPE_PROXIMITY;
data->timestamp = get_current_time();
return 1; // 返回1个事件
}
这里有个坑:PROXIMITY_THRESHOLD 这个阈值怎么定?不同厂商的传感器硬件不一样,有的用5cm,有的用3cm。我见过一个设备,阈值设得太低,导致用户打电话时屏幕关不掉——因为传感器永远认为“远离”。
避坑指南:我曾经调试过一个项目,距离传感器在通话时偶尔闪屏。查了三天,最后发现是HAL里的阈值判断用了整数比较,而驱动返回的是浮点数。精度丢失导致状态翻转。嗯,从那以后我写HAL代码,所有比较都加上了epsilon容差。
总结一下
传感器HAL其实没那么神秘。它就是一组标准接口,让Android系统能统一操作不同厂商的传感器硬件。核心就三个函数:get_sensors_list、activate、poll。再加上一个 setDelay 控制采样频率。
想查看自己设备的HAL实现?用adb连上去,找 /vendor/lib/hw/sensors.*.so,然后用 readelf 看看导出的符号。如果有源码,直接翻 hardware/ 目录下的代码。
搞懂了HAL,你就掌握了传感器调试的主动权。下次遇到距离传感器不灵的问题,别光盯着上层代码看——去HAL里翻一翻,说不定问题就在那里等着你。