Android开机启动权限问题全解,少走弯路
在Android系统开发中,实现自定义服务或脚本的开机自动运行,看似简单,实则暗藏大量权限陷阱。很多开发者在调试时反复遇到“脚本不执行”“init报错”“selinux拒绝访问”“属性设置失败”等问题,最终耗费数天时间排查,却只是因为一个路径写错、一行te规则遗漏,或一个seclabel配置位置不对。本文不讲抽象理论,不堆砌SELinux术语,而是基于真实工程实践,把Android 8.0及以上版本(含主流MTK、高通平台)中开机启动shell脚本的完整链路拆解清楚——从脚本编写、SELinux策略配置、init.rc集成,到常见错误定位与绕过技巧,全部用你能立刻验证的方式呈现。
你不需要提前掌握SELinux语法,也不必翻遍AOSP源码。只要按本文步骤操作,就能让自己的init.test.sh在设备上稳定跑起来。文中所有代码、路径、配置均已在真机环境实测通过,关键细节全部标注说明,帮你避开90%以上的典型坑。
1. 开机启动的本质:不是“运行脚本”,而是“被init托管”
很多人误以为“开机启动”就是让系统在某个时刻执行一条shell命令。实际上,在Android中,一切开机启动行为都必须由init进程统一调度和管控。init是用户空间第一个进程(PID=1),它读取init.rc及其包含的rc文件,按顺序解析service、on、import等指令,并根据SELinux策略决定是否允许该服务启动。
这意味着:
- 你写的脚本本身没有“开机启动能力”,它只是个普通文件;
- 真正具备启动资格的是被init声明为service的实体;
- init只信任自己加载的、且SELinux上下文合法的可执行文件;
- 即使关闭了SELinux(setenforce 0),
file_contexts中的类型映射仍生效,缺失映射会导致init直接跳过该service。
所以,解决问题的第一步,不是改脚本,而是理解init如何“认出”你的脚本。
2. 脚本编写:三处细节决定成败
2.1 解释器路径必须精准匹配系统实际路径
Android的shell解释器路径与Linux发行版不同。常见错误是直接照搬#!/bin/sh,结果脚本静默失败。
正确写法(任选其一,推荐第一种):
#!/system/bin/sh或
#!/system/xbin/sh验证方法:adb shell后执行
which sh或ls -l /system/bin/sh,确认实际路径。
❌ 错误示例:#!/bin/sh、#!/usr/bin/sh、#!/system/bin/bash(Android默认不带bash)。
2.2 脚本内容建议以“设属性”为第一动作
初学者常在脚本里直接创建文件、写日志、调用其他二进制程序,这极易因权限/路径/依赖问题失败。最稳妥的验证方式是:先设置一个系统属性,再通过adb验证是否生效。
示例脚本init.test.sh(保存为UTF-8无BOM格式):
#!/system/bin/sh # 开机启动测试脚本 —— 仅设置属性,最小化干扰 # 设置一个唯一标识属性,便于快速验证 setprop sys.boot.test 1 # 可选:记录时间戳(需确保/data可写) if [ -w /data ]; then echo "test started at $(date)" >> /data/test_boot.log fi # 退出前可加延时,方便串口抓log(调试阶段用) # sleep 1手动验证流程(务必先做):
adb push init.test.sh /data/local/tmp/adb shell chmod 755 /data/local/tmp/init.test.shadb shell /data/local/tmp/init.test.shadb shell getprop sys.boot.test→ 应输出1
若此步失败,说明脚本本身有问题,无需继续往下配置。
2.3 文件权限与存放位置有严格要求
- 权限必须为755(owner可读写执行,group/other可读执行);
- 存放路径推荐
/system/bin/或/vendor/bin/(需对应修改file_contexts); /data/下的脚本无法被init直接调用(init启动时/data可能未挂载或SELinux域受限);/system/etc/init.d/在Android 8.0+已废弃,不再被init扫描。
3. SELinux策略配置:四步缺一不可
Android 8.0起全面启用强制SELinux模式,即使setenforce 0临时关闭,init在解析rc文件时仍会校验file_contexts映射。以下四步必须全部完成,顺序不可颠倒。
3.1 定义服务类型与执行文件类型(.te文件)
新建test_service.te(路径建议:device/mediatek/sepolicy/basic/non_plat/或对应芯片平台目录):
# 定义服务域(domain) type test_service, coredomain; # 定义可执行文件类型(file type) type test_service_exec, exec_type, vendor_file_type, file_type; # 将test_service域关联到init守护进程模板(关键!) init_daemon_domain(test_service); # 允许test_service域执行test_service_exec类型的文件 allow test_service test_service_exec:file { read open getattr execute };关键点说明:
init_daemon_domain()是核心宏,它自动赋予test_service域访问/dev/,/proc/,/sys/等基础资源的权限;allow ... execute规则必须显式声明,否则即使文件上下文正确,init也会拒绝执行;- 不要取消注释
permissive test_service;,它仅用于调试,上线必须删除。
3.2 映射文件路径到SELinux类型(file_contexts)
在device/mediatek/sepolicy/basic/non_plat/file_contexts中添加:
/system/bin/init\.test\.sh u:object_r:test_service_exec:s0注意事项:
- 路径必须用正则转义(
.写成\.),否则匹配失败;- 若脚本放在
/vendor/bin/,则写为/vendor/bin/init\.test\.sh;- 行尾不能有空格或tab,否则编译时报错;
- 此行必须位于
non_plat目录下,plat目录的file_contexts在编译时会被覆盖。
3.3 在init.rc中声明service(非直接修改init.rc)
不要修改system/core/rootdir/init.rc主文件。应在芯片厂商提供的扩展rc文件中添加,例如:
- MTK平台:
device/mediatek/common/init/init.main.rc - 高通平台:
device/qcom/common/rootdir/etc/init.qcom.rc
添加内容如下(注意缩进为4个空格):
service test_service /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:test_service_exec:s0字段说明:
oneshot:执行完即退出,适合初始化脚本;若需常驻,请改用disabled+手动start;seclabel:必须与.te中定义的test_service_exec类型完全一致;class main:确保在main类服务启动阶段运行(早于late_start)。
3.4 编译并刷入镜像
完成上述修改后,执行完整编译(非增量编译):
m -j32 # 或针对sepolicy单独编译(调试快) m sepolicy生成的boot.img或system.img需完整刷入设备。仅push文件无法生效,因为SELinux策略在内核启动时已加载。
4. 常见错误与精准定位方法
当脚本未执行时,不要盲目猜测。按以下顺序逐项检查,90%问题可5分钟内定位。
4.1 检查init是否识别到service
adb shell getenforce # 确认为Enforcing adb shell dmesg | grep -i "test_service" # 查看kernel log adb shell cat /proc/1/cmdline # 确认init进程已加载对应rc文件若dmesg无输出,说明init根本没解析到该service,重点检查:
- rc文件是否被
import(查看init.rc中是否有import /system/etc/init/xxx.rc); - service名称是否含非法字符(只允许字母、数字、下划线);
seclabel值是否拼写错误。
4.2 检查SELinux拒绝日志(最常用)
adb shell dmesg | grep avc # 或更精准 adb shell dmesg | grep -i "avc.*test_service"典型错误及修复:
| 错误日志片段 | 原因 | 修复方案 |
|---|---|---|
avc: denied { execute } for path="/system/bin/init.test.sh" | 缺少allow test_service test_service_exec:file execute规则 | 在.te中补全该allow规则 |
avc: denied { entrypoint } for path="/system/bin/init.test.sh" | file_contexts未映射或映射错误 | 检查file_contexts正则、路径、编译是否生效 |
avc: denied { setprop } for property="sys.boot.test" | test_service域无setprop权限 | 添加allow test_service system_prop:property_service set; |
4.3 检查脚本执行时的实时状态
在脚本开头加入日志输出(需确保/data可写):
#!/system/bin/sh echo "[$(date)] test_service start" >> /data/test_debug.log setprop sys.boot.test 1 echo "[$(date)] setprop done" >> /data/test_debug.log然后:
adb shell tail -f /data/test_debug.log # 重启后观察是否有输出若文件为空,说明init根本未调用该脚本;若有“start”但无“done”,说明setprop失败(可能是属性未在property_contexts中声明)。
5. 进阶技巧:绕过复杂配置的临时方案
在紧急调试或原型验证阶段,可采用以下安全、可控的简化方案,避免陷入SELinux配置泥潭。
5.1 利用已有的、权限宽松的服务域
Android系统自带shell域权限较宽。可将脚本映射到shell_exec类型(需谨慎):
# file_contexts /system/bin/init\.test\.sh u:object_r:shell_exec:s0并在rc中指定:
seclabel u:object_r:shell_exec:s0注意:此方案仅限调试,禁止用于量产固件,因
shell_exec拥有过高权限,存在安全风险。
5.2 通过init trigger间接启动(规避service声明)
在init.rc中添加trigger:
on property:sys.boot_completed=1 start test_service_helper service test_service_helper /system/bin/sh class main user root group root oneshot seclabel u:object_r:shell_exec:s0 exec - /system/bin/sh -c "setprop sys.boot.test 1"此方式利用系统启动完成事件触发,无需自定义domain,适合快速验证逻辑。
6. 总结:一张表理清所有关键点
| 环节 | 必须检查项 | 常见错误 | 验证命令 |
|---|---|---|---|
| 脚本本身 | 解释器路径、权限755、手动可执行 | #!/bin/sh、权限644、存放在/data/ | adb shell /system/bin/init.test.sh && adb shell getprop sys.boot.test |
| SELinux类型 | .te中init_daemon_domain+allow execute、file_contexts正则匹配 | 缺少execute规则、.未转义、路径写错 | adb shell ls -Z /system/bin/init.test.sh(应显示test_service_exec) |
| init.rc集成 | seclabel值与te类型一致、rc文件被import、缩进正确 | seclabel拼写错误、rc未import、使用tab缩进 | adb shell getenforce && adb shell dmesg | grep test_service |
| 编译刷机 | 修改后执行完整编译、刷入system/boot分区 | 仅push文件、增量编译未更新sepolicy | adb shell cat /proc/1/cmdline | grep xxx.rc |
真正高效的Android系统开发,不在于记住多少命令,而在于建立一套可复现、可验证、可回溯的问题排查路径。本文所列每一步,都对应一个明确的验证动作和失败反馈。当你下次再遇到“开机脚本不运行”时,不必再从头搜索零散博客,只需按这张表逐项核对,就能快速锁定根因。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。