Android6.0带来了新的权限管理方式,本文主要来源于官方文档,加入了本身的理解,目的是想总结Android6.0权限管理的新方式,其余部分可能主要是总结式的带过,后续再详细分析。html
Android安全体系结构的核心是:java
默认状况下没有任何应用有权限去执行对其余应用、操做系统、用户有不利影响的操做。这是一个核心的设计理念。记住这句话对后面的权限管理能够很好的理解。android
正式因为这样的设计理念,默认状况下应用不能去读写用户的私有数据(好比Email和Contacts),不能去读写其余App的文件,不能执行网络访问,不能保持设备始终唤醒等等。api
由于每个Android 应用都是在一个进程沙盒中运行的,应用必须明确分享的资源和数据,经过申明须要的额外权限这种方式(这些额外权限不禁基本沙河提供)。应用静态的声明这些权限(在Manifest里面),而后Android系统会请求用户赞成这些权限。
安全
Android应用沙盒不依赖于建立应用的技术(这句话不是很懂,懂的大神能够指一下),特别的是Dalvik虚拟机并非安全边界的,全部的应用均可以运行native code(好比参见NDK),全部类型的应用-Java,native,hybird,都是以一样的方式封装在沙盒内而且相互之间是一样的安全等级。网络
全部的Apk文件都必须由他的开发者使用私有的签名证书签名,这个证书是开发者身份的惟一标识,这个证书是由开发者本身生成的开放式的证书,用于本身签名应用。证书的目的是标识应用的身份,这样可让系统知道是该容许仍是拒绝应用访问签名级别的权限(signature-level permissions),以及容许仍是拒绝应用所请求的给予相同Linux身份来做为不一样的应用。app
在安装一个app package的时候,android系统会给每个package一个独立的Linux user ID。这个User ID在这个应用在当前设备的生命周期内都是固定不变的,在不一样的设备,相同的package的用户ID可能各不相同,但能够肯定的是在一台设备上一个package的用户ID是固定不变的。async
由于安全执行是发生在进程层面的,两个不一样的package不能运行在相同的进程中,他们会被做为不一样的Linux用户来运行。ide
可是你能够在manifest中使用sharedUserId属性来指定不一样的package有相同的User ID,这样这两个不一样的package将会被视为相同的APP,会有相同的User ID和文件权限。ui
固然为了保证安全,只有两个APP签名一致且申明了相同的sharedUserId才会被给予相同的User ID。
一个新建的Android应用默认是没有权限的,这意味着它不能执行任何可能对用户体验有不利影响的操做或者访问设备数据。为了使用受保护的功能,你必须包含一个或者多个<uses-permission>标签在你的app manifest中。
Android 6.0中权限分为两种,普通权限和危险权限(即运行时权限,下面统称运行时权限)。
若是你的应用manifest中只申明了普通权限(也就是说,这些权限对于用户隐私和设备操做不会形成太多危险),系统会自动授予这些权限。
若是你的应用manifest中声明了运行时权限(也就是说,这些权限可能会影响用户隐私和设备的普通操做),系统会明确的让用户决定是否授予这些权限。系统请求用户授予这些权限的方式是由当前应用运行的系统版原本决定的。
若是你的设备运行的是Android6.0(API level 23)及以上的系统,而且你的应用的targetSdkVersion也是23或者更高,那么应用向用户请求这些权限是实时的。这意味着用户能够随时取消这些运行时权限的受权。因此应用在每次须要用到这些运行时权限的时候都须要去检查是否还有这些权限的受权。具体请求方式后面会讲到。
若是你的设备运行在Android5.1(API level 22)及如下的系统中,或者你的app的targetSdkVersion是22或者更低。系统会请求用户在apk安装的时候授予这些权限。
若是你的应用更新的时候添加了一个权限,系统会在用户更新应用的时候请求用户授予这个权限,一旦用户安装了这个应用,惟一能够取消受权的方式就是卸载掉这个应用。注意这句话的意思,想一下若是app的targetSdkVersion是22或者如下,可是运行在Android6.0及以上的设备中会有什么问题?后面会分析这个问题。
经常来讲一个受权失败会抛出SecurityException,然而这并非在全部状况下都会发生。好比,发送一个广播去检查受权(SendBroadcast(Intent)),数据会被发送给全部接收者,可是当这个方法的请求返回的时候,你不会收到任何一个由于受权失败抛出的异常,其实在大多数状况下,受权失败只会打印系统日志。
任何应用也能够定义和执行他们本身的权限。
简单的说,若是你的app targetSdkVersion是3,而你当前运行的系统版本是4,那么在android version 4 中新添加的权限会自动添加到你的app中。
因此建议常常更新你的targetSdkVersion到最新版本。
下面来回答上面的那个问题,若是app的targetSdkVersion是22或者如下,可是运行在android 6.0或以上版本的手机中,会发生什么?
安装过程当中,会一块儿请求用户授予全部权限,若是用户拒绝,將不能安装这个app,只有用户所有赞成这些受权,才能安装这个应用,可是问题来了,安装好了这个应用以后,android6.0以上的系统中,用户是能够去设置中取消受权的,并且是随时均可以取消,因此不少运行时权限可能也得不到,目前官方的作法是,若是用户取消该项受权,那么依赖该项受权的方法的返回值为null,因此你的app可能会报空指针异常。之后是否会针对22如下的app作改变还不得而知,毕竟crash是很难让人接受的,可是crash是由用户形成的,用户应该也能够理解。
系统权限会被传递给两种不一样的保护级别,咱们所知道这两种最重要的保护级别就是普通权限和运行时权限。
普通权限的覆盖区域是在你的app须要访问沙盒之外的数据和资源的时候,可是对用户隐私和其余app的操做只有不多的影响,好比开启手电筒的权限。这个时候,系统会自动受权这些普通权限。
运行时权限的覆盖区域是你的app想要的数据和资源涉及用户的隐私信息,或者是可能潜在的影响用户的存储数据或者其余app的操做。好比,请求获取用户联系人信息的权限。若是一个app申明了运行时权限,用户必须明确的受权这些权限给app。
若是一个app在manifest中请求了一个运行时权限,并且app尚未获得这个运行时权限所在的权限组中的任何一个运行时权限受权,那么系统会弹出一个对话框,描述app想要访问的运行时权限的权限组,这个对话框不会描述这个权限组中某一个特定的权限。
好比,你的app想要请求READ_CONTACT权限,对话框只会描述app想要请求设备的联系人,若是用户受权经过,系统会授予app所请求的该项权限。
若是一个app在manifest中请求一个运行时权限,而且这个app已经在相同的权限组中有了另外一个运行时权限的受权,那么系统不会弹出对话框,而是会当即授予app该项运行时权限的受权。
好比,一个app以前请求过一个READ_CONTACT的权限而且被受权经过,以后又请求一个WRITE_CONTACT(在同一个权限组)权限,那么系统会自动授予该权限。
其实全部权限(普通权限、运行时权限、用户自定义权限)都属于特定的权限组,可是只有运行时权限的权限组才会影响用户体验。
Permission Group | Permissions |
---|---|
CALENDAR |
|
CAMERA |
|
CONTACTS |
|
LOCATION |
|
MICROPHONE |
|
PHONE |
|
SENSORS |
|
SMS |
|
STORAGE |
有两个权限比较特殊,分别是SYSTEM_ALERT_WINDOW
和 WRITE_SETTINGS
都属于比较敏感的权限,不少时候都永不到,若是须要这个权限,应该首先声明,而后发送Intent去请求用户受权,而后系统会显示详细的管理界面。
第一.判断
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
第二.若是是android6.0以上的系统,则检查是否获取受权
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
若是返回值为PackageManager.PERMISSION_GRANTED
,则能够继续以后的操做,若是返回值为PERMISSION_DENIED
,则表明没有受权该权限。
第三.shouldShowRequestPermissionRationale()
能够获得是否须要弹出一个解释申请该权限的提示给用户,若是为true,则能够弹。
第四.请求该权限
示例以下:
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
执行获取权限后的操做
}
第五.请求权限以后,在onRequestPermissionsResult()
返回值中能够获得用户是否受权,若是受权,就能够操做该运行时权限对应的方法
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
运行时权限的特色是,实时性,用户能够随时取消受权,因此每次调用运行时权限的方法都须要判断或者请求一次运行时权限。
在执行运行时权限申请的同时想一下是否真的有必要,想一下使用Intent的方式启动其余应用是否能够达到需求,好比ACTION_IMAGE_CAPTURE
,是直接申明CAMERA的权限本身作一个照相机仍是发送ACTION_IMAGE_CAPTURE
请求让别的应用处理并在onActivityResult()
返回值更方便
若是设备运行在5.1或者如下的设备,或者targetSdkVersion在22或如下,系统会在安装app的时候让用户受权权限。再说一遍,系统只会提示用户app须要的权限组,而不会提示某一个特定的权限。
为了执行自定义权限,你必须在你的manifest中声明一个或多个<permission>标签。
好比:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.me.app.myapp" > <permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY" android:label="@string/permlab_deadlyActivity" android:description="@string/permdesc_deadlyActivity" android:permissionGroup="android.permission-group.COST_MONEY" android:protectionLevel="dangerous" /> ... </manifest>
<protectionLevel>属性是必须的,告诉系统当app申请该权限的时候,要怎样通知用户。
<permissionGroup>属性是可选的,能够帮助系统显示自定义属性属于哪一个权限组,当通知用户弹出框的时候,固然你能够选择某一个自定义权限属于已知的权限组,也能够属于某一个自定义权限组,建议属于已知的权限组。
<android:label>至关于权限组的提示,要简短
<android:description>是某一个特定权限的描述,规则是两句话,第一句描述,第二句警告用户若是受权会发生什么后果。
好比,CALL_PHONE权限
<string name="permlab_callPhone">directly call phone numbers</string> <string name="permdesc_callPhone">Allows the application to call phone numbers without your intervention. Malicious applications may cause unexpected calls on your phone bill. Note that this does not allow the application to call emergency numbers.</string>
以后,你会在setting—>Application中看到该自定义权限。