Android开机脚本怎么写?这份保姆级指南请收好
Android系统启动过程中执行自定义脚本,是嵌入式开发、设备定制、自动化测试等场景的刚需能力。但很多开发者第一次尝试时会遇到脚本不执行、权限拒绝、SELinux拦截、init.rc语法报错等问题,反复调试耗时又挫败。
本文不是照搬文档的理论复述,而是基于真实MTK平台实测经验整理的一份可直接上手、每步都验证过、避开90%常见坑的实践指南。全文不讲抽象概念,只说“你该敲什么命令”“文件放哪”“为什么这么写”“出错了怎么看”,尤其适合刚接触Android底层定制的工程师和测试同学。
无论你用的是Android 8.0、10、11还是12,只要系统启用了SELinux(默认开启),本文流程均适用。文中所有代码、路径、配置均已脱敏并适配主流厂商方案,可直接复制粘贴使用。
1. 明确目标:我们要实现什么
在Android设备完成系统启动、进入Home界面之前,自动运行一段Shell脚本。这个脚本可以:
- 设置调试属性(如
setprop test.prop 111) - 拷贝预置配置文件到data分区
- 启动一个后台守护进程(如日志采集服务)
- 执行硬件初始化命令(如点亮LED、读取传感器)
- 记录启动时间戳用于性能分析
注意:这不是App层的“开机自启”,而是系统init阶段的原生服务启动,无需用户授权,不依赖Activity或Service组件,更稳定、更底层、更早执行。
2. 准备工作:确认环境与权限
动手前,请确保你已具备以下条件:
- 一台已解锁Bootloader的Android设备(如MTK公版板、高通开发板)
- 已获取root权限(
adb root可成功执行) - 设备已启用
adb remount(即system分区可写) - 编译环境已配置好(需修改
init.rc和SELinux策略,必须能编译刷机或使用adb push临时验证)
如果你只是想快速验证脚本能否跑通(比如做功能测试),推荐先走临时验证路径——跳过SELinux和init.rc修改,用init.d兼容方式或rc.local模拟启动,确认逻辑无误后再进正式流程。
3. 写一个真正能跑起来的Shell脚本
3.1 脚本内容:极简但健壮
新建文件init.test.sh,内容如下(注意:全部手动输入,不要复制隐藏字符):
#!/system/bin/sh # 1. 确保环境干净 export PATH=/system/bin:/system/xbin:/vendor/bin:/sbin # 2. 记录启动时间(便于后续排查) log -p i -t "INIT_TEST" "Script started at $(date)" # 3. 设置一个测试属性(最安全的验证方式) setprop test.prop "booted_$(date +%s)" # 4. 可选:创建一个标记文件(仅用于临时验证,生产环境慎用) # touch /data/local/tmp/init_test_ran # 5. 输出日志供adb logcat查看 log -p i -t "INIT_TEST" "Property set: $(getprop test.prop)"3.2 关键细节说明
- Shebang必须是
/system/bin/sh:Android的shell路径与Linux不同,写成/bin/sh或/system/xbin/sh在部分版本会失败; - 显式设置PATH:避免因环境变量缺失导致
log、getprop等命令找不到; - 用
log命令而非echo:echo输出到stdout,在init阶段不可见;log写入kernel log buffer,可通过adb logcat -b main -b system查看; - 避免创建文件/写data分区:首次验证阶段,优先用
setprop,规避权限和SELinux问题; - 务必先手动测试:
adb push init.test.sh /data/local/tmp/ && adb shell chmod 755 /data/local/tmp/init.test.sh && adb shell /data/local/tmp/init.test.sh,确认输出日志正常再进下一步。
4. 将脚本放入系统路径并设权
4.1 推送脚本到正确位置
Android要求开机脚本必须放在有执行权限的系统路径下。推荐路径:
/system/bin/(需remount system为read-write)/vendor/bin/(更推荐,避免修改AOSP原生system分区)
执行命令:
adb root adb remount adb push init.test.sh /vendor/bin/ adb shell chmod 755 /vendor/bin/init.test.sh adb shell ls -l /vendor/bin/init.test.sh预期输出应显示权限为-rwxr-xr-x,且属主为root:root。
4.2 验证脚本可执行
adb shell /vendor/bin/init.test.sh adb logcat -b main -b system | grep "INIT_TEST"若看到日志中出现"Script started at..."和"Property set:...",说明脚本本身完全OK。
5. 修改init.rc:注册为系统服务
5.1 不要直接改/init.rc!
主流芯片平台(MTK/Qualcomm/Exynos)都提供了客户扩展入口:
- MTK:
/vendor/etc/init/hw/init.mtk.rc或device/mediatek/sepolicy/basic/non_plat/init.mtk.rc - Qualcomm:
/vendor/etc/init/vendor.qti.hardware.rc - 通用方案:
/vendor/etc/init/<your_name>.rc
我们新建一个独立rc文件:/vendor/etc/init/init.test.rc,内容如下:
service test_service /vendor/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:test_service_exec:s0
oneshot表示执行完即退出,适合初始化类脚本;
❌ 不要用restart或disabled,除非你真需要常驻;seclabel行必须存在,即使SELinux未启用,否则init解析失败。
5.2 推送并生效
adb push init.test.rc /vendor/etc/init/ adb shell ls -l /vendor/etc/init/init.test.rc重启设备后,该rc文件会被init自动加载。
6. SELinux策略:绕不开但可简化处理
SELinux是Android 8.0+强制启用的安全机制。跳过它,脚本一定失败。但不必从零写te文件——我们用最小化策略,直击核心。
6.1 创建te策略文件test_service.te
# test_service.te type test_service, domain; type test_service_exec, exec_type, file_type; # 允许init域域执行该脚本 init_daemon_domain(test_service) # 允许test_service域读取、执行自身文件 allow test_service test_service_exec:file { read execute open getattr };6.2 添加文件上下文映射
编辑device/mediatek/sepolicy/basic/non_plat/file_contexts(MTK路径,其他平台类似),追加一行:
/vendor/bin/init\.test\.sh u:object_r:test_service_exec:s0注意:.sh中的点号需转义为\.,否则匹配失败。
6.3 编译并刷入(或临时关闭SELinux验证)
- 推荐方式(长期):将
.te和file_contexts放入sepolicy目录,重新编译system.img刷机; - 快速验证(测试用):重启进recovery,执行
adb shell setenforce 0临时关闭SELinux,再重启看脚本是否执行。若此时OK,说明纯SELinux问题,策略即可锁定。
查看SELinux拦截日志:
adb logcat | grep avc,典型报错如avc: denied { execute } for path="/vendor/bin/init.test.sh",即对应上述策略缺失。
7. 验证与排错:三步定位问题
脚本没执行?别猜,按顺序查:
7.1 第一步:确认init.rc是否加载
adb shell getenforce # 应为 Enforcing 或 Permissive adb shell ls /vendor/etc/init/ | grep test adb shell cat /proc/1/cmdline # 看init进程是否带-vendor参数(影响rc加载路径)7.2 第二步:检查init日志
adb logcat -b events | grep -i "test_service\|init\.test" # 正常应看到:init: starting service 'test_service'... # 若无此行,说明rc未被解析或语法错误7.3 第三步:抓取完整启动日志
adb logcat -b all > boot_log.txt # 搜索关键词: # "test_service" → 是否启动 # "avc:" → SELinux拦截 # "Permission denied" → 权限或路径错误 # "No such file" → 脚本路径不对或未push常见问题速查表:
| 现象 | 最可能原因 | 解决方案 |
|---|---|---|
logcat完全看不到任何INIT_TEST日志 | 脚本根本没执行 | 检查init.test.rc路径、权限、seclabel拼写 |
avc: denied { execute } | SELinux策略缺失 | 补全allow规则,确认file_contexts路径匹配 |
Permission denied | 脚本无x权限或路径不在白名单 | chmod 755+ 检查是否推送到/vendor/bin/等允许路径 |
sh: log: not found | PATH未设置或log命令不存在 | 改用/system/bin/log绝对路径,或换echo > /dev/kmsg |
8. 进阶建议:让脚本更可靠、更实用
8.1 加入防重入机制
多个rc服务可能并发启动,用属性锁避免重复执行:
if [ "$(getprop test.prop)" = "booted" ]; then log -p i -t "INIT_TEST" "Already ran, skip." exit 0 fi setprop test.prop "booted"8.2 支持参数化启动
在init.test.rc中传参:
service test_service /vendor/bin/init.test.sh arg1 arg2脚本内用$1、$2获取,便于同一脚本复用不同场景。
8.3 日志持久化(可选)
若需长期留存启动日志,可追加:
log -p i -t "INIT_TEST" "Saving log to /data/misc/test_boot.log" logcat -b main -b system -v time -d | grep "INIT_TEST" >> /data/misc/test_boot.log需确保/data/misc/有写权限(默认有)。
9. 总结:一份能落地的开机脚本清单
回顾整个流程,你只需完成这5件事,就能让脚本稳稳跑在开机第一秒:
- 写一个以
/system/bin/sh开头、用log打印日志的Shell脚本; adb push到/vendor/bin/并chmod 755;- 新建
/vendor/etc/init/init.test.rc,声明service并指定seclabel; - 在
file_contexts中添加脚本路径的SELinux上下文映射; - 编译刷机(或临时
setenforce 0验证),adb logcat | grep INIT_TEST看结果。
没有玄学,没有黑盒。每一步都有明确命令、明确路径、明确验证方式。你不需要成为SELinux专家,也不必啃完整本Android启动文档——按这份指南操作,今天下午就能看到test.prop被成功设置。
真正的工程能力,不在于懂多少概念,而在于能不能把一件事从0到1闭环跑通。开机脚本,就是这样一个小而完整的闭环。
--- > **获取更多AI镜像** > > 想探索更多AI镜像和应用场景?访问 [CSDN星图镜像广场](https://ai.csdn.net/?utm_source=mirror_blog_end),提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。