4. HAL Service 实现框架:生命周期、注册与启动

HAL Service 是整个硬件抽象层的执行核心。说白了,它就是运行在系统服务进程里的一个 Native 服务,负责跟底层的硬件驱动打交道,向上给 Framework 提供稳定的接口。

我刚开始接触 HAL Service 时,总觉得它跟普通的 Android Service 差不多。后来踩了几个坑才明白——它的生命周期管理、注册流程、启动顺序,每一个环节都有讲究。搞不清楚这些,写出来的 HAL Service 要么跑不起来,要么跑起来就崩。

4.1 HAL Service 的生命周期

一个典型的 HAL Service,从生到死会经历这几个阶段:

阶段 触发时机 关键动作
构造 进程启动,main() 执行 分配资源、初始化成员变量
注册 构造完成后立即执行 调用 registerAsService() 向 HwServiceManager 注册
运行 注册成功后进入事件循环 等待客户端请求,处理事务
销毁 进程退出或异常终止 释放资源、关闭硬件设备

这里有个容易忽略的点:HAL Service 没有像 Java Service 那样的 onStart/onStop 回调。它的生命周期完全由进程的 main() 函数控制。你写多少代码,它就活多久。

注意:HAL Service 进程一旦 crash,不会自动重启。这是跟 Java Framework Service 最大的区别。我曾经在项目里遇到过,Sensor HAL 进程因为一个空指针挂了,整个系统的传感器都失效了,直到下次重启才恢复。后来我加了一个 watchdog 机制,才解决了这个问题。

4.2 HwServiceManager 的作用

HwServiceManager 是 Android 8.0 之后引入的组件。它的角色,你可以理解成「硬件服务的电话总机」。

为什么需要它?

  • 服务发现:客户端(比如 Framework 的 SensorService)想知道某个 HAL Service 在不在,得通过 HwServiceManager 来查
  • 服务注册:每个 HAL Service 启动后,必须把自己的名字和接口告诉 HwServiceManager
  • 服务保活:HwServiceManager 会监控已注册服务的状态,如果服务挂了,它能感知到

我个人的习惯是,把 HwServiceManager 看作一个「轻量级的 ServiceManager」。它不负责进程间通信的细节,只负责维护一个服务名到服务实例的映射表。

核心概念:HwServiceManager 本身也是一个独立的进程,在系统启动时由 init 进程拉起。它的代码在 hardware/interfaces/hwservicemanager/ 目录下。

4.3 HAL Service 的注册流程

注册流程,说白了就是三步:

  1. 创建服务实例:在 main() 函数里 new 出你的 HAL Service 对象
  2. 调用注册接口:通过 registerAsService() 把服务注册到 HwServiceManager
  3. 进入事件循环:调用 joinRpcThreadpool() 让服务开始监听客户端请求

来看一个典型的代码骨架:

int main() {
    // 1. 创建服务实例
    sp<IFoo> service = new Foo();

    // 2. 注册服务
    status_t status = service->registerAsService("default");
    if (status != OK) {
        ALOGE("Failed to register Foo HAL service: %d", status);
        return 1;
    }

    // 3. 进入事件循环
    joinRpcThreadpool();
    return 0;
}

嗯,这里要注意:registerAsService() 的第一个参数是实例名。同一个 HAL 接口可以有多个实例,比如 "default"、"custom"、"debug" 等等。客户端在获取服务时,需要指定实例名才能拿到对应的服务。

避坑指南:我曾经在项目里犯过一个低级错误——注册服务时忘了检查返回值。结果服务没注册成功,客户端一直拿不到服务,日志里也没有任何错误信息。排查了半天才发现是注册失败了。所以,一定要检查 registerAsService() 的返回值。

4.4 HAL Service 的启动与初始化

HAL Service 的启动,不是由 Framework 直接拉起的。它走的是 Android 传统的 init 进程启动流程。

具体来说:

  • 在 device/<vendor>/<device>/init.rc 里定义 service 条目
  • init 进程解析 rc 文件,启动对应的可执行文件
  • 可执行文件的 main() 函数执行,完成服务初始化和注册

一个典型的 init.rc 配置长这样:

service vendor.foo-hal /vendor/bin/hw/vendor.foo-hal
    class hal
    user system
    group system
    seclabel u:r:hal_foo:s0

这里有几个关键点:

  • class hal:表示这个服务属于 hal 类,系统启动时会批量启动所有 hal 类服务
  • user/group:指定服务运行的 uid/gid,通常是 system
  • seclabel:SELinux 上下文,必须跟策略文件里定义的一致
重要:SELinux 上下文配置错了,服务根本起不来。我遇到过好几次,init 进程启动服务时因为 seclabel 不匹配,直接报 "Permission denied"。排查方法很简单——看 dmesg | grep avc 的输出,里面会明确告诉你哪个上下文被拒绝了。

初始化阶段,除了注册服务,通常还需要做这些事:

  1. 打开硬件设备:通过 open() 系统调用打开 /dev 下的设备节点
  2. 初始化硬件:设置寄存器、配置中断、分配 DMA 缓冲区等
  3. 创建工作线程:有些 HAL Service 需要后台线程处理异步事件
  4. 设置回调:注册死亡通知、状态变化回调等

我个人习惯把初始化逻辑拆成几个小函数,每个函数只做一件事。这样调试起来方便,出问题了也能快速定位。

4.5 整体流程梳理

把上面这些串起来,HAL Service 的完整生命周期可以用下面这张图来表示:

HAL Service 完整生命周期流程图 init 进程解析 rc 文件 启动 HAL Service 可执行文件 main() 函数执行 创建服务实例 registerAsService() 向 HwServiceManager 注册 硬件初始化 打开设备节点 / 配置寄存器 分配 DMA 缓冲区 / 创建工作线程 joinRpcThreadpool() 进入事件循环,等待请求 服务运行中 处理客户端 RPC 请求 进程退出 / 销毁 关键提醒 • 注册失败不会自动重试 • 进程 crash 不会自动重启 • SELinux 配置错误直接挂

这张图把整个流程串起来了。你想想看,从 init 启动到服务注册,再到硬件初始化和事件循环,每一步都有明确的职责。搞清楚了这些,写 HAL Service 的时候心里就有底了。

总结一下:HAL Service 的实现框架,核心就是三件事——注册(让系统能找到你)、初始化(让硬件能工作)、事件循环(让客户端能调用你)。把这三点做好,一个稳定的 HAL Service 就搭起来了。