本文来自于腾讯Bugly公众号(weixinBugly),未经做者赞成,请勿转载,原文地址:https://mp.weixin.qq.com/s/OQRHEufCUXBA3d3DMZXMKQhtml
本文主要讲解了Android 权限管理方面几个点:java
提到Android 权限管理,业内人士都知道Google 在Android 6.0时提出了运行时权限管理机制,在Android 6.0以前,所申请的权限只须要在AndroidManifest.xml列举就能够,从而容易致使一些安全隐患,所以,在Android 6.0 时,Google 为了更好的保护用户隐私提出了新的权限管理机制(官网 :Working with System Permissions),同时将其分为两大类:android
(1)Normal Permissionsgit
Normal Permissions 通常不涉及用户隐私,是不须要用户进行受权的,好比手机震动、访问网络等;github
(2)Dangerous Permissionshell
Dangerous Permission通常是涉及到用户隐私的,须要用户进行受权(动态申请),好比读取SIM卡状态、访问通信录、SD卡读写等。api
经过 adb shell pm list permissions -d -g 能够查看 Dangerous Permission (以权限组形式)安全
Dangerous Permission group微信
如上图所示 :Dangerous Permission 通常以 Permission group 形式存在,只要 Permission group中某一个 permission 被Granted,则整个Permission group下的权限均被Granted (目前是这样,之后规则说不定会变)。网络
本节主要介绍介绍如何进行权限检查及权限兼容,主要分为如下几类:
(1)targetSdkVersion>=23,终端设备是6.0(api 23)以上系统;
安装的时候不会得到权限,在运行时向用户申请对应权限。这部分权限检查比较简单,不涉及权限兼容,使用官方方案就能够 ,使用 Context::checkSelfPermisson ,建议使用ContextCompat::checkSelfPermisson检查权限 便可 ,通常检查流程 以下:
判断是否有对应权限
(ContextCompat::checkSelfPermisson)
判断是否须要解释对应权限用途(ActivityCompat::shouldShowRequestPermissionRationale)
若是须要解释,则现实自定义权限界面便可
不须要解释的话,直接请求对应权限
(ActivityCompat::requestPermissions)
上述状况较为简单,在此再也不赘述。
(2)targetSdkVersion<23,终端设备是6.0(api 23)以上系统;
使用的是老的权限机制,在app 安装时会询问AndroidManifest.xml文件中的权限,可是用户能够在设置列表中关闭相关权限,这种状况可能会对app正常运行形成必定影响。
(3) 终端设备系统小于6.0(api 23)
你们可能要问,终端设备系统小于6.0状况还须要考虑吗,确定是用的老的权限管理机制,在app 安装时会询问AndroidManifest.xml文件中的权限,用户关闭不了,真的是这样吗 ?
答案是否认的,在实测中发现,目前有很多国产Rom 手机在6.0以前就有关闭权限的开关。这种状况也是咱们兼容的对象。
下面将会以本身开发过程当中遇到的问题进行展开 ,目前企鹅FM支持免流了,须要使用READ_PHONE_STATE权限 (读取SIM卡状态),因为以前未对改权限是否关闭没有进行相关判断,所以收到了不少例由于上述权限关闭,致使免流失败的状况。
适配过程以下 :
(1)使用 try catch 来检查权限是否关闭
想法很简单,若是改权限被用户禁止了,那确定会异常,所以能够在catch 中作文章,结果发现这一招根本没有用,为啥了 ?由于使用 READ_PHONE_STATE 权限的方法内部已经try catch ,外面没法捕获,所以该方法失效。
(2)ContextCompat::checkSelfPermisson
既然在6.0 可使用Context::checkSelfPermisson进行权限检查,那可否使用support v4 中的ContextCompat::checkSelfPermisson 方法了,试一下,发如今api 23 如下失效,为了探究缘由,查看了ActivityCompat::requestPermissons 内部实现,以下
ActivityCompat::requestPermissons
内部权限检查方法在api 23 如下,使用的是 PackageManager::checkPermission,再去查看PackageManager::checkPermission方法,以下:发现只要权限在AndroidManifest.xml中注册过,均会认为该权限granted ,所以上述方法在api 23 如下也失效。
PackageManager::checkPermission
查阅相关资料和请教组内同事,发现Support V4 下面有一个专门检查权限的工具类PermissionChecker。
(3)PermissionChecker
查看PermissionChecker源码发现 ,PermissionChecker内部实际上使用的是AppOpsManagerCompt,而AppOpsManager是在api 19 加入进入的(AppOpsManager后面介绍)
PermissionChecker::checkPermission
进而查看AppOpsManagerCompat 内部实现
AppOpsManagerCompat::permissionToOp
IMPL实现以下:
IMPL实现
从上图能够看出:在api 23如下, AppOpsManagerImpl::permissionToOp 直接返回为null ,这直接致使api 23如下权限检查将会返回 granted ,所以,该方法在api 23 下,权限检查方法也会失效。
(4)AppOpsManager
API 19以上 ,Google 官方提供了 AppOpsManager 类来检查权限,看到这个api 时,脑海浮现出 “天无绝人之路啊”,里面有两个比较重要的方法 :AppOpsManager::checkOp(int op ,int uid ,String packageName) (hide方法)和AppOpsManager::checkOp(String op,int uid ,String packageName)(public 方法 ,api 23 以上可用),不经思考,直接写出了以下两个方法
1)AppOpsManager::checkOp(int op ,int uid ,String packageName)
须要使用反射 :
AppOpsManager::checkOp(int op ,int uid ,String packageName)
2)AppOpsManager::checkOp(String op,int uid ,String packageName)
API >= 23 才可使用 :
AppOpsManager::checkOp(String op,int uid ,String packageName)
在实测中发现,api 低于23时 ,OP_READ_PHONE_STATE =51 找不到,致使反射失败。
仔细察看了一下 6.0 (API 23 )_NUM_OP = 62,以下,为什么找不到51 了
6.0 (API 23 )_NUM_OP = 62
难不成 每一个版本还不同,查看其余版本,验证了这个想法:
**5.1.1 (API 22 )_NUM_OP = 48**
5.1.1
Op个数限制 (须要找到对应源码说明**)详情 参看 :
查看Android 对应版本 _NUM_OP (目前最高支持5.1.1版本)
此时,OP_READ_PHONE_STATE = 51 在6.0(API 23)如下,经过反射是找不到的,所以对于READ_PHONE_STATE权限检查仅限于6.0及6.0以上。
同时也仔细看了一下AppOpsManager 类介绍,并非为开发者设计的,不过其余权限兼容可使用这种方法,前提是 要看OP_*是在什么版本才有的,需作兼容方案 。
public class AppOpsManager
extends Object
API for interacting with "application operation" tracking.
This API is not generally intended for third party application developers; most features are only available to system applications. Obtain an instance of it through Context.getSystemService with Context.APP_OPS_SERVICE.
(5)最后查看了几个第三方权限库(暂未看完)
既然在这里讲解跳转到app 管理权限页面的方法,可想而之,事情绝对不太简单。Android 碎片化不只在存在于ui适配 ,一样也存在于这里,致使咱们没法使用同一种方式跳转到app管理权限页面(适配,Android 开发永远的痛)。
那有没有办法能够简化适配工做,减小开发量,方法固然有,不过须要咱们本身去总结和探索的,目前已有方法:
(1)直接跳转到系统设置页
Intent intent =newIntent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.fromParts("package",getPackageName(), null)); try{ startActivity(intent); }catch(Exception exception) { exception.printStackTrace(); }
记得要添加上 try catch ,不加可能会crash。这种方式就不须要适配各个厂商的不一样版本rom,缺点是,用户只能跳转到系统设置页,而后去找对应app 的权限管理(总会有一些用户找不到)
(2)站在前人的肩上
引用前人经验:Android各大手机品牌手机跳转到权限管理界面 (未一一验证,毕竟没那么多手机)
那是否是前人经验必定对了,那就不必定了,在当时多是对的,在如今可能就行不通了,如今以MIUI跳转到app 权限管理页面为例进行说明。
1)MIUI 6/7
Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR"); localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); localIntent.putExtra("extra_pkgname", context.getPackageName()); try{ startActivity(intent); }catch(Exception exception) { exception.printStackTrace(); }
2)MIUI 8
Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR"); localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); localIntent.putExtra("extra_pkgname", context.getPackageName()); try{ startActivity(intent); }catch(Exception exception) { exception.printStackTrace(); }
对比1)和2)发现,在MIUI 6/7 和MIUI 8 上面,权限管理页面的activity名字不同了,所以使用MIUI6/7的方法在MIUI8上就会失效,若是没有加上try catch ,就会直接crash。
对于上述变化,做为一个开发者通常都是不知道的,即使经过反馈发现了这个问题,也有可能不知道对应的activity是什么,此刻要么搜索网上有没有相似解决方案,要么求助于对应rom 开发厂商的开发者论坛 (有时解决回应速度至关慢),那有没有更好的办法了,方法详见(3)部分。
(3)查看某个ROM的某个版本的权限管理页面的activity
这里以华为p8为例简要说明,详细步骤以下:
1)经过设置找到对应app的权限管理页面,以下:
企鹅fm在华为p8上的权限管理页面
2)找到对应页面的activity
方法一:经过add 工具查看栈顶Activity
adb shell dumpsys activity | grep "mFocusedActivity"
企鹅fm在华为p8上的权限管理页面对应的activity
更为详细的堆栈信息
详细的堆栈信息
方法二:使用Activity Tracer工具
使用方法:可参见我以前的文章 :Android开发—— 小工具,大效率
使用Activity Tracer查看权限管理页面对应的activity
文中引用连接:
更多精彩内容欢迎关注腾讯 Bugly的微信公众帐号:
腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的状况以及解决方案。智能合并功能帮助开发同窗把天天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同窗定位到出问题的代码行,实时上报能够在发布后快速的了解应用的质量状况,适配最新的 iOS, Android 官方操做系统,鹅厂的工程师都在使用,快来加入咱们吧!