【全栈第11课】HAL 的 SELinux + VINTF 声明(让 HAL 真正起来)
Android 全栈工程师进阶教程 第11课。HAL 的 SELinux + VINTF 声明(让 HAL 真正起来)。基于公开 AOSP 知识整理,含原理、可编译示例、常见问题定位。
> 本文是《Android 全栈工程师进阶教程》系列第 11 课。完整 26 课见 GitHub 仓库。
【全栈第11课】HAL 的 SELinux + VINTF 声明(让 HAL 真正起来)
0. 这节课你将做出什么
给 L9 的 hello HAL 补全 SELinux 策略,让 HAL 进程能被 init 拉起、能注册、能被 framework 调——真正在设备上跑起来。 学完你能回答:HAL 进程的 SELinux 和 L6 的 service 标签有什么不同?为什么 HAL 要单独一个域(domain)?HAL 起不来时按什么顺序排查?
1. 背景:HAL 进程比 service 标签复杂
L6 我们给 Java 系统服务打了个 service 标签就行——因为它跑在 system_server 里,system_server 这个 domain 早就配好了。
但 HAL 是独立的 vendor 进程(L9),它需要自己的一整套 SELinux 配置:
- 它是个新进程 → 要有自己的 domain(进程类型)
- init 要能拉起它 → 要 exec 类型 + init_daemon_domain
- 它要注册服务 → 要 service 标签 + 注册权限
- framework 要能调它 → 要 client/server 域配对
少配任何一项,HAL 就起不来(avc denied)或调不通。
2. 全局图:HAL 跑起来需要的 SELinux 拼图
init ──拉起──▶ hal_hello_default(HAL 进程域)
│ exec_type + init_daemon_domain(让 init 能 exec)
│ hal_server_domain(...) → 自动获得"被 servicemanager add + 被 client 调"权限
▼ addService
hal_hello_service(服务标签)← service_contexts 绑定
▲ find
framework(client 域)─ hal_attribute 配对 ─ 能找/调
3. 某真实机型 真实源码印证(照搬 vibrator HAL sepolicy)
HAL 进程域 system/sepolicy/vendor/hal_vibrator_default.te:
type hal_vibrator_default, domain; // 定义 HAL 进程域
hal_server_domain(hal_vibrator_default, hal_vibrator) // 标为 vibrator HAL 的 server
type hal_vibrator_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_vibrator_default) // 让 init 能拉起这个可执行文件
hal attribute system/sepolicy/public/hal_vibrator.te:
hal_attribute(vibrator) // 生成 hal_vibrator_client / hal_vibrator_server 配对属性
这套是所有 AIDL HAL 的 sepolicy 标准模板。
4. 前置准备
- 依赖 L6(SELinux 基础)、L9(hello HAL)、L10(framework 调 HAL)。
- 改/加
system/sepolicy下:hal attribute、HAL 进程域 te、service_contexts。
5. 动手:给 hello HAL 配 SELinux(三件事)
文件 1:hal attribute public/hal_hello.te
# hal_attribute(hello) 会自动生成 hal_hello_client / hal_hello_server 两个属性,
# 供 framework(client)和 HAL 进程(server)分别 attach,把"谁能调谁"配对好。
hal_attribute(hello)
文件 2:HAL 进程域 vendor/hal_hello_default.te(照搬 vibrator)
type hal_hello_default, domain; // ① HAL 进程的 domain
hal_server_domain(hal_hello_default, hal_hello) // ② 标为 hello HAL 的 server,
// 宏自动给它"被 add + 被 client 调"的权限
type hal_hello_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_hello_default) // ③ 让 init 能 exec 这个 /vendor/bin/hw/ 下的程序
逐行讲为什么:
type ... domain:HAL 是新进程,必须有自己的进程类型,否则跑在不受控的默认域。hal_server_domain(hal_hello_default, hal_hello):这个宏是核心——它把hal_hello_default标记成hal_hello这个 attribute 的 server,自动展开出"允许它注册 hello_service、允许 client 调它"等一堆 allow。省得你手写一堆规则。init_daemon_domain:L9 的 .rc 里service vendor.hello-hal-default /vendor/bin/hw/...,init 要 exec 这个文件,这个宏给 init 切换到 hal_hello_default 域的权限。
文件 3:service_contexts 绑服务标签
vendor.example.demo.IHelloHal/default u:object_r:hal_hello_service:s0
把 L9 注册的服务名绑到 hal_hello_service 标签(类似 L6,但这是 HAL 服务)。
做完三件事:init 能拉起 HAL(域+exec)、HAL 能注册(hal_server_domain)、framework 能调(hal attribute 配对)。
6. 看底层:hal_server_domain 宏展开了什么
hal_server_domain(hal_hello_default, hal_hello) 不是一条规则,是个宏,大致展开成:
typeattribute hal_hello_default hal_hello_server; // 把进程域归入 server 属性
allow hal_hello_default hal_hello_service:service_manager { add find }; // 能注册/查自己的服务
allow hal_hello_client hal_hello_server:binder call; // 允许 client 调 server
... 等等
所以你只写一行宏,SELinux 编译时会展开成 HAL 该有的全套权限。这就是为什么 AOSP 用宏——HAL 的权限模式是固定的,宏封装好了。
7. 编译 & 运行(验证状态如实)
make selinux_policy # 或整编
# 刷机后:
adb shell lshal | grep hello # HAL 起来了吗
adb shell ps -Z | grep hello-service # 看 HAL 进程跑在 hal_hello_default 域(-Z 显示 SELinux 上下文)
adb logcat | grep -iE "avc.*hello" # 应该没有 denied
预期:
ps -Z: u:r:hal_hello_default:s0 ... hello-service.default ← 进程在正确的域
lshal: vendor.example.demo.IHelloHal/default ← 起来了
(无 avc denied)
> 验证状态(诚实):本课作为学习样板放 zbl-lesson 目录;真实生效需进 system/sepolicy 真实策略文件并整编。sepolicy 语法对照 某真实机型 hal_vibrator 真实策略写。
8. 踩坑提醒
- 少了 init_daemon_domain:init 没权限 exec HAL → 进程根本不起,logcat 有 init 相关 avc。
- 少了 hal_server_domain:HAL 起了但 addService 时 avc denied(没注册权限)。
- service_contexts 没绑:服务落到默认标签,framework find 不到(同 L6 的 add denied)。
- client 域没 attach hal_hello_client:framework 调 HAL 时 binder call denied(L10 的 avc 问题)。
- vendor 和 system sepolicy 分开:HAL 进程域在 vendor 侧,framework client 权限在 system 侧,两边都要配。
9. 常见问题分析与定位(HAL SELinux 实战)
9.1 常见问题清单
- HAL 进程根本不起(init 拉不起来)
- HAL 起了但 addService 时 avc denied
- framework 调 HAL 时 binder call denied
- HAL 访问 /dev 驱动节点 denied(为 L20 铺垫)
- ps -Z 看 HAL 跑在错误的域
9.2 分析与定位
通用第一步:
adb shell getenforce
adb shell dmesg | grep -iE "avc.*hello|init.*hello"
adb shell ps -Z | grep hello # 进程在不在、在哪个域
① HAL 不起(init 拉不起)
- 现象:ps 里没有 hello-service 进程;logcat init 相关报错。
- 原因:
init_daemon_domain没配 / .rc 路径错 / 可执行文件没装对(L9)。 - 定位:
adb logcat | grep -iE "init.*hello|cannot execve";adb shell ls -Z /vendor/bin/hw/ | grep hello(看 exec 类型对不对)。
② addService denied
- 现象:HAL 进程起了,但
lshal显示 N/A,logcatavc denied { add } ... hal_hello_service。 - 原因:
hal_server_domain没配,或 service_contexts 没绑(落默认标签)。 - 修复:补 hal_server_domain + service_contexts。
③ framework 调 HAL binder denied
- 现象:L10 的 client 调用时
avc denied { call } scontext=...client... tcontext=...hal_hello... - 原因:framework 的 domain 没 attach hal_hello_client,或没给 binder call 权限。
- 修复:
hal_client_domain(<framework域>, hello)或对应 allow。
④ HAL 访问 /dev 节点 denied(L20 会用)
- 现象:HAL open /dev/xxx 时
avc denied { read/write } ... chr_file。 - 修复:
allow hal_hello_default <节点type>:chr_file rw_file_perms;
⑤ 跑在错误的域
- 现象:
ps -Z显示 HAL 进程在vendor_init或别的域,不是 hal_hello_default。 - 原因:init_daemon_domain / domain_trans 没配对,域切换没发生。
- 定位:看 exec 文件的 ls -Z 类型,确认 type_transition 链。
9.3 定位决策树
HAL SELinux 出问题
├─ 进程不起 → logcat init.*hello/cannot execve → init_daemon_domain/.rc路径/exec类型(ls -Z)
├─ addService denied → hal_server_domain + service_contexts
├─ framework 调 denied(binder call) → hal_client_domain 给 framework 域
├─ 访问 /dev denied → allow hal_hello_default 节点type:chr_file rw_file_perms(L20)
└─ 跑错域 → ps -Z + exec ls -Z → 检查 init_daemon_domain/type_transition
10. 小结
- HAL 比 service 标签复杂:需要进程域 + exec 类型 + 服务标签 + client/server 配对。
- 三件事:hal_attribute(配对)+ HAL 进程域 te(hal_server_domain + init_daemon_domain)+ service_contexts(标签)。
- hal_server_domain 宏展开成 HAL 全套权限,省手写。
- vendor 和 system 两边都要配(HAL 进程在 vendor,framework client 在 system)。
- 排查口诀:不起查 init_daemon_domain+exec类型;add denied 查 hal_server_domain+service_contexts;调用 denied 查 client 域;访问节点加 chr_file allow。
下节预告
L12:打通 App→Framework→HAL 全竖线 —— 把 L1-L11 串成一条完整竖线(阶段二收官)。