第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框架理解成三层结构:
你看这个图就清楚了。最上层是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或者返回空指针 |
我踩过的坑:HAL实现中的常见VTS失败
做HAL开发这些年,我总结了几类最常见的VTS失败场景。分享出来,希望大家少走弯路。
1. HIDL接口版本不匹配
Android大版本升级时,HIDL接口可能会增加新方法。比如从Android 10到11,某个HAL接口新增了一个getVersion()函数。如果你只实现了旧接口,VTS会报「method not found」。
我曾经在升级项目时,漏掉了两个新增的HIDL方法。VTS跑了半小时,挂了十几个case。排查起来特别痛苦,因为错误日志只告诉你「找不到方法」,不告诉你少了哪个。
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跑失败了,别慌。我一般按这个步骤来排查:
- 看日志:VTS会生成详细的测试报告,在out/host/linux-x86/vts/testcases/ 目录下。找到对应的test_result.xml,里面会写明哪个case失败、期望值是什么、实际值是什么。
- 复现:用vts-tradefed单独跑那个失败的case,加上 --log-level VERBOSE 参数,能看到更详细的日志。
- 本地验证:写一个简单的测试程序,直接调用你的HAL接口,看看是不是VTS的测试逻辑有问题(虽然概率很小,但我也遇到过VTS的bug)。
- 对比参考实现:Google在AOSP中提供了HAL的参考实现(比如Camera的EmulatedCamera),对比一下你的实现和参考实现有什么差异。
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开发时,先把接口规范吃透,再动手写代码,能省去很多返工的时间。