Android Framework 常见解决方案(03)android系统 资源文件添加:TaoToken 统一 Key 通道下的编译验证
1. 从一次 system.img 里找不到资源文件说起Android Framework 开发里有个很典型的场景你往frameworks/base/data/etc/放了一个资源文件Android.mk也照着模板改了单模块编译mmm能过out/target/product/xxx/system/etc/下确实能看到文件。可一旦跑整编make -j刷进设备后adb shell ls /system/etc/就是找不到它。这个问题在 Android 系统源码资源文件添加、Android.mk 参与系统编译的链路里非常高频尤其是 Android O 之后PRODUCT_PACKAGES和PRODUCT_COPY_FILES两套机制并存很容易只做了一半。这篇聚焦的就是这条完整链路资源文件res/、assets/、data/etc/下的预置文件怎么通过Android.mk进入系统编译怎么被PRODUCT_PACKAGES或PRODUCT_COPY_FILES收进system.img以及编译完成后怎么验证它真的打进去了。同时因为现在做 Framework 开发经常要多个工具协作——本地编译、远程构建机、代码助手、模型对话——鉴权配置如果每个工具一套 Key管理起来很乱。我会顺带说明怎么用 TaoToken 统一 Key/API 通道把这些工具的鉴权收敛到一处让编译验证和工具调用不互相干扰。适合谁看正在改 Android 系统源码、需要往 system 分区加资源文件的 Framework 工程师被「单编能过、整编丢失」坑过的同学以及想把手上一堆 AI 编码工具鉴权统一管理的开发者。下面所有命令和 mk 片段都可以直接复制改路径使用。2. TaoToken 统一 Key 通道多工具协作时的鉴权前置做 Android 系统编译验证时工具链往往不止一个。你可能本地用 Claude Code 或 Cline 读代码、远程构建机上跑make、再用某个模型对话工具查报错。如果每个工具都单独配一套 API Key切换环境时很容易搞混尤其是构建机是共享的Key 散落在各个配置文件里不好管。TaoToken 在这里的作用是把这些工具的鉴权收敛到一个统一通道。它的 API 入口是https://taotoken.net/api你只需要在 TaoToken 控制台生成一个 Key然后让各个工具都指向这个 Base URL 和同一个 Key。这样本地、构建机、代码助手用的是同一套凭证换机器时只改一处。具体操作上先去控制台创建 Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentconsole创建完在 API Keys 页面复制出来https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi-keys拿到 Key 之后不同工具的接入方式略有差异但核心三件套是一样的Base URL、Key、Model ID。以 Claude Code 这类工具为例配置里需要写全这三项缺一个都会在请求时报鉴权或模型找不到的错。如果你用的是 Coding Plan 做长期编码任务可以在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding-plan查看套餐和接入说明。需要验证某个模型是否可用时直接用模型对话页面测一条请求最省事https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel-chat这里要强调一点TaoToken 是统一鉴权通道不是让你绕过任何本地编译流程。Android 系统编译该跑的source build/envsetup.sh、lunch、make一步都不能少它只是让你在调用外部工具时不用维护多套 Key。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc把 Key 配好之后回到正题——资源文件怎么进 system.img。这两件事是并行的鉴权通道解决工具协作Android.mk 解决编译产物。3. 可复制的 Android.mk 与 PRODUCT 配置片段这一节是核心直接给可复制的配置。以在系统中构建一个新资源system/etc/cursor.png为例路径和原文保持一致。第一步创建资源文件本身frameworks/base/data/etc/cursor.png第二步修改同目录下的frameworks/base/data/etc/Android.mk。在文件末尾追加一段BUILD_PREBUILT模块定义######################## include $(CLEAR_VARS) LOCAL_MODULE : cursor.png LOCAL_MODULE_CLASS : ETC LOCAL_MODULE_PATH : $(TARGET_OUT_ETC) LOCAL_SRC_FILES : $(LOCAL_MODULE) include $(BUILD_PREBUILT)这里几个变量的含义要清楚LOCAL_MODULE_CLASS : ETC决定它归到 etc 类LOCAL_MODULE_PATH : $(TARGET_OUT_ETC)决定最终落到system/etc/LOCAL_SRC_FILES指向同目录的文件。改完这一步单模块编译mmm frameworks/base/data/etc/就能在 out 目录看到产物。但整编时它不会自动进 system.img因为整编走的是 product 的包列表。所以第三步在build/make/target/product/base.mk的PRODUCT_PACKAGES里加上模块名PRODUCT_PACKAGES \ netd \ ping \ ping6 \ cursor.png \ platform.xml \ privapp-permissions-platform.xml \ pppd第四步如果这个资源还需要通过拷贝方式进镜像比如某些 sdk_base 场景在build/make/target/product/sdk_base.mk的PRODUCT_COPY_FILES里补一行PRODUCT_COPY_FILES : \ frameworks/native/data/etc/android.hardware.fingerprint.xml:system/etc/permissions/android.hardware.fingerprint.xml \ frameworks/native/data/etc/android.software.autofill.xml:system/etc/permissions/android.software.autofill.xml \ frameworks/av/media/libeffects/data/audio_effects.conf:system/etc/audio_effects.conf \ device/generic/goldfish/audio_policy.conf:system/etc/audio_policy.conf \ frameworks/base/data/etc/cursor.png:system/etc/cursor.pngPRODUCT_PACKAGES和PRODUCT_COPY_FILES的区别值得记一下前者是「编译出一个模块再装进镜像」后者是「直接把源文件拷到目标路径」不经过模块系统。预置二进制、配置类文件常用 COPY_FILES需要走模块依赖或带 class 的用 PACKAGES。两者可以同时存在但别对同一个文件重复配置否则可能出现覆盖顺序问题。如果你在工具协作时用到了 Cline MCP 或 Codex 的auth.json记得把 Base URL、Key、Model ID 三件套写全。Codex 的auth.json里字段名和 Claude Code 不同但语义一致缺 Model ID 会在请求阶段直接报模型不存在。Cline 的 MCP 配置同理Base URL 指向https://taotoken.net/apiKey 用控制台生成的那一个。配置改完编译命令按标准流程走source build/envsetup.sh lunch aosp_arm64-userdebug make -j$(nproc) systemimage只想快速验证资源模块可以先单编mmm frameworks/base/data/etc/单编通过后再整编能省不少时间。4. 验证资源是否成功打入 system.img编译过了不等于打进镜像了这一步必须实测。有三种验证方式从快到慢。第一种直接看 out 目录的中间产物。整编完成后ls -l out/target/product/device/system/etc/cursor.png如果这里没有说明PRODUCT_PACKAGES或PRODUCT_COPY_FILES没生效回到第 3 节检查拼写和路径。注意device换成你lunch时选的 target。第二种检查 system.img 内部。用simg2img把稀疏镜像转成 raw再挂载或解包simg2img out/target/product/device/system.img system_raw.img mkdir -p /mnt/sysimg sudo mount -o loop system_raw.img /mnt/sysimg ls -l /mnt/sysimg/system/etc/cursor.png sudo umount /mnt/sysimg如果挂载不方便也可以用debugfs直接读debugfs -R ls -l /system/etc system_raw.img第三种刷机后真机验证这是最可靠的adb root adb remount adb shell ls -l /system/etc/cursor.png adb shell md5sum /system/etc/cursor.png把设备上的 md5 和源文件对比md5sum frameworks/base/data/etc/cursor.png两个值一致说明文件完整打进去了。我试过在整编后只做第一种检查就以为万事大吉结果刷机发现文件是旧版本——因为增量编译缓存了旧的 system.img 片段。所以真机 md5 对比这一步别省。如果验证时发现文件存在但内容不对多半是PRODUCT_COPY_FILES的源路径写错或者有另一个模块覆盖了同一目标路径。用grep -rn cursor.png build/ frameworks/base/全局搜一下看有没有重复定义。5. 本篇常见报错与排查对照这一节列真实会遇到的报错对照着查。报错一build/make/core/main.mk:xxx: *** PRODUCT_PACKAGES: cursor.png not found这是模块名没被模块系统识别。原因通常是Android.mk没被 include 进当前 product 的模块扫描路径或者LOCAL_MODULE拼写和PRODUCT_PACKAGES里的不一致。检查frameworks/base/data/etc/Android.mk是否在build/make/target/product/的扫描范围内以及模块名大小写。Android 模块名区分大小写。报错二单编能过整编后 out 目录没有文件典型的两套机制只做了一半。Android.mk定义了模块但PRODUCT_PACKAGES没加整编就不会把它装进镜像。回到base.mk补上模块名。反过来如果只加了PRODUCT_COPY_FILES没定义模块拷贝能生效但模块依赖链里没有它某些场景会丢。报错三401 Unauthorized或local proxy failed这类是工具鉴权问题和编译无关。如果你在编译过程中调用了外部代码助手或模型工具报 401 说明 Key 没配或过期local proxy failed说明 Base URL 指向不对。检查三件套Base URL 是否为https://taotoken.net/api、Key 是否从控制台正确复制、Model ID 是否填了有效值。三个都对了还报错去接入文档核对字段名https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc报错四reading choices相关解析错误这通常出现在模型返回内容解析阶段工具拿到的响应格式和预期不符。先确认 Model ID 和工具要求的模型类型匹配再确认 Base URL 没有多余路径。有些工具会在 Base URL 后自动拼/v1/chat/completions如果你填的 URL 已经带了路径就会重复拼接。报错五OAuth 相关失败部分工具用 OAuth 流程而非直接 Key。这种情况下仍然需要把 Base URL 指向统一通道OAuth 的 token 获取地址和 API 地址要分开配。如果工具只支持 Key 模式就退回 Key 方式别混用。报错六刷机后文件是旧版本增量编译缓存导致。执行make installclean或删掉out/target/product/device/system/下对应文件再整编。最稳的是make clean后全量编一次虽然慢但能排除缓存干扰。排查顺序建议先看编译日志有没有模块相关 warning再看 out 中间产物最后真机验证。三步定位比盲目重编快得多。6. 把鉴权通道和编译链路分开管回到开头那个问题资源文件进不了 system.img根因几乎都在PRODUCT_PACKAGES/PRODUCT_COPY_FILES这一层和工具鉴权是两件独立的事。我的做法是把它们彻底分开管——编译链路只关心 mk 配置和 product 包列表工具鉴权统一走 TaoToken 一个 Key。这样分工的好处是构建机上不用为每个工具维护不同凭证换人换机器只改一处 Base URL 和 Key。需要长期跑编码或 Agent 任务时用 Coding Plan 的接入方式https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding-plan临时验证模型或查报错直接开模型对话测一条https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel-chatKey 管理和文档入口固定在这两个地方收藏一下省得每次翻https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi-keys https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc最后留一个实用习惯每次改完Android.mk和 product 配置先mmm单编确认模块能出再整编最后一定做一次真机 md5 对比。这三步做完资源文件进 system.img 这件事基本不会再翻车。编译验证和工具鉴权各管各的出问题时排查范围也清晰。