
做Android系统定制的朋友应该都遇到过这种情况需求一句话很简单——把vendor指纹改掉结果从改mk到最终上板验证硬生生耗掉一整个下午。我这次碰到的问题是修改ro.vendor.build.fingerprint一直不生效板子跑起来后getprop看到的还是公版默认值一开始怀疑是没刷进去反复整刷了几轮都一样最后一路从镜像产物查到构建脚本才发现是踩了“属性生成位置”和“init加载顺序”两个坑。这篇文章就把这次排查的过程完整写下来包括fingerprint在Android构建系统里是怎么生成的、为什么改了不生效、最终是怎么解决的以及后续做同类修改时要注意的几个雷区。给正在做RK3566/RK3576这类方案定制或者被vendor分区属性折腾得头疼的朋友一个参考。1. 现象复盘改了这个属性之后板子稳如泰山1.1 需求来源与第一版改动这次项目背景是给一块RK3566方案的行业终端做系统定制应用层需要根据vendor指纹区分不同的项目和固件版本。需求本身不复杂一般做法就是在产品mk里定义一个定制值覆盖默认指纹。项目用的AOSP 12 SDK设备目录在device/rockchip/rk3566_xxx/下整体构建体系是RK公版SDK那一套。第一版改动我选择在device/rockchip/rk3566_xxx/rk3566_xxx.mk里追加快属性PRODUCT_VENDOR_PROPERTIES \ ro.vendor.build.fingerprintrockchip/rk3566_xxx/rk3566_xxx:12/RKQ1.211119.001/20240522:userdebug/test-keys编译方式也没有特殊处理直接增量编译后打包刷机。当时预期很简单vendor/build.prop里出现这个key刷进vendor分区系统起来就能读到。结果刷完机adb shell getprop ro.vendor.build.fingerprint输出的还是rk3566/rk3566_evb/rk3566_evb:12/RKQ1.211119.001/20240522:userdebug/test-keys自定义值完全没有出现。我一开始以为是自己刷机姿势不对于是重新整刷super分区又换了一个fastboot脚本结果一样板子根本不吃这一套。1.2 第一反应是不是没刷进去遇到属性不生效我第一反应是“镜像没更新”。Android 10以后vendor分区通常被合并进super分区如果只刷了boot/systemvendor里那点改动确实会丢失。所以我单独重新编译了vendorimage确认产物时间戳是新的然后整包刷入。刷完再查还是一样的旧值。此时我心里大概有数了不是刷机问题而是构建产物里可能压根没有这个key或者有key但被系统里另外的值覆盖了。当时我在板子上顺手试了adb shell setprop ro.vendor.build.fingerprint custom_value结果自然是失败的因为ro开头的属性在system_server起来之后不允许二次设置。如果要在运行期验证属性是否能改直接用setprop去动ro属性没什么意义更好的方式是查当前生效属性值和它的来源文件。这块排查我习惯先看三样东西adb shell getprop ro.vendor.build.fingerprint看当前生效值adb shell cat /vendor/build.prop | grep fingerprint看vendor文件里到底写了什么上解包工具或者在out目录里直接搜看最终打进镜像的vendor/build.prop内容。在板子上执行后发现/vendor/build.prop里确实有ro.vendor.build.fingerprint值却是公版默认不是我定义的那个。这一步基本把问题从“刷机问题”拉回到“构建系统没有按预期生成属性值”。2. fingerprint在Android构建系统里到底怎么生成的2.1 build.prop的生成链路要搞明白为什么不生效必须先弄清楚ro.vendor.build.fingerprint是从哪个脚本写进build.prop的。AOSP里system和vendor分区的build.prop不是同一个生成入口system/build.prop由build/make/tools/buildinfo.sh生成vendor/build.prop由build/make/tools/vendor_buildinfo.sh生成odm分区的build.prop也有类似的独立生成逻辑。这两个脚本读取的变量并不完全相同。以AOSP 12为例system build.prop里ro.build.fingerprint主要来自BUILD_FINGERPRINT这个值由构建系统根据产品名、设备名、Build ID、Build Number、构建类型拼出来拼装规则大致是$(PRODUCT_BRAND)/$(PRODUCT_NAME)/$(PRODUCT_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)vendor/build.prop里的ro.vendor.build.fingerprint则对应TARGET_VENDOR_BUILD_FINGERPRINT。这个变量在AOSP里的默认行为是“没有显式声明时就跟随BUILD_FINGERPRINT”也就是说如果只改BUILD_FINGERPRINT正常情况下vendor的值会跟着变如果只改PRODUCT_VENDOR_PROPERTIES则是在vendor build.prop的生成阶段追加额外属性。听上去很清晰但实际坑就藏在“默认跟随”和“追加属性”之间。2.2 属性到底写进哪个分区属性变量的归并表很多开发在这块吃过亏以为只要在mk里写了ro.vendor.*前缀构建系统就会自动把它放进vendor分区。实际上构建系统看的是“你用了哪个属性集变量”而不是“属性名是什么前缀”。我用一张表总结常见属性变量和写入位置变量名写入位置备注PRODUCT_PROPERTY_OVERRIDESsystem/build.prop早期版本常用新项目尽量少用PRODUCT_SYSTEM_PROPERTIESsystem/build.prop推荐用于system属性PRODUCT_VENDOR_PROPERTIESvendor/build.propvendor分区属性PRODUCT_ODM_PROPERTIESodm/etc/build.propodm分区属性PRODUCT_DEFAULT_PROPERTY_OVERRIDESdefault.prop启动早期加载我第一版用的PRODUCT_VENDOR_PROPERTIES确实应该写进vendor/build.prop理论上方向没错。但问题在于ro.vendor.build.fingerprint这个key不是普通vendor属性它是vendor_buildinfo.sh里已经存在的标准输出项会被构建系统后面的逻辑再次赋值所以我追加的属性很可能在生成过程中被覆盖掉。2.3 init上电后属性装载顺序为什么vendor会被system“抢答”这一步是整个排查的核心。Android init进程启动时会按固定顺序加载分区里的build.prop常见顺序大致是default.prop/system/build.prop/vendor/build.prop/odm/etc/build.proppersist属性关键在于ro开头的属性有一个“全局只有第一次设置生效”的机制。init先加载system/build.prop如果里面已经存在ro.vendor.build.fingerprint这个key就算被注册过了接着再到vendor/build.prop里遇到同样的key再想覆盖就会被忽略。有一个很常见的情况公版SDK的common.mk或system.prop里用PRODUCT_PROPERTY_OVERRIDES写了ro.vendor.build.fingerprint把这个理应属于vendor分区的属性塞进了system。结果就是vendor里改得再正确也敌不过system里的先发制人。我这个项目就是典型公版平台mk里有一行PRODUCT_PROPERTY_OVERRIDES \ ro.vendor.build.fingerprint$(BUILD_FINGERPRINT)这一行把ro.vendor.build.fingerprint写进了system/build.prop后面vendor/build.prop里即使存在自定义值或生成值init加载时也被system先抢占了。表现在设备上就是你改vendor属性改了个寂寞getprop永远输出system里那份旧值。3. 排查过程从“瞎改”到“看图说话”3.1 第一板斧直接grep源码树看看谁在写这个属性这种问题最忌讳继续盲目改mk然后反复编译太浪费时间。正确做法是先看“谁的代码在写这个属性”。我当时的命令是在源码根目录执行grep -rn ro.vendor.build.fingerprint \ --include*.mk \ --include*.prop \ --include*.sh \ device/ vendor/ build/结果很快暴露了问题除了vendor自动生成逻辑外device/rockchip/common/里确实有页面把ro.vendor.build.fingerprint加进了PRODUCT_PROPERTY_OVERRIDES。也就是说在构建system/build.prop的时候这个key就被人为地写进了system分区。这时候我再回头看自己的PRODUCT_VENDOR_PROPERTIES就明白了一半。另一半在于即便我把它正确写进vendor/build.prop由于init加载顺序中system在前、vendor在后vendor里的值也不可能生效。这里补充一个细节为什么系统属性合并时公版SDK那行值会“恰好”压过我的定制因为mk文件的include顺序会影响PRODUCT_PROPERTY_OVERRIDES最终合并顺序后include的命令在生成build.prop时往往覆盖先include的同名key。我新增的mk在项目目录平台公共mk在继承链更后面的位置于是“后写”的公版值反过来覆盖了我“先写”的定制值。3.2 第二板斧直接翻产物out目录源码树里看到影子还不够要在最终产物里确认。AOSP构建完成后out/target/product/rk3566_xxx/下会有完整的镜像文件系统目录。我直接查grep -n ro.vendor.build.fingerprint \ out/target/product/rk3566_xxx/system/build.prop \ out/target/product/rk3566_xxx/vendor/build.prop结果印证了判断system/build.prop里有ro.vendor.build.fingerprint值是公版 SDK 生成的默认指纹vendor/build.prop里也有ro.vendor.build.fingerprint值同样是公版默认我在PRODUCT_VENDOR_PROPERTIES里加的自定义值根本没有出现在产物里。到这一步问题已经从“可能没刷进去”转变为“构建时vendor/build.prop里没生成出我的值”。为什么PRODUCT_VENDOR_PROPERTIES写入失败再往深一层看AOSP的vendor build.prop生成逻辑里ro.vendor.build.fingerprint这类关键属性不是简单从属性集变量里读的而是直接由vendor_buildinfo.sh按变量生成。如果你没有把值真正传递给TARGET_VENDOR_BUILD_FINGERPRINT后加的PRODUCT_VENDOR_PROPERTIES会被排序后合并但未必能覆盖脚本默认输出的同名字段。换句话说普通PRODUCT_VENDOR_PROPERTIES适合加“新属性”想改“系统脚本固定输出的属性”就要从源头变量下手。3.3 第三板斧make -n看构建命令为了验证这个判断我操作了一下构建系统看vendor build.prop具体由哪条命令生成make -n vendorimage | grep -i buildinfo\|vendor_buildinfo输出里有类似这样的命令片段build/make/tools/vendor_buildinfo.sh \ out/target/product/rk3566_xxx/obj/vendor_build.prop这就明确了vendor/build.prop的生成入口是vendor_buildinfo.sh脚本内容就是从TARGET_VENDOR_BUILD_FINGERTIPRINT等变量里取值。所以我后来强制在BoardConfig里设置了自定义值并同步修改了相关变量才把vendor里的值真正“焊死”。3.4 最终定位三个层面的问题叠加总结一下这次排查有以下几个“案发点”生成层面ro.vendor.build.fingerprint不是普通vendor属性不能单纯靠PRODUCT_VENDOR_PROPERTIES追加覆盖必须从TARGET_VENDOR_BUILD_FINGERPRINT或构建脚本的取值源头处理产物层面公版SDK用PRODUCT_PROPERTY_OVERRIDES把该属性写进了system/build.prop污染了system分区加载层面init先加载system/build.prop再加载vendor/build.propsystem里已有的ro属性提前占坑vendor里即便有值也无法覆盖。任何一个层面单出都不至于这么折腾。三个问题叠一起就会呈现“改了不生效、换变量不生效、整刷还不生效”的诡异现象。4. 修改方案让vendor指纹真正生效的三步操作4.1 在正确的变量里写值第一件事是把vendor指纹的定义放到正确的构建变量。AOSP里系统提供了TARGET_VENDOR_BUILD_FINGERPRINT在BoardConfig.mk里设置即可TARGET_VENDOR_BUILD_FINGERPRINT : rockchip/rk3566_xxx/rk3566_xxx:12/RKQ1.211119.001/20240522:userdebug/test-keys这个变量会被vendor_buildinfo.sh读取直接写入vendor/build.prop的ro.vendor.build.fingerprint。它比PRODUCT_VENDOR_PROPERTIES更权威能覆盖默认生成逻辑。如果你不只是想改vendor而是想让整个产品的fingerprint统一成定制值建议在Product.mk里全局修改BUILD_FINGERPRINT并在BoardConfig里同步TARGET_VENDOR_BUILD_FINGERTIPRINT。这样ro.build.fingerprint和ro.vendor.build.fingerprint就不会互相矛盾。4.2 清理system里的“抢注”源光改vendor还不够system/build.prop里那行公版ro.vendor.build.fingerprint必须处理掉否则加载顺序决定了vendor里的自定义值永远被遮蔽。处理方式有两种第一种直接改平台公共mk。将device/rockchip/common/xxx.mk里PRODUCT_PROPERTY_OVERRIDES中的ro.vendor.build.fingerprint相关行删除或者改成PRODUCT_VENDOR_PROPERTIES。这种方案最干净但如果你所在项目不允许动公共SDK就需要第二种。第二种项目mk里做过滤。在产品mk里用filter-out把平台公共mk加进来的同名属性移除PRODUCT_PROPERTY_OVERRIDES : \ $(filter-out ro.vendor.build.fingerprint%,$(PRODUCT_PROPERTY_OVERRIDES))这种方法比较脏但适合合入责任边界比较严格的团队。需要注意在mk继承链上执行顺序要对必须确保平台公共mk先展开项目mk后过滤。4.3 完整重编与设备侧清理避免缓存误判修复后编译别急着用增量。我这次吃了增量编译的亏第一次只改mk不干净导致产物里老是残存旧值。推荐做法rm -rf out/target/product/rk3566_xxx/vendor make -j$(nproc) vendorimage如果你改了system侧属性还需要rm -rf out/target/product/rk3566_xxx/system make -j$(nproc) systemimage最后整包刷入时留意一下Android 10以上vendor在super里增量刷机只刷boot/system明显不够建议直接fastboot flashall或者整包升级。验证时优先在out目录确认产物cat out/target/product/rk3566_xxx/vendor/build.prop | grep fingerprint cat out/target/product/rk3566_xxx/system/build.prop | grep fingerprint产物没问题再上板避免反复无效刷机。4.4 一致性检查别只盯着一个属性指纹这类属性有一个特点相互关联。实际产品里和ro.vendor.build.fingerprint一起被读的通常还有ro.build.fingerprintro.odm.build.fingerprintro.bootimage.build.fingerprintro.build.version.security_patchro.vendor.build.security_patch如果只改vendor指纹不保留其他字段一致某些安全校验或者系统服务会认为分区之间“指纹不匹配”甚至可能触发回退逻辑。这次我后续处理时就把BUILD_FINGERPRINT、TARGET_VENDOR_BUILD_FINGERPTIPRINT、TARGET_ODM_BUILD_FINGERPRINT还有TARGET_BOOTIMAGE_BUILD_FINGERPRINT一起梳理了一遍确保它们指向同一个Build ID和构建类型。命令行板子上统一验证adb shell getprop | grep fingerprint adb shell cat /vendor/build.prop | grep fingerprint adb shell cat /system/build.prop | grep fingerprint三个输出一致才敢说这次改成功了。5. 常见问题与避坑清单5.1 常见坑速查表这次排查踩了不少坑整理成速查表方便以后遇到类似问题直接对照现象常见原因处理方向改了mk但out产物没变化增量编译缓存、mk include顺序不对删除对应产物目录强制重建system/build.prop里有vendor属性平台mk误用PRODUCT_PROPERTY_OVERRIDES过滤或移除该属性定义vendor里改了但getprop不动init先加载systemro属性被抢注清理system里的同名属性只改BUILD_FINGERPRINTvendor值没变vendor有独立变量控制显式设置TARGET_VENDOR_BUILD_FINGERPRINT刷完机还是旧值super分区里的vendor没更新整包刷入或单独刷vendor到super改了vendor属性导致部分服务异常指纹族之间不一致同步修改boot/odm/system相关指纹5.2 排查命令清单再分享几个好用的命令排查构建属性问题必备# 查看设备当前生效的所有指纹 adb shell getprop | grep fingerprint # 查看vendor分区里实际内容 adb shell cat /vendor/build.prop | grep fingerprint # 在源码树中搜索属性写入点 grep -rn ro.vendor.build.fingerprint --include*.mk --include*.prop --include*.sh device/ vendor/ build/ # 查看构建命令确认build.prop生成入口 make -n vendorimage | grep -i buildinfo\|vendor_buildinfo # 检查super分区中vendor镜像是否更新 fastboot getvar all | grep -i slot fastboot flash super super.img我在实际排障过程中sys.system.build.fingerprint这类属性也要顺手看下。Android 10以后有些版本使用带.sys.或.bootimage.前缀的衍生属性单独排查主属性容易漏掉它们。5.3 操作心得这次事件给我最大的一个教训构建系统里“添加同名属性”和“覆盖已有属性”是两码事。普通属性用PRODUCT_VENDOR_PROPERTIES追加没问题但ro.vendor.build.fingerprint这种由vendor_buildinfo.sh直接生成的“系统保留属性”绕开系统变量去硬加就是徒劳。如果你也想让vendor指纹真正生效建议直接动手设置TARGET_VENDOR_BUILD_FINGERPRINT这个变量就是它的唯一正确入口。另外项目里建议加一个产物自检脚本在构建完成后自动检查out/target/product/xxx/vendor/build.prop和system/build.prop里的指纹是否满足预期。有了这道CI拦路后面再有人误改属性或者平台SDK偷偷塞属性一下子就能暴露出来不用等到刷机才发现。