news 2026/3/8 8:01:22

Android开发必备技能:开机启动脚本编写与调试技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android开发必备技能:开机启动脚本编写与调试技巧

Android开发必备技能:开机启动脚本编写与调试技巧

在Android系统定制和深度开发中,让自定义服务或脚本在设备上电后自动运行是一项基础但关键的能力。无论是实现硬件初始化、日志采集、远程监控,还是为车载、IoT设备添加专属功能,掌握开机启动脚本的完整链路——从编写、集成到SELinux适配与调试——都是Android系统工程师绕不开的核心技能。

本文不讲抽象理论,不堆砌源码片段,而是以一个真实可复现的“测试开机启动脚本”镜像为蓝本,带你走通从零编写、部署、验证到排障的全流程。所有步骤均基于Android 8.0及以上主流版本(AOSP及MTK平台实测通过),代码简洁、路径明确、权限清晰,小白照着做就能看到getprop test.prop返回值,老手也能从中获得调试思路和避坑经验。


1. 为什么不能只写个.sh就完事?

很多开发者第一次尝试时会发现:脚本手动执行没问题,放进init.rc却完全没反应。这不是你的脚本写错了,而是Android早已不是Linux桌面环境。

Android启用了强制性的SELinux策略,且init进程以u:r:init:s0域运行,它不会无条件执行任意路径下的可执行文件。此外,init.rc语法有严格规范,服务声明、执行上下文、用户组设置、启动时机(early-init/init/late-init)都直接影响脚本能否真正跑起来。

所以,一个能“开机就动”的脚本,必须同时满足四个条件:

  • 脚本本身语法正确、路径可访问、解释器声明无误
  • SELinux类型(type)已正确定义
  • 文件上下文(file_context)已绑定该类型
  • init.rc中服务声明符合语法,且seclabel指向正确域

缺一不可。下面我们就按这个逻辑顺序,逐层构建、逐层验证。


2. 编写一个最小可行脚本

我们不追求复杂功能,先确保“能跑”。目标很明确:设备启动后,设置一个系统属性test.prop,值为111。后续只需执行adb shell getprop test.prop,返回111即代表成功。

2.1 创建脚本文件

新建文件init.test.sh,内容如下:

#!/system/bin/sh # 注意:Android系统默认shell路径是 /system/bin/sh 或 /system/xbin/sh # 不要写成 #!/bin/sh —— 这在Android上会直接失败 # 设置一个测试属性(推荐方式:避免创建文件引发权限问题) setprop test.prop 111 # 可选:写入日志便于调试(需确保logd已就绪) log -t "INIT_TEST" "Script executed successfully, test.prop = $(getprop test.prop)"

2.2 关键细节说明

  • 解释器路径必须准确/system/bin/sh是AOSP标准路径;部分厂商可能用/system/xbin/sh(如旧版BusyBox)。若不确定,可先adb shell ls /system/bin/sh /system/xbin/sh确认。
  • 不要创建文件或修改系统分区:初学者常试图echo "ok" > /data/misc/test.log,但/data挂载完成晚于init阶段,此时写入会失败。用setprop是最轻量、最稳妥的验证手段。
  • 务必先手动验证:将脚本push到设备并赋予执行权限:
    adb push init.test.sh /data/local/tmp/ adb shell chmod +x /data/local/tmp/init.test.sh adb shell /data/local/tmp/init.test.sh adb shell getprop test.prop # 应输出 111
    手动能跑,才说明脚本逻辑和权限无问题,再进入下一步。

3. 定义SELinux类型与上下文

Android 8.0起全面启用sepolicy(SELinux策略),任何新服务都必须被策略识别,否则init会拒绝启动。

3.1 创建TE(Type Enforcement)策略文件

新建test_service.te,内容如下:

# 定义服务域类型 type test_service, coredomain; # 定义脚本文件类型(可执行) type test_service_exec, exec_type, vendor_file_type, file_type; # 允许init域过渡到该服务域(关键!) init_daemon_domain(test_service); # 允许shell域(adb shell)读取该脚本(仅用于调试,非必需) # allow shell test_service_exec:file { read open getattr execute };

说明:init_daemon_domain()是宏,它自动添加了inittest_servicetransitiondyntransition等必要权限。比手动写allow init test_service:process ...更安全、更标准。

3.2 绑定文件上下文

file_contexts中为脚本文件指定类型。路径取决于你使用的平台:

  • 高通平台:通常在device/qcom/sepolicy/vendor/file_contexts
  • MTK平台:如参考文档所述,在device/mediatek/sepolicy/basic/non_plat/file_contexts
  • 通用AOSP:可放在device/<vendor>/<product>/sepolicy/vendor/file_contexts

添加一行(注意空格和缩进):

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

重要提醒:

  • 正则转义:.sh中的点号必须写成\.,否则匹配失败;
  • 路径必须与init.rcservice声明的路径完全一致
  • 即使你临时关闭了SELinux(setenforce 0),这行也必须添加,否则init无法识别该文件类型,服务仍不会启动。

4. 在init.rc中声明服务

不要直接修改system/core/rootdir/init.rc主文件——它由AOSP维护,易被覆盖。应使用厂商推荐的扩展机制。

4.1 推荐做法:使用独立的init.XXX.rc

大多数芯片方案都预留了扩展入口。例如:

  • MTK平台:在device/mediatek/common/init/init.mt6765.rc(或对应芯片名)末尾添加
  • 高通平台:在device/qcom/msmxxx/init.qcom.rc中添加
  • 通用方式:在device/<vendor>/<product>/rootdir/init.<product>.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 参数详解

字段说明
class main归入main类,随init主流程启动(早于late-init
user root/group root以root身份运行,确保权限足够(测试阶段推荐)
oneshot执行一次即退出,适合初始化类脚本;若需常驻,请改用disabled+start xxx触发
seclabel必须与file_contexts中定义的类型一致,否则SELinux拒绝加载

4.3 启动时机选择

  • on early-init:内核刚启动、/dev未挂载前 → 仅限极底层硬件操作
  • on init/dev挂载后、/system挂载前 → 少用,风险高
  • on late-init/system/data均已挂载 →推荐绝大多数场景
  • on property:sys.boot_completed=1:系统UI就绪后 → 适合依赖Framework的服务

本例用class main即可,它在late-init之前、/system已挂载后执行,安全且及时。


5. 编译、刷机与快速验证

完成上述三步后,需重新编译并刷写boot.imgsystem.img。具体命令依平台而异,但核心流程一致:

5.1 编译步骤(以AOSP为例)

# 1. 确保te文件和file_contexts已加入编译系统 # (通常在Android.mk或BoardConfig.mk中引用) # 2. 编译system.img(含init.rc和脚本) m systemimage # 3. 编译boot.img(含init程序和sepolicy) m bootimage # 4. 刷写(谨慎操作!) fastboot flash system system.img fastboot flash boot boot.img fastboot reboot

5.2 刷机后验证流程

设备重启后,按顺序执行以下命令,每一步都是关键检查点:

# 步骤1:确认服务是否被init识别 adb shell 'ls -Z /system/bin/init.test.sh' # 应显示 u:object_r:test_service_exec:s0 # 步骤2:确认init是否加载了该服务 adb shell 'getprop | grep test.prop' # 若已启动,此处应立即出现 test.prop=111 # 步骤3:若未出现,检查init日志(最有效!) adb shell dmesg | grep -i "test\|avc\|init" # 关键线索:AVC denied 表示SELinux拦截;init: starting service 'test_service' 表示已触发 # 步骤4:手动触发服务(调试用) adb shell start test_service adb shell getprop test.prop # 应返回111

调试黄金法则:永远先看dmesg,再查logcatdmesg输出init和kernel级日志,AVC avc: denied错误会直接告诉你缺哪条SELinux规则。


6. 常见问题与实战排障指南

即使严格按步骤操作,仍可能遇到“无声失败”。以下是高频问题及对应解法,全部来自真实项目踩坑总结。

6.1 问题:getprop test.prop始终为空

可能原因检查方法解决方案
脚本未被init执行`adb shell psgrep test_service`
SELinux拦截(最常见)adb shell dmesg | grep avc输出类似avc: denied { execute } for path="/system/bin/init.test.sh"→ 需补充allow init test_service_exec:file execute;test_service.te
脚本路径错误adb shell ls -l /system/bin/init.test.sh确认文件存在、权限为-rwxr-xr-x、路径与init.rc中完全一致
init.rc未生效adb shell cat /proc/1/cmdline若显示init而非init --second-stage,说明未加载新版init.rc,检查编译是否包含该文件

6.2 问题:脚本执行了,但log命令无输出

  • log命令依赖logd服务,它在late-init阶段才启动。若脚本在early-initinit阶段运行,log会静默失败。
  • 解决方案:改用echo "msg" > /dev/kmsg(内核日志)或坚持用setprop(最可靠)。

6.3 问题:MTK平台编译报错“type test_service is not defined”

  • 原因:test_service.te未被Android.mkBoardConfig.mk引用。
  • 解决方案:在device/mediatek/sepolicy/basic/non_plat/Android.mk中添加:
BOARD_SEPOLICY_DIRS += \ device/mediatek/sepolicy/basic/non_plat

7. 进阶建议:让脚本更健壮、更工程化

完成基础功能只是起点。在实际项目中,还需考虑以下几点:

7.1 权限最小化原则

  • 生产环境禁止使用user root。应创建专用用户(如testuser),并在init.rc中指定:
    user testuser group testuser system inet net_admin
  • 对应在sepolicy中定义该用户类型,并授予最小必要权限(如仅net_admin而非root)。

7.2 支持热更新与调试开关

在脚本开头加入判断逻辑,避免每次刷机:

#!/system/bin/sh # 检查是否启用测试模式(可通过adb setprop动态控制) if [ "$(getprop test.enable)" = "1" ]; then setprop test.prop 111 log -t "INIT_TEST" "Test mode enabled" else log -t "INIT_TEST" "Test mode disabled" fi

然后调试时只需:

adb shell setprop test.enable 1 adb shell stop test_service && adb shell start test_service

7.3 日志归档与错误捕获

增强脚本鲁棒性:

#!/system/bin/sh exec 2>/dev/log/test_init.log # 重定向stderr到日志文件 set -e # 遇错退出 setprop test.prop 111 || { log -t "INIT_TEST" "Failed to set property" exit 1 }

8. 总结:掌握开机启动,就是掌握Android系统控制权

写一个开机脚本,表面看是几行代码和几处配置,背后却是对Android启动流程、SELinux机制、init语法、系统分区结构的综合理解。本文带你走通的不是“如何让test.prop变111”,而是一套可复用、可迁移、可调试的系统级开发方法论

  • 从最小脚本入手,手动验证先行,杜绝“黑盒”调试
  • SELinux不是障碍,而是安全护栏——用dmesg读懂它的语言
  • init.rc是系统脉搏,理解classoneshotseclabel才能精准施控
  • 所有配置必须闭环验证:ls -Zpsgetpropdmesg

当你能自信地为任意Android设备添加专属启动行为时,你就真正跨过了系统开发的门槛。下一步,可以尝试让脚本拉起一个守护进程、监听USB设备、或与HAL层交互——那将是另一个精彩世界。


获取更多AI镜像

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

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

如何验证识别准确性?Speech Seaco Paraformer测试集构建方法

如何验证识别准确性&#xff1f;Speech Seaco Paraformer测试集构建方法 1. 为什么需要专门构建测试集&#xff1f; 语音识别模型的“准确率”不是一句空话。官方标注的98%、99%数字背后&#xff0c;藏着严格的数据筛选逻辑——它只在特定录音条件、标准发音、干净环境、限定…

作者头像 李华
网站建设 2026/3/7 20:27:24

突破AR空间感知瓶颈:RealSense深度技术全解析

突破AR空间感知瓶颈&#xff1a;RealSense深度技术全解析 【免费下载链接】librealsense Intel RealSense™ SDK 项目地址: https://gitcode.com/GitHub_Trending/li/librealsense 主题&#xff1a;虚实融合的技术困境与解决方案 在AR开发中&#xff0c;你是否曾遇到这…

作者头像 李华
网站建设 2026/3/2 9:58:14

从零开始掌握MIT许可证合规实战指南

从零开始掌握MIT许可证合规实战指南 【免费下载链接】PictureSelector Picture Selector Library for Android or 图片选择器 项目地址: https://gitcode.com/gh_mirrors/pict/PictureSelector 在当今开源生态中&#xff0c;MIT许可证作为最受欢迎的开源许可协议之一&am…

作者头像 李华
网站建设 2026/3/2 1:13:13

网络暴力语音识别:恶意语气AI检测部署方案

网络暴力语音识别&#xff1a;恶意语气AI检测部署方案 在网络内容治理日益严格的今天&#xff0c;文字层面的违规检测已相对成熟&#xff0c;但语音场景——尤其是直播、语音社交、在线教育、客服通话等实时音频流中——恶意语气、羞辱性语调、煽动性情绪往往藏在“没说错字”…

作者头像 李华
网站建设 2026/2/18 3:57:18

一文说清qtimer::singleshot的调用时机与陷阱

以下是对您提供的技术博文进行 深度润色与重构后的版本 。我以一名资深 Qt 开发者兼嵌入式系统教学博主的身份,将原文从“技术文档式说明”彻底转化为 有温度、有节奏、有实战血肉的技术分享体 ——去除了所有AI腔调和模板化结构,强化了逻辑流、经验感与可读性;同时严格…

作者头像 李华
网站建设 2026/3/6 4:07:02

Open-AutoGLM项目复现:跟着视频5分钟成功运行

Open-AutoGLM项目复现&#xff1a;跟着视频5分钟成功运行 你是否想过&#xff0c;只需一句话就能让手机自动完成复杂操作&#xff1f;比如“打开小红书搜美食”“在淘宝比价后下单最便宜的洗发水”“关注抖音上那个穿蓝衬衫的博主”——不用手动点、不用复制粘贴、不需写脚本。…

作者头像 李华