Android权限机制

为什么有权限机制

我们知道 Android 应用程序是沙箱隔离的,每个应用都有一个只有自己具有读写权限的专用数据目录。但是如果应用要访问别人的组件或者一些设备上全局可访问的资源,这时候权限机制就能系统化地规范并强制各类应用程序的行为准则。

Android 安全性概览

权限的本质

在 Android 中,一个权限,本质上是一个字符串,一个可以表示执行特定操作的能力的字符串。比如说:访问 SD 卡的能力,访问通讯录的能力,启动或访问一个第三方应用中的组件的能力。 权限被授予了之后,首先会在内存和本地中有记录,这在调用系统binder服务和其他应用组件时做鉴权依据,比如调用系统binder服务时会通过Binder.getCallingUid()拿到调用者的Uid,而Uid一般都是与应用包名一一对应的,再拿这个Uid到PMS里去查这个应用对应的权限。 其次会按被授予的权限将应用分到某个组。 可以参考https://www.jianshu.com/p/a17c8bed79d9

自定义权限

自定义权限的应用场景在于限制其它应用对本应用四大组件的访问。具体用法可以参考https://www.cnblogs.com/aimqqroad-13/p/8927179.html

image.png

pm list permissions -f 命令可以详细查看 Android 所有预定义的权限。

更详细的权限信息参考 https://developer.android.com/reference/android/Manifest.permission?hl=zh-cn#WRITE_EXTERNAL_STORAGE

可以看到一个权限的信息包括:定义的包名、标签、描述、权限组保护级别

权限组

权限根据设备的功能或特性分为多个组。如果应用已在相同权限组中被授予另一危险权限,系统将立即授予该权限,如READ_CONTACTS和WRITE_CONTACTS。

权限的级别

  • normal 级别 权限保护级别的默认值,无须用户确认,只要声明了,就自动默默授权。如:ACCESS_NETWORK_STATE。

  • dangerous 级别 赋予权限前,会弹出对话框,显式请求权限。如:READ_SMS。因为 Android 需要在安装时赋予权限,所以安装的确认对话框,也会显示列出权限清单。

  • signature 级别 signature 级别的权限是最严格的权限,只会赋予与声明权限使用相同证书的应用程序。 以系统内置 signature 级别权限为例,Android 系统应用的签名由平台密钥签发,默认情况下源码树里有 4 个不同的密钥文件:platform、shared、media 和 testkey。所有核心平台的包(如:设置、电话、蓝牙)均使用 platform 密钥签发;搜索和通讯录相关的包使用 shared 签发;图库和媒体相关的包使用 media 密钥签发;其他的应用使用 testkey 签发。定义系统内置权限的 framework-res.apk 文件是使用平台密钥签发的,因此任何试图请求 signature 级别内置权限的应用程序,需要使用与框架资源包相同的密钥进行签名。

  • signatureOrSystem 级别 可以看做是一种折中的级别,可被赋予与声明权限具有相同签名证书密钥的应用程序(同 signature 级别)或者系统镜像的部分应用,也就是说这允许厂商无须共享签名密钥。Android 4.3 之前,安装在 system 分区下的应用会被自动赋予该保护级别的权限,而 Android 4.4 之后,只允许安装在 system/priv-app 目录下的应用才能被主动赋予。

特殊权限

SYSTEM_ALERT_WINDOWWRITE_SETTINGS由于其特殊性,其申请方式与其它权限都不同。

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

其授予流程如下:

  1. 起了com.android.settings下一个叫Settings$OverlaySettingsActivity的Activity,并把需要授权应用的包名带了过去。

  2. 通过AppOpsManager给指定的包名授予权限。

(关于AppOpsManager是什么可以参考:https://segmentfault.com/a/1190000009214983)

image.png

普通运行时权限的请求授予流程

image.png
 //检测是否有写的权限
 int permission = ActivityCompat.checkSelfPermission(this,
 "android.permission.WRITE_EXTERNAL_STORAGE");
 if (permission != PackageManager.PERMISSION_GRANTED) {
 // 没有写的权限,去申请写的权限,会弹出对话框
 ActivityCompat.requestPermissions(this,
 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
 }
 } catch (Exception e) {
 e.printStackTrace();
 }

这里简要分析下ActivityCompat#requestPermissions的流程:

  1. 判断android版本是否小于6.0,若小于,让PMS查询一把自己是否有声明这个权限;若大于6.0,则启动了com.android.packageinstaller/.permission.ui.GrantPermissionsActivity这个具有悬浮窗样式的Activity。

  2. 在GrantPermissionsActivity中做了权限变更的操作后,最终还是调用到PMS去做实质上的更新操作,com.android.server.pm.permission.PermissionManagerService#grantRuntimePermission。

  3. 查询该权限是否在manifest文件中有声明。

  4. 做授权操作,更新内存中的权限信息。

  5. 将权限信息持久化到本地文件/data/system/users/0/runtime-permissions.xml中,以便重启时可以恢复权限信息。

更详细的权限授予流程源码分析可以参考:https://segmentfault.com/a/1190000009214983

不同权限的授予方式

普通权限: 清单文件中声明即可。

危险权限: 方式一: pm grant application_package android.permission.CHANGE_CONFIGURATION 方式二:appops set application_package permission_num 0/1

appops可以授予的权限参考android.app.AppOpsManager中的声明

系统签名权限: 方式一:将app迁移到system/priv-app目录中。 方式二:看不懂,参考https://blog.csdn.net/abcd_3344_abcd/article/details/50698759

权限的发展

android 4.4 访问sd卡需要申请权限。 您的应用在 Android 4.4 上运行时无法读取外部存储空间上的共享文件,除非您的应用具有 READ_EXTERNAL_STORAGE权限。也就是说,没有此权限,您无法再访问 getExternalStoragePublicDirectory()返回的目录中的文件。但是,如果您仅需要访问 getExternalFilesDir() 提供的您的应用特有目录,那么,您不需要 READ_EXTERNAL_STORAGE`权限。

android 6.0 运行时权限。 此版本引入了一种新的权限模式,如今,用户可直接在运行时管理应用权限。这种模式让用户能够更好地了解和控制权限,同时为应用开发者精简了安装和自动更新过程。用户可为所安装的各个应用分别授予或撤销权限。 对于以 Android 6.0(API 级别 23)或更高版本为目标平台的应用,请务必在运行时检查和请求权限。要确定您的应用是否已被授予权限,请调用新增的 checkSelfPermission()方法。要请求权限,请调用新增的 requestPermissions()方法。即使您的应用并不以 Android 6.0(API 级别 23)为目标平台,您也应该在新权限模式下测试您的应用。 如需了解有关在您的应用中支持新权限模式的详情,请参阅使用系统权限。如需了解有关如何评估新模式对应用的影响的提示,请参阅权限最佳做法。

android 7.+ 应用间共享文件要使用FileProvider。 对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file://URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。 要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider`类。如需了解有关权限和共享文件的详细信息,请参阅共享文件。

android 8.+
同一权限组的权限在被授予了之后也需要显式的再申请一次。
在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。 对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。 例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。

android 9
隐私权限变更。
为了增强用户隐私,Android 9 引入了若干行为变更,如限制后台应用访问设备传感器、限制通过 Wi-Fi 扫描检索到的信息,以及与通话、手机状态和 Wi-Fi 扫描相关的新权限规则和权限组。

android 10
隐私权变更。
外部存储访问权限范围限定为应用文件和媒体,在后台运行时访问设备位置信息需要权限,针对从后台启动 Activity 的限制等。

android 11
隐私权限变更。
更详细的版本变更请参考https://developer.android.com/preview/privacy?hl=zh-cn

你可能感兴趣的:(Android权限机制)