Selinux编写

Android 系统服务 SELinux 配置的正确姿势(设备专用目录)

对于你自己新增的系统服务(比如 TestService 通过 publishBinderService("testtld", ...) 发布的服务),不应该直接修改 system/sepolicy 目录 ,而应该放在你项目的 板级(device)目录 下,这样才不会污染 AOSP 原始代码,方便移植和维护。

1. 策略文件应该放在哪里?

路径格式为:

bash 复制代码
device/<vendor>/<board>/sepolicy/

比如你的开发板是 KICKPI_3576,那么完整路径可能是:

bash 复制代码
device/rockchip/rk3576/sepolicy/

2. 需要创建的文件和内容

device/<vendor>/<board>/sepolicy/ 下新建两个文件:

文件一:service.te
bash 复制代码
type testtld_service, service_manager_type;

含义 :定义一个名为 testtld_service 的安全类型,并把它标记为"服务管理器能识别的类型"(service_manager_type)。

文件二:service_contexts
bash 复制代码
testtld              u:object_r:testtld_service:s0

含义 :当系统看到服务名为 testtld 的服务时,自动给它贴上 u:object_r:testtld_service:s0 这个安全标签。

注意testtld 这个名字必须和你在 Java 代码里 publishBinderService("testtld", ...) 用的名字完全一致。

3. 让编译系统找到你的策略文件

编辑 device/<vendor>/<board>/BoardConfig.mk,添加:

bash 复制代码
BOARD_SEPOLICY_DIRS += device/<vendor>/<board>/sepolicy

这样 make 时会自动把你定义的文件合并到最终策略中。

详解 service_contexts 那一行配置

你看到的这一行:

bash 复制代码
testtld                u:object_r:testtld_service:s0

本质上是一张 "服务名 → 安全标签" 的对照表。SELinux 通过它来决定哪个安全类型管理着这个服务。

下面逐段拆解:

字段 说明
服务名 testtld 你在 publishBinderService("testtld", ...) 里起的名字,必须一模一样。
用户 u SELinux 用户,Android 里固定为 u,照写即可。
角色 object_r 对于文件、服务这类"被动实体",固定用 object_r,照写即可。
类型 testtld_service 这是核心 。它就是我们刚刚在 service.te 里定义的自定义类型,用于给这个服务贴上唯一的"身份证"。
安全级别 s0 多级安全(MLS)中的最低级别,Android 中普通服务都固定使用 s0,照写即可。

所以,这一行实际上是在说:
"名字叫 testtld 的服务,安全性上就交给 testtld_service 这个类型来管了。"

有了这行映射之后,我们才能在后续为 testtld_service 编写具体的访问规则(比如哪些进程可以找到它、使用它)。

附加部分:权限规则(按需添加)

一般情况下,system_server 注册和查找自己创建的服务不需要额外 allow 规则。如果后续你遇到 avc: denied 日志(比如某个 system_app 访问你的服务被拒绝),可以在 device/<vendor>/<board>/sepolicy/ 下新建对应的 .te 文件补充权限,例如:

system_app.te(如果服务只给系统应用使用)

bash 复制代码
allow system_app testtld_service:service_manager find;

总结

  1. 设备专用策略别动 system/sepolicy,全放在 device/<vendor>/<board>/sepolicy/

  2. 通过 service.te 声明你的服务类型

  3. 通过 service_contexts 把服务名和类型绑定

  4. BoardConfig.mk 中加入 BOARD_SEPOLICY_DIRS 让编译找到这些文件

  5. service_contexts 里那一长串不用怕,你只需要关心 服务名类型 这两个自定义部分,其余照抄就行

这样你的 TestService 就会被 SELinux 正确识别,不会再被安全策略拦截了。

如何和framework交互

第一层:Framework 上层 ↔ TestService 系统服务

这一层是你自己定义的一个系统服务 ,别人通过 servicemanager 找到它。

位置 关键词 作用
TestService.java publishBinderService("testtld", ...) 向 servicemanager 注册你的服务,名字固定为 testtld
上层客户端代码 ServiceManager.getService("testtld") 通过相同的名字找到你的服务
service_contexts testtld u:object_r:testtld_service:s0 告诉 SELinux:servicemanager 中叫 testtld 的服务,必须贴上 testtld_service 这个安全标签
service.te type testtld_service, service_manager_type; 声明 testtld_service 是一种"被 servicemanager 认可"的类型
客户端权限文件 allow system_app testtld_service:service_manager find; 允许 system_app 标签的进程通过 servicemanager 查找 testtld_service 标签的服务

所以这一层交互的关键词是:testtld 这个名字。

只要 Java 代码里的服务名和 SELinux 配置文件里的名字完全一致,系统启动时就能通过标签安全地匹配上。


第二层:TestService 系统服务 ↔ C++ HAL 服务

这一层是你写的 TestService 作为一个客户端,去调用真正的硬件抽象层(HAL)。

HAL 服务运行在独立的进程,通过 hwservicemanager 管理,和第一层的 servicemanager 是两套不同的机制。

位置 关键词 作用
TestService.java ServiceManager.waitForDeclaredService(IHelloTest.DESCRIPTOR + "/default") 在 hwservicemanager 中查找 HAL 服务。 DESCRIPTOR 自动生成为 "android.hardware.testtld.IHelloTest" 加上实例后缀就变成 "android.hardware.testtld.IHelloTest/default"
IHelloTest.aidl package android.hardware.testtld; 且接口名就是 IHelloTest 这个完全决定了 DESCRIPTOR 的值。
C++ HAL 服务注册代码 通常会调用类似 registerAsService("default") 在 hwservicemanager 中注册为默认实例,实例名就是 "default"
C++ HAL 头文件 IHelloTest.h 自动生成 同样包含 descriptor"android.hardware.testtld.IHelloTest",用于匹配

所以这一层交互的关键词是:IHelloTest.DESCRIPTOR + 实例名 default

"android.hardware.testtld.IHelloTest/default" 这个完整字符串。

Java 用这个字符串去 hwservicemanager 查找,C++ HAL 服务也必须用完全相同的 descriptor 和实例名注册。

一句话理解整个交互

bash 复制代码
上层应用
   ↓  通过 servicemanager 查找 "testtld" 服务 (ITestHalManager 接口)
TestService.java
   ↓  通过 hwservicemanager 查找 "android.hardware.testtld.IHelloTest/default" 服务 (IHelloTest 接口)
C++ HAL 服务

所有的连接点都是字符串:

  • "testtld" 连接了 Framework 上层和你的 TestService

  • "android.hardware.testtld.IHelloTest/default" 连接了 TestService 和 C++ HAL

  • SELinux 规则又分别根据这些字符串给服务打上安全标签,确保只有授权者能访问

你只要确保:

  1. publishBinderService 的名字 = service_contexts 里的服务名 = 别人查找的名字

  2. IHelloTest.DESCRIPTOR(由 AIDL 自动生成) = C++ 侧注册的 descriptor

  3. 实例名 "default" 与 C++ 侧注册的实例名一致

整个链路就完美联通了。没有哪一处是"自动"知道另一处的,全靠这些字符串对上号。

相关推荐
Danileaf_Guo5 小时前
手搓KVM虚拟化!Ubuntu 26.04 + KVM 7.0.0,告别VMware的低成本玩法
linux·运维·服务器·ubuntu
网络点点滴5 小时前
NPM的包版本管理
前端·npm·node.js
中海德--陈顺真5 小时前
HONEYWELL 扫描架控制板 51000398
运维·服务器·人工智能
wuminyu5 小时前
专家视角看Java多态性的底层基石vtable(虚函数表)构建过程解析
java·linux·c语言·jvm·c++
光影少年5 小时前
react性能优化比较好的办法有哪些?
前端·react.js·性能优化
fix一个write十个5 小时前
从零搭建音视频通话太痛苦?这个 Vue3 CallKit 让你 5 分钟搞定 1v1 + 群聊通话
前端·vue.js·github
lbb 小魔仙5 小时前
2026远程办公软件夏季深度横测:ToDesk、向日葵、网易UU远程全面对比,远控白皮书
android·服务器·网络协议·tcp/ip·postgresql
豹哥学前端5 小时前
告别割裂式学习:待办清单项目,一次性掌握数组、本地存储与事件委托
前端·javascript
JYeontu5 小时前
照片墙太死板?做一个会随风摇摆的绳串图片交互效果
前端·javascript·css