新年新开始,决定开始写点笔记。
本文介绍了Android几乎所有类型的权限。
工作需要,从一个老问题开始 - Android的动态权限申请。
谷歌爸爸关于权限相关的介绍页面:Permissions overview
下面是翻译谷歌爸爸的相关文档内容。
Android之所以有权限这个东西,是要保护用户隐私。应用必须通过请求权限,才能获取用户敏感数据,如联系人、短信等,以及一些系统功能,如相机或访问网络。这个设计的最重要的目的是,没有任何一个应用在默认情况下,能够做出威胁到用户隐私、系统安全以及其他应用的操作。
1、权限申明
应用必须在AndroidManifest.xml中使用
如果你的应用仅仅是申请普通权限,对设备运转或用户隐私没有风险,系统会默认应用取得该权限。
如果你的应用申请了有潜在风险的权限(可能对设备运转或用户隐私有风险),例如发送短信的权限,就必须用户明确同意之后,应用才能获得该权限。
关于普通权限和危险权限的详细信息,参考:Protection levels
2、危险权限的申请
只有危险权限才需要用户的同意。Android是否询问用户取决于用户设备的Android系统版本,以及你的应用的 targetSdkVersion 值。
(1)运行时权限申请 -- Android6.0或更高
当用户设备运行的Android版本为Android6.0(API23)及以上,并且应用的 targetSdkVersion 大于等于23时,安装应用时,不会询问用户任何权限相关问题,你的应用必须在运行的过程中请求权限。当应用请求权限时,会弹出系统弹窗提示用户,你的应用请求获取什么权限组。该弹窗有拒绝和同意按钮。
当用户选择拒绝后,若应用再次弹出该权限申请框,弹窗中会有一个"不再弹出"的复选框,当用户勾选并再次拒绝后,将不会再弹出权限申请的弹窗,就算应用再次请求该权限,系统也不再弹出申请窗口。
尽管用户同意了该权限申请,应用也不应该认为一直拥有该权限,用户还可以通过系统的设置界面对应用权限进行修改。所以应用在每一次使用相关权限前都应该检查是否拥有该权限,再进行后续操作以避免崩溃发生。
如何处理运行时权限申请,请参阅:Request App Permissions -- 中文文档
(2)安装时权限申请 -- Android 5.1.1 或更低
如果用户设备运行在 Android 5.1.1 (API level 22) 或更低版本上,并且应用的 targetSdkVersion 小于等于22时,系统会在安装应用时,询问用户是否给予应用所有的危险权限。
如果用户选择 "同意" ,系统将通过应用所申请的这些危险权限,如果用户选择 "取消" ,系统将取消安装操作。
如果应用在更新时有了新的权限需求,用户需要在安装新版本之前,接受这些新权限。
如何检查和请求权限,请参阅:Request App Permissions -- 中文文档
3、仅在默认处理程序中使用的权限
这部分谷歌爸爸有中文文档:仅在默认处理程序中使用的权限
4、系统硬件相关权限
应用获取硬件相关权限,比如:蓝牙、相机等同样需要申请,但是并不是所有设备都有相应的硬件功能。举例:如果你的应用需要申明相机权限,必须在 AndroidManifest.xml 文件中使用
如果你申明了该权限为非必须权限 -- android:required="false" ,则谷歌商店会允许你的应用安装在没有该硬件功能的设备上。你必须在使用该功能之前,调用 PackageManager.hasSystemFeature() 方法判断设备是否具有该硬件功能,并且当设备不支持该功能时友好的禁用掉应用中相关功能。
如果你没有使用
详细内容参阅:Google Play 和根据功能进行过滤 -- 中文文档
5、定义自定义应用权限
权限不仅仅是用于请求系统功能,应用提供的服务同样可以通过自定义权限来控制谁可以使用其所提供的功能。
更多信息参阅:定义自定义应用权限 -- 中文文档
6、Activity权限定义
7、服务权限定义
8、广播权限定义
同样,代码中通过 Context.registerReceiver() 方法动态注册的广播接收者也可以指定自定义权限。换句话说,在调用 Context.sendBroadcast() 时可以提供权限,以限制允许哪些广播接收者接收广播。
也就是说,接收者和广播者都可能需要权限。发生这种情况时,必须通过两次权限检查,才能将 Itent 传递给关联的目标。
更多详细信息参阅:Restricting broadcasts with permissions
9、内容提供者权限定义
权限检查发生在你第一次尝试获取内容提供者时(如果你没有读和写的任何一种权限,会抛出安全异常 -- 你连看它一样的权利都没有,惨不惨!),当你操作内容提供者时也会检查权限。调用 ContentResolver.query() 方法要求具有读权限,调用 ContentResolver.insert(), ContentResolver.update(),ContentResolver.delete() 方法要求具有写权限。如果不具有相应权限,进行以上这些操作将会抛出异常。
10、URI权限
仅仅是上面说到的内容提供者权限相关配置是不够的,内容提供者依赖读、写权限来保护自己的数据,而其他应用需要指定具体的URIS来操作相应的数据。
一个典型的例子就是邮件应用。邮件应用的数据受权限保护,因为这是敏感的用户数据。然而,一个图片浏览器可以具有邮件中图片的读权限,但不应该具有整个邮件的读权限。
此问题的解决方案是使用前缀 URI ,当启动 Activity 或者返回结果到 Activity 时,调用者可以设置 Intent.FLAG_GRANT_READ_URI_PERMISSION、 Intent.FLAG_GRANT_WRITE_URI_PERMISSION 。这使得 Activity 可以在 Intent 中接收特定的数据,不论其是否拥有该内容提供者的任何权限。这种机制使得权限控制粒度更细,并不仅限于读或者写。也可以减少应用所需要的权限。
更细致的权限控制,需要配合 android:grantUriPermissions 属性和
Context.grantUriPermission(), Context.revokeUriPermission(), 和 Context.checkUriPermission() 方法。
11、其他权限定义
远程调用相关的东西,看不懂了,没有去捣腾。
12、自动的权限兼容
随着时间的流逝,平台可能会加入新的限制,应用必须进行相应的权限申请,哪怕以前并不需要。因为已经旧的应用可能默认拥有该权限,因此Android会默认赋予旧应用相关权限。Android系统会根据 targetSdkVersion 版本来决定是否给予应用该权限,如果应用的target版本低于该权限添加的版本,系统将会给予应用该权限。
如 READ_EXTERNAL_STORAGE 权限,该权限是从API19加入的,如果你的应用 targetSdkVersion 为18或者更低,将自动取得该权限。
13、保护等级
权限被分为几个等级,权限的等级影响到其是否需要在运行时进行申请。
有三个权限等级影响到第三方应用,分别是:普通、签名和危险权限。
具体的权限属于哪个等级,请查阅:权限等级表
(1)普通权限
普通权限覆盖了应用需要取得应用沙盒外的数据或资源的权限,此部分权限对于用户、系统和一起应用来说仅有很小的风险。
如果应用申明了普通权限,在应用安装时,系统会同意该类权限,不会让用户选择,并且用户也不能取消该权限。
(2)签名权限
如果安装的应用签名与申明该权限的应用签名相同,则系统会在安装时允许该权限。
部分签名权限不允许第三方应用使用。
(3)危险权限
危险权限覆盖了应用需要取得用户隐私信息,或对用户数据、系统和其他应用有潜在风险的权限。例如,读取用户联系人的权限就是危险权限。如果应用申明了危险权限,就必须经过用户的同意,才能使用该权限。直到用户同意该权限之前,应用不能提供依赖于该权限的功能给用户使用。
使用危险权限,必须在运行时像用户申请权限。更多细节参阅上文中的危险权限申请部分。
(4)特殊权限
有两个权限比较特殊,它们不像普通权限亦或是危险权限。它们是:SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 。
这两个权限特别特别的敏感,因此一般应用应避免使用。如果应用需要这些权限之一,则它必须在清单中声明该权限,并发送请求用户授权的Intent。系统通过向用户显示详细信息来响应此请求。
更详细的信息参阅: SYSTEM_ALERT_WINDOW和 WRITE_SETTINGS 。
Android系统提供的所有权限信息,请参阅 Manifest.permission.
14、权限组
权限被分为不同的权限组。并且在申请权限时,是按权限组申请的。一个权限组可能对应于Manifest中的多个权限配置。举例说明:SMS权限组包括了 READ_SMS 和 RECEIVE_SMS 权限,权限分组申请使得用户专注于更有意义的信息上,避免被淹没在权限申请的弹窗中。
所有危险权限均有所属的权限组,并且所有权限不论保护等级都有所属的权限组。
如果用户设备运行的Android版本为Android6.0(API23)及以上,并且应用的 targetSdkVersion 大于等于23,以下系统行为会表现在权限申请的过程中:
1、如果应用当前不具有该权限组下任何一个权限,系统会弹窗提示该权限组影响的内容。弹窗不会具体指明应用需要的是权限组中的哪一部分内容。举例:如果应用需要 READ_CONTACTS 读取联系人权限,系统弹窗只会提示用户:应用需要读写联系人的权限。如果用户允许,系统也不会给予应用读写联系人的权限,仅仅是应用申请的读权限。
2、如果应用已经具有该权限组下的其他某个权限,系统会立即同意该权限,并不会弹窗让用户决定。举例:如果应用已经具有 READ_CONTACTS 读联系人权限,那么应用申请 WRITE_CONTACTS 写联系人权限时,系统会立即通过。
如果用户设备运行在 Android 5.1.1 (API level 22) 或更低版本上,并且应用的 targetSdkVersion 小于等于22,系统会在安装应用时询问用户是否同意权限。并且,系统只会告诉用户,应用需要哪些权限组的权限,并不指出单个权限。例如:当应用请求 READ_CONTACTS 读联系人权限事,安装应用的弹窗中会列出联系人权限组中的所有权限,当用户同意后,应用获得的也只是 READ_CONTACTS 读联系人权限。