历时四年,Google终于修复了一个我发现的Android Framework Bug

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的结论。

历时四年,给Google提交的Android Framework Bug终于被Fixed了相关推荐

  1. 初识Android framework层

    Android系统的构成如下,从上到下依次是 Application应用层 Framework框架层 LIbrary系统库层 Linux内核层 关于Framework层: Android的Framew ...

  2. 金三银四的面试黄金季节,Android面试题来了!

    金三银四的跳槽季节,你准摆好了吗? 首先我们分享一个Android知识图谱. 下面是一些面试官基本必问的问题,请一定要去了解! 基础知识 – 四大组件(生命周期,使用场景,如何启动) java基础 – ...

  3. 一、初识GVR ---- Android VR视频/Google VR for Android /VR Pano/VR Video

    原文链接: http://blog.csdn.net/qq_24889075/article/details/52118633 http://www.jianshu.com/p/09c0822b9d1 ...

  4. 一起看 I/O | Google TV 和 Android TV OS 的最新进展

    在接触到的娱乐内容与日俱增的今天,有研究表明,三分之一的美国家庭现每周的电视观看时长在 25 个小时以上.随着电视体验的不断演进,我们也为自己设定了一个目标,那就是为用户打造量身定做的电视使用体验,让 ...

  5. Google OAuth2 for Android(type of web OAuth)

    Google OAuth2 for Android(type of web OAuth) 一.应用创建和注册 1.登录google 登录Google 应用 2.创建项目 3.创建OAuth同意屏幕 4 ...

  6. Android的BUG(四) - Android app的卡死问题

    做android,免不了要去运行一些跑分程序,常用的跑分程序有quadrant(象限),nbench,安兔兔等.作为系统工程师,对这些跑分 程序都非常的不屑,这个只能是一个不客观的参考,但客户都喜欢拿 ...

  7. RecyclerView(四)设置分割线样式(Android 5.0 新特性)

    Android RecyclerView(四)设置分割线样式(Android 5.0 新特性) 样式一 在这里,其实是设置了每一个 条目布局中的子布局的android:layout_margin = ...

  8. Google+ 团队的 Android UI 测试

    https://github.com/bboyfeiyu/android-tech-frontier/tree/master/android-blog/Google%2B%20%E5%9B%A2%E9 ...

  9. 【多图】Google工程师解析Android系统架构--what is android(转载)

    导读:Sans Serif是Google的一位工程师,近日发布了一篇博文非常清楚的描述了Android系统架构,中国移动通信研究院院长黄晓庆在新浪微博上推荐了该文,并认为文中对Android的介绍很好 ...

最新文章

  1. 微信新版支持读取iPhone M7/M8协处理器运动数据 与好友PK一下运动量吧
  2. 谷歌、阿里们的杀手锏:3大领域,10大深度学习CTR模型演化图谱(附论文)
  3. 《精通Wireshark》—第2章2.6节总结
  4. 有重叠与无重叠序列之序列检测与序列产生
  5. 绝地求闪退be服务器未运行,绝地求生大逃杀BE启动失败,应用程序无法正常启动...
  6. 分享我第一次做项目的感受
  7. mysql官网 ab_MySQLAB同步
  8. JWTToken超时刷新策略
  9. 如何使用Docker在GitLab上设置CI
  10. React系列---Babel
  11. 微信小程序调试之【不在以下合法域名列表中】
  12. 2014校招 百度试题及答案
  13. php 调用cron jobs,在CentOS 6.4中使用CronJobs执行PHP不起作用?
  14. EJB的beans们
  15. Mac 安装 Grunt
  16. Atitit pdf转文本 pdf2txt v4 t83.docx Atitit pdf转文本 pdfutil 目录 1.1. Pdfbox cmd 模式 TextToPDF 1 1.2. Pdf
  17. python继承list_python中 class和l
  18. 不到90天的时间,备考数据库系统工程师还来得及吗?
  19. python读取组合惯导数据,并进行坐标转换到北东天、utm坐标系
  20. 交互设计——超越人机交互

热门文章

  1. 《Python Cookbook 3rd》笔记(1.5):实现一个优先级队列
  2. C++ Byte转十六进制字符串输出
  3. codeforces 160A-C语言解题报告
  4. 哪些人不能要 哪些人不能留
  5. IT巨头互掐云存储:Dropbox能否一马当先
  6. 程序员成长之路(转)
  7. 程序员面试、算法研究、编程艺术、红黑树4大系列集锦与总结
  8. PHP工程师需要掌握的知识(转载)
  9. Redis-ha(sentinel)搭建
  10. 跟我一起玩Win32开发(20):浏览文件夹