AI 技术博客
Android全栈8 分钟阅读5777

【全栈第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. 踩坑提醒

  1. 少了 init_daemon_domain:init 没权限 exec HAL → 进程根本不起,logcat 有 init 相关 avc。
  2. 少了 hal_server_domain:HAL 起了但 addService 时 avc denied(没注册权限)。
  3. service_contexts 没绑:服务落到默认标签,framework find 不到(同 L6 的 add denied)。
  4. client 域没 attach hal_hello_client:framework 调 HAL 时 binder call denied(L10 的 avc 问题)。
  5. vendor 和 system sepolicy 分开:HAL 进程域在 vendor 侧,framework client 权限在 system 侧,两边都要配。

9. 常见问题分析与定位(HAL SELinux 实战)

9.1 常见问题清单

  1. HAL 进程根本不起(init 拉不起来)
  2. HAL 起了但 addService 时 avc denied
  3. framework 调 HAL 时 binder call denied
  4. HAL 访问 /dev 驱动节点 denied(为 L20 铺垫)
  5. 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,logcat avc 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. 小结

  1. HAL 比 service 标签复杂:需要进程域 + exec 类型 + 服务标签 + client/server 配对。
  2. 三件事:hal_attribute(配对)+ HAL 进程域 te(hal_server_domain + init_daemon_domain)+ service_contexts(标签)。
  3. hal_server_domain 宏展开成 HAL 全套权限,省手写。
  4. vendor 和 system 两边都要配(HAL 进程在 vendor,framework client 在 system)。
  5. 排查口诀:不起查 init_daemon_domain+exec类型;add denied 查 hal_server_domain+service_contexts;调用 denied 查 client 域;访问节点加 chr_file allow。

下节预告

L12:打通 App→Framework→HAL 全竖线 —— 把 L1-L11 串成一条完整竖线(阶段二收官)。

评论