要求
在一个工厂测试APP中,要测试一个按钮是否好用,驱动同事提供了一个设备节点/sys/class/fn_key/fn_key_gpio/fn,这个节点由于某些特殊原因,无法做成正常按钮那样来监听,只能不断读取这个节点来处理,当打开按钮时,节点值会变成1,关闭按钮会变成0,我需要做的是在工厂测试APP对应的测试项中一直读,这个操作可以用handler+runnable来处理。但是读取这个节点的时候会有权限问题,导致无法读取,节点权限报错提示如下:

log
12-30 18:54:08.147 4873 4873 W com.qti.factory:
type=1400 audit(0.0:121): avc: denied { read } for name="fn" dev="sysfs" ino=96682 scontext=u:r:system_app:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=0
各部分含义:
| 字段 | 值 | 解释 |
|---|---|---|
| type=1400 | 1400 | SELinux审计日志的标准类型标识 |
| avc: denied | denied | 表示访问被拒绝 |
| { read } | read | 被拒绝的操作是"读取" |
| name="fn" | fn | 要访问的文件名是"fn" |
| dev="sysfs" | sysfs | 文件所在的设备是sysfs虚拟文件系统 |
| **scontext=** | u:r:system_app:s0 | 源上下文 - 发起访问的进程 |
| **tcontext=** | u:object_r:sysfs:s0 | 目标上下文 - 被访问的对象 |
| **tclass=** | file | 目标类型是"文件" |
| permissive=0 | 0 | SELinux处于强制模式(1=宽容模式) |
发生了什么?
- 进程
system_app试图读取 一个名为fn的文件 - 这个文件位于sysfs文件系统中
- SELinux策略不允许system_app域读取sysfs类型的文件
- 因此访问被拒绝
根据上面的信息,要做的就是允许system_app访问这个节点。
权限添加
虽然提供的节点是/sys/class/fn_key/fn_key_gpio/fn,但如果直接针对这个节点添加权限,你发现可能会不生效,这也是我遇到的问题,针对这个节点添加权限编译后还是无法读取。 建议先在adb中执行readlink -f /sys/class/fn_key/fn_key_gpio/fn,这条命令的作用是主要用于解析Android设备上符号链接的最终真实路径。
shell
执行命令 readlink -f /sys/class/fn_key/fn_key_gpio/fn
返回结果 /sys/devices/platform/soc/a8c000.i2c/i2c-4/4-0038/fn_key/fn_key_gpio/fn
在执行命令后可以看到节点的真实路径,我们要对这个真实的路径添加权限才行。
对符号链接和真实路径的说明
在 Linux 和 Android 中,/sys/class/... 下的大部分节点其实都是符号链接(Symbolic Link) ,指向 /sys/devices/... 下真正的硬件设备路径。
为什么这会导致 SELinux 报错?
- 标签未应用到真实文件: 当你在
file_contexts中写/sys/class/fn_key/fn_key_gpio/fn时,SELinux 工具(如restorecon)有时只会尝试给那个"快捷方式"(链接文件)打标签,而不会给它指向的"真实文件"打标签。 - 内核访问机制: 当你的 App 尝试打开这个文件时,内核会解析链接。如果真实路径
/sys/devices/platform/soc/a8c000.i2c/i2c-4/4-0038/fn_key/fn_key_gpio/fn没有任何特殊定义,它就会继承sysfs的默认标签(即u:object_r:sysfs:s0)。
之前的权限修改(即针对/sys/class/权限修改)
bash
文件 init.target.rc
新增chmod 777 /sys/class/fn_key/fn_key_gpio/fn
这个chmod 777我不知道有没有生效,ai说是没有,但我看前辈都加了,我也跟着加了
文件 file.te
新增 type sysfs_fn_key, fs_type, sysfs_type;
在file.te定义了一个标签
文件 system_app.te
新增 allow system_app sysfs_fn_key:file {read write open ioctl getattr};
运行system_app对我们定义的标签有各种权限
文件 file_contexts
新增 /sys/class/fn_key/fn_key_gpio/fn u:object_r:sysfs_fn_key:s0
将之前的节点跟定义的标签关联起来
这只是针对符号链接的权限添加,没有起作用。
修改之后的权限(即 对/sys/devices/)
这里只是修改了file_contexts,将/sys/devices/真正的节点跟我们定义的标签sysfs_fn_key相关联
bash
文件 file_contexts
旧的,但我没删 /sys/class/fn_key/fn_key_gpio/fn u:object_r:sysfs_fn_key:s0
新增 /sys/devices/platform/soc/a8c000.i2c/i2c-4/4-0038/fn_key/fn_key_gpio/fn u:object_r:sysfs_fn_key:s0
将之前的节点跟定义的标签关联起来
这样重新编译后就能生效了。
验证权限添加是否成功的一些辅助命令
查看节点所属标签
shell
adb shell命令 ls -Z /sys/class/fn_key/fn_key_gpio/fn
返回结果 u:object_r:sysfs:s0 /sys/class/fn_key/fn_key_gpio/fn
可以看到,返回了节点所属标签,仍然是sysfs,不是我们自定义的标签,没有建立起关联,因此权限没有生效。
临时关闭selinux权限验证
这个方法只在debug版本试过。有的时候添加了一个节点,没有加对应的权限,又想验证节点是否好用的,可以用以下方法临时关闭selinux权限检查。以下命令均是adb
shell
1. setenforce 0
2. chown -R system pwrdet //其中pwrdet为节点所在目录
3. chgrp -R system pwrdet //其中pwrdet为节点所在目录
说明:上面这三条命令我都是一块用,没有单独用。