Android Build系列专题【篇七:VINTF源码解析】

是结合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编译主机上,
  • 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、checkCompatibility整机检测

cpp 复制代码
//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去合并配置的所有设备清单和兼容性矩阵。

PS:它不负责整机 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编译工具

四、服务注册流程

五、vintf调试命令

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

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

1、主函数

cpp 复制代码
//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 来打印当前设备的相关信息,我们用的多就是查看当前设备的设备清单配置和框架兼容性矩阵配置了,这块代码逻辑也非常简单:

cpp 复制代码
//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:打印设备清单

bash 复制代码
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兼容性矩阵

bash 复制代码
<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 接口的版本匹配情况。

bash 复制代码
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配置文件推送上去,即可正常运行,方式如下:

bash 复制代码
#步骤一:推送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机型

相关推荐
plainGeekDev8 小时前
Android Framework 面试题:Binder都说不清楚,简历别写精通了
android·java
萌新杰少8 小时前
安卓原生项目迁移KMP——核心迁移
android·kotlin·jetbrains
小孔龙8 小时前
AndroidManifest.xml 配置速查手册
android
七牛云行业应用8 小时前
OpenAI Codex手机版上线实战:iOS/Android 5步配置远程控制指南(2026)
android·ios·智能手机
背包客(wyq)8 小时前
YOLO手势检测识别模型Android端部署测试
android·yolo
peakmain98 小时前
基于 Hilt 实现 Android 网络库可插拔替换 Skill
android·架构·ai编程
黄林晴8 小时前
Google I/O 2026 Android开发者速览
android·android studio
DogDaoDao9 小时前
Android 播放器开发:从零构建全功能视频播放器
android·ffmpeg·音视频·播放器·mediacodec·编解码
真鬼12310 小时前
【Unity安卓】Unity 嵌入 Android Studio 完整流程
android·unity·android studio