Android 6.0 Dangerous Permissions与授权机制
来源:互联网 发布:无法触碰影评 知乎 编辑:程序博客网 时间:2024/06/06 00:17
android 6.0 Marshmallow版本之后,系统不会在软件安装的时候就赋予该app所有其申请的权限,对于一些危险级别的权限,app需要在运行时一个一个询问用户授予权限。
From android M permissions will be granted at runtime. User consent is not required for Normal permissions but for Dangerous permissions user is required to grant the permission to application.
Normal permissions:
https://developer.android.com/guide/topics/security/normal-permissions.htmlDangerous permissions:
Dangerous permissions cover areas where the app wants data or resources that involve the user’s private information https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
Normal permissions
android.permission.ACCESS_LOCATION_EXTRA_COMMANDSandroid.permission.ACCESS_NETWORK_STATEandroid.permission.ACCESS_NOTIFICATION_POLICYandroid.permission.ACCESS_WIFI_STATEandroid.permission.ACCESS_WIMAX_STATEandroid.permission.BLUETOOTHandroid.permission.BLUETOOTH_ADMINandroid.permission.BROADCAST_STICKYandroid.permission.CHANGE_NETWORK_STATEandroid.permission.CHANGE_WIFI_MULTICAST_STATEandroid.permission.CHANGE_WIFI_STATEandroid.permission.CHANGE_WIMAX_STATEandroid.permission.DISABLE_KEYGUARDandroid.permission.EXPAND_STATUS_BARandroid.permission.FLASHLIGHTandroid.permission.GET_ACCOUNTSandroid.permission.GET_PACKAGE_SIZEandroid.permission.INTERNETandroid.permission.KILL_BACKGROUND_PROCESSESandroid.permission.MODIFY_AUDIO_SETTINGSandroid.permission.NFCandroid.permission.READ_SYNC_SETTINGSandroid.permission.READ_SYNC_STATSandroid.permission.RECEIVE_BOOT_COMPLETEDandroid.permission.REORDER_TASKSandroid.permission.REQUEST_INSTALL_PACKAGESandroid.permission.SET_TIME_ZONEandroid.permission.SET_WALLPAPERandroid.permission.SET_WALLPAPER_HINTSandroid.permission.SUBSCRIBED_FEEDS_READandroid.permission.TRANSMIT_IRandroid.permission.USE_FINGERPRINTandroid.permission.VIBRATEandroid.permission.WAKE_LOCKandroid.permission.WRITE_SYNC_SETTINGScom.android.alarm.permission.SET_ALARMcom.android.launcher.permission.INSTALL_SHORTCUTcom.android.launcher.permission.UNINSTALL_SHORTCUT
Dangerous permissions(覆盖面是APP想要的那些用户的隐私信息)
- android.permission-group.CALENDAR
- android.permission.READ_CALENDAR
- android.permission.WRITE_CALENDAR
- android.permission-group.CAMERA
- android.permission.CAMERA
- android.permission-group.CONTACTS
- android.permission.READ_CONTACTS
- android.permission.WRITE_CONTACTS
- android.permission.GET_ACCOUNTS
- android.permission-group.LOCATION
- android.permission.ACCESS_FINE_LOCATION
- android.permission.ACCESS_COARSE_LOCATION
- android.permission-group.MICROPHONE
- android.permission.RECORD_AUDIO
- android.permission-group.PHONE
- android.permission.READ_PHONE_STATE
- android.permission.CALL_PHONE
- android.permission.READ_CALL_LOG
- android.permission.ADD_VOICEMAIL
- android.permission.WRITE_CALL_LOG
- android.permission.USE_SIP
- android.permission.PROCESS_OUTGOING_CALLS
- android.permission-group.SENSORS
- android.permission.BODY_SENSORS
- android.permission-group.SMS
- android.permission.SEND_SMS
- android.permission.RECEIVE_SMS
- android.permission.READ_SMS
- android.permission.RECEIVE_WAP_PUSH
- android.permission.RECEIVE_MMS
- android.permission-group.STORAGE
- android.permission.READ_EXTERNAL_STORAGE
- android.permission.WRITE_EXTERNAL_STORAGE
授权操作说明
只有那些targetSdkVersion 设置为23及以上的应用才会出现异常,在使用危险权限的时候系统必须要获得用户的同意才能使用,要不然应用就会崩溃,出现这个错误java.lang.SecurityException: Permission Denial...
所以targetSdkVersion如果没有设置为23版本或者以上,系统还是会使用旧规则:在安装的时候赋予该app所申请的所有权限。
如果以前的老应用申请的权限被用户手动关闭了,不会抛出异常,不会崩溃,只不过调用那些被用户禁止权限的api接口返回值都为null或者0。
在Android M的api中,我们可以通过ContextCompat.checkSelfPermission检测软件是否有某一项权限。如果没有该权限,可以使用ActivityCompat.requestPermissions去请求一组权限。请求完成,会有相对应的回调方法,通知软件用户是否授予了权限。通过在Activity或者Fragment中重写onRequestPermissionsResult方法。如果用户一旦拒绝过某权限的授权。下一次弹框时,用户会有一个“不再提醒(Never ask again)”的选项的来防止app以后继续请求授权。如果这个选项在拒绝授权前被用户勾选了。下次为这个权限请求requestPermissions时,对话框就不弹出来了,系统会直接回调onRequestPermissionsResult函数,回调结果为最后一次用户的选择。所以为了应对这种情况,系统提供了一个shouldShowRequestPermissionRationale()函数,这个函数的作用是帮助开发者找到需要向用户额外解释权限的情况。
应用安装后第一次访问,直接返回false;
第一次请求权限时,用户拒绝了,下一次shouldShowRequestPermissionRationale()返回 true,这时候可以显示一些为什么需要这个权限的说明;
第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时:shouldShowRequestPermissionRationale()返回 false;
设备的系统设置中禁止当前应用获取这个权限的授权,shouldShowRequestPermissionRationale()返回false,此时可以弹出dialog,提醒用户该权限的重要性;
ActivityCompat.shouldShowRequestPermissionRationale()在6.0之前版本调用,永远返回false。
项目实战
AndroidManifest.xml添加如下代码
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="25" /><!-- Dangerous Permissions Start --><!-- android.permission-group.CALENDAR --><uses-permission android:name="android.permission.READ_CALENDAR" /><uses-permission android:name="android.permission.WRITE_CALENDAR" /><!-- android.permission-group.CAMERA --><uses-permission android:name="android.permission.CAMERA" /><!-- android.permission-group.CONTACTS --><uses-permission android:name="android.permission.READ_CONTACTS" /><uses-permission android:name="android.permission.WRITE_CONTACTS" /><uses-permission android:name="android.permission.GET_ACCOUNTS" /><!-- android.permission-group.LOCATION --><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><!-- android.permission-group.MICROPHONE --><uses-permission android:name="android.permission.RECORD_AUDIO" /><!-- android.permission-group.PHONE --><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.CALL_PHONE" /><uses-permission android:name="android.permission.READ_CALL_LOG" /><uses-permission android:name="android.permission.ADD_VOICEMAIL" /><uses-permission android:name="android.permission.WRITE_CALL_LOG" /><uses-permission android:name="android.permission.USE_SIP" /><uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /><!-- android.permission-group.SENSORS --><uses-permission android:name="android.permission.BODY_SENSORS" /><!-- android.permission-group.SMS --><uses-permission android:name="android.permission.SEND_SMS" /><uses-permission android:name="android.permission.RECEIVE_SMS" /><uses-permission android:name="android.permission.READ_SMS" /><uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" /><uses-permission android:name="android.permission.RECEIVE_MMS" /><!-- android.permission-group.STORAGE --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- Dangerous Permissions End -->
DangerousPermissions.java
package com.example.permissionhelperdemo;import android.Manifest;/** * Dangerous permissions * <ol> * <li>android.permission-group.CALENDAR * <ul> * <li>android.permission.READ_CALENDAR</li> * <li>android.permission.WRITE_CALENDAR</li> * </ul> * </li> * * <li>android.permission-group.CAMERA * <ul> * <li>android.permission.CAMERA</li> * </ul> * </li> * * <li>android.permission-group.CONTACTS * <ul> * <li>android.permission.READ_CONTACTS</li> * <li>android.permission.WRITE_CONTACTS</li> * <li>android.permission.GET_ACCOUNTS</li> * </ul> * </li> * * <li>android.permission-group.LOCATION * <ul> * <li>android.permission.ACCESS_FINE_LOCATION</li> * <li>android.permission.ACCESS_COARSE_LOCATION</li> * </ul> * </li> * * <li>android.permission-group.MICROPHONE * <ul> * <li>android.permission.RECORD_AUDIO</li> * </ul> * </li> * * <li>android.permission-group.PHONE * <ul> * <li>android.permission.READ_PHONE_STATE</li> * <li>android.permission.CALL_PHONE</li> * <li>android.permission.READ_CALL_LOG</li> * <li>android.permission.WRITE_CALL_LOG</li> * <li>android.permission.ADO_VOICEMAIL</li> * <li>android.permission.USE_SIP</li> * <li>android.permission.PROCESS_OUTGOING_CALLS</li> * </ul> * </li> * * <li>android.permission-group.SENSORS * <ul> * <li>android.permission.BODY_SENSORS</li> * </ul> * </li> * * <li>android.permission-group.SMS * <ul> * <li>android.permission.SEND_SMS</li> * <li>android.permission.RECEIVE_SMS</li> * <li>android.permission.READ_SMS</li> * <li>android.permission.RECEIVE_WAP_PUSH</li> * <li>android.permission.RECEIVE_MMS</li> * <li>android.permission.READ_CELL_BROADCASTS</li> * </ul> * </li> * * <li>android.permission-group.STORAGE * <ul> * <li>android.permission.READ_EXTERNAL_STORAGE</li> * <li>android.permission.WRITE_EXTERNAL_STORAGE</li> * </ul> * </li> * * <ol> */public class DangerousPermissions { private static final String CALENDAR = Manifest.permission.READ_CALENDAR; private static final String CAMERA = Manifest.permission.CAMERA; private static final String CONTACTS = Manifest.permission.READ_CONTACTS; private static final String LOCATION = Manifest.permission.ACCESS_FINE_LOCATION; private static final String MICROPHONE = Manifest.permission.RECORD_AUDIO; private static final String PHONE = Manifest.permission.READ_PHONE_STATE; private static final String SENSORS = Manifest.permission.BODY_SENSORS; private static final String SMS = Manifest.permission.SEND_SMS; private static final String STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE; public static final String[] PERMISSIONS_IN_DIFFERENT_GROUP = new String[] { CALENDAR, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE, }; public static final String[] PERMISSIONS_IN_SAME_GROUP = new String[] { Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR };}
PermissionHelper.java
package com.example.permissionhelperdemo;import android.app.Activity;import android.content.Context;import android.content.pm.PackageManager;import android.os.Build;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;public class PermissionHelper { public static final int PERMISSION_REQUEST_CODE = 1; /** * 如果API小于23就不用检查权限了,获取不到就返回0或null<br> * 但如果API大于23,用户未同意便会出现下面错误: java.lang.SecurityException: Permission Denial... */ public boolean needCheckPermissions() { boolean result = Build.VERSION.SDK_INT < 23; System.out.println("Build.VERSION.SDK_INT < 23 = " + result); if (result) { return false; } return true; } /** * checkSelfPermissions函数是否有效仅跟Build.VERSION.SDK_INT有关,与targetVersion无关 */ public boolean needCheckPermissions(Context context) { boolean result = context.getApplicationInfo().targetSdkVersion < 23; System.out.println("context.getApplicationInfo().targetSdkVersion < 23 = " + result); if(result) { return false; } return true; } /** * 校验权限<br> * 如果API小于23,ContextCompat.checkSelfPermission和ActivityCompat.requestPermissions并不会正常工作,并总是返回0(PERMISSION_GRANTED),即使你是在Android 6.0(API23)上运行 */ public boolean checkSelfPermissions(Context context, String... permissions) { if (permissions == null || permissions.length == 0) { return true; } for (String permission : permissions) { boolean result = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; System.out.println("ContextCompat.checkSelfPermission " + permission + " -> " + result); if (!result) { return false; } } return true; } public void requestPermissions(Activity activity, String... permissions) { ActivityCompat.requestPermissions(activity, permissions, PERMISSION_REQUEST_CODE); }}
MainActivity.java
package com.example.permissionhelperdemo;import android.app.Activity;import android.content.pm.PackageManager;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity implements OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button permissionInSameGroupBtn = (Button) findViewById(R.id.permission_in_same_group_btn); Button permissionInDifferentGroupBtn = (Button) findViewById(R.id.permission_in_different_group_btn); permissionInSameGroupBtn.setOnClickListener(this); permissionInDifferentGroupBtn.setOnClickListener(this); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { for(int i=0; i<permissions.length; i++) { boolean result = grantResults[i] == PackageManager.PERMISSION_GRANTED; System.out.println("onRequestPermissionsResult " + permissions[i] + " -> " + result); boolean shouldShow = shouldShowRequestPermissionRationale(permissions[i]); System.out.println("shouldShowRequestPermissionRationale " + permissions[i] + " -> " + shouldShow); } } private void checkPermission(String... permissions) { PermissionHelper permissionHelper = new PermissionHelper(); permissionHelper.needCheckPermissions(); permissionHelper.needCheckPermissions(this); boolean result = permissionHelper.checkSelfPermissions(this, permissions); System.out.println("permissionHelper.checkSelfPermissions -> " + result); permissionHelper.requestPermissions(this, permissions); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.permission_in_same_group_btn: checkPermission(DangerousPermissions.PERMISSIONS_IN_SAME_GROUP); break; case R.id.permission_in_different_group_btn: checkPermission(DangerousPermissions.PERMISSIONS_IN_DIFFERENT_GROUP); break; } }}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.permissionhelperdemo.MainActivity" > <Button android:id="@+id/permission_in_same_group_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="PERMISSIONS_IN_SAME_GROUP" /> <Button android:id="@+id/permission_in_different_group_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="PERMISSIONS_IN_DIFFERENT_GROUP" /></LinearLayout>
可能会遇到的问题
如果大家使用eclipse编译项目有可能会报错,这是因为libs/android-support-v4.jar包太旧,可以将appcompat_v7项目的libs/android-support-v4.jar复制到本项目相应目录。
appcompat_v7的目录为 sdk\extras\android\support\v7\appcompat
如果还是不行,请更新SDK。
运行截图
# 测试手机三星SM-A9000,Android 6.01, API 23总结
Android在赋予权限的时候采取了一个非常取巧的办法——将相似的权限分为一组,用户只能对组进行授权。一旦授权则意味着允许该组的所有权限行为。
- checkSelfPermission用户赋予权限后返回true,否则返回false
该方法只能check单个permission(Manifest.permission.*),不能check权限组(Manifest.permission_group.*)。并且仅在手机API >= 23时有效,与target API 或 Build target API均无关,当手机API < 23时总是返回true。 - ActivityCompat.requestPermissions与onRequestPermissionsResult(Project Build target API >= 23)配合使用,用来获取用户是否授权的返回结果。并且当手机API < 23时,这2个方法无效。
- shouldShowRequestPermissionRationale(min API >= 23)在第一次拒绝时返回true;再次拒绝或用户确认赋予权限时,均返回false。
参考链接
- http://www.th7.cn/Program/Android/201603/780587.shtml
- http://stackoverflow.com/questions/7339743/android-permissions-how-can-i-learn-which-are-dangerous-vs-normal
补充说明
看了这么多,可能有些人还是不懂这个到底有什么用。
举个实际的例子:
有的手机,比如乐1S,升级到Android 6.0 后,默认安装应用之后是不会赋予任何权限的。这时候我打开该款蓝牙应用就会搜不到任何设备。当然你可以提示用户去“系统设置”打开“位置权限”(Android 6.0之后蓝牙需要位置权限,但蓝牙不属于危险权限,默认会帮你开启)。但是更好的做法是直接在代码中判断是否具有该权限,如果没有弹窗提示用户开启。
另外,还需要注意的一点是,该授权机制虽然可以判断是否具有没有在AndroidManifest.xml中声明的权限(结果肯定是否啦。。。),但是即使调用相关函数,也不能让系统弹窗提示用户授予该权限。
- Android 6.0 Dangerous Permissions与授权机制
- Android Dangerous Permissions
- 关于Android 6.0+ Dangerous Permissions (运行时权限,敏感权限) 《实用篇》
- Normal and Dangerous permissions
- android6.0 Dangerous permissions and permission groups.
- App权限—normal and dangerous permissions
- Android的安全机制---Security and Permissions
- android 6.0 Runtime Permissions Check
- system permissions for android 6.0
- android permissions
- Android 6.0 开发者对系统权限的使用与练习(Permissions Best Practices)
- Android 6.0 应用权限(二) -- 与系统权限一起工作(Working with System Permissions)
- Android 6.0 权限模型——Permissions
- Android 6.0 新特性之 RunTime Permissions
- Dangerous
- 谈Android系统及程序授权机制
- android 6.0 权限授权
- android ubuntu no permissions
- Linux守护进程之Supervisor
- MXnet on windows 10 预编译版本安装常见问题指南
- MyBatis启动流程
- 求穿过平面上最多点的直线(设计思想)
- java-mysql数据库基本操作学习笔记(1)
- Android 6.0 Dangerous Permissions与授权机制
- java Map的删除操作和Map相关
- 【9801】黑白棋游戏 - Pascal
- TCP运输连接管理解析(三次握手过程解析)
- Hibernate1(入门)
- hive 配置 mysql时的问题(Relative path in absolute URI: ${system:java.io.tmpdir%7D/$%7Bsystem:user.name%7D
- SCSI设备IO过程:磁盘上线与IO过程
- Centos7手动部署Openstack Mitaka版安装配置--(一)环境准备
- Hive metastore database is not initialized. Please use schematool(...) to create the schema.