第25章 供应商测试套件(VTS):VTS测试框架,HAL实现兼容性验证

说到VTS,很多刚接触Android系统开发的工程师会觉得它就是个「跑测试的工具」。其实不然。VTS的全称是Vendor Test Suite,它存在的意义,说白了就是确保你写的HAL代码能跟Android框架层好好配合。

我刚开始做HAL开发时,总觉得「我代码能编译过、能跑起来不就完了?」结果有一次提交给Google认证,VTS跑了一晚上,挂了十几个case。嗯,从那以后我再也不敢小看VTS了。

VTS到底在测什么?

VTS不是测你的App好不好用,也不是测系统流畅度。它专门盯着HAL接口——也就是硬件抽象层。你想想看,Android框架层通过HAL调用底层硬件,如果HAL接口实现不规范,上层应用就会莫名其妙地崩溃。

举个例子:Camera HAL的open()函数,Google规定了它必须返回哪些错误码。你返回了一个自定义的-999,上层框架不认识,直接Crash。VTS就是来抓这种问题的。

核心验证点:
  • HAL接口的返回值是否符合AOSP定义
  • HAL函数的调用顺序是否合规
  • HAL实现是否支持所有必须的特性
  • HAL在异常场景下的行为是否可预期

VTS测试框架长什么样?

VTS基于Trade Federation(简称Tradefed)测试框架。Tradefed是Google自己搞的一套自动化测试框架,VTS只是它上面的一个测试集合。

我个人习惯把VTS框架理解成三层结构:

测试用例层(Test Cases) VTS提供的各类HAL测试用例,如CameraVTS、AudioVTS、LightVTS等 Tradefed测试框架 负责测试调度、设备管理、结果收集、报告生成 设备端执行层 通过adb推送测试二进制到设备,调用HAL接口并验证结果

你看这个图就清楚了。最上层是Google写好的测试用例,中间是Tradefed调度框架,底层才是真正在设备上跑的东西。

如何运行VTS测试?

跑VTS其实不复杂。前提是你得先编译好整个Android源码,并且刷机到设备上。然后执行:

# 运行所有VTS测试(耗时很长,建议晚上跑)
$ vts-tradefed run vts

# 只跑某个模块的测试,比如Camera HAL
$ vts-tradefed run vts -m VtsHalCameraProviderV2_4TargetTest

# 跑单个测试用例
$ vts-tradefed run vts -m VtsHalCameraProviderV2_4TargetTest --test com.android.camera.hal2_4.CameraHidlTest#testOpenClose

我记得第一次跑全量VTS时,整整跑了6个多小时。中间还因为设备掉线中断了一次。所以我的建议是:先跑单个模块,确认没问题了再跑全量

HAL兼容性验证到底验证什么?

这里我展开讲讲。HAL兼容性验证不是泛泛地「测一下功能」,它有非常具体的检查点。

验证维度 具体检查内容 常见失败原因
接口签名 函数名、参数类型、返回值类型是否匹配 HIDL接口版本不对,或者自己改了接口定义
返回值规范 成功/失败是否返回规定的Status码 返回了Status::UNKNOWN或自定义错误码
生命周期 open/close/init/deinit调用顺序是否正确 close之后还在使用HAL句柄
并发安全 多线程同时调用HAL是否会导致崩溃 HAL内部没有加锁,或者锁使用不当
异常处理 传入非法参数时是否返回正确错误码 直接assert或者返回空指针
注意:VTS测试是Android兼容性认证(CTS/VTS)的必过项。如果VTS有fail项,Google不会给你发认证。我见过有厂商因为VTS没过,导致产品无法预装GMS,损失惨重。

我踩过的坑:HAL实现中的常见VTS失败

做HAL开发这些年,我总结了几类最常见的VTS失败场景。分享出来,希望大家少走弯路。

1. HIDL接口版本不匹配

Android大版本升级时,HIDL接口可能会增加新方法。比如从Android 10到11,某个HAL接口新增了一个getVersion()函数。如果你只实现了旧接口,VTS会报「method not found」。

我曾经在升级项目时,漏掉了两个新增的HIDL方法。VTS跑了半小时,挂了十几个case。排查起来特别痛苦,因为错误日志只告诉你「找不到方法」,不告诉你少了哪个。

我的习惯:每次升级Android版本后,先diff一下HIDL接口文件。用git diff hardware/interfaces/看看新增了哪些方法,提前补上。

2. 返回值用错了枚举值

HIDL中定义了Status枚举,比如OK、UNKNOWN_ERROR、INTERNAL_ERROR等。有些工程师图省事,所有失败场景都返回UNKNOWN_ERROR。VTS会检查特定场景下是否返回了正确的错误码。

举个例子:Camera HAL的configureStreams(),如果传入了不支持的参数格式,应该返回Status::BAD_VALUE。你返回了Status::INTERNAL_ERROR,VTS就挂了。

3. 生命周期管理混乱

这个坑最常见。HAL的open()和close()必须成对调用,而且close之后不能再调用任何其他方法。有些实现中,close只是标记了一个flag,没有真正释放资源。VTS会反复open/close来测试,很容易暴露问题。

// 错误示例:close之后没有置空指针
ErrorCode MyHal::close() {
    // 只设置了标志位,没有释放mDevice
    mIsClosed = true;
    return ErrorCode::OK;
}

// 后续调用时,mDevice已经是悬空指针
ErrorCode MyHal::getInfo(Info* info) {
    if (mIsClosed) {
        // 这里应该返回ErrorCode::INVALID_STATE
        // 但实际代码可能直接用了mDevice->getInfo()
        return mDevice->getInfo(info); // 崩溃!
    }
}

如何高效排查VTS失败?

VTS跑失败了,别慌。我一般按这个步骤来排查:

  1. 看日志:VTS会生成详细的测试报告,在out/host/linux-x86/vts/testcases/ 目录下。找到对应的test_result.xml,里面会写明哪个case失败、期望值是什么、实际值是什么。
  2. 复现:用vts-tradefed单独跑那个失败的case,加上 --log-level VERBOSE 参数,能看到更详细的日志。
  3. 本地验证:写一个简单的测试程序,直接调用你的HAL接口,看看是不是VTS的测试逻辑有问题(虽然概率很小,但我也遇到过VTS的bug)。
  4. 对比参考实现:Google在AOSP中提供了HAL的参考实现(比如Camera的EmulatedCamera),对比一下你的实现和参考实现有什么差异。
避坑指南:我曾经遇到一个VTS失败,查了两天才发现是HIDL的@entry注解用错了。@entry注解表示这个方法是入口点,VTS会检查入口点方法的调用顺序。如果你把非入口方法也加了@entry,VTS会报调用顺序错误。这种问题光看代码很难发现,建议仔细阅读HIDL接口定义文件中的注释。

VTS与CTS的区别

很多新人分不清VTS和CTS。我简单说一下:

  • CTS(Compatibility Test Suite):测的是Android框架层API,确保App开发者调用的接口在各个设备上行为一致。
  • VTS(Vendor Test Suite):测的是HAL接口,确保设备厂商实现的硬件抽象层符合规范。

说白了,CTS是给App开发者用的,VTS是给设备厂商用的。两者都是Android认证的必过项。

嗯,关于VTS的内容就讲到这里。核心就是记住一句话:VTS不是用来测功能的,是用来测接口规范的。你的HAL实现功能再强大,接口不规范,VTS照样挂。做HAL开发时,先把接口规范吃透,再动手写代码,能省去很多返工的时间。


公众号:蓝海资料掘金营,微信deep3321