Android权限适配里的那些坑

开头不得不吐槽下:简书的markdown编辑器居然不支持目录索引,非常的坑.

安卓的安全和权限架构


先来看下google官方文档中对android安全架构的介绍:

Android 安全架构的中心设计点是:在默认情况下任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。这包括读取或写入用户的私有数据(例如联系人或电子邮件)、读取或写入其他应用程序的文件、执行网络访问、使设备保持唤醒状态等。
由于每个 Android 应用都是在进程沙盒中运行,因此应用必须显式共享资源和数据。它们的方法是声明需要哪些权限来获取基本沙盒未提供的额外功能。应用以静态方式声明它们需要的权限,然后 Android 系统提示用户同意。
应用沙盒不依赖用于开发应用的技术。特别是,Dalvik VM 不是安全边界,任何应用都可运行原生代码(请参阅 [Android NDK](https://developer.android.google.cn/tools/sdk/ndk/index.html))。各类应用 — Java、原生和混合 — 以同样的方式放在沙盒中,彼此采用相同程度的安全防护。

可以看出 android安全架构的两个中心点就是 权限沙盒模型.
沙盒模型 属于进程级别的安全防护,它设置进程级别的隔离带来保证进程间互不影响.而权限 则属于开发者可以在应用层控制的属性,google设计权限机制 就是为了让开发者带上脚链跳舞,在授予应用开发者申请权限的能力的同时,又极力限制开发者对权限的滥用,避免app的恶意行为.

permission的三个重要性级别


为了后面便于分析permission的重要性级别与 android:permission 之间产生的化学反应,首先列出permission的三个重要性级别

protectionlevel.png

permission的危险级别由 android:protectionlevel 这项属性来决定.它一共有四个级别:

  • normal
The default value. A lower-risk permission that gives requesting applications access to isolated application-level features, with minimal risk to other applications, the system, or the user. The system automatically grants this type of permission to a requesting application at installation, without asking for the user's explicit approval (though the user always has the option to review these permissions before installing).

正常级别的权限属于最低危险等级的权限级别.这类权限不需要用户通过对话框显式地同意,即只要开发者在manifest里申请了权限,系统就会自动授予app这些权限.

  • dangerous
A higher-risk permission that would give a requesting application access to private user data or control over the device that can negatively impact the user. Because this type of permission introduces potential risk, the system may not automatically grant it to the requesting application. For example, any dangerous permissions requested by an application may be displayed to the user and require confirmation before proceeding, or some other approach may be taken to avoid the user automatically allowing the use of such facilities.

危险级别的权限通常具有访问用户隐私数据的属性(例如读取手机状态信息,收发短信,拨打电话),所以这类权限通常不会被自动授予app.

  • signature
A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission. If the certificates match, the system automatically grants the permission without notifying the user or asking for the user's explicit approval.

签名级别的权限:文档里是这样解释的: 只有当请求该权限的application具有与声明权限的application相同的证书时,这项权限才会被授予.如果证书相同,这项权限会被自动授予而不用显式地通知用户或征求用户同意.

<permission>和<use-permission>标签

Paste_Image.png

先来看看 <permission> 标签的定义, 它用于定义权限,主要属性有两个,除了 protectionLevel 这个属性以外,还有就是 permissionGroup 这个属性,它将不同权限定义在一个权限组中.关于权限组,需要知道的一点是当某个权限组中包含了某个 Dangerous-Permission 的时候,如果用户同意了使用该危险权限,那么app会自动被授予使用该用户组中其它权限的权力.

    public static final class permission_group {
        public static final String CALENDAR = "android.permission-group.CALENDAR";
        public static final String CAMERA = "android.permission-group.CAMERA";
        public static final String CONTACTS = "android.permission-group.CONTACTS";
        public static final String LOCATION = "android.permission-group.LOCATION";
        public static final String MICROPHONE = "android.permission-group.MICROPHONE";
        public static final String PHONE = "android.permission-group.PHONE";
        public static final String SENSORS = "android.permission-group.SENSORS";
        public static final String SMS = "android.permission-group.SMS";
        public static final String STORAGE = "android.permission-group.STORAGE";

        public permission_group() {
            throw new RuntimeException("Stub!");
        }
    }

这里把安卓中所有的用户组列出来,类别有日历,相机,联系人,定位,MicroPhone,通话,传感器,短信,存储 这几个类别,都是涉及用户隐私的相当敏感的权限.
<use-permission> 这个标签用的比较多了,就是在application标签下声明要获取某项权限.
<permission-tree> 这个标签用的特别少,,我在google上搜索了一圈也没见到有详细的介绍
,大概的用法是: 可以通过PackgeManager.addPermission来动态添加某个权限加入到 permission-tree 中,前提是在相同的package中,也就是包名相同,这个方法的具体实现在 ApplicationPackageManager
中,查阅其它资料了解到这个方法应该是给系统级别的操作使用的,日常开发app应该基本用不到.

Android 4.4 , 5.0 , 6.0对权限的处理方法


终于讲到安卓各版本对申请权限的不同处理办法了.总的来说android对权限的控制是越来越强的.

  • 在4.4系统上,只要开发者在 manifest 里面申请对应的权限,apk在安装到手机上后,都会弹出权限列表展示给用户该app将要获取到哪些权限,只要用户同意了安装这些权限就会自动被授予给app.
  • 5.0系统对权限的处理与4.4基本没有差别,但一些第三方厂商的rom在应用管理中提供可关闭权限的开关,这样做就造成了很大的适配困扰,等下后面再讲.
  • 6.0以上的系统在处理申请权限时,会弹出dialog来询问用户是否授予该权限,除了同意和拒绝dialog还会提供不再询问这个选项.

主动申请权限的最佳实践和碎片化处理


  • 在 Android 6.0 以上主动申请权限时,谷歌的官方文档和视频给出了最佳实践 :
    用户如果勾选了不再询问选项,则后续申请该权限时将不再弹出对话框,如果用户拒绝过该权限的申请但没有勾选不再询问,那么ActivityCompat.shouldShowRequestPermissionRationale()将返回true,开发者可在此时展示一个界面来介绍为何应用需要使用该权限,来争取用户授予权限
Paste_Image.png

上图是官方文档中对于权限申请的最佳实践代码.


12月13号:补充shouldShowRequestPermissionRationale()的原理

shouldShowRequest.png

可以看到 shouldShowRequestPermissionRationale 这个方法只有在API 23 上才会生效.

image.png

最终调用的函数的逻辑如下: 首先判断权限如果被授予了就会直接返回false,然后会判断该权限是否不含 SYSTEM,POLICY,USER 这三个标志位并且含有 USER_SET这个标志位.

FLAG.png

SYSTEM.png

再看这几个标志位的含义:大意就是权限的授予状态是否被FIXED了也就是锁死了,而锁死的原因可能是用户点选了不再询问, 也有可能是因为该app是系统app。


Paste_Image.png

处理的结果会在 onRequestPermissionResult() 中返回,开发者可在这个回调中获取每一项权限具体的申请情况,然后提示对应的信息给用户。


在Flyme5.1 上测试,发现targetsdk<23 使用 checkSelfPermission仍然失效,并且会有动态权限申请的弹框出现,这说明flyme在5.0上也做了动态权限的逻辑.

  • 在Android 5.0上,上述的 CheckPermission(),requestPermission()方法统统不能使用,因为这些api都是在6.0之后才加入的,但偏偏某些第三方rom加入了权限的开关界面,这样一来,如果用户在安装apk后进入应用管理界面手动关闭了权限,那么必定会对app的正常逻辑造成影响,而此时又没有api可以主动获取权限的获取状况.
    关于这点,我参阅了 腾讯bugly的一篇文章http://blog.csdn.net/tencent_bugly/article/details/77085531,这篇文章给出的解决方案是通过反射 AppOpsManager 这个类并调用 checkOp(int op, int uid,String packageName) 方法来检查是否获取了权限,之后再跳转到每个rom具体的应用管理开关界面来,然后主动提示用户打开开关.文章中提到可以通过 adb shell dumpsys activity activities 这个命令来获取各个row的权限开关界面,然后再通过显式intent的方法跳转到具体界面,但经过我的测试, checkOp(int op,int uid,String PackageName)方法无法保证100%能正常运行,这恐怕也是因为第三方rom对这个类的修改.
    所以在 加入权限开关的5.0第三方rom系统,暂时找不到一个完美的解决方案,当然这也可能是因为我测试的范围不够大也不够精细,所以如果有哪位阅读了本文的开发者有完美的解决方案,请一定在我的文章下留言帮我解惑.

对于 signature 权限的一点想法


在介绍 protectionLevel 的时候,有这么一个级别 signature ,关于它的解释是:只有证书也就是keystore相同的应用才能被授予该项权限,而我们又知道 activity等组件是可以声明android:permission这项属性的,结合自定义权限使用就可以保证只有拥有了特定权限才能使用该组件.但我们知道 manifest中声明的自定义权限是可以被轻易的看到的,某些恶意应用和开发者在manifest中找到这些自定义权限后完全可以通过嗅探来攻击四大组件.
你可能会问只要把四大组件的 exported属性声明为false不就可以了么,但是这样一来,某些被应用信任的进程或插件运行的进程就无法访问到该组件了.
我想,解决的办法就是使用为自定义权限声明 signature_ 危险级别,这样一来只有拥有相同证书的apk跑起来的进程可以使用该权限,在很大程度上保证了app的安全.
不过这只是我结合官方文档的合理推论,并没有实际实践过,如果有哪位大神实践过,请一定帮我指正错误之处.

参考文章链接

腾讯bugly:
http://blog.csdn.net/tencent_bugly/article/details/77085531
google官方运行时权限最佳实践:
https://developer.android.google.cn/training/permissions/requesting.html#explain

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,200评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,526评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,321评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,601评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,446评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,345评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,753评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,405评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,712评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,743评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,529评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,369评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,770评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,026评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,301评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,732评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,927评论 2 336

推荐阅读更多精彩内容