Android 开发中的权限申请

一、权限动态申请

        我们都知道,从 Android 6.0 开始,部分危险权限在 xml 注册的同时,还需要动态申请。

1、需要动态申请的权限

Manifest.permission.CONTACTS //联系人
Manifest.permission.PHONE //电话
Manifest.permission.CALENDAR //日历
Manifest.permission.CAMERA //相机
Manifest.permission.SENSORS //传感器
Manifest.permission.LOCATION //位置
Manifest.permission.STORAGE //存储
Manifest.permission.MICROPHONE //麦克风
Manifest.permission.CONTACTS //短信

        需要动态申请的权限主要分为这 9 类,当然每类中可能不止一个权限,但只要动态申请一个就默认获取整个类别的权限。

2、申请单个权限

//常量,用于回调
int MY_PERMISSION_APPLY = 1;
//要使用的相机权限
int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
//判断是否有相机权限
if (permission == PackageManager.PERMISSION_GRANTED) {
    //有权限直接执行            
} else { 
    //没有权限,提示获取权限
    String[] perms = {"android.permission.CAMERA"};
    ActivityCompat.requestPermissions(this, perms, MY_PERMISSION_APPLY);
}

        在弹出权限申请后,需要对用户的选择结果进行处理,在回调方法中执行:

public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
    switch(permsRequestCode){
        case MY_PERMISSION_APPLY:
            boolean albumAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
            if(!albumAccepted){
                //用户拒绝了权限
            }else{
                //用户同意了权限
            }
            break;
    }
}

 3、同时申请多个权限

//常量,用于回调
int MY_PERMISSION_APPLY = 1;
//要使用的相机和存储权限
String[] permissions = new String[]{
            Manifest.permission.CAMERA,
            Manifest.permission.STORAGE
    };
//用于存放未同意的权限
List<String> mPermissionList = new ArrayList<>();
 
//清空未同意权限
mPermissionList.clear();
//循环得到未同意权限
for (int i = 0; i < permissions.length; i++) {
    if (ContextCompat.checkSelfPermission(mContext, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
        mPermissionList.add(permissions[i]);
    }
}
 
//判断是否有未同意权限
if (mPermissionList.isEmpty()) {
    //都有权限直接执行  
} else {
    //将List转为数组
    String[] permissions = mPermissionList.toArray(new String[mPermissionList.size()]);
    ActivityCompat.requestPermissions(this, permissions, MY_PERMISSION_APPLY);
}

        然后,同样添加上面的回调方法处理用户的选择结果。 

二、本地文件读写权限变更

        需求:Android 11.0 版本,sdk 32,使用本地 Download 下文件,在申请动态权限后,仍然提示缺少权限问题。

1、Manifest.xml

        我们都知道 Android 开发某些功能(文件读写)时需要在 Manifest.xml 中添加对应权限。才能对 Android 手机中的图片、文件等内容进行处理。Manifest.xml 中添加读写权限如下:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

2、动态权限

        Android 6.0 时在 Manifest.xml 添加权限的同时,还需要在使用时动态申请权限,这个大家应该也很熟悉,这里仅展示权限申请代码,至于用户拒绝、不再提示等操作及回调不再展示。

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M
        && context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {//请求权限
    ((Activity)context).requestPermissions(new String[]{
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}

3、作用域存储

        Android 10.0 开启了作用域存储,即使完成了上面的 Manifest.xml 中添加权限和代码中动态申请权限,在访问包名以外的文件夹时,依旧提示权限不足。这时我们在 Manifest.xml 添加属性关闭作用域存储,这时谷歌官方提供的一个暂时解决方案。

<application
    android:requestLegacyExternalStorage="true">

</application>

4、外部存储权限

        Android 11.0 开始,作用域存储开始强制启用,上面的关闭方法不在起作用,这时如果还需要访问外部文件夹,就需要用户手动开启一个危险权限——外部存储权限。

Manifest.xml 新增外部存储权限

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

手动设置打开

        该权限属于特殊权限需要用户手动去设置中打开,通过 Environment.isExternalStorageManager() 判断是否开启,未开启跳转设置开启界面。

public static boolean checkStorageManagerPermission(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
        context.startActivity(intent);
        return false;
    }
    return true;
}

        打开后就可以正常访问 Download 等共享目录了。

三、危险权限弹窗处理

1、需求分析

1)系统应用直接授予权限

2)使用指定签名第三方应用直接授予权限

3)使用普通签名第三方应用正常提示

2、实现方案分析

系统APP

        系统 APP 可以直接修改 DefaultPermissionGrantPolicy.java 中的 grantDefaultSystemHandlerPermissions() 方法直接为系统应用配置默认权限。

限制条件:需要修改系统源码,所以第三方应用无法实现

文件位置:frameworks/base/services/core/java/com/android/server/pm/permission/

修改权限属性

1)直接修改权限级别在 frameworks/base/core/res/AndroidManifest.xml 中将 dangerous(危险)权限修改为 normal(正常)权限,所有应用均直接获得权限,不适用上面需求。

2)将 dangerous(危险)权限修改为 Signature(签名)权限,这样指定签名APP可以获得权限,但第三方其他签名 APP 无法获取权限。

        ps:是否可以设置同名权限:例如设置摄像头同名权限,级别 Signature,同签名自动拥有权限,其他第三方APP申请默认dangerous 摄像头权限弹出提示。

        需要验证,感觉可行性不大。

指定APP通过权限

        PackageManagerService.java 的grantPermissionsLpw 中添加指定包名通过权限,网络上很多资料都可以找到该方法,但现在的源码中找到了该文件,未找到该方法。

        ps:是否可以将上面的判断包名变成判断签名,待验证。

权限白名单(最优)

1、/frameworks/base/core/res/res/values/config.xml中增加包名白名单

2、/frameworks/base/core/res/res/values/symbols.xml中声明白名单变量

3、frameworks/base/services/core/java/com/android/server/pm/ permission/PermissionManagerService.java中的grantPermissions方法中判断权限白名单

 参考:Android 9.0 设置白名单赋予应用权限 - 二的次方 - 博客园

        该方法添加白名单也需要修改配置文件,所以第三方指定签名文件也无法直接得到权限。所有权限通过也不适用。

        但通过源码逻辑发现,下面代码应该是APP系统签名判断方法:

if(bp.isSignature()){
    allowedSig = grantSignaturePermission(perm, pkge, bp, origPermissions);
}

        所以上面的判断白名单可以改成判断签名通过权限,即:

if(bp.isRuntime()){

    else{
        allowedSig = grantSignaturePermission(perm, pkge, bp, origPermissions);
        if(allowedSig){
            grant = GRANT_INSTALL;
        }
    }

}

        这样使用指定签名APP和系统APP都能跳过权限验证。

直接处理提示框

        直接在 GrantPermissionsActivity 中弹框时判断处理,但是根据需求也需要进行签名验证。

可行方案预测(待验证)

1、在 AndroidManifest.xml 中自定义一个新权限,级别为Signature;

2、在DefaultPermissionGrantPolicy.java将新定义的权限加入权限组;

        这样指定签名的应用直接获得了新权限,又因为同组权限只要有一个权限是通过状态,其他权限不在验证直接通过。

        需要直接通过的APP需要使用指定签名,同时还需要申请我们自定义的新权限。

猜你喜欢

转载自blog.csdn.net/c19344881x/article/details/128920179
今日推荐