1. 课程导入

Android 系统从 Android 8.0(Treble 项目)开始,对系统架构进行了彻底的分层重构,将原本与 Framework 紧耦合的硬件抽象层(HAL)拆分为独立的 HAL Service 进程,通过 HIDL/Binder 与 Framework 通信。这一变化让 HAL 从"库"变成了"服务",也让 HAL 开发从"改 C 代码"变成了"写一个可独立运行、可调试、可升级的系统服务"。本课程的目标,就是带你从零掌握 Android HAL Service 的开发与调试全流程。

一、学习目标:学完你能做什么

本课程不是泛泛介绍 HAL 概念,而是以"能独立交付一个可用的 HAL Service"为最终验收标准。具体学习目标如下:

  • 理解 Treble 架构下 HAL 的分层模型,能说清 Framework、HAL Service、Kernel Driver 三者的边界与通信方式。
  • 掌握 HIDL 与 AIDL 两种 HAL 接口定义语言,能根据 Android 版本选择正确的接口类型并编写 .hal / .aidl 文件。
  • 能独立完成一个 HAL Service 的目录结构搭建、接口实现、编译配置(Android.bp)与开机自启动(init.rc)。
  • 掌握 HAL 调试的核心手段:logcat、dumpsys、lshal、Binder 调用跟踪、SELinux 权限排障。
  • 能定位并解决 HAL 开发中最常见的四类问题:服务起不来、接口找不到、权限被拒、数据不通。

二、核心概念:先建立正确的"心智模型"

在动手之前,必须先厘清几个容易混淆的核心概念,否则后续调试会处处踩坑。

HAL Service 的本质是一个 Binder 服务进程。它由 init 进程根据 .rc 文件拉起,注册到 ServiceManager,Framework 通过 Binder 代理调用它的接口。它和普通 App 进程最大的区别是:运行在 native 层、以 C++ 为主、受 SELinux 严格约束、随系统启动。

HIDL 与 AIDL 是两代接口技术。Android 8.0~11 主要使用 HIDL(hardware/interfaces/ 目录),Android 11 之后 Google 主推 AIDL for HAL,Android 13 起新 HAL 基本都用 AIDL。两者都支持 Binder 化通信,但语法、版本管理、passthrough 模式差异明显。

Passthrough 与 Binderized 是两种运行模式。Passthrough 模式下 HAL 以库形式被加载进调用进程,主要用于兼容旧 HAL;Binderized 模式才是 Treble 的目标形态,HAL 独立成进程。理解这一点,是看懂 lshal 输出的前提。

核心心智模型:Framework(Java/C++)→ Binder → HAL Service(C++ 进程)→ ioctl/sysfs → Kernel Driver → 硬件。任何一环断裂,都会表现为"上层功能不可用"。

三、整体知识框架:一张图看清全貌

下图给出了本课程的知识全景。左侧是 HAL Service 从接口定义到运行时的完整链路,右侧是贯穿始终的调试手段。建议在学习每一章时都回看这张图,明确当前所处的位置。

Android HAL Service 开发与调试知识全景 开发链路(从接口到运行) ① 接口定义 .hal / .aidl ② 接口实现 C++ 实现类 ③ 编译配置 Android.bp ④ 启动配置 init.rc + SELinux ⑤ 运行时服务 Binder 进程 运行时调用关系 Framework Java / C++ Client HAL Service Binderized 进程 Kernel Driver ioctl / sysfs 硬件设备 Sensor / Camera / ... Binder IPC 系统调用 寄存器/中断 调试手段(贯穿全程) logcat / dmesg — 日志定位 lshal / dumpsys — 服务状态 Binder 调用跟踪 — 通信链路 SELinux 审计 — 权限排障 VTS / 单元测试 — 接口验证 strace / perfetto — 性能分析 调试 实战路线: 环境搭建 → HIDL/AIDL 接口 → 实现与编译 → init/SELinux → 启动验证 → 调试排障 → 测试交付 每章对应路线图上的一个节点,最终串成一条可落地的 HAL Service 交付流水线。

四、实战路线图:七步走完一个 HAL Service

把上面的知识框架落到操作层面,就是下面这条七步路线。本课程后续章节基本按此顺序展开。

  1. 环境搭建:下载 AOSP 源码,配置 lunch 目标,确认能编译出 system.img。
  2. 接口定义:在 hardware/interfaces/ 下新建接口目录,编写 .hal 或 .aidl,定义方法、参数、回调。
  3. 接口实现:继承生成的 Stub 类,实现具体逻辑,处理线程模型与错误码。
  4. 编译配置:编写 Android.bp,声明 cc_binary、依赖库、vintf_fragments。
  5. 启动配置:编写 init.rc 注册服务,配置 SELinux te 文件与 file_contexts。
  6. 启动验证:刷机后通过 lshal 确认服务已注册,通过 dumpsys 查看状态。
  7. 调试排障:按"起不来→找不到→被拒绝→不通"的顺序逐层排查。

五、典型场景与常见误区

典型场景:假设你要为一个自定义传感器开发 HAL。Framework 层通过 ISensor.getData() 调用,HAL 层需要打开 /dev/sensor0,读取数据并回调给上层。整个链路涉及接口定义、Binder 通信、设备节点访问、SELinux 策略四类工作,任何一类出错都会导致"上层拿不到数据"。

常见误区一:把 HAL 当成普通 C 库来写。Treble 之后 HAL 是独立进程,必须考虑进程生命周期、Binder 线程池、死亡通知。
常见误区二:忽略 SELinux。很多"服务起不来"其实是 avc: denied,而不是代码问题。
常见误区三:接口版本管理混乱。HIDL 的 @1.0、@1.1 必须严格对应,随意改接口会导致 VTS 失败。

六、关键概念速查清单

概念 含义 关键点
Treble Android 8.0 引入的分层架构 HAL 独立成进程,Framework 与 Vendor 解耦
HIDL HAL 接口定义语言(8.0~11) 支持 passthrough 与 binderized
AIDL for HAL 新一代 HAL 接口(11+) 语法更接近 Java AIDL,推荐新项目使用
Binderized HAL 作为独立 Binder 进程运行 Treble 目标形态,可独立升级
VINTF Vendor Interface 清单 声明 HAL 版本与可用性,影响匹配
SELinux 强制访问控制 HAL 进程的域、设备节点标签必须配对

七、一个最小可运行示例的骨架

为了让你对"一个 HAL Service 长什么样"有直观感受,下面给出一个最小服务的目录与关键文件骨架。后续章节会逐行展开。

hardware/interfaces/demo/1.0/
├── Android.bp
├── IDemo.hal
├── default/
│   ├── Android.bp
│   ├── Demo.cpp
│   ├── Demo.h
│   └── demo-service.rc
└── vintf/
    └── demo.xml

# demo-service.rc 关键内容
service demo-hal /vendor/bin/hw/android.hardware.demo@1.0-service
    class hal
    user system
    group system
    seclabel u:r:hal_demo_default:s0

这段骨架里,IDemo.hal 定义接口,Demo.cpp 实现逻辑,Android.bp 负责编译,demo-service.rc 负责启动,demo.xml 负责 VINTF 声明。五者缺一不可,这也是本课程反复强调的"HAL Service 五件套"。

学习建议:不要一上来就啃 AOSP 全部源码。先按本课程路线图,用一个小接口(比如只提供一个 hello() 方法)跑通全流程,再逐步增加复杂度。跑通一次,胜过读十遍文档。