本文旨在全面介绍Android M 上新发布的runtime permission的相关功能,与之相关的app编程以及一些相关重要API,并对framework部分作了简单介绍。
一、什么是runtime permission
先了解一下什么叫install time permission model。 在app安装过程中要求用户授予app相应权限,否则不能安装,这叫install time permission. 在android M以前的版本都是installtime permission。app安装以后用户不能修改permission的授权情况,也不允许对单项permission进行单独授权和撤回操作。
在android M版本上,对permission的管理作了部分改进,针对dangerous permission, 不在安装时候给予授权,而是在运行过程中咨询用户是否给予app相应权限, 这叫runtime permission model。注意只是针对dangerous permission. 其他类型的permission,例如normal或者signature 和signatureOrSystem,仍然在安装时给予授权。
· 默认情况下,app安装后,所有的dangerous permission 都是deny状态。
· app运行过程中, app必须主动询问用户是否允许app拥有这些权限。用户可以选择允许或者不允许。
· 用户也可以在系统settings里面,对某个app的某项permission单独给予权限或撤销权限。
· 运营商或者厂商可以预安装app,但是不会预先给与dangerous permission权限。
· 只有activity可以申请相应的permission. Service不再能直接申请dangerous permission,如果service想要申请相应的permission, 有两种方式:
a) 添加一个activity去请求相应的permission
b) 通过share UID的方式获取与其UID相同的application的权限。后一种方式通常使用在多个apk需要运行在同一个application中的情况。
注意:有一些permission 可以预先授权给相应的系统默认的一些基础功能的app。比如ACTION_CALLL就会预先授权给系统默认的打电话app.而短信相关的一些permmisions会预先授权给短信app.预授权功能通过packagemanager中的新类DefaultPermissionGrantPolicy实现。
同时注意,在M 机器上,必须安装Google 签发的PackageInstaller,这是google强制要求的。该app负责授权相关的界面显示和操作等相关功能。
二、 兼容性问题
1. 如果target 不是M的app 安装在M 版的机器上,那么所有的permission 按照以前的旧的方式管理,也就是install time permission model.
需要注意的是,在M的机器上,用户可以在settings里面撤销对permission的授权,app 运行中因为没有相应授权,可能会出现crash或者其他意外情况。
2. 如果app使用了新的M上的permission model, 但是运行在非M的机器上,那么所有的permission 还是按照install time permission去管理。
注意:如果一个app 是针对M 的preview release开发的,那么它的manifest文件必须指明minimum SDK 版本为MNC, 这种情况下, 该APP是不能运行在M以下的机器上的。
三、受影响的permissions。
只有dangerous permission 被要求采用新的runtime permission 管理方式。
其他的,如normal、signature 和signatureOrSystem permission 还是按照以前的permission 管理方式,在安装时给与授权。
1. dangerous permission定义。
用户可以定义自己的dangerous permission。 manifest文件中,permission 定义:
- <permission android:description="string resource"
- android:icon="drawable resource"
- android:label="string resource"
- android:name="string"
- android:permissionGroup="string"
- android:protectionLevel=["normal" | "dangerous" |
- "signature" | "signatureOrSystem"] />
2.
关于permissionGroup。
注意上面的定义中有一项permissionGroup。 相关的一些permission可以归类为到一个permission Group, 比如CONTACTS 组包括读联系人和写联系人等相关permission.对于一个permission group, app只要获得该组内任意一个permission的授权,就可以拥有该组下的所有permission的授权。例如一个app 要求SEND_SMS 和 RECEIVE_SMS两个permission, 当app要使用SEND_SMS相关功能时,需要请求用户給予SEND_SMS权限,稍后app要使用RECEIVE_SMS相关功能时,RECEIVE_SMS就已经自动授权给了app,不需要app再申请一次。
3. 新增adb命令
配合runtime permission, 也新增了一些相关的adb 命令:
1) 查看所有的dangerous permissions:
adb shell pm list permissions –g –d
2) 安装app并且对所有列在app manifest文件下的所有permission给予授权:
adb install -g <path_to_apk>
3) 授权给某个app某个permission:
adb pm grant <package_name> <permission_name>
4) 撤销授权:
adb pm revoke <package_name> <permission_name>
下表列出了目前M上的所有的dangerous permission 和相应组:
四、app如何编写适用于runtime permission的代码
如果APP想要在M上运行,并且使用新的permission model, 除了在manifest 里声明需要的permission, APP还要在运行阶段去检查是否拥有相应的permission, 如果没有该permission,则要咨询用户是否给予相应的permissin。
1. Manifest 中SDK版本声明
要声明该app适用于M新的permission model,需要在manifest文件中声明:targetSdkVersion等于MNC 并且compileSdkVersion等于android-MNC。
如果这个app是针对M的preview release开发的,必须声明minSdkVersion等于MNC。
2. 定义M preview release上特有的permission.
在manifest文件中,app可以使用<uses-permission-sdk-m>来声明所需要的permission只适用于M版本。那么如果app 是被安装在M以下的版本,这个permission会被忽略——不提示用户需要这个permission,也不会授权给app这个permission. 而当app在M版本上运行时,<uses-permission-sdk-m>等同于<uses-permission>
,会按照新的
permission
管理方式管理。
用这种方式可以在
app
升级的时候声明需要新的
permission
。
例如,如果新增需要READ_CONTACTS permission,可以在manifest文件中添加以下声明:
<uses-permission-sdk-m android:name="android.permission.READ_CONTACTS" />
3. app 如何编程
使用了dangerous permission的app,在运行过程中,要检查是否有相应的权限,如果没有,要在适当的时候提醒用户为什么需要这个权限,并且请求用户给予相应的权限。这涉及到四个基本API:
1) 检查是否有权限:checkSelfPermission(String)
2) 是否需要提示用户为什么需要这个权限:shouldShowRequestPermissionRationale (String permission)
returns true 如果app以前要求过这个权限,但是用户拒绝了。
returns false 如果以前用户拒绝给予这个权限并且在系统对话框中选择了“Don't ask again ”选项。
returns false 如果device policy 禁止app拥有这个权限。这种通常都是在DevicePolicyManager中设置的。
3) 请求权限: requestPermissions (String[] permissions, int requestCode)
调用该函数,系统会调出一个对话框,提示用户是否要给予相应权限。用户允许或拒绝相应的权限后,app的onRequestPermissionsResult(int, String[], int[])会被调用,告诉app相应的permission是被授权或者拒绝。
4) 处理回调:onRequestPermissionsResult(int, String[], int[])
请求permission后系统调用该函数,通知app permission是被允许还是拒绝。
编码范例:app想要显示contacts。From android developer.
private void showContacts() {
if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (shouldShowRequestPermissionRationale(
Manifest.permission.READ_CONTACTS)) {
// Explain to the user why we need to read the contacts
}
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant
return;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showContacts();
}
}
五、关于自动授权
· 带有PRIVATE_FLAG_PRIVILEGED & FLAG_PERSISTENT flag的app会被自动给予授权。
· 系统默认的基础app会被自动给予相应的permission.比如默认的电话本app自动会拥有CONTACT相关的permission.
自动授权是通过函数grantDefaultPermissions() @DefaultPermissionFrantPolicy.java实现的。该函数会在system ready和创建新用户时调用。DefaultPermissionFrantPolicy.java文件是PM(package manager service)中在M上新增的一个类,用于实现自动授权的相关功能。
六、framework实现介绍:
App的permission状态在PM中统一管理维护。PM提供了一些相应的API。PackageInstaller 负责permission管理的相关UI,并通过PM的相关接口设置permission状态。DevicePolicyManager也会根据device的要求对某些app设置一些permission的状态。PM中关于permission管理的代码实现并不复杂,不做详细介绍,只指出下面关键的几点,知道以下几点会非常有助于理解相应的代码。
v 在PM中新增了几个新类:
· DefaultPermissionGrantPolicy:这个在自动授权中说过负责自动授权的相关功能。
· PermissionsState: 每个app在package manager service 里面维护一个该实例,记录该app的所有permission的状态。
· PermissionData:在PermissionsState 中管理了一组PermissionData , 记录app的每个permission 的状态信息。
· SettingBase: PM中某个App的相关基本的Setting信息
PM中permission相关类图:
v Permission flag: 在PM中,会用到这些flag判断permisssion状态。
· PackageManager.FLAG_PERMISSION_USER_SET: user 选择允许或者拒绝的授权。
· PackageManager.FLAG_PERMISSION_USER_FIXED:user选择允许或者拒绝授权同时购选了“never ask again”选项。
· PackageManager.FLAG_PERMISSION_POLICY_FIXED:device policy设定的权限。
· PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE:如果permission被标记了这个flag,那么表示,app升级后被deny的permission,会依然是deny的状态。这个flag会在下面的情况中用到。适用于L以前版本的app,安装得到M的device上,如果它的dangerous permission被撤销了,比如通过settings里面的permission管理撤销或者device policy中设定,那么该APP升级到适用于M新的permission模式后,那么升级后这个permission依然是撤销的状态。也就是dangerous permission如果在升级之前被撤销过,升级后依然是撤销的状态。
· PackageManager.FLAG_PERMISSION_SYSTEM_FIXED 系统app获得的自动授权的permission。
· PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT 默认的系统基本功能app获得的自动授权的permission.
Framework 部分permission 管理逻辑相对简单,理解以上内容后看相关代码会很容易。
七、其他API.
revokeRuntimePermission()
addOnPermissionsChangeListener()
removeOnpermissionsChangeListener()
grantRuntimePermission()
isPermissionRevokedByPolicy()
getPermissionControllerPackageName()
getPermissionFlags()
updatePermissionFlags()
BuildRequestPermissionsIntent()
http://blog.csdn.net/april_12345/article/details/49025813