历时四年,给Google提交的Android Framework Bug终于被Fixed了
2014年在做一个Android终端设备开发过程中,发现了一个Android Framework层的Bug,给Google提交了issue和解决方案,和外界传言一致Google一般不太在意个人开发者提交的issue,直到2017年12月,再次提交了issue,在几轮沟通无果下,忍不住喷了Google几句后,终于该issue被转交给了Android development team处理,又经过快三个月的时间收到了下面的答复:
google issues:https://issuetracker.google.com/issues/70016687
issue背景:
在对我们自己研发的一款Android终端设备进行Camera拍照压力测试时,发现当拍照张数达到数万张时,出现OOM,导致系统崩溃。
issue分析解决过程:
该项目为开发一款Android工业终端设备,采用TI芯片方案,由于芯片方案商支持不够完善(主要TI被高通打的放弃了移动端芯片市场),Camera的HAL层需要我们自己移植适配。
分析思路:
首先看下Android系统架构图中中Camera功能模块的分布情况,从App层->Framework->HAL->Kernel一路下来:
(图片取自https://blog.csdn.net/asd1031/article/details/53699867)
- 首先怀疑测试app自身存在内存问题。
- Android application Framework,libraries层和jni相关模块是经过Google大量验证的模块,出现问题的概率比较小,暂时排除。
- 怀疑HAL层移植问题。
怀疑1:测试app问题
压力测试app由测试同学开发提供的,该app每隔3s触发一次拍照操作,并对拍摄的照片进行清理,以达到拍摄10万张照片的测试目的。基于以上分析,为了排除测试app问题,采用Android原生Camera app进行压力测试,编写monkey测试脚本,触发原生Camera app拍照,进行压力测试。(此处遇到的问题是:如何实现对照片的清理工作,直接触发shell环境下rm操作,并不会清除Android内部文件缓存索引,拍摄几千张照片后仍然会导致存储空间用尽,解决此问题也耗费了点时间,不过不是本文的重点,此处不做展开)
结果:通过原生Camera app进行测试后,仍然出现内存泄漏,此处基本排除测试app的问题。
怀疑2:HAl层
基于之前的分析,我们把怀疑对象聚集在我们自己集成的Android HAL层,在分析之前,简单描述下Android Camera拍照的流程:Linux Kernel提供标准的v4l2接口,供上层(此处即为HAL)获取图像原始数据,HAL层拿到图像数据进行编码(一般为jpeg),回调给Camera service。其中Linux Kernel下Camera驱动和HAL适配层一般由芯片厂商提供,其余部分由Linux Kernel和Android系统官方维护,开发。
这样对HAL的测试分为三步:
- 验证芯片厂商的Camera驱动是否存在问题。
- 验证HAL层图像数据捕获流程是否存在问题。
- 验证图像编码流程是否存在问题。
Camera驱动
Android系统是基于Linux Kernel开发,支持标准的v4l2接口,只需要编写一个简单的基于v4l2的视频捕获程序就可以验证camera驱动的问题,此处测试验证没有内存泄漏,排查驱动问题。
HAL层视频捕获流程
测试思路非常明确,难点在于要把芯片提供商的HAL层源码中进行视频捕获功能的模块剥离出来,单独进行压力测试。(由于原厂提供的HAL层代码,耦合比较严重,在不影响内部流程,结构的情况下,要找到适合的切面mock一些数据接口,才好进行有效的测试。)
经过以上的工作,进行了压力测试,系统未出现内存泄漏,基本排除HAL层捕获流程。
HAL层图像编码流程
继续对图像编码部分剥离,进行压力测试,发现内存泄漏,基本定位大概的泄漏位置,不过由于Android整个编码过程也进行层层的封装,泄漏位置还需要继续细致的定位,这样经过层层的细化,像剥洋葱一样一层层mock输入数据,最终定位在Android系统层的jpeg编码处理中:(frameworks/base/core/jni/android/graphics/YuvToJpegEncoder.cpp)
关于Android的jpeg编码:Android系统jpeg编码支持硬编码和软编码,如果芯片集成了jpeg硬件编码模块,会优先选择硬编码,而如果没有该模块,会采用软件的jpeg编码进行处理。
Android采用的软件编码库是业内知名的libjpeg库,而正是对这个库的使用出了问题:
bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, int height, int* offsets, int jpegQuality) { jpeg_compress_struct cinfo; skjpeg_error_mgr sk_err; skjpeg_destination_mgr sk_wstream(stream); cinfo.err = jpeg_std_error(&sk_err); sk_err.error_exit = skjpeg_error_exit; if (setjmp(sk_err.fJmpBuf)) { return false; } jpeg_create_compress(&cinfo); cinfo.dest = &sk_wstream; setJpegCompressStruct(&cinfo, width, height, jpegQuality); jpeg_start_compress(&cinfo, TRUE); compress(&cinfo, (uint8_t*) inYuv, offsets); jpeg_finish_compress(&cinfo); return true; }
坑就在上面这个接口函数中:
熟悉libjpeg的同学可能会注意到,上面的接口在调用完jpeg_finish_compress()后,没有调用jpeg_destroy_compress(),这个接口是释放压缩工作过程中所申请的资源,就是代码中的cinfo结构,该结构只占十几个字节的内存, 这样就导致了每拍摄一张照片,就泄漏一个cinfo的内存,当拍照数量达到万级时,才会有所察觉。
对这种数据流的控制,pipeline方式是比较好的方案,因为可以明确输入输出,这样非常方便通过伪造输入数据对各个模块进行单独的压力测试,最难控制的就是“洋葱”式的包裹调用,要像“剥洋葱”一样一层层的剥离,找准切面十分麻烦。
这个bug是否影响到你的Android手机
七成的概率下你的手机应该不会有这个问题,即时有这个问题你也很难发现这个问题,因为上面讲到android系统有两种编码方式选择,优先使用硬件编码模块,如果没有硬件编码模块,才会使用软编码的方式,而目前大部分中高端的芯片方案都集成了硬件模块,只有在少数低端芯片上才会使用软编码的方式,并且即使你的手机没有硬编码模块,用的软编码,也很难遇见这个问题,因为对于普通用户,持续拍摄上万张照片是不太可能的,第一受限于手机的存储空间(一万张照片,至少要30G的空间),第二即使能拍摄上万张照片,但要保持手机一直工作不重启也还是比较苦难的(总会死个机啥的)。
哈哈,这么一说发现这个bug其实是一个不会发生的bug了!!!不过我们之前的产品,定位于工业级别,对图像采集有比较高的要求,所以制定了10万张照片的测试标准,也就让我发现了这个不会影响到大部分人的bug。
最后再吐槽下Google
该bug在2014年就已经提交了issue,不过没持续关注,过了几个月被莫名其妙的关闭了,当时没有在意,不过当Android 6.0,7.0版本出来时,我都看了下这个bug,一直存在,所以在去年(2017年)12月份又提了一个issue,Google方面的处理人仍然各种推诿扯皮,最后我没忍住喷了几句,这次Google方面回复会转给开发团队处理,终于在今年(2018年)给出了fixed的结论。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Vue单元测试探索
作者:江敏熙 贝聊前端开发工程师 为什么要单元测试? 项目的现状 当前我在公司里负责的项目,可以分为两类: 一类是相似度很高的项目,比如管理后台,这类项目的页面通过各种公共组件搭建而成。公共组件的复用性很高,所以质量尤为重要。如果开发人员在修改了公共组件之后留下了bug,那么将会直接降低了整个项目的质量。我希望让程序去测试这些公共组件,保证每一个公共组件是可用的。 另一类是公司的核心项目,这些项目特点是维护周期长,并且会不断加入新的功能。在项目版本迭代的过程中,当一些原来通过了测试的旧功能发生了bug,一般只能到了测试阶段才能被测试人员发现。我希望由程序去保证部分核心功能的正常运作,当核心功能发生了bug能快速的察觉到,而不是到了测试阶段才发现。 为了解决上面的问题,我尝试引入单元测试。 单元测试的作用 降低bug发生几率,快速定位bug,减少重复的手工测试。 提高代码质量,为项目带来更高的代码可维护性。 方便项目的交接工作,测试脚本就是最好的需求描述。 接下来谈谈如何进行单元测试。 搭建测试框架 测试工具一览 Mocha Mocha(发音"摩卡")诞生于2011年,是现在最流行的Ja...
- 下一篇
谷歌面临欧洲监管机构创纪录罚款
据“华尔街日报”周二报道,谷歌正面临欧盟委员会数十亿欧元的罚款。这次罚款的原因是谷歌涉嫌迫使智能手机厂商捆绑其应用程序。 据该报报道,欧洲数字市场监管机构一直在调查互联网巨头在手机操作系统市场上滥用主导地位的行为。 华尔街日报有关消息人士称,他们预计欧盟的反托拉斯监管机构会抨击谷歌的移动商业行为。不仅仅是罚款,还要求谷歌取消鼓励应用程序捆绑到Android手机上的策略。欧盟委员会可能会得出这样的结论,即作为Alphabet的子公司,谷歌正在扼杀竞争对手以保护移动设备的广告收入。 由于Android拥有欧洲近四分之三的移动市场,欧盟委员会对谷歌鼓励智能手机制造商在其设备上安装谷歌搜索及其Chrome浏览器的做法表示担心。而且谷歌还会向这些手机制造商支付预安装谷歌应用的费用。 与此同时,华盛顿邮报报道称,谷歌可能不会取消与使用Android的智能手机厂商的应用捆绑交易。在Android智能手机上提供Google Chrome等搜索工具,Google可以通过植入广告和收集移动用户的数据并从中获利。 谷歌的贸易组织计算机与通信行业协会负责竞争和欧盟监管政策的副总裁Jakob Kucharczy...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS8编译安装MySQL8.0.19
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装Nodejs环境