1. 项目背景与核心需求
最近在调试一块基于RK3588的开发板时,遇到了一个典型问题:Android 12系统下某些硬件设备节点的权限设置不符合我们的应用需求。比如摄像头接口默认只有system组可读写,而我们的应用运行在普通用户权限下需要访问这些设备。这种场景在嵌入式开发中非常常见——当硬件方案确定后,软件层面需要根据实际业务需求调整Linux内核的设备权限配置。
RK3588作为瑞芯微旗舰级处理器,在智能座舱、边缘计算盒子等场景广泛应用。这类产品往往需要深度定制Android系统,其中设备权限管理就是基础又关键的环节。与手机不同,嵌入式设备的硬件接口开放程度更高,开发者常需要调整默认权限配置来满足特定外设的访问需求。
2. Android权限体系解析
2.1 Linux内核层权限基础
Android的权限体系建立在Linux的UGO(User/Group/Other)权限模型上。通过ls -l /dev可以看到类似这样的输出:
code复制crw-rw---- 1 system camera 238, 0 2023-01-01 10:00 video0
其中:
- 第一个
rw-表示所有者(system)有读写权限 - 第二个
rw-表示所属组(camera)有读写权限 - 第三个
---表示其他用户无权限
2.2 Android特有机制
在标准Linux基础上,Android增加了:
- SELinux:强制访问控制机制,即使传统权限允许,SELinux策略也可能禁止访问
- 硬件抽象层(HAL):部分设备通过HAL提供服务而非直接设备节点
- 权限组映射:Android将Linux组ID与权限组(如android.permission.CAMERA)动态关联
3. RK3588设备树与权限配置
3.1 设备节点创建机制
RK3588的设备节点主要通过以下方式生成:
- 设备树(Device Tree):定义硬件资源与驱动绑定
- 内核驱动:通过
class_create()和device_create()动态创建设备 - ueventd:在Android启动阶段根据规则创建设备节点
以摄像头为例,内核驱动中通常会这样创建:
c复制video_dev = device_create(video_class, NULL, devno, NULL, "video%d", nr);
3.2 永久性权限修改方案
方案1:修改ueventd.rc(推荐)
路径:/system/core/rootdir/ueventd.rc 或 vendor/ueventd.rc
添加规则示例:
code复制/dev/video0 0660 system camera
需要重新编译bootimage生效
方案2:运行时chmod(临时方案)
通过adb或init.rc脚本执行:
bash复制chmod 666 /dev/video0
chown system:camera /dev/video0
缺点是设备重启后失效
方案3:修改内核驱动
在驱动代码中直接指定权限:
c复制device_create(video_class, NULL, devno, NULL, "video%d", nr);
// 改为
struct device *dev = device_create(video_class, NULL, devno, NULL, "video%d", nr);
if (IS_ERR(dev)) {
pr_err("Failed to create device\n");
} else {
dev->mode = 0660; // 直接设置权限
}
需要重新编译内核模块
4. SELinux策略调整
即使修改了传统权限,SELinux仍可能阻止访问。需要检查avc拒绝日志:
bash复制adb shell dmesg | grep avc
典型输出:
code复制avc: denied { read write } for pid=123 comm="cameraserver"
name="video0" dev="tmpfs" ino=1234 scontext=u:r:cameraserver:s0
tcontext=u:object_r:video_device:s0 tclass=chr_file permissive=0
对应修改方法:
- 找到device对应的SELinux类型:
bash复制ls -Z /dev/video0
- 在
device/rockchip/common/sepolicy中添加规则:
code复制allow cameraserver video_device:chr_file { read write open };
5. 完整实现流程
5.1 环境准备
- 已编译的Android 12源码环境
- RK3588开发板与调试工具
- 目标设备的准确路径(如/dev/video0)
5.2 操作步骤
-
确认当前权限设置:
bash复制adb shell ls -l /dev/video* -
修改ueventd.rc文件:
bash复制vim system/core/rootdir/ueventd.rc # 添加规则 /dev/video0 0660 system camera -
更新SELinux策略(如需):
bash复制vim device/rockchip/common/sepolicy/cameraserver.te # 添加对应allow规则 -
重新编译并烧写:
bash复制source build/envsetup.sh lunch rk3588-userdebug make bootimage -j8 fastboot flash boot out/target/product/rk3588/boot.img -
验证修改:
bash复制adb shell ls -l /dev/video0 adb shell getenforce adb shell dmesg | grep avc
6. 常见问题排查
6.1 权限修改未生效
- 检查ueventd.rc是否被正确打包到boot.img
- 确认没有更高优先级的ueventd.override文件覆盖规则
- 检查selinux是否处于enforcing模式
6.2 应用仍无法访问设备
- 确认应用进程的selinux上下文:
bash复制
adb shell ps -Z | grep <your_app> - 检查应用是否声明了正确的Android权限:
xml复制<uses-permission android:name="android.permission.CAMERA" />
6.3 设备节点路径变化
某些驱动会动态分配设备号,建议:
- 使用静态设备名:
c复制// 在驱动中指定设备名 device_create(..., "front_camera"); - 通过sysfs查找稳定路径:
bash复制ls /sys/class/video4linux/
7. 高级技巧与优化建议
7.1 权限动态管理
对于需要灵活控制的场景,可以:
- 编写init.rc服务:
rc复制service set_cam_perm /system/bin/sh /vendor/etc/set_cam_perm.sh class main user root oneshot - 在脚本中实现条件判断:
bash复制#!/vendor/bin/sh if [ "$(getprop ro.boot.mode)" == "factory" ]; then chmod 666 /dev/video0 else chmod 660 /dev/video0 fi
7.2 权限与功能解耦
建议通过HAL层封装设备访问:
- 实现一个v4l2 HAL服务
- 应用通过Binder调用HAL接口
- HAL服务集中管理设备权限
7.3 调试技巧
- 实时监控权限变更:
bash复制
adb shell inotifywait -m /dev -e create,modify,attrib | grep video - 检查所有ueventd规则:
bash复制adb shell getprop | grep ueventd adb shell cat /{system,vendor,odm}/etc/ueventd*.rc
8. 安全注意事项
- 最小权限原则:不要随意设置为666,精确控制哪些进程需要访问
- SELinux审核:所有新加的allow规则都应经过安全评估
- 权限继承风险:某些驱动会创建多个相关设备节点,需要全部检查
- 兼容性测试:修改后需测试:
- 普通用户场景
- 恢复出厂设置
- OTA升级流程
在RK3588这类高性能平台上,合理的权限配置不仅能满足功能需求,还能有效提升系统安全性。实际项目中,建议建立设备权限矩阵文档,记录每个设备的:
- 默认权限
- 修改原因
- 关联进程
- 测试用例
这种规范化管理在团队协作和长期维护中尤为重要。