ATV Fail 解决经验总结
一、概述
1.1 场景/背景
在进行ATV认证测试时,会进行高达几十万几百万的测试用例测试,并不是每个项目都能顺利完成测试,这就导致我们会经常遇到很多复杂棘手的问题。不同系统的问题复杂多变,有些问题可能都是由一个原因引起,有些问题可能因为环境引起.....这个时候就需要对问题进行复盘和归纳总结,本文章记录了一些常见问题的解决思路和不同问题的解决手法,能够帮助你更快地上手并解决ATV项目中存在的一些问题。
1.2 术语和缩略语
缩略语/术语 | 全 称 | 说 明 |
---|---|---|
越权 | 权限越界 | 出现了系统不允许的权限 |
1.3参考资料
二、各大模块常见问题分析思路
须知
-
测试工具目录架构(Android12 CTS测试工具为例):
- jdk目录:存放一些必要的测试环境
- logs目录:存放测试过程中生成的log日志文件
- results目录:存放最终测试结果
- testcases目录:存放测试过程会用到的一些测试apk
- tools目录:存放测试工具使用到的so文件和python脚本
- NOTICE.txt:版权说明
-
常用分析工具:
- logcat文件:logs目录中对应测试log文件夹中,device_logcat_test开头文件保存着测试过程中完整的系统log
- results目录:对应测试result目录中存放记录测试结果的html文件,test_result_failures_suite.html中记录大致测试结果,test_result.html中记录详细测试结果以及详细报错
- 测试代码在线查看网址:Android Code Search,Google Git
- Daliy测试工具下载网址:https://ci.android.com/builds/branches/aosp-main/grid
-
results测试报告内容架构
-
示例
- Suite / Plan:记录使用的测试命令
- Suite / Build:记录测试工具版本
- Host Info:记录测试的主机信息以及内核版本(测试电脑)
- Start time / End Time:记录测试开始时间以及测试结束时间
- Tests Passed:记录测试pass条数
- Tests Failed:记录测试fail条数
- Modules Done:记录测试完成模块数量
- Modules Total:总共需要完成模块数量
- Fingerprint:测试设备指纹信息
- Security Patch:测试设备安全补丁信息
- Release (SDK):测试设备系统版本以及Android SDk版本
- ABIs:测试设备芯片架构
- Module:测试模块
- Passed: 测试模块中pass case数量
- Failed:测试模块中Failed case数量
- Assumption Failure:测试模块中豁免case数量
- Ignored:测试模块中忽略case数量
- Total Tests:该测试模块总共需要测试case数量
- Done:该模块测试完成情况
-
-
fail case测试结果架构
-
示例
- 左上角记录使用的测试架构以及测试模块,表格中是该模块中测试fail的测试项报错信息
- Test:报错case
- Result:测试结果
- Details:报错信息
-
一 . 常见模块异常
1 . 问题初步验证
-
环境排查
- 单跑测试项验证
- 网络排查
- 设备排查(测试盒子,测试电脑,测试外设)
- 恢复出厂尝试
2 . 视频播放类异常
-
解码器异常
-
关键字:Decode(解码器),MediaDecode(视频解码器),AudioDecode(音频解码器)
-
分析思路:
- 具体问题具体分析,先确认解码器是因为什么而异常,可能会因为网络问题,以及系统不稳定造成,更换网络和重置设备尝试
- 可以从测试项名称分析测试内容以及测试报错
- 检查Logcat,看看有没有其他异常报错,常见的可能会有lib so库之类报错
- 查看测试代码,详细分析测试项具体测试内容
- 对比pass版本和fail版本之间的差异,如果如果是so库有差异,可以手动更换尝试
-
示例:
-
图中我们可以看到都是关于Decode方面的报错,其中testAllComponentInstantiation case有提到具体so库报错,我们可以先从这条case入手:
-
分析报错:报错中可以看到,OMX.amlogic.vp6a.decoder.sw这个so初始化失败了,但没有过多的报错信息,这个时候我们就要从logcat中找到初始化失败原因
-
分析log:打开log文件夹中device_logcat_test开头txt文件,定位到testAllComponentInstantiation
我们可以看到在报错前omx_core_soft打印了一行报错,“libstagefright_soft_amsoftdec.so无法打开,原因是libamffmpeg.vendor.so 没有找到“,之后就是一系列的初始化失败打印。因此,我们可以初步判断是libamffmpeg.vendor.so缺失导致的测试失败,那么这几条报错的case错误原因都比较明了了
-
验证问题:将缺失的so库添加回去后进行复测,注意需要保持测试环境一致
-
回归测试:验证报错的case后,确认没有问题后,需要进行回归测试。因为我们添加的so是视频类解码库,所以我们需要评估添加后是否会影响到其他case,但由于关于media的测试模块很多,所以回归测试我们最好需要把所有的关于media模块都测试一遍,把风险降至最低
-
-
-
-
网络异常
-
关键字:Timed out(超时),Measured Sample(采样率)
-
分析思路:
- 超时很大一部分原因都是因为网络波动导致
- 检查网络是否通畅
- 找到视频播放连接,手动在不同网络下播放尝试(不一定每条case都有连接)
-
示例:
- 排查测试环境问题,确认网络环境是否通畅,更换不同区域网络(如美区网络),重置设备等
- 观察测试现象,是否有测试应用闪退或者播放器没有加载现象
-
-
色域异常
-
关键字:Color
-
分析思路:
- 检查设备(盒子,电视)颜色设置是否正确
- 更换不同分辨率,HDR的电视尝试
- 查看盒子支持色域,色深等,并且来回切换尝试
-
示例:
- 报错中提到没有匹配的颜色,可以在系统或者电视中尝试修改色深或者色域
- 如Android11 系统,在setting➡Device Preferences➡Display&Sound➡Screen resolution➡Color Space Setting 中修改色深为RGB 8bit 尝试
- 不同系统中的设置颜色的选项可能在不同位置,需要多尝试几遍
-
3 . 应用程序异常
-
安全性异常
-
权限异常:
-
关键字:Permissions
-
分析思路
-
这个问题比较常见,我们首先需要看看报错中是否有提到哪个apk出现权限问题,一般来说都会提到权限有异常的apk
-
分析Android不同版本权限上的差异,看看权限的使用是否符合当前Android版本的规则要求
-
如果没有明确的报错指出哪个apk出现问题,那么可以从三个方面入手
- Android系统自身应用一般不会出现权限问题(以android开头包名的apk),这时候可以排查预制的系统apk
- 检查系统开放权限的配置清单,看看是不是系统没有开放这个权限,一般这个问题出现的场景为:报错上出现很多apk同时出现越权行为(持有未被系统允许的权限)
- 分析log和测试代码流程
-
-
示例:
-
可以看到报错中出现了很多apk都同时持有一个权限:BIND_ACCESSIBILITY_SERVICE,根据报错的意思大概是这些apk不能持有这个权限,所以现在出现了越权错误
-
通过观察,我们就可以发现报错的apk都是系统应用,按理来说系统应用本身权限就很高,只要应用本身申请了权限就没问题,所以我们需要排查是否有外部因素影响:
- 排查系统开放权限清单,看看是否配置了BIND_ACCESSIBILITY_SERVICE权限
- 在Android系统中,同一个进程下的应用是可以共享权限的,这些系统应用都运行在system_server系统进程中,如果其中一个应用的权限出现异常,那么很可能导致整个进程中的应用同时存在异常
- 排查除了Android原生系统应用以外的其他预制应用,如包名带有com.sdmc,com.amlogic等应用都为预制的系统应用。在adb中使用pm disable命令一个个进行尝试
- 最后发现是com.sdmc.adsystem应用导致异常,disable掉该应用后测试pass
-
发现异常源头后交给对应开发的人员解决,在修改之前还需要确认出问题的权限作用是什么,一般来说删掉这条权限即可
-
-
-
APK签名异常:
-
关键字:Key
-
分析思路:
- 观察报错,这一类的报错一般都有明确说明哪个apk的key出现问题
- 恢复出厂,检查该apk是否为测试apk残留
-
示例:
- 图中报错信息已经给得很详细了,并且指出详细的应用包名
- 观察包名,看看是否为测试apk残留在系统没有被卸载
- 该示例中的包名为android.hdmicec.app,恢复出厂设置,使用adb命令:pm list package |grep android.hdmicec.app或者dumpsys package android.hdmicec.app查看系统是否还存在该apk,如果依旧存在说明为系统内置apk,需要提交给对应开发人员修改apk系统签名
-
-
版本异常:
-
关键字:debuggable,targetSdk
-
分析思路
- 检查apk的编译版本,targetSdk,key等是否符合,这一类的错误一般在报错中都有明确说明
- targetSdk需要和系统属性ro.build.version.sdk吻合
- apk的编译版本版本也需要和系统版本吻合,如user版本的系统需要release版本的apk
-
示例:
- 报错中已经明确指出错误apk为debug版本,这个时候需要检查系统是否为user版本
- 我们可以使用getprop |grep ro.build.type来查看系统的编译版本
- 如果系统为user版本,那么需要将错误apk重新编译成release版本后重新测试
-
-
网络异常:
-
关键字:Udp,InternalHost
-
分析思路:
- 首先搞清楚测试项测试的是网络那一块内容,目前遇到的实质性网络问题都是端口占用问题
- 分析报错,udp端口占用一般都会提示哪一个端口号占用,InternalHost端口则没有提示,不过我们也可以apk方向分析是哪个apk占用了InternalHost端口
- google原生apk一般不会存在端口占用问题,我们可以进一步缩小查找范围,看看预置的apk和OEM厂商的apk是否存在问题
-
示例:
-
这条case报错中我们可以分析出两个有用信息
- 占用端口为3478
- 使用这个端口的应用UID为1000,是一个系统应用(UID1000为系统进程)
-
有了详细的端口号我们就可以找出是哪个应用占用了这个端口
-
使用adb命令,输入netstat -anp |grep udp 查看系统所有的udp端口
现在我们就能看到,使用3478的端口是一个包名为com.sdmc.tms的应用程序
-
现在可以使用dumpsys package com.sdmc.tms |grep userId 再次对比对应的UID是否也符合
确认问题应用后修改该应用的udp端口重新复测整个模块即可
-
-
-
4. 系统异常
-
安全性异常
-
SE权限异常:
-
关键字:Permissions
-
分析思路:
-
SE权限异常的问题目前遇到两种,一种是因为kernel编译版本是debug原因导致的异常。一种是进程进行读写操作时,权限等级不够的异常
-
两种异常的报错信息大致相同,但详细内容不同,因此我们需要仔细查看acv报错信息的内容
-
如果需要解决SE权限异常的问题,我们还需要看懂acv的报错才行,例如:
我们需要对该acv报错进行解析,图上的acv报错大致解析结果如下:
- Found illegal SELinux denial(s):检测到 SELinux 拒绝访问的错误
- avc: denied { read }:SELinux 拒绝了一个读取操作。在这种情况下,进程尝试读取一个文件
- name="stat" dev="sysfs" ino=41031:被拒绝访问的文件的名称是 "stat","stat" 是一个系统调用,用于获取文件的状态信息,如文件大小、修改时间等。"dev="sysfs"": 这表示被拒绝访问的文件所在的设备为"sysfs"。"ino=41031": 这表示被拒绝访问的文件的 inode 号为 41031。inode 是文件系统中用于标识文件的唯一标识符
- scontext=u:r:dumpstate:s0 tcontext=u:object_r:sysfs:s0 tclass=file:scontext 表示发起请求的进程的 SELinux 上下文,tcontext 表示被访问的文件的 SELinux 上下文,tclass 表示被访问的资源类型(文件)
- permissive=0": permissive 指示 SELinux 是否处于宽松模式(permissive mode),0 表示不是
-
-
-
总结起来大致就是:SELinux 拒绝了一个名为 "stat" 的文件的读取请求。请求是由上下 文为 "u:r:dumpstate:s0" 的进程发起的,而该文件的上下文为 "u:object_r:sysfs:s0"。 SELinux 未处于宽松模式,因此拒绝了该操作
-
如果是因为kernel编译版本是debug原因导致的报错,则是会在"dev"属性中显示debugfs,例如:
-
指纹异常:
-
关键字:BuildFingerprint
-
分析思路:
- 设备的指纹是由一个个属性拼接起来的,一般报错都会告诉你哪个属性出现问题
- 注意报错内容,测试期望属性和设备实际属性
- 分析源码,看是在哪个属性出现问题
-
示例:
-
注意报错中预期的属性,首先检查测试预期属性是否和项目实际立项属性符合
-
如果符合,那么可以从测试代码中分析:
可以看到测试期望的fingerprint是从Build.FINGERPRINT中获取到的,我们可以继续往下追溯,看看Build.FINGERPRINT对应的系统属性是什么
经过查找可以看到Build.FINGERPRINT对应的系统属性是ro.build.fingerprint,如果该属性没有值的话则会使用多个属性拼接起来。可以使用getprop |grep ro.build.fingerprint命令查看设备中ro.build.fingerprint属性详细值
-
目前我们知道了正确的指纹后就能挨个比对差异属性了,我在adb中打印所有测试的属性值,后面可以发现是ro.product.brand和对应指纹上的属性值不符
修改成正确的属性后即可解决
-
-
Lib库异常
-
关键字:OsTest
-
分析思路:
- Lib库异常一般是指系统中某个so库出现异常,比如无法打开,加载失败,没有找到等
- 先从报错中分析,看看是否有指明哪个so库出现异常
- 如果报错中没有更多有用信息,那么需要去log中寻找,这一类的报错一般来说log中都有提示是哪个so出现异常
-
示例:
- 这条case的报错中就有指明libbt-vendor_uwe.so打开失败,那么就需要查找失败原因
- 从开发人员中得到的信息为wifi模组需要使用的so库,那么就需要确认该so是否支持当前设备的wifi模组
- 例如该so支持的wifi模组为8822cs,而当前设备wifi模组为6275p,那么的话需要将不支持的so去掉
- 去掉so后还需要验证该模块,并且保证wifi功能正常不受影响
-
-
其他异常
-
键值异常:
-
关键字:testAllKeys,RemoteControl
-
分析思路:
- 分析详细报错原因,一般都会说明是哪个按键出现问题
- 通过log定位具体异常kl文件
-
示例:
-
报错中并没有多少有用信息,这个时候我们可以单跑这条case,再查看详细报错
可以看到报错的按键为Entel,测试期望键值为66实际为23
-
查找log,看看是哪个kl文件里面的键值信息出现问题,搜索关键字:keyLayout
可以直接定位到报错的kl文件,在/vendor/usr/keylayout/Generic.kl路径下面,而且上面两行也有说明该kl文件里面的键值有问题
-
定位出kl文件后参照正常的kl文件修改差异按键即可解决
-
-
-
-
5 . HDMI/CEC常见异常
-
关键字:HdmiCecHostTest,message ,HdmiHost
-
分析思路:
- cec或者hdmi测试的问题分析思路大致,目前大部分情况都是电视或者设备引起的
- 排查设备功能,cec功能是否开启,hdmi需要看看软件对应的HDR,Dolby等功能是否能够和硬件对应上
- 检查测试设备连接是否正常
- 更换电视尝试
-
示例:
- cec报错Could not find message XXX,一般都是没有正确接收到电视传回来的回调导致的
- 每执行一次cec操作,电视都会返回一次对应的操作回调
- 这种情况可以尝试更换电视,恢复出厂重新尝试
二 . 公共问题分析经验
1 . CTS/GSI/STS
-
CTS主要测试内容说明
- CTS为Android系统兼容性测试,主要测试该版本系统是否符合Google制定的Android系统运行规则,包括系统应用兼容性测试,应用稳定性测试,应用安全性测试,系统稳定性测试,系统功能性测试,外设功能测试等一系列系统测试,检查设备是否正确实现了 Android 的核心功能和API,并且能够与其他 Android 应用程序和服务进行互操作
-
GSI主要测试内容说明
- GSI测试需要烧录Android通用系统映像(system.img),它是 Android 的标准系统映像,可在符合要求的设备上运行,GSI 测试是针对基于 GSI 构建的系统进行的兼容性测试,以确保这些系统在遵循 Android 平台规范的前提下能够正确运行和与其他 Android 设备和应用程序进行交互。
-
STS主要测试内容
- STS测试使用的是userdebug的系统版本,它包含一系列测试用例,用于检查设备的安全功能、漏洞修复和安全策略是否符合最佳实践和安全标准
-
主要分析操作思路
- CTS和GSI的测试项差别不大,我们需要知道的是,烧录system.img之后会将原来系统的system分区擦除,换上system.img中的system分区,该分区由Google提供。那么如果CTS测试上出现的错误但GSI测试中没有,很大可能是system分区出现了问题。反之,如果GSI上出现的错误但CTS上没有出现,那么起码可以得知,该错误应该不是由system分区引起,可能是由其他分区引起。借用此方法,能够进一步缩小排查范围
- CTS和GSI,STS都为开源内容,在日常分析中,我们几乎可以在Android Code Search找到全部所需要的测试代码。在一些较为复杂和难见的问题中,借助开源代码结合logcat分析,即使我们不知道系统源码的情况下,大多数问题都能分析大致的错误原因
- 在正常测试过程中,需要使用正式(user)版本作为测试系统,而STS使用的是userdebug版本进行测试,但在分析过程中,CTS大部分问题都可以使用userdebug的调试版本作为测试系统,userdebug系统的超级管理员(root)权限可以帮助我们更好的进行调试和分析,比如手动更换系统应用,读取系统隐藏属性,更换系统so运行库,查看系统所有分区内容等操作(注:GSI由于使用的是Google的system,因此无法使用userdebug系统来进行分析)
2 . GTS/TVTS
-
GTS主要测试内容
- GTS为Google GMS的测试,主要测试GMS服务内容,确保GMS服务在设备上的正常运行,包括但不限于GMS服务的功能性测试,稳定性测试,兼容性测试等一系列测试。该测试内容为Google GMS专属内容,因此并不开源
-
TVTS主要测试内容
- TVTS 主要针对 Android 电视设备的测试。它包含一系列测试用例,用于验证 Android 电视设备的功能、稳定性和用户体验,以及Google视频服务(Youtube,Play Movie,Live Channel)一系列播放测试,稳定性测试,压力测试等,确保其符合 Android 电视平台的要求
-
主要分析操作思路
-
首先我们需要知道的是,测试GTS/TVTS必不可少的一些条件
- Widevine key:Google的DRM数字版权管理,设备需要确保烧录了正确的Widevine key后,才能通过google的加密服务器读取内容
- Attestation key:在Android设备中,Attestation Key 必不可少;如果设备中缺少Attestation Key,CTS/GTS中的测试项会失败。密钥认证旨在提供一种方法来强有力地确定非对称密钥对是否由硬件支持(如果来自HW Keymaster)。在APP获取Keymaster密钥对后,APP可以要求Keymaster提供一个证书链(证书密钥签名的证书,而根证书来自google),并验证证书链是否有效。 应用程序应该自行验证证书。由Google提供
- APE_key.json文件:该文件中记录Google OEM/ODM合作厂商信息,根据此文件才能访问Google服务器上一些隐藏信息,主要用于校验厂商的测试资格,服务器交换密匙等,需要将该文件设置变量,确保GTS测试套件能够正常读取该文件信息,如:export APE_API_KEY='path/to/key_file.json'(注:APE_Key属于公司机密内容,禁止随意拷贝和外泄)
-
因为GTS/TVTS属于闭源内容,所以我们的问题分析方法比较有限,GTS部分问题有时候需要结合其他测试报告中的内容去分析,如:CTS测试中出现了apk key不符的问题,GTS也会有相应的报错
-
GTS测试会检查对应项目的一系列密匙,大部分问题其实都出在这个上面,如果测试过程中出现"L1,L2,L3","widevine","Drm","id attesta"等涉及到加密有关的内容,那么大概率需要检查项目对应key是否烧录正确
-
如果遇到给出的信息实在过少的报错,可以使用通用分析问题思路
-
3 . VTS
-
VTS测试主要测试内容
- VTS 是由设备制造商(OME)使用的测试套件,用于测试设备硬件和固件的兼容性。VTS 测试包含一系列测试用例,主要验证设备的硬件功能、系统稳定性和性能等方面是否符合规范。设备制造商使用 VTS 进行自我测试,以确保其设备能够与 Android 平台兼容并提供一致的用户体验
-
主要分析操作思路
-
VTS测试系统比GSI多烧录一个vendor-boot.img映像,烧录这个映像后会提升系统权限,可以理解成userdebug版本的GSI测试软件
-
VTS测试会测到系统以及系统底层驱动,HW等,这里要注意的是,如果测试系统底层这块报错,是无法在系统日志中定位到测试case的,理论上来说系统日志中保存的是系统运行状态的打印,而在系统底层(kernel)这块的日志不会体现在系统日志中,这个时候我们发现很难从庞大的系统日志中精准定位出问题所在
-
在分析过程中,如果报错和日志中没有提示或者无法获取过多的有用信息,这个时候我们可以分析测试源码,值得一提的是,VTS测试源码其实已经在系统源码中,有些报错甚至会告诉你报错的源码路径,比如:
可以看到报错中已经详细说明测试在源码中哪一行报错,这个时候我们就可以查看项目源码来分析大体错误原因
-
4 . 复杂问题特殊分析方法
-
在我们遇到一些很棘手的问题时,比如某个段落给出的报错信息较少,想验证自己的猜想是否正确等等一系列原因,我们可以采用一些特殊的分析方法
- 修改测试代码:这里指的修改,并不是修改测试代码本身的测试逻辑,而是添加log打印,打印一些我们需要的信息
- 使用userbug版本分析:这个算是比较常用的一种分析手法,在分析一些问题中,我们需要查找,替换某个文件或者so,往往会因为权限问题无法操作,这个时候我们只能使用userdebug版本来操作
- 使用其他项目进行对比(横向对比法):如果报错项目为第一次测试版本,往下没有可以使用的对比版本,那么有些问题我们就可以使用同一套公版项目来做对比。当然这种情况比较少。一般来说只需要对比公版即可,除非同一套公版的其他项目也遇到该问题。
5 . 通用分析问题思路
-
在XTS测试中,总计测试项估计有百万来条,因此我们不可能每个case报错都能够分析透彻,特别是给出的报错信息特别少的case
-
如果我们在尝试了各种办法都没法提取过多的有用信息,那么可以尝试通用问题分析方法:
- 1. 定位版本:首先我们可以定位出能够pass的版本,包括不局限于项目版本,基线版本,公版等
- 2. 对比差异:如果定位出来能够pass的版本,那么我们就能对应一下当前报错版本和pass版本之间的差异,排查两版本之间的改动,包括但不限于patch排查,分区排查,文件排查等。这个过程可能有点缓慢,为了加快排查速度我们可以使用二分法排查,并且保持思路清晰,我们还需要把排查过的版本,文件等信息记录