上一篇https://blog.csdn.net/qq_27672101/article/details/156652853是结合google官方文档的说明,来对VINTF这套机制进行了一个详细的说明,包括我们如何配置使用这套机制。

本篇是从源码角度来解析一下VINTF具体是如何设计的,其AOSP源码路径在system/libvintf/:

先看看编译入口Android.bp文件,主要声明了两个东西,libvintf和vintf:

  • libvintf.so:运行在设备中,伟设备提供了vintf关键操作的库,例如hal层服务注册的时候调用此库getDeviceManifest此类方法获取校验vintf。
  • vintf.bin:运行在设备中,调试的时候可以输入adb shell vintf -h等命令来打印当前设备vintf相关配置。
  • assemble_vintf.bin:运行在host编译主机上,合并配置的所有设备清单和兼容性矩阵。
  • checkvintf.bin:运行在host编译主机上,全量编译或者OTA包制作的时候调用checkvintf --check-compat命令进行兼容性检测
  • vintffm.bin:运行在host编译主机上,只在编 system且不编 vendor 镜像(典型 GSI)时启用,我们基本上用不上

他们之间的关系如下:

一、libvintf.so核心功能

首先先介绍一下libvintf.so,因为此库封装了vintf机制所需要的核心功能和核心api,先看看其定义:

1、VintfObject单例对象

VINTF设计了一个单例对象VintfObject来对DM/FCM这些配置进行解析,例如最核心的能力就是拼接所有的dm/fcm这些配置,存放到数据库中。先提供一张封装图:

VintfObject提供了如下接口,其中标红的接口是我们常用的:

静态/成员 API 作用
GetInstance() 单例,线程安全,带缓存
GetDeviceHalManifest() 组装 vendor/odm/product/APEX 的 Device Manifest
GetFrameworkHalManifest() 组装 system/system_ext 等的 Framework Manifest
GetDeviceCompatibilityMatrix() Device Matrix(厂商对系统的要求)
GetFrameworkCompatibilityMatrix() Framework Matrix(系统对厂商的要求,按 FCM level 选)
GetRuntimeInfo(flags) 内核版本、config.gz、sepolicy、CPU 等
checkCompatibility(error, flags) 整机三路兼容检查(见下)
checkDeprecation(...) 是否使用已废弃 HAL
checkUnusedHals(...) Device Manifest 里是否有 FCM 未要求的 HAL
checkMissingHalsInMatrices(...) FCM 是否漏声明 HAL
getKernelLevel() 内核 FCM level

2、GetDeviceHalManifest清单获取

3、checkCompatibility整机检测

//aosp/system/libvintf/VintfObject.cpp
int32_t VintfObject::checkCompatibility(std::string* error, CheckFlags::Type flags) {
    status_t status = OK;
    // ========== 阶段 1:前置检查 — 四份 XML 元数据是否都能加载 ==========
    // 以下 Get* 会触发 fetch:从 /system、/vendor、/odm、APEX 等路径读 XML 并缓存
    // Framework Manifest:系统分区声明“我提供了哪些 HAL”
    if (getFrameworkHalManifest() == nullptr) {
        appendLine(error, "No framework manifest file from device or from update package");
        status = NO_INIT;  // 仍继续检查其它项,把能发现的错误都记下来
    }
    // Device Manifest:厂商分区声明“我提供了哪些 HAL”
    if (getDeviceHalManifest() == nullptr) {
        appendLine(error, "No device manifest file from device or from update package");
        status = NO_INIT;
    }
    // Framework Compatibility Matrix:系统对厂商的要求(按 device level 选对应 FCM)
    if (getFrameworkCompatibilityMatrix() == nullptr) {
        appendLine(error, "No framework matrix file from device or from update package");
        status = NO_INIT;
    }
    // Device Compatibility Matrix:厂商对系统的要求
    if (getDeviceCompatibilityMatrix() == nullptr) {
        appendLine(error, "No device matrix file from device or from update package");
        status = NO_INIT;
    }
    // RuntimeInfo:/proc、config.gz、sepolicy 等运行时信息(可选,由 flags 控制)
    // 默认 flags=DEFAULT 会启用 Runtime 检查,但默认关闭 AVB 子检查
    if (flags.isRuntimeInfoEnabled()) {
        if (getRuntimeInfo() == nullptr) {
            appendLine(error, "No runtime info from device");
            status = NO_INIT;
        }
    }
    // 任一加载失败则直接返回,不做后续“逻辑兼容性”比对,返回值是负的 status_t(如 -ENODEV),不是 INCOMPATIBLE(1)
    if (status != OK) return status;

    // ========== 阶段 2:Device Manifest ↔ Framework Matrix ==========
    // 语义:厂商声明提供的 HAL/版本/实例,是否满足系统 FCM 的最低要求
    // 实现落在 HalManifest::checkCompatibility()(比对该侧 HAL、VNDK、sepolicy 等)
    if (!getDeviceHalManifest()->checkCompatibility(*getFrameworkCompatibilityMatrix(), error)) {
        if (error) {
            // 在已有详细错误前加一行总述,便于 log 里一眼看出是哪一对不匹配
            error->insert(0, "Device manifest and framework compatibility matrix are incompatible: ");
        }
        return INCOMPATIBLE;  // 明确的不兼容,返回 1
    }

    // ========== 阶段 3:Framework Manifest ↔ Device Matrix ==========
    // 语义:系统声明提供的 HAL,是否满足厂商 DCM 的要求(反向契约)
    if (!getFrameworkHalManifest()->checkCompatibility(*getDeviceCompatibilityMatrix(), error)) {
        if (error) {
            error->insert(0,  "Framework manifest and device compatibility matrix are incompatible: ");
        }
        return INCOMPATIBLE;
    }

    // ========== 阶段 4:Runtime Info ↔ Framework Matrix(可选)==========
    // 检查内核版本、kernel config、sepolicy 版本等是否满足 FCM 中 <kernel> 等要求
    // Zygote 开机检查常用 DISABLE_AVB;完整 OTA 检查可能带 --kernel 并开启更多项
    if (flags.isRuntimeInfoEnabled()) {
        if (!getRuntimeInfo()->checkCompatibility(*getFrameworkCompatibilityMatrix(), error, flags)) {
            if (error) {
                error->insert(0,  "Runtime info and framework compatibility matrix are incompatible: ");
            }
            return INCOMPATIBLE;
        }
    }

    // 三路(或两路,若关闭 Runtime)整机全部通过
    return COMPATIBLE;  // 0
}

如上函数逻辑,总结如下:

  • 获取dm fcm dcm fm配置,加载失败直接返回错误
  • 进行第一路检测:Device Manifest ↔ Framework Matrix
  • 进行第二路检测:Framework Manifest ↔ Device Matrix
  • 进行第三路检测:Runtime Info ↔ Framework Matrix

那么核心问题来了,那些地方会调用此函数进行整机检测?

调用场景 位置/方式 失败后果
开机检查

Build.isBuildConsistent() 

→ VintfObject.verifyBuildAtBoot() 

→ checkCompatibility()

不会重启

弹系统错误对话框

输出日志Slog.e

编译全量检查

Makefile 

→ checkvintf --check-compat

当PRODUCT_ENFORCE_VINTF_MANIFEST=true 时

m 编译失败(exit 1)
OTA / target_files

check_target_files_vintf.py 

→ checkvintf  --check-compat

打包/OTA 校验失败
手动调试 adb shell vintf / vintf legacy

仅打印 INCOMPATIBLE

不拦系统

assemble_vintf -c 组装 XML 时可选检查 该 Make 规则失败
单元测试 libvintf / vintf_object_tests

个人觉得比较重要的地方就是全量编译的时候调用此函数,这块后续继续解析;另外就是开机检查但是只是报错,不会导致系统重启,暂时没有遇到这块问题,最后就是HAL层服务注册,这里的逻辑并没有调用此函数,细节后续解析。

二、assemble_vintf编译合并

assemble_vintf工具被定义在cc_binary_host,表示此可执行文件不会被安装在设备中,而是被安装在PC端,即在编译android镜像的时候,编译工具会编译打包安装cc_binary_host声明的模块。

我们继续看看编译日志来进行佐证:

1、主函数

那么如何使用assemble_vintf工具呢?先看看assemble_vintf能提供那些参数?

如上逻辑如下:

  • 解析 -i / -o / -m / -c / --kernel / -l / -n ...
  • 打开输入/输出/check 文件,设置模式标志
  • assembleVintf->assemble()   // 真正干活

那么有那些参数呢?这些参数有什么差别呢?

参数 作用
-i a.xml:b.xml 多输入;: 分隔;后续文件主要贡献 <hal>
-o out.xml 输出路径
-m 由 manifest 生成 兼容矩阵骨架(不是合并 matrix)
-c matrix.xml 组装后与对侧文件做 compat(需 PRODUCT_ENFORCE_VINTF_MANIFEST=true)
--kernel ver:config 写入 kernel 要求(FCM 或 device manifest)
-l / -n 输出只要 HAL / 不要 HAL

assemble_vintf 主要作用就是用来生成/合并 etc/vintf 下的XML文件,主要如下:

  • assemble_vintf 是编译期的 VINTF XML「组装器」
  • 把多个片段/源文件合并成一份合法的 manifest 或 matrix
  • 填入编译时才能确定的字段->校验 XML->再写入即将打进镜像的 */etc/vintf/

白话文翻译:就是可以通过assemble_vintf去合并配置的所有设备清单和兼容性矩阵

注意事项:它不负责整机 DM↔FCM 兼容性终检(那是 checkvintf),也不在设备上运行

维度 说明
是什么 Host 端 cc_binary_host,system/libvintf 的一部分
主要作用 编译期合并 VINTF 片段、注入 build 变量、校验并输出最终 XML
何时跑 编 vendor_manifest、vintf_fragment、APEX VINTF 等模块时由 Make/Soong 自动调用
不做什么 不做整机 checkCompatibility 终检;不装到手机;不替代 checkvintf
工作本质 把「分散的声明 + 编译环境」变成「镜像里那一份标准 manifest/matrix」

2、如何合并设备清单文件?

最后剩下的疑问就是,编译系统会来调用这个工具吗?是的!

无论你执行单编命令mm还是执行全编命令,若产品依赖了下面这些模块,Ninja 就会自动执行对应规则里的 assemble_vintf。

1)vintf_fragment bp文件直接模块指定

在第二章有提到配置设备清单的最优方法,就是在android.bp中通过vintf_fragment对某个模块指定设备清单文件,最后也会走到如下代码逻辑,最终调用了assemble_vinft工具:

  • 编译系统:Soong机制
  • 代码路径:aosp/build/soong/android/vintf_fragment.go
  • 输出路径:vendor/etc/vintf/manifest/目录下,详细可以参考案例三中针对fragment和manifest的差别
  • 使用方式:

vintf_fragment {
    name: "xxx",
    src: "manifest_xxx.xml",
}

2)DEVICE_MANIFEST_FILE宏控配置

在第二章有提到配置设备清单的通用方法,其中之一就是配置DEVICE_MANIFEST_FILE宏控就是指定xml配置文件,最后就会走到如下代码逻辑,最终调用了assemble_vinft工具:

  • 编译系统:Soong机制
  • 代码路径:asop/build/soong/android/vintf_data.go
  • 输出路径:
  • DEVICE_MANIFEST_FILE     --->  生成 /vendor/etc/vintf/manifest.xml
  • ODM_MANIFEST_FILES       ----> 生成 /odm/etc/vintf/manifest.xml
  • SYSTEM_MANIFEST_FILE   ----> 生成 /system/etc/vintf/mainfest.xml
  • PRODUCT_MANIFEST_FILES  ----> 生成/product/etc/vintf/manifest.xml

3)DEVICE_MANIFEST_SKUS宏控配置

在第二章有提到使用SKU的方式配置设备清单,即向DEVICE_MANIFEST_SKUS宏控指定所有sku的名称,此种方式最后就会走到如下代码逻辑,最终调用了assemble_vinft工具:

  • 编译系统:Make机制
  • 代码路径:aosp/build/make/target/board/android-info.mk
  • 使用方式:

  • 注意事项:此时只是编译阶段单纯的合并manifest文件,针对SKU的区分是在运行时执行,详细参考后文

4)LOCAL_VINTF_FRAGMENTS宏控

在第二章中有提到make编译机制同样提供了指定设备清单文件的LOCAL_VINTF_FRAGMENTS宏控,此种方式最后就会走到如下代码逻辑,最终调用了assemble_vinft工具:

  • 编译系统:make机制
  • 代码路径:asop/build/make/core/base_rules.mk
  • 注意事项:copy-vintf-manifest-checked函数就是用来拷贝vintf xml文件的

5)copy-vintf-manifest-checked

  • 编译系统:make机制
  • 代码路径:build/make/core/definitions.mk
  • 主要作用:make机制提供了通用的指定manifest文件的机制,即可以自定义宏控,指向此函数即可

6)build/soong/apex/builder.go

  • 何时:APEX 包内带有 VINTF manifest/XML
  • 命令:与 fragment 类似,assemble_vintf -i $in -o $out,再打进 APEX

3、如何合并兼容性矩阵文件?

既然设备清单文件是通过合并的,那么针对Compatibility Matrix也是调用assemble_vintf工具进行合并的吗?

先给一个结论,fcm相对来说更加复杂一些,因为涉及到不同版本的兼容,因此采用了如下策略:

  • FCM 的「片段 + kernel 要求」在编译时用 assemble_vintf 拼好
  • 「按 device level 选哪几份 FCM 再合成」在运行时用 libvintf 做,不是 assemble 成一个大 XML
矩阵类型 编译期是否用 assemble_vintf 是否合并成「一个 FCM 文件」
Framework FCM(各 Level,如 .5.xml、.202404.xml) 是(vintf_compatibility_matrix 模块) 否:每个 Level 单独 生成一份,装进 system/etc/vintf/
设备扩展 FCM(compatibility_matrix.device.xml) 单独一份
Product FCM(product/.../compatibility_matrix.xml) 单独一份
Device Matrix(DCM,vendor) 是(vintf_data type=device_cm) 通常一份 vendor/.../compatibility_matrix.xml
多份 FCM 合成「当前设备用的那一份」 不用 assemble_vintf 由 libvintf 在开机/checkvintf 

1)vintf_compatibility_matrix

  • 编译机制:soong机制
  • 代码路径:hardware/interfaces/compatibility_matrices/build/vintf_compatibility_matrix.go
  • 模块类型:vintf_compatibility_matrix

每个 FCM Level 在 hardware/interfaces/compatibility_matrices/Android.bp 里各是一个模块,最终是通过assemble_vintf工具完成。例如:

vintf_compatibility_matrix {

name: "framework_compatibility_matrix.202404.xml",

stem: "compatibility_matrix.202404.xml",

srcs: ["compatibility_matrix.202404.xml"],

kernel_configs: ["kernel_config_v_6.1", "kernel_config_v_6.6"],

}

构建命令本质为:

POLICYVERS=... PLATFORM_SEPOLICY_VERSION=... FRAMEWORK_VBMETA_VERSION=... \

assemble_vintf -i compatibility_matrix.202404.xml:kernel_config_...:... \

-o compatibility_matrix.202404.xml

注意:kernel 片段由 kernel_configs 模块生成后,作为 -i 的额外输入

AssembleVintf.cpp 里对 Framework Matrix 会:

  • 合并多个输入 matrix 片段(CompatibilityMatrix::combine(...))
  • 注入 sepolicy、AVB 等编译期变量
  • 通过 assembleFrameworkCompatibilityMatrixKernels() 写入 <kernel> 要求

2)system_compatibility_matrix

system_compatibility_matrix不是合并的产物, 即aosp原生那套兼容版本的配置,不是由assemble_vintf工具进行合并拼接,而是有phony脚本依赖

phony {

name: "system_compatibility_matrix.xml",

required: [

"framework_compatibility_matrix.5.xml",

"framework_compatibility_matrix.6.xml",

...

"framework_compatibility_matrix.device.xml",

],

}

这是 phony 依赖:保证各 Level 的 FCM 都编出来并安装,不会用 assemble_vintf 把它们合成一个 system_compatibility_matrix.xml 文件。

镜像里实际是:

/system/etc/vintf/compatibility_matrix.5.xml

/system/etc/vintf/compatibility_matrix.6.xml

...

/system/etc/vintf/compatibility_matrix.device.xml # 设备扩展

/product/etc/vintf/compatibility_matrix.xml # product 扩展(若有)

3)DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE宏控

https://blog.csdn.net/qq_27672101/article/details/156652853中有介绍可以通过此宏控来配置单独模块的FCM配置文件,此宏控DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE包括DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE宏控最终也是调用assemble_vintf工具进行合并拼接。主要逻辑如下:

三、checkvintf编译检测

checkvintf工具被定义在cc_binary_host,表示此可执行文件不会被安装在设备中,而是被安装在PC端,即在编译android镜像的时候,编译工具会编译打包安装cc_binary_host声明的模块。

我们继续来看看编译日志:

从上日志可以看出在编译过程中会编译生成checkvintf.bin可执行文件,并且还执行了checkvintf相关命令:

[2026-05-18T23:16:50.692Z] 2026-05-19 07:16:46 - check_target_files_vintf.py - INFO    : Command `checkvintf --check-compat

--dirmap /apex:/tmp/APEXdf5d_3kt

--dirmap /odm:/tmp/merge_target_files_hu1faxfn/output/ODM

--dirmap /product:/tmp/merge_target_files_hu1faxfn/output/PRODUCT

--dirmap /system:/tmp/merge_target_files_hu1faxfn/output/SYSTEM

--dirmap /system_ext:/tmp/merge_target_files_hu1faxfn/output/SYSTEM_EXT

--dirmap /vendor:/tmp/merge_target_files_hu1faxfn/output/VENDOR

--kernel /tmp/merge_target_files_hu1faxfn/output/META/kernel_version.txt:/tmp/merge_target_files_hu1faxfn/output/META/kernel_configs.txt

--property ro.product.first_api_level=30

--property ro.boot.product.hardware.sku=wifi

--property ro.boot.product.vendor.sku=lahaina

` returns 'compatible'

从如上命令有个重大发现,后面跟了一些属性sku属性相关,这个后续会介绍。

1、主函数

同样该工具主函数提供了如下几种参数模式:

主函数具体如何解析这些参数可以参考如下代码:

//aosp/system/libvintf/check_vintf.cpp
int main(int argc, char** argv) {
    android::base::SetLogger(android::vintf::details::Logger);
    using namespace android::vintf;
    using namespace android::vintf::details;
    // ─────────────────────────────────────────────────────────────
    // 分支 A:Legacy 简易模式(两个位置参数,无 '-' 开头)
    // 用法:checkvintf device_manifest.xml framework_matrix.xml
    // 直接读两个文件,做 manifest.checkCompatibility(matrix),打印 true/false
    // ─────────────────────────────────────────────────────────────
    if (argc == 3 && *argv[1] != '-' && *argv[2] != '-') {
        int ret = checkCompatibilityForFiles(argv[1], argv[2]);
        if (ret >= 0) return ret;  // 0=兼容, 1=不兼容;<0 则继续走下面标准解析
    }

    // ─────────────────────────────────────────────────────────────
    // 分支 B:标准 CLI 模式,解析所有长/短选项到 multimap<Option, string>
    // ─────────────────────────────────────────────────────────────
    Args args = parseArgs(argc, argv);
    if (!iterateValues(args, HELP).empty()) {
        return usage(argv[0]);  // 打印帮助到 stderr,返回 EX_USAGE
    }

    // 提前解析公共参数(后续多个模式都会用到)
    // dirmap 例:--dirmap /system:/out/.../system --dirmap /vendor:...
    auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
    // properties 例:-D ro.product.first_api_level=33
    auto properties = getProperties(iterateValues(args, PROPERTY));

    // ─────────────────────────────────────────────────────────────
    // 模式 1:--dump-file-list
    // 列出设备上 check-compat 需要读取的文件路径(供 adb pull / 解包 OTA)
    // ─────────────────────────────────────────────────────────────
    if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
        // SKU 影响 ODM manifest 路径选择(多 SKU 机型)
        auto it = properties.find("ro.boot.product.hardware.sku");
        const std::string sku = it == properties.end() ? "" : it->second;
        for (const auto& file : dumpFileList(sku)) {
            std::cout << file << std::endl;  // 每行一个绝对路径,如 /vendor/etc/vintf/...
        }
        return 0;  // 成功,不涉及兼容性判断
    }

    // ─────────────────────────────────────────────────────────────
    // 模式 2:--check-one
    // 只检查单个分区(/system 或 /vendor),编译期 system/vendor 分步检查用
    // ─────────────────────────────────────────────────────────────
    if (!iterateValues(args, CHECK_ONE).empty()) {
        return checkOne(dirmap, properties);  // EX_OK 或 EX_SOFTWARE
    }

    // ─────────────────────────────────────────────────────────────
    // 模式 3:--check-compat(默认主模式)
    // 必须显式带有 -c / --check-compat,否则下面会 usage()
    // ─────────────────────────────────────────────────────────────
    auto checkCompat = iterateValues(args, CHECK_COMPAT);
    if (checkCompat.empty()) {
        return usage(argv[0]);  // 未指定模式 → 打印帮助并退出
    }

    // --rootdir=/path 等价于 --dirmap /:/path(整棵设备树根在 host 某目录)
    auto rootdirs = iterateValues(args, ROOTDIR);
    if (!rootdirs.empty()) {
        if (std::distance(rootdirs.begin(), rootdirs.end()) > 1) {
            LOG(ERROR) << "ERROR: Can't have multiple --rootdir options";
            return usage(argv[0]);
        }
        // 插入一条映射:设备路径 "/" → host 上的 rootdir
        args.emplace(DIR_MAP, "/:" + *rootdirs.begin());
        // 注意:若用户同时传了 --dirmap,getDirmap 时两条都会生效(multimap)
    }

    // 可选:--kernel <version或release文件>:<config文件路径>
    // 用于 RuntimeInfo vs FCM 的内核/config 检查;不传则跳过 Runtime 相关检查
    std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
    auto kernelArgs = iterateValues(args, KERNEL);
    if (!kernelArgs.empty()) {
        runtimeInfo = getRuntimeInfo(kernelArgs);
        if (runtimeInfo == nullptr) {
            return usage(argv[0]);  // kernel 参数格式错误
        }
    }

    // check-compat 必须能映射到至少一个分区目录
    if (dirmap.empty()) {
        LOG(ERROR) << "ERROR: Missing --rootdir or --dirmap option.";
        return usage(argv[0]);
    }
    // ─────────────────────────────────────────────────────────────
    // 核心:整机 VINTF 检查
    // 构建 Host 版 VintfObject,执行 checkCompatibility / deprecation / unusedHals 等
    // ─────────────────────────────────────────────────────────────
    auto compat = checkAllFiles(dirmap, properties, runtimeInfo);
    // ─────────────────────────────────────────────────────────────
    // 结果处理:stdout 给脚本解析,stderr 给人看详情
    // ─────────────────────────────────────────────────────────────
    if (compat.ok()) {
        std::cout << "COMPATIBLE" << std::endl;
        return EX_OK;           // 0,Makefile / OTA 脚本认为通过
    }
    if (compat.error().code() == 0) {
        // 元数据能加载,但逻辑上不兼容(DM↔FCM 等失败)
        LOG(ERROR) << "ERROR: files are incompatible: " << compat.error();
        std::cout << "INCOMPATIBLE" << std::endl;
        return EX_DATAERR;      // 65,表示「数据/契约不兼容」
    }
    // 加载失败、XML 解析失败、缺少 manifest 等(负的 status_t)
    LOG(ERROR) << "ERROR: " << strerror(compat.error().code()) << ": " << compat.error();
    return EX_SOFTWARE;         // 70,表示工具/环境错误
}

如上主函数逻辑可以总监如下几个流程:

  • 参数如果是两个文件,者直接调用checkCompatibilityForFiles去检测是否符合VINTF规范,例如传递DM文件和FCM文件,就会check配置的模块是否兼容,但是在编译日志没有找到!

checkvintf device_manifest.xml framework_matrix.xml

  • 参数如果是--hlep,者调用usage函数打印此工具用法。在编译日志中没有找到!
  • 参数如果是--check-one,者调用checkOne检测单个分区的VINTF是否兼容。在编译日志中没有找到!

  • 参数如果有--check-compat,者继续找到--dirmap  --kernel  --property等参数,最后调用checkAllFiles函数来检测所有分区的VINTF配置文件是否兼容。在编译日志中主要就是调用了此命令

2、全链路兼容性检查

既然编译命令中只找到了checkvintf --check-compat这样的命令,那么它到底是干嘛的呢?先给一个结论,在编译期(不是运行时)模拟设备启动,将设备清单(manifest)和兼容性矩阵(matrix)加载到一起做交叉校验,确认"这套固件组合能兼容启动"

1)命令格式

Command `checkvintf --check-compat

--dirmap /apex:/tmp/APEXdf5d_3kt

--dirmap /odm:/tmp/merge_target_files_hu1faxfn/output/ODM

--dirmap /product:/tmp/merge_target_files_hu1faxfn/output/PRODUCT

--dirmap /system:/tmp/merge_target_files_hu1faxfn/output/SYSTEM

--dirmap /system_ext:/tmp/merge_target_files_hu1faxfn/output/SYSTEM_EXT

--dirmap /vendor:/tmp/merge_target_files_hu1faxfn/output/VENDOR

--kernel /tmp/merge_target_files_hu1faxfn/output/META/kernel_version.txt:/tmp/merge_target_files_hu1faxfn/output/META/kernel_configs.txt

--property ro.product.first_api_level=30

--property ro.boot.product.hardware.sku=wifi

--property ro.boot.product.vendor.sku=lahaina

` returns 'compatible'

如上编译日志中拷贝出来的命令格式,各个字段含义如下:

  • --check-compat:指定本次校验做全链路兼容性检查。具体包含两个方向的校验:

校验方向 含义
设备清单 vs 框架矩阵 设备 manifest 声明的 HAL 是否满足框架 compatibility_matrix 要求(框架要求的 HAL,设备必须提供且版本≥)
框架清单 vs 设备矩阵 框架 manifest 提供的 HAL 是否满足设备 compatibility_matrix 要求(设备要求的 HAL,框架必须提供且版本≥)
  • --dirmap <分区>:<本地路径>(6个分区映射)。把编译的临时分区挂载到临时目录上,然后依次去进行兼容性检查。各个分区检查顺序如下:
设备路径 映射到本地 读取内容
/apex/etc/vintf/ APEX 目录 APEX 内的 manifest fragment
/odm/etc/vintf/manifest.xml ODM/ ODM 设备清单
/odm/etc/vintf/manifest_{sku}.xml ODM/ SKU 专属 ODM 清单
/odm/etc/vintf/compatibility_matrix.xml ODM/ ODM 兼容性矩阵
/product/etc/vintf/manifest.xml PRODUCT/ Product 设备清单
/product/etc/vintf/compatibility_matrix.xml PRODUCT/ Product 兼容性矩阵
/system/etc/vintf/manifest.xml SYSTEM/ Framework manifest
/system/etc/vintf/compatibility_matrix.xml SYSTEM/ Framework compatibility matrix
/system_ext/etc/vintf/manifest.xml SYSTEM_EXT/ SystemExt 清单
/vendor/etc/vintf/manifest.xml VENDOR/ Vendor 设备清单
/vendor/etc/vintf/compatibility_matrix.xml VENDOR/ Vendor 兼容性矩阵
  • --kernel <version>:<configs> 注入内核信息,用于校验内核配置兼容性,对我们影响应该不大,主要涉及两个文件:

文件 内容示例 用途
kernel_version.txt 4.19.1105.15.123-android14-... 内核版本号,与矩阵中 <kernel version> 比较
kernel_configs.txt /proc/config.gz 解压后的内容,如 CONFIG_Y=y 逐项检查矩阵中 <config> 要求的内核配置是否开启
  • --property <key>=<value> 属性注入,编译过程中主动传入一些属性,主要用来模拟测试实际运行中不同属性下针对不同VINTF配置的一个检查
  • --property ro.product.first_api_level=30  主动传入此属性值,VINTF是否存在兼容性问题?
  • --property ro.boot.product.hardware.sku=wifi 主动传入不同sku属性值,校验所有SKU是否存在兼容性问题
  • returns 'compatible' 返回VINTF检验结果,compatible表示检测通过!

2)编译系统如何执行checkvintf

入口一:全量编译阶段

当执行make进行全量编译的时候,其实就是走的aosp/build/make/core/Makefile文件,在此文件中后期阶段,会去调用checkvintf工具,对VINTF机制进行所有分区校验,参考如下代码:

PS:我们是否可以通过关闭PRODUCT_ENFORCE_VINTF_MANIFEST=false编译宏控来暂时跳过VINTF机制呢?

入口二:OTA target_files包编译校验阶段

PC 端制作 OTA 包时,通常会调用脚本ota_from_target_files.py,此脚本最后就会调用到check_target_files_vintf.py,从而调用checkvintf --check-compat命令对VINTF进行校验:

这里其实通常涉及到两个场景:

  • 场景 A:标准 OTA 包制作
ota_from_target_files.py \
    -i old_target_files.zip \    # 旧版本 target_files
    new_target_files.zip \       # 新版本 target_files
    ota_package.zip              # 输出 OTA 包
# 内部会对新旧 target_files 都做 VINTF 校验
# 确保升级后的固件组合兼容
  • 场景 B:merge target_files(多模块合并)
merge_target_files.py \
    --system-target-files system.zip \
    --vendor-target-files vendor.zip \
    -o merged_output/
# 合并后自动调用 CheckVintf() 校验
# 这就是你日志中看到的那个调用

3)checkAllFiles流程

checkAllFiles() 是 checkvintf --check-compat 的核心:在 Host 上 模拟一台已组好的 Android 设备,对 所有已通过 dirmap 映射的分区 上的 VINTF 元数据做 一整轮合规检查,并把多项检查结果 汇总成一个 Result。

//aosp/system/libvintf/check_vintf.cpp
android::base::Result<void> checkAllFiles(
        const Dirmap& dirmap,                      // 设备路径 → host OUT 目录映射表
        const Properties& props,                    // --property 注入的假 sysprop
        std::shared_ptr<StaticRuntimeInfo> runtimeInfo) {  // --kernel 可选;无则跳过内核检查

    // 阶段 A:构造「离线设备」的 I/O 与属性 ─────────────────────────
    // 之后 libvintf 请求 /vendor/etc/vintf/manifest.xml 时,
    // 实际读的是 dirmap["/vendor"] + "/etc/vintf/manifest.xml"
    auto hostFileSystem = std::make_unique<HostFileSystem>(dirmap, UNKNOWN_ERROR);
    auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
    hostPropertyFetcher->setProperties(props);  // 如 vendor.sku → 选 manifest_yupik.xml
    CheckFlags::Type flags = CheckFlags::DEFAULT;
    if (!runtimeInfo) flags = flags.disableRuntimeInfo();

    // 注入依赖,得到与真机同逻辑的 VintfObject(非单例,避免污染)
    auto vintfObject =
        VintfObject::Builder()
            .setFileSystem(std::move(hostFileSystem))       // 跨分区读 XML 的入口
            .setPropertyFetcher(std::move(hostPropertyFetcher))
            .setRuntimeInfoFactory( std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
            .build();
    // 累积所有检查失败信息;nullopt = 尚未失败
    std::optional<android::base::Error<>> retError = std::nullopt;

    // 阶段 B:检查 1 — 整机 Treble 契约(最重)────────────────────
    // 内部会 getDeviceHalManifest / getFrameworkHalManifest /
    // getFrameworkCompatibilityMatrix / getDeviceCompatibilityMatrix /
    // 可选 getRuntimeInfo,然后:
    //   DM vs FCM, FM vs DCM, RI vs FCM
    std::string compatibleError;
    // 核心逻辑:最终还是通过libvintf.so封装的checkCompatibility函数进行三路检测
    int compatibleResult = vintfObject->checkCompatibility(&compatibleError, flags);
    if (compatibleResult == INCOMPATIBLE) {
        SetErrorCode(&retError) << compatibleError;       // 逻辑不兼容,code=0
    } else if (compatibleResult != COMPATIBLE) {
        SetErrorCode(&retError, -compatibleResult) << compatibleError;  // 文件缺失/解析错
    }

    // 阶段 C:检查 2 — 是否使用已废弃 HIDL ───────────────────────
    auto hidlMetadata = HidlInterfaceMetadata::all();  // 全树 HIDL 接口元数据
    std::string deprecateError;
    int deprecateResult = vintfObject->checkDeprecation(hidlMetadata, &deprecateError);
    if (deprecateResult == DEPRECATED) {
        SetErrorCode(&retError) << deprecateError;
    } else if (deprecateResult != NO_DEPRECATED_HALS) {
        SetErrorCode(&retError, -deprecateResult) << deprecateError;
    }

    // 阶段 D:检查 3 — 是否存在扩展 FCM ───────────────────────────
    // 例如 product/system_ext/device 的 framework matrix 扩展
    auto hasFcmExt = vintfObject->hasFrameworkCompatibilityMatrixExtensions();
    AddResult(&retError, hasFcmExt);  // 查询失败也记入 retError

    // 阶段 E:检查 4 — DM 中「多余」的 HAL(条件执行)────────────
    // 需要已加载 DM;根据 target FCM level 决定是否检查
    auto deviceManifest = vintfObject->getDeviceHalManifest();  // 再次触发跨分区加载 DM
    Level targetFcm = Level::UNSPECIFIED;
    if (deviceManifest == nullptr) {
        SetErrorCode(&retError, -NAME_NOT_FOUND) << "No device HAL manifest";
    } else {
        targetFcm = deviceManifest->level();  // Device target FCM version
    }

    // FCM≥R 或有扩展 matrix 时才检查 unused HAL(DM 有而 FCM 无)
    if (hasFcmExt.value_or(false) ||
        (targetFcm != Level::UNSPECIFIED && targetFcm >= Level::R)) {
        AddResult(&retError, vintfObject->checkUnusedHals(hidlMetadata));
    } else {
        LOG(INFO) << "Skip checking unused HALs.";
    }

    // 阶段 F:返回汇总结果 ───────────────────────────────────────
    if (retError.has_value()) {
        return *retError;   // main 打印 INCOMPATIBLE 或 EX_SOFTWARE
    } else {
        return {};          // main 打印 COMPATIBLE
    }
}

最后总结一下checkAllFiles最终还是调用的libvintf.so中定义的checkCompatibility函数,进行VINTF三路检测。

四、libvintf运行时检测

第一章介绍了通用libvintf.so核心库,第二章和第三章介绍了编译阶段合并vintf配置,检测vintf配置,那么本章重点介绍系统运行时,是如何对vintf机制进行校验的。主要有如下两个入口:

  • 开机启动阶段VINTF校验:校验失败只打印日志,不会阻塞开机过程
  • HAL层服务注册的时候:校验失败会拦截HAL层服务的启动和注册

他们都是系统级进程集成libvintf.so,然后调用checkCompatibility来进行VINTF校验。

1、开机启动校验

2、HAL层服务注册

我们先看看HAL层服务的注册逻辑,参考Android Binder【总篇:吃透Binder核心原理与架构】的内容,HAL层服务需要向service manager进程进行服务注册,代码流程如下:

如上流程代码整理下来流程图如下:

五、vintf调试命令

在aosp/system/libvintf/Android.bp中有针对vintf.bin的定义,他被安装在/system/bin/vintf.bin上面,通常被作为调试工具进行使用:

//aosp/system/libvintf/Android.bp
cc_binary {
    name: "vintf",
    defaults: ["libvintf-defaults"],
    shared_libs: [
        "libbase",
        "libjsoncpp",
        "libvintf",
    ],
    srcs: [    "main.cpp",    ],
}

1、主函数

//aosp/system/libvintf/main.cpp
static const std::vector<Option> gAvailableOptions{
    {'h', "help", "Print help message.", [](auto) { return USAGE; }},
    {'v', "verbose", "Dump detailed and raw content, including kernel configurations", [](auto o) {
         o->verbose = true;
         return OK;
     }}};
// A convenience binary to dump information available through libvintf.
int main(int argc, char** argv) {
    ParsedOptions options;
    //解析参数:注意参数只支持gAvailableOptions中定义的-h -v
    Status status = parseOptions(argc, argv, gAvailableOptions, &options);
    //判断vintf是否可用
    if (status == USAGE) usage(argv[0], gAvailableOptions);
    if (status != OK) return status;
    //执行命令
    options.fn(options);
}

如上主函数非常简单,从这段代码可以看出来,我们可以执行adb shell vintf -h命令来查看此命令的帮助:

C:\Users\pengcheng.ding>adb shell vintf -h
vintf: dump VINTF metadata via libvintf.
        -h, --help: Print help message.
        -v, --verbose: Dump detailed and raw content, including kernel configurations
        [legacy|dm|fm|dcm|fcm|ri]:
            legacy: Print VINTF metadata.
            dm: Print Device HAL Manifest.
            fm: Print Framework HAL Manifest.
            dcm: Print Device Compatibility Matrix.
            fcm: Print Framework Compatibility Matrix.
            ri: Print Runtime Information.

由此可见,我们还可以执行vintf  legacy|dm|fm|dcm|fcm|ri 来打印当前设备的相关信息,我们用的多就是查看当前设备的设备清单配置和框架兼容性矩阵配置了,这块代码逻辑也非常简单:

//aosp/system/libvintf/main.cpp
void dumpLegacy(const ParsedOptions&);
void dumpDm(const ParsedOptions&);
void dumpFm(const ParsedOptions&);
void dumpDcm(const ParsedOptions&);
void dumpFcm(const ParsedOptions&);
void dumpRi(const ParsedOptions&);

struct DumpTargetOption {
    std::string name;
    std::function<void(const ParsedOptions&)> fn;
    std::string help;
};

std::vector<DumpTargetOption> gTargetOptions = {
    {"legacy", &dumpLegacy, "Print VINTF metadata."},
    {"dm", &dumpDm, "Print Device HAL Manifest."},
    {"fm", &dumpFm, "Print Framework HAL Manifest."},
    {"dcm", &dumpDcm, "Print Device Compatibility Matrix."},
    {"fcm", &dumpFcm, "Print Framework Compatibility Matrix."},
    {"ri", &dumpRi, "Print Runtime Information."},
};

struct ParsedOptions {
    bool verbose = false;
    std::function<void(const ParsedOptions&)> fn = &dumpLegacy;
};

void dumpDm(const ParsedOptions&) {
    auto dm = VintfObject::GetDeviceHalManifest();
    if (dm != nullptr) std::cout << toXml(*dm);
}

void dumpFm(const ParsedOptions&) {
    auto fm = VintfObject::GetFrameworkHalManifest();
    if (fm != nullptr) std::cout << toXml(*fm);
}

void dumpDcm(const ParsedOptions&) {
    auto dcm = VintfObject::GetDeviceCompatibilityMatrix();
    if (dcm != nullptr) std::cout << toXml(*dcm);
}

void dumpFcm(const ParsedOptions&) {
    auto fcm = VintfObject::GetFrameworkCompatibilityMatrix();
    if (fcm != nullptr) std::cout << toXml(*fcm);
}

// Keep field names in sync with VintfDeviceInfo's usage
void dumpRi(const ParsedOptions&) {
    const RuntimeInfo::FetchFlags flags = RuntimeInfo::FetchFlag::CPU_INFO |
                                          RuntimeInfo::FetchFlag::CPU_VERSION |
                                          RuntimeInfo::FetchFlag::POLICYVERS;
    auto ri = VintfObject::GetRuntimeInfo(flags);
    if (ri != nullptr) {
        Json::Value root;
        root["cpu_info"] = ri->cpuInfo();
        root["os_name"] = ri->osName();
        root["node_name"] = ri->nodeName();
        root["os_release"] = ri->osRelease();
        root["os_version"] = ri->osVersion();
        root["hardware_id"] = ri->hardwareId();
        root["kernel_version"] = to_string(ri->kernelVersion());
        std::cout << root << '\n';
    }
}

2、调试命令

1)adb shell vintf dm:打印设备清单

C:\Users\pengcheng.ding>adb shell vintf dm
<manifest version="9.0" type="device" target-level="202504">
    <hal format="aidl">
        <name>android.hardware.audio.core</name>
        <version>3</version>
        <fqname>IConfig/default</fqname>
    </hal>
    <hal format="aidl">
        <name>android.hardware.audio.core</name>
        <version>3</version>
        <fqname>IModule/default</fqname>
    </hal>
.................

2)adb shell vintf fcm:打印framework兼容性矩阵

<compatibility-matrix version="9.0" type="framework" level="202504">
    <hal format="aidl">
        <name>android.hardware.audio.core</name>
        <version>1-3</version>
        <interface>
            <name>IConfig</name>
            <instance>default</instance>
        </interface>
        <interface>
            <name>IModule</name>
            <instance>a2dp</instance>
            <instance>bluetooth</instance>
            <instance>default</instance>
            <instance>hearing_aid</instance>
            <instance>msd</instance>
            <instance>r_submix</instance>
            <instance>stub</instance>
            <instance>usb</instance>
        </interface>
    </hal>
.................................
    <kernel version="6.12.0" level="202504">
        <conditions>
            <config>
                <key>CONFIG_CC_HAS_AUTO_VAR_INIT_ZERO</key>
                <value type="tristate">y</value>
            </config>
        </conditions>
        <config>
            <key>CONFIG_INIT_STACK_ALL_ZERO</key>
            <value type="tristate">y</value>
        </config>
    </kernel>
    <sepolicy>
        <kernel-sepolicy-version>30</kernel-sepolicy-version>
        <sepolicy-version>29.0</sepolicy-version>
        <sepolicy-version>30.0</sepolicy-version>
        <sepolicy-version>31.0</sepolicy-version>
        <sepolicy-version>32.0</sepolicy-version>
        <sepolicy-version>33.0</sepolicy-version>
        <sepolicy-version>34.0</sepolicy-version>
        <sepolicy-version>202404</sepolicy-version>
        <sepolicy-version>202504</sepolicy-version>
    </sepolicy>
    <avb>
        <vbmeta-version>1.0</vbmeta-version>
    </avb>
</compatibility-matrix>

3)adb shell vintf legacy:用来验证兼容性

它展示了系统要求(Framework) 和设备提供(Device) 之间,对于每一个 HAL 接口的版本匹配情况。

C:\Users\pengcheng.ding>adb shell vintf legacy
======== HALs =========
DM: device manifest. FM: framework manifest.
FCM: framework compatibility matrix. DCM: device compatibility matrix.

        FM               android.frameworks.cameraservice.service.ICameraService/default (@3)
        FM               android.frameworks.devicestate.IDeviceStateService/default (@1)
        FM               android.frameworks.location.altitude.IAltitudeService/default (@2)
        FM               android.frameworks.sensorservice.ISensorManager/default (@1)
        FM               android.frameworks.stats.IStats/default (@2)
        FM               android.frameworks.vibrator.IVibratorControlService/default (@1)
             FCM         android.hardware.audio.core.IConfig/default (@1)
             FCM         android.hardware.audio.core.IConfig/default (@2)
   DM        FCM         android.hardware.audio.core.IConfig/default (@3)
             FCM         android.hardware.audio.core.IModule/a2dp (@1)
......................................................
======== Device HAL Manifest =========
<manifest version="9.0" type="device" target-level="202504">
    <sepolicy>
        <version>202504</version>
    </sepolicy>
</manifest>
======== Framework HAL Manifest =========
<manifest version="9.0" type="framework">
    <vendor-ndk>
        <version>30</version>
    </vendor-ndk>
    <vendor-ndk>
        <version>31</version>
    </vendor-ndk>
    <vendor-ndk>
        <version>32</version>
    </vendor-ndk>
    <vendor-ndk>
        <version>33</version>
    </vendor-ndk>
    <vendor-ndk>
        <version>34</version>
    </vendor-ndk>
    <system-sdk>
        <version>29</version>
        <version>30</version>
        <version>31</version>
        <version>32</version>
        <version>33</version>
        <version>34</version>
        <version>35</version>
        <version>36</version>
    </system-sdk>
</manifest>
======== Device Compatibility Matrix =========
<compatibility-matrix version="9.0" type="device">
    <system-sdk>
        <version>36</version>
    </system-sdk>
</compatibility-matrix>
======== Framework Compatibility Matrix =========
<compatibility-matrix version="9.0" type="framework" level="202504">
    <sepolicy>
        <kernel-sepolicy-version>30</kernel-sepolicy-version>
        <sepolicy-version>29.0</sepolicy-version>
        <sepolicy-version>30.0</sepolicy-version>
        <sepolicy-version>31.0</sepolicy-version>
        <sepolicy-version>32.0</sepolicy-version>
        <sepolicy-version>33.0</sepolicy-version>
        <sepolicy-version>34.0</sepolicy-version>
        <sepolicy-version>202404</sepolicy-version>
        <sepolicy-version>202504</sepolicy-version>
    </sepolicy>
    <avb>
        <vbmeta-version>1.0</vbmeta-version>
    </avb>
</compatibility-matrix>
======== Runtime Info =========
kernel = Linux/localhost/6.12.38-android16-5-maybe-dirty-debug/#1 SMP PREEMPT Thu Jan  1 00:00:00 UTC 1970/aarch64;1.3/1.3;kernelSepolicyVersion = 33;
#CONFIG's loaded = 2351;

======== Summary =========
Device Manifest?    GOOD
Device Matrix?      GOOD
Framework Manifest? GOOD
Framework Matrix?   GOOD
Device HAL Manifest <==> Framework Compatibility Matrix? GOOD
Framework HAL Manifest <==> Device Compatibility Matrix? GOOD
Runtime info <==> Framework Compatibility Matrix?        GOOD
VintfObject::checkCompatibility?                         GOOD
VintfObject::CheckDeprecation (against device manifest) (w/o hidlmetadata)? GOOD

如上打印,我们主要关注的格式是:

[DM]   [FM]    [FCM]    [DCM]     HAL名称/实例名     (@版本号)

  • DM和FCM都配置了的,设备提供的音频模块版本 3,满足了系统框架的要求,兼容性正常表示兼容性正常:

DM FCM android.hardware.audio.core.IModule/default (@3)

  • 只有FCM配置了的,设备可能缺少对 A2DP 音频模块版本 1 的支持,可能存在兼容性问题

FCM android.hardware.audio.core.IModule/a2dp (@1)

六、案例总结

1、案例之自定义IGoogleKey和OemSarSensor HAL

这里还是以如上案例,自定义两个IGoogleKey.hal和IOemSarSensor.hal

步骤一:创建HAL服务进程

参考Android Init 系列专题【篇七:hal服务进程自定义】第一章

步骤二:device manifest配置

步骤三:framework compatibility matrix配置

最后:自检设备清单和兼容矩阵配置

如上device manifest和framework compatibility matrix能匹配,所以编译成功。

2、案例之自定义VINTF简易集成

这里的案例不再是完整的实现一个IXXX HAL服务进程,背景是高通已经实现好了一个HAL服务端进程vendor.qti.gptupdater@1.1-service和一个HAL客户端进程gpt_updater_client,(HAL服务进程可以修改分区)并且已经提供了对应的可执行bin文件,我们只需要快速编译进去即可。

根据高通文档的介绍,我们已经手动把服务端进程和客户端进程以及vintf配置文件推送上去,即可正常运行,方式如下:

#步骤一:推送HAL客户端进程
adb push .\bin\gpt_updater_client /vendor/bin
#步骤二:推送HAL服务端进程
adb push .\bin\hw\vendor.qti.gptupdater@1.1-service /vendor/bin/hw
#步骤三:推送VINTF配置
adb push .\etc\vintf\manifest\vendor.qti.gptupdater@1.1.xml /vendor/etc/vintf/manifest
#步骤四:推送依赖动态库
adb push .\lib\libgptupdater.so /vendor/lib
adb push .\lib64\libgptupdater.so /vendor/lib64
adb push .\lib64\librecovery_updater.so /vendor/lib64
adb push .\lib\librecovery_updater.so /vendor/lib
adb push .\lib\hw\vendor.qti.gptupdater@1.1-impl.so /vendor/lib/hw
adb push .\lib64\hw\vendor.qti.gptupdater@1.1-impl.so /vendor/lib64/hw
adb push .\lib\vendor.qti.gptupdater@1.1.so /vendor/lib
adb push .\lib64\vendor.qti.gptupdater@1.1.so /vendor/lib64
#步骤五:启动GPT进行分区分割
./vendor/bin/hw/vendor.qti.gptupdater@1.1-service & gpt_updater_client split_partition

如上是通过push的,这里为了快速正式版本验证,就按照如下操作快速集成:

报错一:VINTF metadata found in PRODUCT_COPY_FILES

[2026-02-02T14:48:36.782Z] FAILED: 
[2026-02-02T14:48:36.782Z] In file included from build/make/core/main.mk:1351:
[2026-02-02T14:48:36.782Z] build/make/core/Makefile:49: error: VINTF metadata found in PRODUCT_COPY_FILES: vendor/tinno/product/MC913/trunk/partition_append/vendor/etc/vintf/manifest/vendor.qti.gptupdater@1.1.xml:vendor/etc/vintf/manifest/vendor.qti.gptupdater@1.1.xml, use DEVICE_MANIFEST_FILE / DEVICE_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!.
[2026-02-02T14:48:36.782Z] 22:48:35 ckati failed with: exit status 1

如上报错信息,可以看到VINTF文件不允许通过PRODUCT_COPY_FILES宏进行拷贝,请使用DEVICE_MANIFEST_FILE、DEVICE_MATRIX_FILE、vintf_compatibility_matrix、vintf_fragments 这几种方式进行代替,这几种方式其实就是前文讲解的device manifest的配置方式。

# device manifes配置

DEVICE_MANIFEST_FILE += $(LOCAL_PATH)/partition_append/vendor/etc/vintf/manifest/vendor.qti.gptupdater@1.1.xml

报错二:Shipping FCM Version cannot be inferred from Shipping API level 30

FAILED: out/target/product/MC913/gen/ETC/vendor_manifest.xml_intermediates/manifest.xml
/bin/bash -c "BOARD_SEPOLICY_VERS=30.0     PRODUCT_ENFORCE_VINTF_MANIFEST=true     PRODUCT_SHIPPING_API_LEVEL=30     out/host/linux-x86/bin/assemble_vintf -o out/target/product/MC913/gen/ETC/vendor_manifest.xml_intermediates/manifest.xml         -i vendor/tinno/product/MC913/trunk/partition_append/vendor/etc/vintf/manifest/vendor.qti.gptupdater@1.1.xml"
Warning: Shipping FCM Version is inferred from Shipping API level. Declare Shipping FCM Version in device manifest directly.
Error: Shipping FCM Version cannot be inferred from Shipping API level 30.Declare Shipping FCM Version in device manifest directly.

如上报错,意思就是无法从device manifest配置文件中推导出应该使用哪一个FCM,即android系统编译的时候会根据vintf.xml中定义的版本号去推导framework compatibility matrix使用哪一个版本,具体原理可以参考第三章第四节的内容。检查我这边device manifest配置文件的内容如下:

<manifest version="1.0" type="device">
    <hal format="hidl">
        <name>android.hardware.boot</name>
        <transport>hwbinder</transport>
        <fqname>@1.1::IBootControl/default</fqname>
    </hal>
    <hal format="hidl">
        <name>vendor.qti.gptupdater</name>
        <transport>hwbinder</transport>
        <fqname>@1.1::IGPTUpdater/default</fqname>
    </hal>
</manifest>

根据提示在device manifest中添加版本号,因为这是A14,对应可以加target-level为5:

<!-- target-level 5 = Android 11 (API 30) FCM; required when PRODUCT_ENFORCE_VINTF_MANIFEST=true -->
<manifest version="1.0" type="device" target-level="5">
    <hal format="hidl">
        <name>android.hardware.boot</name>
        <transport>hwbinder</transport>
        <fqname>@1.1::IBootControl/default</fqname>
    </hal>
    <hal format="hidl">
        <name>vendor.qti.gptupdater</name>
        <transport>hwbinder</transport>
        <fqname>@1.1::IGPTUpdater/default</fqname>
    </hal>
</manifest>

总结:经过多轮调试,但都没有成功,因此给出如下结论

暂时没法通过PRODUCT_COPY_FILES去拷贝vintf配置文件,当然定制libvintf库之后说不定是可以实现。

3、案例之override覆盖设备清单失效

问题背景:某项目有多个SKU,主要分为两类wifionly和蜂窝,因此我需要针对蜂窝机型,配置如下设备清单,针对wifionly机型,不需要配置如下设备清单:

缺陷方案:针对如上需求,最初使用了如下方案

  • 通过LOCAL_VINTF_FRAGMENTS宏控在所有机型安装如上配置

LOCAL_VINTF_FRAGMENTS += android.hardware.secure_element.xml

  • 通过ODM_MANIFEST_SKUS宏控在wifionly机型进行override覆盖

ODM_MANIFEST_SKUS := wifi wifitr wifieea wifiir wifiirtr wifiireea dectwifi dectwifieea slimcell slimcelleea

ODM_MANIFEST_WIFI_FILES := device/qcom/lahaina612/manifest_wifi.xml
ODM_MANIFEST_WIFITR_FILES := device/qcom/lahaina612/manifest_wifitr.xml

原因分析:经过XTS测试结果可以证明如上方案并没有生效,在检查sku这些属性之后都是正确的,之前对override=true的理解,就是覆盖之前的配置,因为name中并没有声明版本和接口,所以在通用的地方对ndroid.hardware.secure_element配置了SIM1和SIM2的配置,然后执行如上代码,应该替换为空,然而实际上并不是这样的。

核心一:manifest和fragment前世今生?

manifest和fragment本质是一样的,都叫做清单,他们的格式和功能都是一样的,在device侧配置就叫做设备清单。但他们还是有如下几个区别:

  • Manifest主要是由设备端配置,主要通过DEVICE_MANIFEST_*这样的宏控指定,编译输出路径通常在/vendor/etc/vintf/manifest.xml或者manifest_{sku}.xml,相当于只要配置整个device侧全部生效。
  • Fragment主要是由各模块配置,主要通过LOCAL_VINTF_FRAGMENTS宏控指定,编译输出路径通常在/vendor/etc/vintf/manifest/目录下的各个模块文件中,例如android.hardware.secure_element.xml或者android.hardware.radio.sim.xml,相当于和device的配置进行了隔离。
  • 无论是在manifest还是fragment都可以配置设备清单,最终运行时libvintf把所有文件合并

核心二:libvintf合并manifest/fragment的顺序?

libvintf在合并manifest和fragment的顺序如下:

  • /vendor/etc/vintf/manifest.xml
  • /vendor/etc/vintf/manifest_{sku}.xml
  • /odm/etc/vintf/manifest.xml
  • /odm/etc/vintf/manifest_{sku}.xml
  • /vendor/etc/vintf/manifest/*.xml ->合并vendor manifest目录下的所有fragments配置文件
  • /odm/etc/vintf/manifest/*.xml   ->合并odm manifest目录下的所有fragments配置文件

核心三:override替换的工作机制

libvintf根据如上顺序依次进行合并,如果在合并的时候,有遇到override = true的配置,那么就替换覆盖之前已有的配置,但是如果后续在继续添加对应配置就无法管控了。

因此结合如上override的工作原理和合并顺序,这个案例属于如下场景:

  • 合并odm/etc/manifest.xml,此时没有secure_element配置,更没有SIM1和SIM2的配置
  • 合并odm/etc/manifest_wifi.xml,此时存在secure_element配置,且override进行覆盖,不会有SIM1和SIM2的配置
  • 合并vendor/etc/vintf/manifest/目录下的fragments配置,找到android.hardware.secure_element.xml配置文件,添加了SIM1和SIM2的配置
  • 合并odm/etc/vintf/manifest/目录下的fragments配置,此时并没有什么配置,保持如上不变

最后结果就是虽然在odm manifest sku的时候进行覆盖和替换,但是最后才去加载vendor fragments android.hardware.secure_element.xml配置文件,所以SIM1和SIM2会存在。

解决方案:基于如上原理进行如下优化

  • 去掉android.hardware.secure_element.xml通用配置

  • 通过SKU配置非wifi机型

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐