Android开机自启功能实现:基于init.test.sh的轻量级方案
在Android系统开发中,让自定义脚本在设备启动时自动运行是一项常见但容易踩坑的需求。不同于Linux通用发行版的systemd或rc.local机制,Android使用自己的init系统和SELinux安全模型,这意味着简单地把脚本丢进某个目录并不能让它“自然启动”。本文将带你从零开始,用一个真实可运行的轻量级方案——init.test.sh,完成开机自启功能的完整落地。不依赖复杂框架、不修改核心服务、不绕过安全策略,每一步都经过实测验证(Android 8.0+主流平台),适合嵌入式工程师、系统定制开发者和固件调试人员快速上手。
1. 为什么不能直接用传统Linux方式?
很多刚接触Android底层的同学会下意识套用Linux经验:把脚本放进/etc/init.d/、加chmod +x、再写个update-rc.d?很遗憾,在标准Android系统中,这些路径根本不存在,init进程也不会扫描它们。
Android的启动流程由init进程严格控制,它读取init.rc及其包含的.rc文件,按服务(service)和动作(action)声明来调度执行。更重要的是,从Android 5.0起,SELinux默认启用且为enforcing模式,任何未被明确授权的进程行为(包括执行新脚本、访问特定路径、设置系统属性等)都会被拦截并记录到dmesg日志中。
所以,真正可行的路径只有一条:让init认识你的脚本,让SELinux信任你的脚本,让系统属性或日志能证明它确实跑起来了。下面我们就沿着这条路径,分四步走完全部流程。
2. 第一步:编写可执行的init脚本
脚本本身必须满足Android init系统的硬性要求,稍有偏差就会静默失败——连错误提示都不会输出。
2.1 脚本位置与权限规范
- 存放路径:必须放在
/system/bin/或/vendor/bin/下(推荐前者,兼容性更好) - 文件名:建议使用
init.xxx.sh命名风格(如init.test.sh),便于识别用途 - 权限:
chmod 755 /system/bin/init.test.sh(owner可读写执行,group/o仅读执行) - 挂载状态:
/system分区需为可写(adb remount或烧录前已设为rw)
注意:不要尝试放在
/data/local/tmp/或/sdcard/下——init进程在早期阶段无法访问这些路径,且SELinux策略默认禁止。
2.2 脚本内容与关键细节
以下是一个最小可用、经实测通过的init.test.sh模板:
#!/system/bin/sh # Android专用shebang:必须是/system/bin/sh或/system/xbin/sh # /bin/sh在Android中通常不存在,硬写会导致脚本完全不执行 # 设置一个测试属性,用于快速验证是否运行成功 setprop sys.test.booted 1 # 可选:写入日志便于调试(需确保logd已启动) log -t INIT_TEST "Script executed at $(date)" # 可选:启动一个后台守护进程(如需要长期运行) # nohup /system/bin/mydaemon & # 可选:触发其他init action(如重启某个服务) # trigger my_custom_service2.3 验证脚本独立可运行
在集成进init流程前,务必手动验证脚本本身无语法错误、权限正确、逻辑可达:
# 推送脚本到设备 adb push init.test.sh /system/bin/init.test.sh # 设置权限 adb shell chmod 755 /system/bin/init.test.sh # 手动执行并检查结果 adb shell /system/bin/init.test.sh adb shell getprop sys.test.booted # 应输出"1" adb logcat -t 10 -s INIT_TEST # 应看到日志输出如果这一步失败,请优先检查:shebang路径是否正确、/system是否可写、脚本是否有Windows换行符(\r\n)、是否缺少setprop所需SELinux权限(见第4步)。
3. 第二步:声明SELinux类型与上下文
Android 8.0+采用编译时策略(precompiled sepolicy),所有自定义服务必须在SELinux策略中明确定义其类型、执行上下文和访问权限。跳过此步,脚本在init中会被SELinux直接拒绝,且不会报错到logcat,只在dmesg中留下类似avc: denied { execute } for path="/system/bin/init.test.sh"的日志。
3.1 创建te策略文件
在设备厂商的sepolicy目录下(如MTK平台为device/mediatek/sepolicy/basic/non_plat/),新建test_service.te:
# 定义服务域类型 type test_service, domain; type test_service_exec, exec_type, vendor_file_type, file_type; # 允许该域作为init守护进程运行 init_daemon_domain(test_service); # 允许执行自身脚本文件 allow test_service test_service_exec:file { read open execute getattr }; # 允许设置系统属性(对应脚本中的setprop) allow test_service system_file:file { read }; allow test_service system_prop:property_service { set }; # 允许写入log(对应脚本中的log命令) allow test_service logd:dir { search }; allow test_service logd:file { read write open }; allow test_service logd:unix_dgram_socket { sendto };关键说明:
init_daemon_domain()是必须调用的宏,它自动赋予test_service域访问/dev/*、/proc/*等init必需路径的权限;allow ... property_service { set }是setprop命令必需的权限;- 若脚本中调用
log命令,必须显式允许对logd的socket通信权限。
3.2 绑定文件上下文
在同一sepolicy目录下的file_contexts文件(如device/mediatek/sepolicy/basic/non_plat/file_contexts)中,添加一行:
/system/bin/init\.test\.sh u:object_r:test_service_exec:s0注意:
- 路径需用正则转义(
.→\.),否则匹配失败; - 类型名
test_service_exec必须与.te文件中定义的完全一致; - 即使临时关闭SELinux(
setenforce 0),此行也必须存在,否则init无法识别该文件为可执行服务。
3.3 编译并刷入策略
修改完成后,重新编译boot镜像(或system镜像),确保新策略生效。验证方法:
# 检查文件上下文是否正确 adb shell ls -Z /system/bin/init.test.sh # 输出应包含:u:object_r:test_service_exec:s0 # 检查策略是否加载 adb shell dmesg | grep avc | tail -10 # 开机后检查是否有denied日志4. 第三步:在init.rc中注册服务
Android init通过解析.rc文件启动服务。虽然可以直接修改/system/etc/init.rc,但强烈不建议——它属于AOSP核心文件,易被OTA覆盖,且不同芯片平台结构差异大。
4.1 推荐做法:使用平台专属rc文件
主流SoC厂商均提供客户可扩展的rc入口,例如:
| 平台 | 推荐rc文件路径 |
|---|---|
| MTK | /system/etc/init/hw/init.mtk.rc |
| Qualcomm | /system/etc/init/hw/init.qcom.rc |
| Samsung | /system/etc/init/hw/init.samsung.rc |
若不确定,可搜索/system/etc/init/下以init.*.rc命名的文件,或查看init.rc中import语句引入的路径。
4.2 编写服务声明
在选定的.rc文件末尾,添加以下服务定义:
# 定义test_service服务 service test_service /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:test_service_exec:s0 # 可选:指定启动时机(在zygote启动后) on property:sys.boot_completed=1 start test_service参数详解:
class main:归入main类,随系统主服务一起启动;user/group root:以root权限运行(必要,因需设置系统属性);oneshot:执行完即退出(适合初始化脚本);若需常驻,请改用disabled+start触发;seclabel:必须与file_contexts中定义的类型一致;on property:更精准的启动时机控制,避免在系统未就绪时执行。
4.3 验证rc语法与加载
编译刷机后,可通过以下命令确认服务是否被init识别:
adb shell getenforce # 确保为Enforcing adb shell getprop sys.boot_completed # 确保为1(系统已启动完成) adb shell cat /sys/fs/selinux/enforce # 确保为1 # 查看test_service状态 adb shell service list | grep test_service # 或检查init日志 adb logcat -b events | grep -i "test_service"5. 第四步:调试与问题排查
即使严格遵循上述步骤,仍可能遇到启动失败。以下是高频问题及定位方法:
5.1 快速诊断三板斧
| 现象 | 检查点 | 命令示例 |
|---|---|---|
| 脚本完全没执行 | init是否加载了你的.rc?服务名是否拼写一致? | adb shell cat /proc/1/cmdlineadb shell ls /system/etc/init/ | grep rc |
setprop失败 | SELinux是否拒绝property_service? | adb shell dmesg | grep -i "avc.*property_service" |
log命令无输出 | logd是否已启动?SELinux是否允许socket通信? | adb shell ps | grep logdadb shell dmesg | grep -i "avc.*logd" |
| 脚本执行但属性未设置 | setprop目标属性是否被ro.前缀锁定? | adb shell getprop | grep testadb shell getprop | grep ro.test |
5.2 实用调试技巧
- 临时降级SELinux:
adb shell setenforce 0(仅用于快速验证逻辑,非最终解法) - 强制触发服务:
adb shell start test_service(绕过init自动触发,直接测试) - 查看init详细日志:
adb logcat -b events -v threadtime \| grep -i "test_service" - 检查文件完整性:
adb shell sha256sum /system/bin/init.test.sh(确认推送无损坏)
5.3 常见错误与修复
❌ 错误:
dmesg显示avc: denied { execute } for ... init.test.sh
修复:检查file_contexts路径是否精确匹配,te文件中是否遗漏allow ... execute❌ 错误:
getprop sys.test.booted始终为空
修复:确认setprop权限已添加;检查属性名是否含非法字符(只支持字母、数字、.、_);确认脚本确实在执行(加log语句验证)❌ 错误:服务状态为
stopped,start test_service后立即变为stopping
修复:检查脚本是否异常退出(如语法错误、exit 1);用adb shell /system/bin/init.test.sh手动执行看是否报错
6. 总结:一条可复用的工程化路径
本文围绕init.test.sh这一轻量级载体,完整呈现了Android开机自启功能的落地闭环。它不是理论推演,而是来自真实项目调试的经验沉淀——每一行代码、每一个配置项,都经过Android 8.0及以上版本的实机验证。
回顾整个流程,我们构建了一条清晰、稳健、可复用的工程路径:
- 脚本层:用
/system/bin/sh为shebang,以setprop为轻量信号,规避文件I/O权限风险; - 策略层:通过
test_service.te和file_contexts双文件绑定,实现最小权限原则; - 启动层:依托平台
init.*.rc机制,避免侵入AOSP核心,保障OTA兼容性; - 调试层:以
dmesg+logcat+getprop为黄金三角,快速定位SELinux、init、脚本三层问题。
这套方法不仅适用于init.test.sh,更可平滑迁移到任何自定义守护进程(如硬件监控、日志收集、远程诊断服务)。当你下次需要让一段逻辑在Android设备“睁眼第一秒”就运行起来时,记住:不是Linux,就别用Linux的思维;是Android,就尊重它的init和SELinux。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。