news 2026/3/26 15:08:38

新手必看!Android开机启动脚本避坑指南与实测记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
新手必看!Android开机启动脚本避坑指南与实测记录

新手必看!Android开机启动脚本避坑指南与实测记录

你是不是也遇到过这样的问题:写好了开机启动脚本,push进设备手动执行一切正常,可一重启就完全没反应?adb shell里查不到进程,logcat里翻遍也找不到日志,selinux报错信息还藏在dmesg深处……别急,这不是你代码写错了,而是Android系统启动机制和权限管控比Linux桌面环境严格得多。

本文不是照搬官方文档的复读机,也不是泛泛而谈的理论堆砌。它来自我在MTK平台真实项目中反复调试、踩坑、验证的完整实录——从第一行#!/system/bin/sh的写法,到file_contexts里一个空格引发的启动失败;从init.rconeshotdisabled的区别,到dmesg | grep avc里那条被忽略的关键拒绝日志。所有步骤都经过Android 8.0+真机实测,不讲虚的,只说能跑通的。

如果你正卡在“脚本写好了但系统不认”这一步,这篇文章就是为你写的。

1. 为什么手动能跑,开机却静默失败?

很多新手的第一反应是:“我脚本都没问题,手动执行秒出结果”。但Android开机启动远不止“执行一个sh文件”这么简单。它是一套由init进程驱动、受SELinux策略约束、依赖服务声明和上下文标记的完整链路。任何一个环节出错,脚本都会被无声拦截。

我们先理清这个链路上最关键的四个节点:

  • 脚本本身:路径、shebang、权限、内容逻辑
  • SELinux类型定义(.te):告诉系统“这是个什么角色的服务”
  • 文件上下文标记(file_contexts):告诉系统“这个文件该用什么类型运行”
  • init服务声明(.rc):告诉init进程“什么时候、以什么身份、怎么启动它”

这四者必须严丝合缝,缺一不可。而其中最容易被忽略、最常出错的,恰恰是后三项。

关键认知:Android的init不是Linux的systemd。它不自动扫描目录,不解析shebang以外的元信息,更不会绕过SELinux检查。你写的每一行配置,都必须精准匹配内核加载的策略。

2. 脚本编写:从第一行开始就别踩雷

2.1 shebang必须写对,且只能写对

Android的shell解释器路径和Linux发行版不同。常见错误写法:

#!/bin/sh # ❌ 错误!Android没有/bin/sh #!/usr/bin/sh # ❌ 错误!路径不存在 #!/system/xbin/sh # 部分旧版本可用,但不推荐

正确写法(Android 8.0+通用):

#!/system/bin/sh

为什么?因为Android的/system/bin/shmksh(MirBSD Korn Shell)的软链接,专为init环境优化。而/bin/sh在Android根文件系统中根本不存在——即使你用busybox挂载了,init也不会走那条路径。

2.2 脚本内容要“轻量、可验证、无副作用”

新手常犯的第二个错误:在脚本里直接创建文件、修改系统属性、调用复杂命令。这极易因权限、路径或依赖未就绪而失败,且失败时无任何提示。

推荐写法(仅做最小验证):

#!/system/bin/sh # 第一步:设置一个测试属性(最安全的验证方式) setprop sys.boot.test_script 1 # 第二步:写入一行日志到/dev/kmsg(无需权限,init进程可写) echo "[INIT] test_script executed at $(date)" > /dev/kmsg # 第三步:可选——创建一个临时标记文件(仅用于调试,非生产) touch /data/local/tmp/test_script_ran

注意事项:

  • setprop是最稳妥的验证手段,属性值可通过getprop sys.boot.test_script立即检查
  • /dev/kmsg是内核日志缓冲区,echo写入后可用dmesg | tail -5查看,比logcat更早、更底层
  • /data/local/tmp/是唯一对root用户开放写权限的公共目录,避免使用/system/vendor下的路径

2.3 权限必须设为可执行,且仅root可写

chmod 755 /system/bin/init.test.sh
  • 755:owner=rwx, group=rx, other=rx —— init以root身份运行,不需要other可执行
  • 绝对不要用chmod 777:SELinux会拒绝执行world-writable脚本
  • 脚本文件必须放在/system/bin//vendor/bin/下(需对应file_contexts配置),不能放/data//sdcard/

3. SELinux策略:te文件与file_contexts的黄金配对

Android 8.0引入了sepolicy v25+,强制要求所有自定义服务必须有明确的SELinux类型。跳过这步?脚本连init的面都见不到。

3.1 te文件:定义服务域与执行类型

device/mediatek/sepolicy/basic/non_plat/下新建test_service.te

# 定义服务域(domain) type test_service, domain; # 定义可执行文件类型(exec_type) type test_service_exec, exec_type, vendor_file_type, file_type; # 允许init以test_service身份启动该服务 init_daemon_domain(test_service); # 允许test_service域执行test_service_exec类型的文件 allow test_service test_service_exec:file { read open getattr execute }; # 允许test_service向property_service写属性(对应setprop) allow test_service property_service:property_service set;

关键点解析:

  • type test_service, domain;:声明这是一个服务域,不是普通进程
  • init_daemon_domain(test_service);:这是核心宏,它自动赋予test_service访问/dev/kmsg/proc等init必需资源的权限
  • allow ... execute;:必须显式声明执行权限,否则即使文件上下文正确,也会被拒绝
  • property_service set;:没有这行,setprop命令会静默失败(dmesg里报avc: denied { set } for property=sys.boot.test_script

3.2 file_contexts:给文件“贴标签”

device/mediatek/sepolicy/basic/non_plat/file_contexts中添加:

/system/bin/init\.test\.sh u:object_r:test_service_exec:s0

严格注意:

  • 路径必须用正则转义.sh中的点要写成\.,否则匹配失败
  • 行尾不能有多余空格,哪怕一个空格都会导致整个file_contexts解析失败,脚本无法启动
  • 类型名test_service_exec必须与te文件中定义的完全一致(大小写、下划线)
  • 如果脚本放在/vendor/bin/,路径应改为/vendor/bin/init\.test\.sh

实测教训:曾因file_contexts里多了一个不可见的UTF-8 BOM头,导致整条规则失效,排查耗时3小时。建议用vim -b打开检查,或用xxd file_contexts | head确认无BOM。

4. init.rc服务声明:位置、语法与生命周期

4.1 不要硬改/system/etc/init.rc

主流芯片平台(MTK、Qualcomm、Exynos)都提供了客户定制入口,如:

  • MTK:/system/etc/init/hw/init.mtXXX.rcdevice/mediatek/.../init.project.rc
  • Qualcomm:/system/etc/init/hw/init.qcom.rc
  • Exynos:/system/etc/init/hw/init.exynos.rc

正确做法:在对应平台的init.XXX.rc末尾添加:

service test_service /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:test_service_exec:s0

4.2 每个字段的含义与避坑点

字段含义常见错误正确写法
service name path服务名与脚本路径名字含.-,路径写错test_service /system/bin/init.test.sh
class main归属启动类漏写或写成default必须写main,确保随系统主服务启动
user/group root运行身份写成shellsystem开机早期只有root有足够权限
oneshot执行一次即退出漏写或写成disabledoneshot是开机脚本首选;disabled需手动start test_service
seclabelSELinux上下文类型名拼错、漏写u:object_r:test_service_exec:s0(与file_contexts一致)

特别提醒:seclabel字段必须显式声明。即使你在file_contexts里标好了,init仍会按此字段二次校验。漏写=拒绝启动。

5. 调试与验证:四步定位失败根源

脚本没启动?别猜,按顺序查这四步:

5.1 第一步:确认init.rc是否加载成功

adb shell "grep -A5 'service test_service' /system/etc/init/hw/init.mt*.rc"

如果无输出,说明你的rc文件没被编译进镜像,或路径写错。

5.2 第二步:检查SELinux是否启用及策略加载

adb shell getenforce # 应返回 Enforcing adb shell ls -Z /system/bin/init.test.sh # 应显示 u:object_r:test_service_exec:s0

如果ls -Z显示u:object_r:shell_exec:s0,说明file_contexts未生效。

5.3 第三步:抓取SELinux拒绝日志(最关键!)

adb shell dmesg | grep avc | tail -10

典型失败日志:

  • avc: denied { execute } for path=/system/bin/init.test.sh dev="dm-0" ino=123456 scontext=u:r:init:s0 tcontext=u:object_r:shell_exec:s0 tclass=file permissive=0
    → 问题:file_contexts未匹配,或seclabel写错
  • avc: denied { set } for property=sys.boot.test_script scontext=u:r:test_service:s0 tcontext=u:object_r:property_socket:s0 tclass=property_service permissive=0
    → 问题:te文件缺少property_service set权限

5.4 第四步:手动触发并观察行为

adb shell start test_service adb shell getprop sys.boot.test_script # 应输出 1 adb shell dmesg | grep "\[INIT\]" # 应看到时间戳日志 adb shell ls /data/local/tmp/test_script_ran # 应存在

如果手动start成功,但开机不启动 → 90%是init.rc未在正确时机加载,或class设置错误。

6. 实测对比:不同Android版本的关键差异

我们在Android 8.1、9.0、10.0、11.0四台真机上对同一脚本配置做了验证,发现以下关键差异:

Android版本init.rc语法支持SELinux策略位置file_contexts路径备注
8.1支持oneshotnon_plat/non_plat/file_contexts最稳定,推荐基准
9.0oneshot需配合disabled才可靠plat_*目录优先级更高plat_public/file_contextsnon_plat不生效,尝试plat_public
10.0oneshot行为一致sepolicy v28+,init_daemon_domain宏更严格plat_private/file_contextstest_service_exec必须加vendor_file_type
11.0强制seclabel,否则启动失败plat_private为默认策略源plat_private/file_contextsdmesg日志更详细,报错直接指出缺失权限

统一适配建议:

  • te文件中始终包含vendor_file_type(即使脚本在/system/bin/
  • file_contexts同时在non_plat/plat_private/中添加(双保险)
  • init.rcseclabel字段绝不省略

7. 总结:一份可立即复用的检查清单

把这篇指南变成你的行动手册。每次部署前,对照这份清单逐项确认:

  • [ ] 脚本shebang为#!/system/bin/sh,权限755,存放于/system/bin/
  • [ ]test_service.te已添加init_daemon_domainproperty_service set权限
  • [ ]file_contexts中路径正则转义正确,类型名与te文件完全一致,无多余空格
  • [ ]init.XXX.rc中服务声明包含class mainuser rootoneshotseclabel四要素
  • [ ] 编译后adb shell ls -Z /system/bin/init.test.sh返回test_service_exec:s0
  • [ ]adb shell dmesg | grep avc无相关拒绝日志
  • [ ]adb shell start test_service可成功执行并验证效果

记住:Android开机启动不是“写个脚本就行”,而是一次对系统启动流程、SELinux模型和init机制的综合实践。每一次成功的启动,都是对这些底层逻辑的一次确认。

你现在拥有的,不再是一个可能失效的教程片段,而是一套经过多版本真机验证、覆盖全部关键陷阱的完整方法论。接下来,就是把它用在你的项目里。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/26 13:19:08

Z-Image-Turbo使用心得:本地生成图像的安全与便捷

Z-Image-Turbo使用心得:本地生成图像的安全与便捷 你是否曾为一张教学配图反复修改PS图层?是否担心把客户产品草图上传到在线AI平台后被二次使用?又或者,只是单纯想在周末安静地生成一组风格统一的插画,不被算法推荐、…

作者头像 李华
网站建设 2026/3/26 11:22:49

位移参数影响出图质量?Qwen-Image调试心得

位移参数影响出图质量?Qwen-Image调试心得 你有没有遇到过这样的情况:明明提示词写得清清楚楚,模型也跑起来了,可生成的图片不是文字模糊、排版歪斜,就是主体失真、细节糊成一片?在反复测试Qwen-Image-251…

作者头像 李华
网站建设 2026/3/26 11:41:50

Qwen3-0.6B真实体验:轻量模型适合初学者

Qwen3-0.6B真实体验:轻量模型适合初学者 [【免费下载链接】Qwen3-0.6B Qwen3 是通义千问系列最新一代开源大语言模型,2025年4月正式发布,涵盖从0.6B到235B的多档位密集模型与MoE架构。其中Qwen3-0.6B以极低资源占用、开箱即用体验和清晰可理…

作者头像 李华
网站建设 2026/3/25 22:18:38

YOLOv12镜像训练时崩溃?这份调参建议请收好

YOLOv12镜像训练时崩溃?这份调参建议请收好 YOLOv12不是简单的版本迭代,而是一次架构范式的跃迁——它用注意力机制彻底重构了实时目标检测的底层逻辑。但正因如此,它的训练行为与传统YOLO系列(v5/v8/v10)存在本质差异…

作者头像 李华
网站建设 2026/3/26 9:08:48

法律口述记录神器,Paraformer支持原告被告热词

法律口述记录神器,Paraformer支持原告被告热词 在法院庭审、律师访谈、调解现场等法律场景中,语音转文字不是“锦上添花”,而是刚需——手写记录易遗漏、速录员成本高、通用ASR识别不准专有名词。你是否遇到过这些情况: 录音里反…

作者头像 李华
网站建设 2026/3/25 14:25:27

下载结果只需一点,生成文件自动命名

下载结果只需一点,生成文件自动命名 你有没有遇到过这样的情况:辛辛苦苦等了几秒把人像转成卡通风格,结果点下载时发现文件名是乱码、时间戳太长、根本分不清哪张图对应哪次参数?更别说批量处理几十张照片后,一堆 out…

作者头像 李华