资讯动态

MTK Android P差分包去除preloader的三种实现方案

发布时间:2026/9/16 2:54:13 来源:尧图企业网站定制
做MTK Android P平台的朋友应该都遇到过这种需求客户或者测试那边提过来一个单子说差分包OTA增量包里不要带preloader分区问你有没有办法。第一次接到这种需求的人大概率会有点懵——preloader不是BootROM之后第一阶段加载的东西吗这种底层东西和差分包有什么关系其实关系很大而且去掉它并不是什么特别玄学的事今天就拿我实际改过的一个项目来聊聊这个问题。先说结论在MTK Android P的版本上完全可以做到让差分包不包含preloader分区。核心思路有三个方向编译期开关、target_files源头剥离、以及releasetools脚本层过滤。每个方案都有自己的适用场景但目的都一样——让最终生成的update.zip里找不到任何一条写preloader分区的命令。下面我把原理、具体步骤、以及我在实操中踩过的坑一次说清楚。1. 先搞清楚preloader为什么会在差分包里1.1 差分包生成链路回顾MTK Android P的OTA差分包本质上是通过ota_from_target_files这个Python工具对两个target_files.zip做“新老对比”后生成的。target_files.zip是编译过程中间产物里面包含了system、vendor、boot、recovery等所有分区镜像以及META目录下面一堆描述分区、权限、证书的元数据。编译的时候build/make/core/Makefile里有个otapackage目标它会先调用add_to_target_files等函数把各个镜像打包成target_files.zip随后让releasetools去生成最终的OTA包。这个过程里MTK会把preloader当成一个普通分区镜像塞进target_files.zip的PRELOADER目录下。于是在做增量比较的时候releasetools扫描到新旧target_files里的preloader有差异自然就把patch写进了差分包。整个链路其实是这样的编译生成镜像 - 打包target_files.zip - 记录preloader等分区信息 - ota_from_target_files做diff - 产出update.zip - updater-script里出现preloader写入命令所以想去掉差分包里的preloader就得从这条链路的某个环节下手把preloader“摘”出去。1.2 preloader在OTA脚本里是怎么被“写”进去的如果你已经生成了一个带preloader的差分包可以直接解压看一下升级脚本。Android P的MTK平台一般情况下用的是block-based OTA脚本位于META-INF/com/google/android/updater-script。正常内容会有一大堆apply_patch和block_image_update之类的语句。但preloader比较特殊它没有走block mapMTK在releasetools里专门写了一段逻辑最后生成的脚本是类似这样的形式package_extract_file(preloader, /dev/block/platform/bootdevice/by-name/preloader);大意就是直接把target_files里那个preloader镜像用package_extract_file解压出来然后写到emmc的preloader分区节点上。所以在差分包里只要看到这一句就说明preloader确实被带进去了。这里要顺便说一下在MTK平台上preloader是启动链的第一级负责DDR初始化、时钟配置、启动模式选择这些事。它在emmc上不是简单的用户分区很多项目里它有独立的boot partitionmmcblk0boot0也有对应的by-name节点。因为实在太底层如果刷写过程中断电或者镜像本身和主板批次不匹配轻则卡fastboot重则直接变砖。这也是为什么很多定制客户强烈要求差分包里不要带它的原因。1.3 什么场景下需要去掉preloader结合我接触过的需求去preloader的诉求不外乎这几种同硬件平台不同项目共用方案比如同一个芯片平台硬件完全一样只是不同客户贴了不同logo底层preloader没必要跟着系统升级。控制升级风险preloader刷写一旦失败recovery和fastboot可能都进不去售后压力大干脆不带。平台release后冻结preloader芯片厂商发版之后preloader通常不会再动系统侧升级根本不涉及它没必要反复patch。减小差分包体积其实preloader镜像本身不大但如果新老版本差异大patch也可能到几百KB有些客户很在意这个。搞清楚需求来源才能确定用哪种方案。如果是编译期就能决定的建议用开关如果已经有一对target_files那就用剥离或脚本过滤。1.4 几种方案的整体对比我在项目里实际对比过几种做法优缺点大概是这样方案操作位置优点缺点编译宏开关DeviceConfig/ProjectConfig干净、可下发配置需要重新编译target_files老包无效target_files剥离打包产物处理灵活、不用改代码元数据要同步处理容易遗漏releasetools脚本过滤修改ota生成逻辑一劳永逸全项目生效需要维护本地patch升级代码有冲突风险手工改update.zip最终产物应急可用不推荐签名校验就过不了下面分别展开。2. 方案一从编译宏开关入手2.1 先查你的平台有没有这个开关MTK在很早期版本的OTA工具链里就预留了preloader和lk的控制开关。在Android N之前一般是写在ProjectConfig.mk里的MTK_PRELOADER_OTA_UPDATE值改成no或者注释掉差分包生成时会跳过preloader。到了Android PMTK的配置体系做了一次比较大的调整很多配置迁移到了device/mediatek/project/DeviceConfig.mk或者直接被vendor/mediatek/proprietary/build/make/下面的mk文件引用。我看到的代码里名称通常还是MTK_PRELOADER_OTA_UPDATE但不同分支、不同项目定义位置可能会有出入。所以第一步不是急着改而是在你的工程里搜一下grep -rn PRELOADER_OTA vendor/mediatek/ device/ build/make/ --include*.mk --include*.config如果能搜到说明你的平台保留了这套控制逻辑如果搜不到说明这个开关已经被MTK在新版本里去掉了你得走后面两个方案。2.2 修改配置并验证假设你搜到了开关操作方式很简单。以我处理过的一个Android P平台为例在DeviceConfig.mk里找到MTK_PRELOADER_OTA_UPDATE yes改成MTK_PRELOADER_OTA_UPDATE no然后重新编target_filessource build/envsetup.sh lunch your_project-userdebug make target-files-package -j$(nproc)编译产物在out/target/product/project/obj/PACKAGING/target_files_intermediates/project-target_files-*.zip用这个新的target_files去生成差分包preloader就不会出现在patch列表里。2.3 这个开关背后的逻辑我当时好奇去翻了一下MTK的脚本发现这个开关其实控制的不是releasetools的最终行为而是MTK在构建target_files阶段是否把preloader的diff生成信息注册进“升级分区清单”。如果开关是no打包脚本就不会把preloader当作一个可升级分区输出。相当于源头裁剪后面releasetools再怎么会干活也找不到preloader这张“牌”。这一点很关键。如果你只是改了开关但target_files里PRELOADER目录还在最终差分包是不是就绝对干净我实测下来只要MTK的打包脚本正确遵守开关逻辑差分包确实不会再有preloader的写入命令。不过我还是建议生成完以后按照第5章的验证方法再确认一次不要凭感觉。2.4 这个方案的局限最大的问题是它只对重新编译后的新target_files有效。如果你手里的旧target_files编译时还是yes那这条路径上它里面已经带preloader了diff逻辑会强行对比新旧preloader的差异。所以想彻底去掉必须保证新旧两个target_files都得是在no开关下编出来的。另外我遇到过某些MTK项目虽然设置了MTK_PRELOADER_OTA_UPDATE no但releasetools生成差分包时仍然会去检查preloader分区是否一致如果发现不一致会直接报错或者强制写入。这就是典型的版本间逻辑不一致这时候别纠结直接用方案二或者方案三去处理。3. 方案二从target_files源头剥离preloader3.1 为什么这条路更通用大多数实际需求并不是“下一个版本开始不要preloader”而是“我现在手上就有一对target_files新老版本都已经编译好了能不能把它俩生成的差分包里的preloader去掉”。这种情况走编译开关基本不可能总不能为了一个差分包把目标文件重编一遍。所以更通用的做法是把target_files.zip里跟preloader相关的文件先剥掉再丢给releasetools生成差分包。等于从根上让releasetools“看不见”这个分区它自然就不会去比较、不会去写。3.2 具体操作步骤假设你手上已经有一对新老target_filesold_target_files.zip new_target_files.zip下面是一个我多次使用的干净流程。为了避免污染原始文件建议先拷贝一份再操作mkdir -p work cp old_target_files.zip work/old.zip cp new_target_files.zip work/new.zip cd work # 解包 mkdir old_new old_new_root unzip -q old.zip -d old_new_root unzip -q new.zip -d old_new_root_new注意我习惯用两套目录分别处理避免混淆。接下来分别进入解包目录删除PRELOADER相关文件和META目录里的相关索引信息。cd old_new_root rm -rf PRELOADER zip -qr ../old_clean.zip .新包同样处理cd ../old_new_root_new rm -rf PRELOADER zip -qr ../new_clean.zip .可能有朋友会问直接删掉PRELOADER目录就够了吗说实话大部分情况确实够了。但有个别MTK版本在META/下还会存一份preloader的校验信息或者分区大小记录比如META/partition_size_list.txt、META/preloader_*.xml这类文件。如果脚本在生成OTA时要读取这些元数据发现PRELOADER没了但元数据还留着可能报错也可能生成一个异常包。所以稳妥起见我强烈建议在删完PRELOADER目录后再检查一下META目录里有没有包含preloader字样的文件有的话一并处理grep -rl preloader META/ 2/dev/null看到的结果按实际情况删除或修改。这里要特别提醒一点有些文件只是描述性文本删掉不影响打包但有些文件参与分区校验删掉后可能会因为找不到preloader的条目而报错。遇到这种情况不要硬删可以用sed或vim把preloader相关的一行注释掉或删除保留其它内容。3.3 生成差分包的命令清理好两个target_files之后就可以执行标准的差分包生成命令了。Android P平台一般这样跑source build/envsetup.sh lunch your_project-userdebug ./build/tools/releasetools/ota_from_target_files \ -p out/host/linux-x86 \ -k vendor/mediatek/proprietary/scripts/releasetools/keys/platform \ -i old_clean.zip \ new_clean.zip \ update_no_preloader.zip-i参数指定旧包-k指定签名私钥缺一不可。如果你平时是通过MTK的一键脚本比如mk otapackage或者build_ota.sh生成的注意把脚本里传给ota_from_target_files的target_files路径替换成清理后的文件路径。3.4 这个方法的关键细节这里有几个我在实际过程中总结出来的细节分享给大家先解压再压缩不要用zip -d直接删除因为zip -d在某些环境下对包含大量小文件的target_files处理很慢而且可能改坏zip的格式。解压后重新压缩时不要额外引入当前工作目录的影子目录。很多人习惯zip -r ../new_clean.zip *这样没问题但如果你把目标文件也放在当前目录里可能把backup也打进去。用绝对路径区分新旧包避免把新旧包搞混。我见过同事把new_clean.zip和old_clean.zip传参传反结果生成了一个“回退包”刷到设备上直接把系统干挂了。清理元数据之前先备份一份原始target_files万一打包失败还能还原。3.5 这个方案的注意事项对于Android P的MTK平台来说target_files里除了PRELOADER目录可能还有lk.img、tee.img、vbmeta.img这些和启动链路相关的分区镜像。建议只删除需求指定的preloader不要顺手把其它底层分区也删了尤其是lkLittle Kernel它在某些版本里和preloader的配合很紧密乱删可能导致升级后无法开机。另外在生成差分包的同时最好保留一份完整包的生成记录。差分包本身的逻辑是“基于旧包打补丁”如果你清理后的target_files里完全没有preloader但真机上旧版本preloader非常老那升级后也不会有任何preloader的改动。这一点对上层系统没有感知但如果你不确定设备当前preloader版本是否满足新kernel要求就需要先确认硬件版本兼容别到时候设备起不来说OTA包有问题。4. 方案三修改releasetools脚本生成时直接跳过preloader4.1 修改脚本的原理第三种方案是从ota_from_target_files.py或MTK的定制脚本里把preloader的处理逻辑直接拿掉。这样不管传入的target_files有没有PRELOADER目录最终生成的updater-script里都不会有preloader相关的写入语句。Android P的releasetools核心脚本在build/tools/releasetools/下其中ota_from_target_files.py负责整个OTA包的生成流程。在block-based OTA模式下真正往脚本里写分区升级命令的是WriteBlockIncrementalOTAPackages这个函数。它会遍历target_files里的所有分区镜像如果发现新旧包中某个镜像有变化就追加相应的apply_patch或block_image_update命令。所以思路很直接在遍历分区的循环里遇到partition name等于preloader直接跳过。4.2 一个最小改动的示例以ota_from_target_files.py为例在函数里找类似for image_name in image_list:的循环在循环体开头加一段# Skip preloader partition for customer OTA if image_name preloader: continue具体放到哪里取决于你Android P源码的结构。不同小版本代码位置有差异但我给的标准是在生成差异命令之前过滤不要在写入updater-script之后再过滤。否则虽然脚本里没有写preloader但前面的差异计算可能已经报了错。如果你不想改build目录下的原生脚本也可以看看MTK是否提供了自己的releasetools入口。MTK在vendor/mediatek/proprietary/scripts/releasetools/下有不少定制snippets有的版本里直接在mt_ota_from_target_files.py里封装了一层。在这种入口里加过滤后续升级releasetools系统源码时不容易被合并冲突干掉。4.3 签名问题的影响这里要特别说明修改生成脚本不会影响android包本身的签名。因为签名是在releasetools生成包的最后阶段做的针对的是整个zip包而不是单个分区。你只是不写preloader的升级命令签名机制不可能因此报错。真正会出问题的是如果你用了一个不匹配的-k私钥那签名验证无论如何都过不了和preloader无关。所以如果最后刷机时报签名错误优先检查证书问题不要冤枉脚本改动。4.4 这三种方案的适用边界说实话我在真实项目里最常用的是方案二因为它对现有target_files的侵入最小。方案一适合项目早期就确定需求的场景方案三适合整个团队持续出ota包、想一劳永逸的人。使用场景推荐方案项目刚开始规划明确不要preloader方案一手头已有target_files急需出一版差分包方案二长期维护每个版本都不想带preloader方案三5. 验证结果和常见问题排查5.1 怎么确认差分包里已经没有preloader生成完update.zip后别急着发出去先做一次静态确认。我自己的标准流程是这样第一步查看升级脚本unzip -p update.zip META-INF/com/google/android/updater-script | grep -n -i preloader如果没有输出说明升级脚本里已经没有了。这是最基本的判断依据。第二步检查包里的metadataunzip -p update.zip META-INF/com/android/metadata | grep -i preloader有的非AB包metadata里会记录分区列表这里也不该出现preloader。第三步对于AB OTA包还需要看payload里的分区名列表。但MTK Android P一般还是非AB为主不过如果你手头是支持AB的版本就要检查payload.bin里的分区清单。这个用ota_metadata或者payload_info工具都能看到确保preloader不在列表里。5.2 常见问题包能刷但卡fastboot或者黑屏这是我被问过最多的情况。现象是差分包能正常刷入系统重启后却停在fastboot界面或者直接黑屏。排查思路是先确认去掉preloader之后当前设备preloader版本和新版本其他镜像是否兼容。尤其是kernel里对DDR、eMMC、时钟频率的配置如果新kernel依赖新preloader里新增的初始化逻辑而设备上还跑着老preloader那就可能出现底层初始化不正常。解决办法要么坚持保留某个版本以上的preloader作为最低基线要么在去掉preloader之前先在真机上确认新老preloader差异非关键。这个确认往往需要硬件部门配合。5.3 常见问题releasetools生成时报分区校验错误如果你用了方案二在清理target_files时只删了PRELOADER目录但保留了META里跟preloader相关的条目就有可能在生成差分包时报类似这样的错误ERROR: failed to find preloader images这说明releasetools在读取分区列表时发现元数据里声明了preloader但对应的镜像文件不存在。解决办法是回到方案二的正文里把META目录下有关preloader的条目一并处理干净。这一条我反复强调是因为真的容易遗漏。5.4 常见问题差分包刷入后recovery日志里报preloader相关错误在真机验证时按音量键触发recovery日志或者直接adb logcat看升级日志。如果看到类似E: failed to mount /preloader E: Error updating preloader partition大概率说明你用的差分包本身还是带了preloader写入逻辑只是你没查到。不要只看脚本grep结果也要看看updater-script里有没有通过动态生成并执行方式绕过去的写法。MTK的某些定制版本会在脚本中调用run_program去执行一个二进制工具来刷预加载器这种情况下greppreloader字符串不一定能命中因为真正写入的是工具内部逻辑。要想彻底确认只能真机抓log或者对比差分包升级前后preloader分区内容是否有变化dd if/dev/block/platform/bootdevice/by-name/preloader of/sdcard/preloader_after.bin和升级前备份的preloader分区做md5sum一致就说明确实没写。5.5 常见问题如果差分包里还有其它底层分区要不要一起去掉有些朋友看到能去preloader就顺手想把lk、tee、vbmeta全去掉。我的建议是别冲动。preloader在大部分项目里的确可以不动但lk涉及fastboot和内核引导tee涉及安全环境vbmeta在Android Verified Boot体系里有校验作用。这些分区一旦缺失轻则无法启动重则校验失败进recovery。除非需求方拍板并做了充分测试否则不要贸然扩展。如果确实有“只保留系统分区更新”的强烈需求也要分阶段灰度先在测试机验证确认OTA流程完整无误再推向客户。6. 一些实际操作心得做这一步的时候我个人最大的感受是去preloader这件事本身不复杂复杂的是你要清楚这个动作对升级安全意味着什么。差分包的作用本来就是修复旧版本问题如果只修上层应用确实没必要去折腾底层引导程序。但一旦把preloader排除在外背后意味着你对硬件批次、preloader版本、内核兼容性都做了充分的确认。没有这些前置条件哪怕差分包生成得再漂亮到真机上也随时可能翻车。另外一个值得分享的小技巧是修改完方案二或者方案三之后最好把生成好的update.zip丢到本地做一个全量解包扫描关键词不只看preloader还看apply_patch、package_extract_file、mount这几个动作的target有没有指向by-name/preloader。因为MTK有的版本会把preloader挂载成一个虚拟分区脚本里走的是mount语义。用简单文本grep可能漏判但把整个升级脚本仔细读一遍基本不会漏。再就是如果你习惯在一个大工程里同时维护多个项目建议把“去掉preloader”的这个操作沉淀成一个独立脚本输入是old/new target_files输出是清理后的文件再加最终差分包。这样后续接同类需求一条命令就能解决不用每次手动进目录删来删去。我自己就是写了一个build_ota_no_preloader.sh把解包、清理、打包、生成差分包全串起来非常省事。脚本本身也可以纳入版本管理方便团队其他同事复用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价