小白也能懂的Android开机脚本部署,保姆级教程
你是不是也遇到过这样的问题:
想让Android设备一开机就自动执行某个任务——比如备份日志、启动监控服务、初始化硬件参数,甚至只是简单地打个日志确认系统已就绪?但一搜“Android开机启动”,出来的全是init.rc、SELinux、te文件、file_contexts……每个词都像天书,点开几篇博客,还没看到效果,就已经被权限报错、编译失败、selinux拒绝访问劝退了?
别急。这篇教程就是为你写的。
它不假设你熟悉AOSP编译流程,不要求你会写SELinux策略,也不需要你有串口调试线或root过的工程机。我们用最贴近真实开发环境的方式,从一个能跑通的最小闭环开始,手把手带你把第一个开机脚本稳稳跑起来。过程中每一步为什么这么做、哪里容易出错、怎么快速验证,都会说清楚。
全文没有黑话,不堆术语,所有命令和配置都经过实测(基于Android 8.0+主流厂商平台),关键步骤附截图逻辑示意(文字描述版),代码可直接复制粘贴,改两处路径就能用。
准备好了吗?咱们现在就开始。
1. 先搞明白:Android开机脚本到底在哪个环节运行
在动手写代码前,得先知道“开机脚本”不是Linux里那种随便放/etc/rc.local就能跑的东西。Android有一套自己的初始化机制,核心是init进程——它是系统中第一个用户态进程,负责解析init.rc系列脚本,拉起关键服务。
而我们要插入的,就是这个流程里的一个自定义服务(service)。它会在init完成基础初始化后、系统UI启动前,以指定用户身份(通常是root)执行你的shell脚本。
这个过程可以简化为四个必须连通的环节:
- 脚本本身:一个符合Android shell规范的
.sh文件 - 可执行权限与位置:放在
/system/bin/或/vendor/bin/下,并有正确SELinux上下文 - 服务声明:在
init.xxx.rc中定义service块,告诉init“这个脚本要什么时候、以谁的身份跑” - SELinux放行:让
init进程有权限去执行这个脚本(这是90%新手卡住的地方)
这四步缺一不可,但顺序可以优化:我们先做最易验证的——写脚本并手动运行,再逐步接入开机流程。
2. 第一步:写出一个真正能跑通的开机脚本
2.1 脚本内容:极简但健壮
新建一个文件,命名为init.test.sh,内容如下:
#!/system/bin/sh # 这是Android专用shebang,千万别写成 #!/bin/sh 或 #!/usr/bin/env sh # 否则在大多数Android设备上会直接报错:not found # 开头加一句日志,方便后续确认是否执行 log -p i -t "INIT_TEST" "Script started at $(date)" # 设置一个系统属性,这是最安全、最轻量的验证方式 # 不涉及文件读写、不依赖外部路径、不会因权限失败 setprop test.boot.ok 1 # 可选:再写一条日志到dmesg(内核日志),双重确认 echo "[INIT_TEST] boot script executed" > /dev/kmsg # 结束 exit 0为什么推荐用
setprop而不是touch或echo > /data/test.log?
因为setprop是系统级命令,无需额外权限,只要脚本本身能执行,就一定能成功;而写文件极易因/data未挂载、目录不存在、权限不足导致静默失败,让你误以为脚本没跑。
2.2 手动验证:Push到手机,立刻看效果
在电脑端执行(需已开启ADB调试):
# 将脚本推送到/system/bin/(需要remount) adb root adb remount 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 test.boot.ok # 应输出 1 # 查看日志 adb logcat -s INIT_TEST:I如果看到Script started at ...和test.boot.ok = 1,恭喜,你的脚本已经通过第一关——它本身是合法、可执行、无副作用的。
注意:如果你的设备是user版本(非userdebug),
adb remount可能失败。此时可临时用/data/local/tmp/替代/system/bin/测试(后续开机阶段仍需放回/system或/vendor)。
3. 第二步:让init认识它——在init.rc中声明服务
3.1 找对位置:别硬改/system/etc/init.rc
直接修改init.rc风险高、易冲突,且不同厂商路径差异大。更稳妥的做法是:使用厂商预留的扩展入口。
绝大多数Android设备(高通、MTK、紫光展锐等)都会在/system/etc/init/或/vendor/etc/init/下提供独立的.rc文件,例如:
init.mtk.rc(MTK平台)init.qcom.rc(高通平台)init.vendor.rc(通用vendor层)
你不需要自己找源码,只需在设备上执行:
adb shell ls /system/etc/init/*.rc /vendor/etc/init/*.rc 2>/dev/null找到一个存在且看起来“客户可定制”的文件(比如init.vendor.rc)。如果没有,也可以新建一个init.test.rc放入/vendor/etc/init/(需确保该目录在init启动时被include)。
3.2 写入服务定义
在选定的.rc文件末尾,添加以下内容:
# 开机脚本服务:test_boot service test_boot /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:test_service_exec:s0字段说明:
class main:表示该服务属于main类,会在init启动main类服务时被拉起(时机早于late_start类)oneshot:执行完即退出,不常驻(适合初始化类脚本)seclabel:这是SELinux上下文声明,先照抄,下一步我们会配齐它
保存后,不需要重启整机。你可以用以下命令触发init重新加载配置(Android 8.0+支持):
adb shell stop adb shell start然后检查服务是否已注册:
adb shell getenforce # 确认SELinux是Enforcing还是Permissive adb shell ls -Z /system/bin/init.test.sh # 查看当前SELinux上下文(应为u:object_r:shell_exec:s0,待修正) adb shell getprop test.boot.ok # 此时应仍为空,因为服务还没真正运行4. 第三步:绕过SELinux拦路虎——最小化策略配置
4.1 SELinux不是障碍,而是开关
很多教程把SELinux讲得神乎其技,其实对开机脚本而言,它就干一件事:判断“谁”(source)能不能对“什么”(target)执行“哪种操作”(action)。
我们的场景非常明确:
- source:
init进程(类型是init) - target:你的脚本文件(我们将赋予它新类型
test_service_exec) - action:
execute(执行)
所以只需两条规则:
- 告诉系统:
/system/bin/init.test.sh这个文件属于test_service_exec类型 - 告诉系统:
init进程可以executetest_service_exec类型的文件
4.2 实操:三处文件,改完即生效(无需编译)
(1)声明文件类型:file_contexts
路径:/vendor/etc/selinux/plat_file_contexts(或/system/etc/selinux/plat_file_contexts,优先查vendor)
在文件末尾添加一行:
/system/bin/init\.test\.sh u:object_r:test_service_exec:s0注意:
.需转义,否则会被当通配符;路径必须完全匹配。
(2)定义类型与域:test_service.te
新建一个文件test_service.te,内容仅需两行:
type test_service_exec, file_type, vendor_file_type; allow init test_service_exec:file { execute read open getattr };解释:
- 第一行:定义新类型
test_service_exec,并声明它是file_type(普通文件)和vendor_file_type(允许放在vendor分区)- 第二行:允许
init进程对这类文件执行execute/read/open/getattr(基本文件操作)
(3)加载策略(关键!免编译方案)
Android 8.0+ 支持运行时加载SELinux策略。将上面的test_service.te编译为二进制(需checkpolicy工具),但更简单的方法是:直接用sepolicy-inject注入(需设备已root或有adb shell root权限):
# 下载预编译的 sepolicy-inject(GitHub可搜) adb push sepolicy-inject /data/local/tmp/ adb shell chmod 755 /data/local/tmp/sepolicy-inject # 注入规则(一行命令搞定) adb shell "/data/local/tmp/sepolicy-inject -s init -t test_service_exec -c file -p execute -l"执行后,立即生效,无需重启。
验证是否成功:
adb shell ls -Z /system/bin/init.test.sh # 应显示 u:object_r:test_service_exec:s0 adb shell dmesg | grep avc # 若无新avc denied日志,说明策略已覆盖5. 第四步:开机实测与排错指南
5.1 终极验证:重启,看结果
adb reboot # 等待设备完全启动后 adb wait-for-device adb shell getprop test.boot.ok # 应输出 1 adb logcat -s INIT_TEST:I | head -5 # 应看到启动日志如果一切正常,你已经拥有了一个真正可用的开机脚本框架。
5.2 常见问题速查表
| 现象 | 最可能原因 | 快速解决 |
|---|---|---|
getprop test.boot.ok无输出 | 脚本根本没执行 | 检查init.xxx.rc是否被正确include;用`adb shell ps |
dmesg或logcat报avc: denied { execute } | SELinux策略未生效 | 重新执行sepolicy-inject;确认file_contexts路径和正则正确;检查ls -Z输出 |
脚本执行但setprop失败 | init进程无set_prop权限 | 在test_service.te中追加:allow init property_socket:sock_file write;+allow init self:process setpgid;(极少需) |
init.test.sh: not found | shebang错误或脚本损坏 | 确认首行是#!/system/bin/sh;用adb shell file /system/bin/init.test.sh检查是否为text文件;避免Windows换行符 |
提示:所有修改(
.rc、file_contexts)都可以通过adb push热更新,无需刷机。策略注入也是运行时生效。这意味着你可以在10分钟内完成从编写到验证的完整闭环。
6. 进阶建议:让脚本更可靠、更实用
6.1 加一层保险:检测系统就绪状态
有些操作(如访问/data分区、调用am命令)需等待zygote或surfaceflinger就绪。可在脚本开头加等待逻辑:
# 等待property服务就绪(通常最早可用) while [ "$(getprop sys.boot_completed)" != "1" ]; do sleep 1 done # 等待zygote启动(可选) while [ "$(getprop init.svc.zygote)" != "running" ]; do sleep 0.5 done6.2 日志集中管理:统一打到logcat
避免分散在dmesg/logcat//data/log/多个地方。全部走log命令:
log -p i -t "MY_BOOT" "Step 1: Hardware init..." log -p w -t "MY_BOOT" "Warning: Sensor not ready" log -p e -t "MY_BOOT" "Error: Failed to load config"6.3 安全边界:限制脚本能力
生产环境切勿在开机脚本中执行su、mount -o rw,remount等高危操作。如必须,应:
- 使用
runas切换到最小权限用户 - 对输入参数做严格校验(避免命令注入)
- 所有外部文件路径用绝对路径,不依赖
$PATH
7. 总结:你已掌握Android开机脚本的核心脉络
回顾一下,我们完成了什么:
- 写出一个零依赖、易验证的shell脚本,用
setprop规避权限陷阱 - 在
init.xxx.rc中正确定义服务,明确class、user、oneshot语义 - 用三行配置(
file_contexts+test_service.te+sepolicy-inject)绕过SELinux,全程免编译 - 掌握开机实测与排错方法论,不再被
avc denied吓退 - 获得一套可复用的工程化模板:日志规范、等待机制、安全边界
这不是一个“理论上可行”的Demo,而是一个你明天就能用在项目中的最小可行方案。后续无论你要启动守护进程、预加载证书、还是初始化AI推理引擎,底层机制都是一致的——只是脚本内容和SELinux策略细节不同而已。
真正的Android系统定制,往往就始于这样一个小小的init.test.sh。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。