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.html

Dangerous 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想要的那些用户的隐私信息)

  1. android.permission-group.CALENDAR
    • android.permission.READ_CALENDAR
    • android.permission.WRITE_CALENDAR
  2. android.permission-group.CAMERA
    • android.permission.CAMERA
  3. android.permission-group.CONTACTS
    • android.permission.READ_CONTACTS
    • android.permission.WRITE_CONTACTS
    • android.permission.GET_ACCOUNTS
  4. android.permission-group.LOCATION
    • android.permission.ACCESS_FINE_LOCATION
    • android.permission.ACCESS_COARSE_LOCATION
  5. android.permission-group.MICROPHONE
    • android.permission.RECORD_AUDIO
  6. 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
  7. android.permission-group.SENSORS
    • android.permission.BODY_SENSORS
  8. 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
  9. 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中声明的权限(结果肯定是否啦。。。),但是即使调用相关函数,也不能让系统弹窗提示用户授予该权限。

2 0
原创粉丝点击