前言

       从Android6.0开始,Android系统对权限的处理产生了很大的变化。如果APP运行的设备系统版本为Android6.0或更高,并且target在23或更高,那么dangerious级别的权限将由之前的安装时授予变成运行时动态申请。这样一来,当运用到系统权限相关的功能时,就需要手动处理好权限申请的用户交互问题。本文将结合官网中的介绍,来全方位了解权限相关知识点,并介绍一些实际工作中可能用到的技巧。

本文的主要内容如下:

一、为什么要引入“权限”

Android系统引入权限的目的是为了保护Android用户的隐私。Android安全架构设计中一个明确点,默认情况下App是没有权限对其他App、操作系统和用户进行有害的操作。这些有害操作包括读写用户的隐私数据(如通讯录、电子邮件等)、读写其它App的文件、调起硬件设备(如蓝牙、Wifi、相机等),访问网络等。App如果想要进行这些操作,就需要申请相应的权限,如下网址提供了Android系统定义的所有权限【https://developer.android.google.cn/reference/android/Manifest.permission】,均以常量的形式供开发者使用,开发者可以通过Manifest.permission.X的方式调用。

二、Android权限与官网

官网(中文版:https://developer.android.google.cn)中对Andrid权限有非常全面的描述和使用指导,依次通过 首页 》 文档 》 指南,可以看到如下界面,和权限相关的知识点主要都在这里。咱们先了解一下里面都有些什么内容吧!

1、Overview

宏观上介绍了权限相关联的知识点,整体内容如下图所示:

(1)Permission approval

该部分主要从用户交互角度介绍了当前原声机上申请权限的交互形式。比如对话框显示怎样的内容等。

(2)Permissions for optional hardware feature

这部分主要讲手机硬件特征和与之相关的权限问题。比如手机没有相机时,是否允许安装需要申请相机权限的app等。

(3)Permission enforcement

这部分主要讲Permission在四大组件中的其它用法。Permission不仅仅只能用于请求系统功能,还可以通过自定义权限,来限制谁有权限启动/调用或访问指定的组件/数据等。Activity、Service、Broadcast Receiver和Content Provider这四大组件可以结合Permission这方面的功能,来防止被任意访问或修改。

(4)Automatic permission adjustments

这部分主要讲权限与版本前后兼容的问题。比如在高版本中才定义的新权限,在低版本中如何表现的问题等。

(5)Protection levels

App不同的操作,所可能产生的风险也是不一样的。比如获取当前的网络状态,最多也只会让App探测到周围的网络情况,而读写用户的通讯录则不同,用户的信息则有被篡改和道窃的风险。所以,根据可能产生的风险程度,系统将“权限”分为了不同的保护等级。对于第三方app而言,有三个等级,严重程度有轻到重依次为:普通权限(Normal Permission)、签名权限(Signature Permission)和危险权限(Dangerous Permission)。

1)普通权限

普通权限的覆盖区域为,app需要访问“沙盒”以外的数据以及资源的权限,这些对用户隐私和对其它app的操作产生的风险微乎其微。比如,修改系统时区、获取网络状态等。这部分还列出了一些常用的普通权限.

2)签名权限

系统会在安装app的时候授予该类权限,但是只有当这个试图使用这个权限的app和定义这个权限的app被相同的证书签名的时候,才能生效。有些签名权限就不用于第三方app的。该部分还列出了一些常见的可以供第三方app使用的签名权限。

3)危险权限

危险权限覆盖了如下情形,当用户需要的数据和资源涉及到用户隐私信息,或者可能影响到用户的存储数据或其它app的操作,比如读取用户的联系人的能力就是一个危险权限。该部分还列出了一些常见的危险权限。

4)特殊权限

除了上述的3中主要的权限分类外,还有两个特殊的权限:SYSTEM_ALART_WINDOW和WRITE_SETTINGS。至于其特殊性,咱们在后面的章节单独来介绍。

(6)Permission groups

该部分介绍了权限组相关的知识点。

(7)View an app`s permissions

该部分介绍了两个adb命令 ,一个用于查看app的权限申请情况,另外一个用于给app授予所有的权限。

(8)Additional resources

提供了一些链接,用于了解更多关于Android权限相关的知识。

2、Request app permission

这篇文章主要讲解如何在代码中实现权限检查,权限请求,以及如何处理拒绝/同样授权后的代码逻辑。这里面提供了Android API中的接口,具体的代码示例等。

3、App permissions best practices

这边文章主要是从用于体验的角度来指导开发者,如何设计交互,如何做好测试,以及需要注意的原则等。

4、Define custom permissions

这篇文章主要指导开发者如何自定义权限。

三、特殊权限

       上一节中简单提到了特殊权限,其实就是两个权限:SYSTEM_ALART_WINDOW和WRITE_SETTINGS。Android6.0开始,除了危险权限需要动态申请外,这两个特殊权限也是一样需要动态申请。他们尤其敏感,行为也和其他的权限不一样,所以对于大多数app来说一般不应该使用它们。如果app需要其中一个权限,必须在manifest文件中申明,并发送一个intent请求用户授权。系统会显示一个详细的管理界面给用户,让用户决定是否来授权。如下截图是以“微信”为例,在设置中可以看到如下界面,“高级”模块中显示的两项,就是设置这两项权限的入口。

1、WRITE_SETTINGS

在Android6.0及以后,该权限的保护等级已经由原来的dangerious升级为signature,这意味着我们的APP需要用系统签名或者成为系统预装软件才能够申请该权限,并且还需要提示用户跳转到修改系统的设置界面去授予此权限。这就意味着,要想申请该权限,apk必须要签名而且打包,debug模式将无法申请该权限。

(1)官网描述

官网【https://developer.android.google.cn/reference/android/Manifest.permission.html#WRITE_SETTINGS】对该权限的描述如下所示:

(2)权限设置界面

点击“高级”中的“修改系统设置”项,可以进入到该权限的授予界面,如下图所示:

(3)权限申请使用示例

在第四节中将会讲解危险权限检测和申请的具体实例,但是WRITE_SETTINGS权限不能通过这种方式来处理。如果使用checkPermissions()检测该权限,无论你是否已经授权,它都会返回false。如下展示了具体处理该权限的实例:

首先,在AndroidManifest.xml文件中申请该权限

 <uses-permission android:name="android.permission.WRITE_SETTINGS" />

然后,根据官网中的描述,先通过API中的canWrite()方法判断是否允许修改,再通过指定的action跳转到设置界面。

 /**
* 申请权限
*/
private void requestWriteSettings()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
//大于等于23 请求权限
if ( !Settings.System.canWrite(getApplicationContext()))
{
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS );
}
}else{
//小于23直接设置
}
}

最后,根据回调方法和requestCode来处理授权/拒绝逻辑。

 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_WRITE_SETTINGS)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
//Settings.System.canWrite方法检测授权结果
if (Settings.System.canWrite(getApplicationContext()))
{
//获取了权限
}else{
//拒绝了权限
}
}
} }

2、SYSTEM_ALART_WINDOW

(1)官网描述

官网【https://developer.android.google.cn/reference/android/Manifest.permission.html#SYSTEM_ALERT_WINDOW】的描述如下所示:

(2)权限设置界面

点击“高级”中的“显示在其它应用的上层”项,可以进入到该权限的授予界面,如下图所示:

(3)权限申请示例

该权限的申请和WRITE_SETTINGS类似,咱们这里参照前面,就不给出全部代码,仅提供一些关键函数:

在清单文件中声明

 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

结合官网中的说明,使用系统提供的API来判断和跳转授权设置界面。

 //检查是否已经授予权限
if (!Settings.canDrawOverlays(this)) {
//若未授权则请求权限
}
......
//前往设置界面授予权限
private void getOverlayPermission() {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
......
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
//根据requestCode来处理授权/拒绝逻辑。
}

从官网上的说明可以看到,这两个特殊权限的权限级别都是“signature”,可见这两个权限并不是独立的权限级别。

四、动态申请权限示例代码记录

       如下代码是笔者平时工作当中经常使用的一个实例,方便实用,无需再引入第三方jar包。数组变量permissionRequest中定义了一个相机权限作为实例,读者可以根据需要拓展为多个。一般来说,申请权限是在进入app时第一个Activity中,或者在需要用到该权限的地方进行,所以checkAndRequestPermissions()一般在第一个Activity的onCreate()方法中调用,或者在需要使用该权限的地方调用。其他地方代码逻辑比较简单,就不赘述了。

 private String[] permissionRequest = new String[]{
Manifest.permission.CAMERA
};
private boolean isNeedRequestPermission = false;
private static final int REQUEST_PERMISSION = 1;
......
public void checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
for (String permissionStr : permissionRequest) {
if (ActivityCompat.checkSelfPermission(this, permissionStr) != PackageManager.PERMISSION_GRANTED) {
isNeedRequestPermission = true;
break;
} else {
isNeedRequestPermission = false;
}
}
if (isNeedRequestPermission) {
requestPermissions(permissionRequest, REQUEST_PERMISSION);
} else {
//do what you want
}
}
}
......
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
for (int index = 0; index < grantResults.length; index++) {
if (PackageManager.PERMISSION_DENIED == grantResults[index]) {
finish();
return;
}
}
//do what you want
}

五、一种在Service中申请权限的方案

Android中申请权限的函数requestPermissions()和回调函数onRequestPermissionsResult()都是Acitivity.java中提供的,所以在Activity或Fragment中很容易调用这两方法来处理权限问题。但是在Service中就不容易处理了,因为Service中Android API没有提供类似的接口。而且Android中有不少应用是通过IPC方式,用Service向其它APP提供功能,没有Activity或Fragment,这样就无法直接请求权限了。这里介绍一种采用间接的方式处理权限的方案:需要申请权限时,跳转到一个透明的Activity中来完成权限的申请。

PermissionDemoActivity直接继承自Activity,在清单文件中如下设置theme,这样就成了一个透明的Activity。在onRusume()方法中调用finish()方法,否则会报错。

 <activity
android:name=".PermissionDemoActivity"
android:theme="@android:style/Theme.NoDisplay" />

结合上一节中的代码示例,在PermissionDemoActivity的onCreate()方法中申请权限,这样就间接地通过一个透明的Acitivity来实现权限的申请了。

六、常用的与Permission相关的adb命令

android工具adb提供了一些命令,可以方便查看、授权、取消应用的权限,可以为调试程序带来不少的方便,下面简单介绍几个常见的命令。

1、查看指定app中权限申请情况

命令:adb shell dumpsys package [包名]

用途:该命令用于获取该app的package信息,Permission信息只是其中的一部分。

命令使用示例:

 adb shell dumpsys package cn.aaa.bbb

如下下截图为该命令中关于权限的部分信息:

该图显示了4部分权限:

(1)declared permissions。该应用自己声明(即自定义)的权限,这里显示了权限名,权限等级,以及在什么时候获取该权限(INSTALLED 表示安装的时候就会授予该权限)。

(2)requested permissions。这里列出的是AndroidManifest.xml文件中所有request的权限,可以看出这里面包含了动态申请的权限和安装时申请的权限。
    (3)install permissions:安装的时候就赋予的权限。可以和requested permissions对比一下,这里面少了一"android.permission.CAMERA"权限,该权限为动态申请权限。该列表中还展示了权限对应的授予情况,如granted所示,true表示已经被授予了权限。

(4)runtime permissions。这里显示的是运行时才需要申请的权限,即dangerous permission。

2、查看权限的声明者和使用者

命令:adb shell dumpsys package permission <权限名>

用途:该命令可以查看指定权限是谁声明的,有哪些应用申请了该权限。

命令使用示例:

 adb shell dumpsys package permission cn.aaa.bbb.TEST_PERMISSION

如下节选了该权限的定义信息和其中一个使用该权限的应用的关键信息:

 Permissions:
Permission [cn.aaa.bbb.TEST_PERMISSION] (d4d8316):
sourcePackage=cn.aaa.bbb
uid=10078 gids=null type=0 prot=signature|privileged
perm=Permission{f5b497 cn.aaa.bbb.TEST_PERMISSION}
packageSetting=PackageSetting{96e1684 cn.aaa.bbb/10078} Packages:
Package [cn.xxx.xxx] (5d0f51b):
......
declared permissions:
requested permissions:
install permissions:
cn.aaa.bbb.TEST_PERMISSION: granted=true ......

3、移除指定权限

命令:adb shell pm revoke [packageName] [permissionName]

用途:移除packageName应用的permissionName权限(可以同时移除多项权限)。

命令使用示例(如下为删除包名为cn.aaa.bbb 的相机权限):

 adb shell pm revoke cn.aaa.bbb android.permission.CAMERA

执行完该命令后,用前文提到的命令“adb shell dumpsys package cn.aaa.bbb”查看该权限的信息如下:

通过实验发现,该命令对runtime permissions有效,却对install permissions无效,如以下异常信息所示:

4、授予指定权限

命令:adb shell pm grant [packageName] [permissionName]

用途:为packageName应用授予permissionName权限(可以同时授予多项权限)。该命令和上一条移除命令相对应。

参照上一条命令的实例,实验结果如下:

5、查看系统定义的所有权限

命令:adb shell pm list permissions -s[option] 不加-s会显系统中定义的所有权限名列表,加了-s会显示对这些权限的用途说明。

参考:【Viewing an app's permissions:https://developer.android.google.cn/guide/topics/permissions/overview#viewing

下面截图分别展示了命令不加-s和加了-s后的显示结果(重定向到文本中查看),其中不加-s的截图中,一共显示了571条权限,这里截取了一部分,其中可以看到不少自定义的权限。

6、按组查看权限

命令:adb shell pm list permissions -d -g

用途:查看权限的分组情况。这部分是上面一条命令的补充,参数可以根据自己的需要选择。

参考:【https://developer.android.google.cn/training/permissions/usage-notes#testing

下列截图为结果的一部分。

7、授予所有权限

命令:adb shell install -g MyApp.apk

用途:当安装MyApp.apk到模拟器或测试机上时,如果加上-g,可以自动授予所有权限。这一点笔者没有实验过,读者可以自行测试。

参考:该处和第4点一样参考官网说明。

推荐阅读

Android权限的一些细节https://blog.csdn.net/u013553529/article/details/53167072

结语

Anroid权限知识点其实没有太多需要理解的逻辑问题,一般都是一些需要记住的知识点。本文的一些实际操作技巧,很多都是笔者工作中用到的,具有较强的实践参考作用。由于笔者的经验和水平有限,如果有描述不妥当或不准确的地方,请不吝赐教,谢谢!

【朝花夕拾】Android安全之(一)权限篇的更多相关文章

  1. 【转】Android M(6.0) 权限爬坑之旅

    原文网址:https://yanlu.me/android-m6-0-permission-chasm/ 有一篇全面介绍Android M 运行时权限文章写的非常全面:Android M 新的运行时权 ...

  2. 深入了解Android蓝牙Bluetooth——《进阶篇》

    在 [深入了解Android蓝牙Bluetooth--<基础篇>](http://blog.csdn.net/androidstarjack/article/details/6046846 ...

  3. Android开发之深入理解Android 7.0系统权限更改相关文档

    http://www.cnblogs.com/dazhao/p/6547811.html 摘要: Android 6.0之后的版本增加了运行时权限,应用程序在执行每个需要系统权限的功能时,需要添加权限 ...

  4. Android权限申请完全解析(一):Android自带的权限申请

    1.为什么要权限申请 6.0以上就需要了,别问为什么.(不是重点,自行搜索) 2.如何进行权限申请 Android自带的权限申请 EasyPermission权限申请 Ps:EasyPermissio ...

  5. Android开发——Android M(6.0) 权限解决方案

    Android开发--Android M(6.0) 权限解决方案 自从Android M(6.0)发布以来,权限管理相比以前有了很大的改变,很多程序员发现之前运行的好好的Android应用在Andro ...

  6. 深入了解Android蓝牙Bluetooth ——《总结篇》

    在我的上两篇博文中解说了有关android蓝牙的认识以及API的相关的介绍,蓝牙BLE的搜索,连接以及读取. 没有了解的童鞋们请參考: 深入了解Android蓝牙Bluetooth--<基础篇& ...

  7. Android 6.0 - 动态权限管理的解决方案

    Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 一方面让用户更加容易的控制自己的隐私, 一方面需要重新适配应 ...

  8. Android Fragment使用(一) 基础篇 温故知新

    Fragment使用的基本知识点总结, 包括Fragment的添加, 参数传递和通信, 生命周期和各种操作. Fragment使用基础 Fragment添加 方法一: 布局里的标签 标识符: tag, ...

  9. [转]Android样式的开发:shape篇

    转载自Keegan小钢原文链接:http://keeganlee.me/post/android/20150830 Android样式的开发:shape篇Android样式的开发:selector篇A ...

  10. 直接拿来用!最火的Android开源项目(完结篇)

    直接拿来用!最火的Android开源项目(完结篇) 2014-01-06 19:59 4785人阅读 评论(1) 收藏 举报 分类: android 高手进阶教程(100) 摘要:截至目前,在GitH ...

随机推荐

  1. [论文解读]CNN网络可视化——Visualizing and Understanding Convolutional Networks

    概述 虽然CNN深度卷积网络在图像识别等领域取得的效果显著,但是目前为止人们对于CNN为什么能取得如此好的效果却无法解释,也无法提出有效的网络提升策略.利用本文的反卷积可视化方法,作者发现了AlexN ...

  2. POI excel导出

    ******************************* excel表格导出,使用POI实现 ******************************* 实现导出步骤 --配置导出excel ...

  3. java判断一个字符串是否是数字的三种方法

    参考https://blog.csdn.net/ld_flex/article/details/7699161 1 用JAVA自带的函数 public static boolean isNumeric ...

  4. BZOJ_3626_[LNOI2014]LCA_离线+树剖

    BZOJ_3626_[LNOI2014]LCA_离线+树剖 题意: 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度, ...

  5. 二维码神器QRCoder

    好久没有写Blog,都是因为不小心坠入了爱河,时间都给我家那位了,都没时间加班了(嗨呀,不小心撒了一下狗粮),不过,还是希望单身的赶紧找到心仪的另一半,实在找不到,那就加班啊(开个玩笑,别认真). 二 ...

  6. eShopOnContainers 知多少[5]:EventBus With RabbitMQ

    1. 引言 事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉.事件总线是对发布-订阅模式的一种实现.它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需 ...

  7. kolla 多节点部署 openstack

    kolla 介绍 简介 kolla 的使命是为 openstack 云平台提供生产级别的.开箱即用的交付能力.kolla 的基本思想是一切皆容器,将所有服务基于 Docker 运行,并且保证一个容器只 ...

  8. API 测试的具体实现

    目录 API 测试的具体实现 基于 Spring Boot 构建的 API 使用 cURL 命令行工具进行测试 使用图形界面工具 Postman 进行测试 如何应对复杂场景的 API 测试? 总结 A ...

  9. 死磕 java集合之ConcurrentSkipListSet源码分析——Set大汇总

    问题 (1)ConcurrentSkipListSet的底层是ConcurrentSkipListMap吗? (2)ConcurrentSkipListSet是线程安全的吗? (3)Concurren ...

  10. html5中的indexDB

    1.关系型数据库和非关系型数据库 一致性: 事务完成时,必须让所有的数据具有一致的状态,例如要写入100个数据,前99个成功了,结果第100个不合法,此时事务会回滚到最初状态.这样保证事务结束和开始时 ...