论Android适配踩到的坑

说起Android适配,恐怕是每一个Android开发/测试工程师心里的痛,且不论Android设备品牌众多、分辨率各异等痛点,单论Android版本的繁多也会提高Android APP的开发/测试成本。如果能了解Android版本之间的变更差异,会让开发/测试事半功倍。本文即是从这个角度出发,给读者带来一点福利。

故事的开始,须先来说说本文的主角:腾讯路宝,是一款驾车导航APP,腾讯MIG地图平台部打造出品的一款为广大驾车用户提供精准导航和路况的产品。

以下故事就是发生在这款APP上的,且等我慢慢叙来:

一、起因

腾讯路宝Android3.0.0版本从灰度测试开始,就有少量用户反馈:

下载离线地图后,默认存储到“手机”,但不能切换到外置存储卡上,提示如下:

测试这边用了手里的设备都无法复现此问题,开发做了用户回访,发现腾讯地图是可以切换存储位置到外置sd卡上,所以初步排除了“用户sd卡不可用”的异常,但是因为用户的机型拿不到,所以测试找了同款机型(红米NOTE Android4.4.2和Android4.2.2)做测试,都无法复现此问题。

有了上文提到的现象,那么我们就来研究下解决方案:

二、解决方案

仔细研究手腾讯地图的源码,发现腾讯地图在做切换存储位置到外置sd卡时,是将已下载的离线地图连同目录一起移动到了/xx/A/是腾讯地图存储已下载离线地图文件的目录;

而腾讯路宝则是存储到了/yy/B/是腾讯路宝存储已下载离线地图文件的目录;

对比了这个区别之后,路宝开发将路宝代码中的存储目录也移动了下,即做切换存储位置到外置sd卡时,将其保存在/xx/B/目录下,即前半部分路径与腾讯地图相同、后半部分为各自包路径,经测试验证,问题得到解决。

在解决此问题的时候还没有找到发生此bug的根本原因,为了确认问题解决的是否彻底,我利用众测平台,进行了一轮众测,众测结论表明,众测用户中之前能复现此bug的机型上,问题也得到了解决。

问题得到了解决和验证,欢呼之余又陷入了沉思,因为原因我们并未得知,于是就有了下文:

三、根因分析

经过测试人员验证,在之前能复现此bug的机型上,用修改过的包再测试,问题确实得到了解决,但是这个问题为什么会发生、以及测试人员为什么之前测试的过程中没有发现这个bug呢?以及这类问题我们之后的测试过程中如何规避呢?一系列的问题迫使我们进一步深入研究这个bug的根因。

1、首先我们分析了路宝APP的用户反馈、众测用户反馈的机型,发现都是Android4.4的机型,有三星Note3 Android4.4.2、红米Note Android4.4.2,然后度娘Google Android4.4机型的特性,发现:

KitKat(即Android4.4系统版本)之后的版本,Google更改了用户对外置SDcard(Secondary Storage)的写入的权限;

以前我们可以直接获取WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限来直接操作Sdcard,现在则不能,其目的是软件卸载时能将该软件创建的文件全部删除。

在KitKat之前的Android版本会给应用程序单独分出一块外部存储空间(external storage),这块存储空间可能在sdcard(可插拔的外置sdcard)上,也可能在仅仅是在设备内部的闪存上,我们要获得WRITE_EXTERNAL_STORAGE权限才能对这块空间进行访问,如果只是读取内容则不需要权限。

在4.4 KitKat及之后的版本中,Google做了两个变化:1、进行读取时需要READ_EXTERNAL_STORAGE权限;2、访问应用所属的目录下(如:android/data/[package name])存储的数据是不需要任何权限的。

根据这个定义就能解释这个bug了吗?当然不能,测试手里就有三星Note3 Android4.4.2这款机型,但完全不能复现这个bug。

冥冥之中老天助我,在跟开发沟通其他bug的同时,发现另一个开发手里的三星Note3能复现此bug,欣喜若狂,遂借来进行比对分析。

2、继续跟踪中,既然上面提到了Android4.4系统版本禁止第三方APP对外置sdcard的写权限,那么我们分析下测试手里的Note3 Android4.4.2这款未出现bug的机型的Android系统的权限管理文件/system/etc/permissions/platform.xml,截图如下:

再分析从开发借来的三星Note3,也是Android4.4.2系统,其权限管理文件platform.xml截图如下:

恩,区别有了,少了<group gid="media_rw" />一行,但是这行又对bug产生了什么具体的影响呢?

3、与开发人员沟通,开发在试图获取外置sdcard是否有可写权限时,是判断了/storage/extSdCard这个目录,那么我们就顺藤摸瓜,来看下这个目录在Android系统下的权限吧,如下:

先连接设备,通过mount命令查看挂载的文件系统(跟外置sdcard相关的),如下:

/dev/block/vold/179:65 /mnt/media_rw/extSdCard vfat rw,dirsync,nosuid,nodev,noexec,noatime,nodiratime,uid=1023,gid=1023,fmask=0007,dmask=0007,allow_utime=0020,codepage=cp437,iocharset=iso88

/mnt/media_rw/extSdCard /storage/extSdCard sdcardfs rw,nosuid,nodev,relatime,uid=1023,gid=1023 0 0

即/storage/extSdCard挂载在了/mnt/media_rw/extSdCard下面,接下来分别查看/media_rw/extSdCard 和/storage/extSdCard的权限

root@hlte:/storage/emulated/0 # ls -ald /mnt/media_rw/extSdCard ls -ald /mnt/media_rw/extSdCard drwxrwx--- media_rw media_rw 2015-04-14 11:11 extSdCard

即/media_rw/extSdCard的组media_rw用户有全部的读写权限

root@hlte:/storage/emulated/0 # ls -ald /storage/extSdCard ls -ald /storage/extSdCard drwxrwx--x root sdcard_r 2015-04-14 11:11 extSdCard

即/storage/extSdCard的组sdcard_r用户也有全部的读写权限

这样看来,platform.xml中对WRITE_EXTERNAL_STORAGE的权限多出来的<group gid="media_rw" />也没什么区别,因为sdcard_r组就有读写权限......

4、排查暂时又没了头绪,继续求助度娘,惊喜的发现介绍如下:

在Kitkat中,sdcard_r 组有 +rwx 所有权限,实际上这是明显不对的,因为Fuse守护进程会在运行时中积极地参与修改应用的权限。

android.permission.WRITE_EXTERNAL_STORAGE权限被授给sdcard_r组和sdcard_rw组的成员,但在kitkat中认证write权限需要一些动态的检查,因此FUSE守护进程会被用来补充文件系统的权限,FUSE守护进程会强制赋予拥有特定目录的App每个权限(也就是访问自身数据存储的目录android/data/pack-agename...及一些公共目录)。对于sdcard_rw组中使用-w标志配置的非默认所有者,FUSE守护进程也会强制赋予write-protected权限。

所以当路宝APP请求/storage/extSdCard的“WRITE_EXTERNAL_STORAGE”的权限时,是有 <group gid="sdcard_r" />和<group gid="sdcard_rw" />这两个组的权限,但是这两个组在请求写外置sd卡的授权时,会被FUSE守护进程强制赋予不可写的权限,所以在开发机器上能复现这个bug,即离线地图不可切换存储位置。

以上现象在三星note3 Android4.4.2 三星原生系统上可复现。

5、这样就能解释为什么用户的机型上会出现这个问题了,兴奋之余,又有疑虑,既然用户的机型上能复现,那为什么测试的机型上就不能复现呢.....

再看下测试机型上的Android系统权限管理文件platform.xml中的权限如下:

<permission name="android.permission.WRITE_EXTERNAL_STORAGE" > <group gid="sdcard_r" /> <group gid="sdcard_rw" /> <group gid="media_rw" /> </permission>

那么问题来了,media_rw组是否受FUSE守护进程的限制呢,继续度娘,结论如下:

由此看来,虽然sdcard_r组受FUSE守护进程的限制,没有对外置sdcard的写权限,但是media_rw组有对外置sdcard全部的读写权限,且由于/storage/extSdCard是挂载/mnt/media_rw/extSdCard下的,根据linux系统取最大权限的特性,故/storage/extSdCard也有可读写外置sd卡的权限。这也就能解释为什么测试的机型上未复现此bug,且与用户复现bug的相同机型上为什么也不能复现这个bug了。

看到这里,相信你也和我一样,恍然大悟,以上看到了表象,我们再来抽象成通用理论,如下:

四、总结

总结而言,就是对于Goolge原生的Android4.4及以上的系统,都限制了第三方APP对外置sdcard的写权限,但对于不同的厂商的ROM,有对Android权限管理文件platform.xml进行了修改,加入对外置sdcard的写权限。

部分机型(GOOGLE Android原生或者未修改platform.xml中的<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >的gid)且为Android4.4以上的系统能复现此问题,而测试这边为了保障测试系统版本的全面性,Android4.4以上系统均为自己刷的ROM,platform.xml中权限被修改,所以不能复现此问题。

那么以后如何规避这类问题呢:

1、加强代码review,考虑到KitKat系统的这个特性,后续需要写入外置sdcard的相关功能,均改为在外置sdcard的android/data/[package name]进行读写;

2、与适配团队沟通,他们将此功能点加入软件扫描检查点中,不符合的情况将给出警告,督促开发团队优化代码;

此问题从发现到排查,历时不短,感谢开发童鞋被我不厌其烦的骚扰后,还能一如既往的追根究底~感谢bug根因分析中jonah、lances和terry的帮助~感谢度娘~

通过以上实践经验,我们总结了第三方APP若对外置sdcard进行写操作,在Android 4.4及以上的系统一定要注意的事项,以及可以规避的方法,不知道对于正在Android开发/测试中的你,是否有帮助呢?

原文发布于微信公众号 - 腾讯移动品质中心TMQ(gh_2052d3e8c27d)

原文发表时间:2016-02-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cloudskyme

模块化服务规范——OSGI

什么是OSGI OSGi(Open Service Gateway Initiative)有双重含义。一方面它指OSGi Alliance组织;另一方面指该组织...

53530
来自专栏Spark学习技巧

干货!一次kafka卡顿事故排查过程

由于一次功能上线后,导致某数据量急剧下滑,给我们紧张的呢!排查过程也是个学习过程!抛开结果,方法论可供参考~

9240
来自专栏AI科技评论

真正从零开始,TensorFlow详细图文安装入门教程!

GAIR 今年夏天,雷锋网将在深圳举办一场盛况空前的“全球人工智能与机器人创新大会”(简称GAIR)。大会现场,谷歌,DeepMind,Uber,微软等巨头的人...

423140
来自专栏编程一生

美团点评智能支付核心交易系统的可用性实践

15310
来自专栏小狼的世界

RSS的相关知识

rss是RDF Site Summary 的缩写(RDF是Resource Description Framework的缩写 ),是指将网站摘要用xml语言描述...

12630
来自专栏java一日一条

微信、QQ这类IM App怎么做——谈谈Websocket

关于我和WebSocket的缘:我从大二在计算机网络课上听老师讲过之后,第一次使用就到了毕业之后的第一份工作。直到最近换了工作,到了一家是含有IM社交聊天功能的...

34720
来自专栏乐享123

Migrate Centos7 From Centos6

22650
来自专栏开源项目

那些优秀的网络爬虫工具介绍,最后亮了!| 码云周刊第 16 期

技术干货 1、SpringMVC 执行流程及源码解析 2、使用 Vue2 和 Yii2 进行前后端分离开发 3、 SSM (十一) 基于 dubbo 的分布式架...

613100
来自专栏Netkiller

协议测试

协议测试 May 9, 2016 协议测试 What 什么是协议测试 什么事协议? 协议是计算机进程或网络中进行数据交换而建立的规则、标准或约定的集合。 什么是...

50850
来自专栏微信公众号:Java团长

干货!一次kafka卡顿事故排查过程

由于一次功能上线后,导致某数据量急剧下滑,给我们紧张的呢!排查过程也是个学习过程!抛开结果,方法论可供参考~

11020

扫码关注云+社区

领取腾讯云代金券