本文主要介绍 Android 手机系统权限处理方式
前言
随着 Android 设备的广泛引用,Google 对于手机的安全问题也越来越重视 。从 Android M 开始添加了应用动态权限管理,手机的权限管理细粒度,更好的保护了用户的个人隐私并且赋予了用户更大的管理权限。但是各大 Android 手机本身有自己的应用权限管理,这就导致了 Android 开发小伙伴们,在开发工程中要尽可能多的去考虑问题,防止出现权限问题导致应用崩溃。
权限变化及影响
权限变化
在 Android 的各个版本中,不论是普通权限还是敏感权限,都需要在manifest文件中声明,例如权限声明。然而,在不同版本的操作系统或不同的 target SDK level 中的结果是不同的。
如果设备运行 Android M 以下版本的操作系统,或者你的 targetSDK 版本号小于或等于 22,当你在 manifest 文件中请求了一些权限,用户必须在安装过程时有的手机是授予全部权限,部分手机提示用户选择授权。
如果设备运行在 Android M 或者更高版本,并且目标 SDK 版本号大于或等于 23,应用程序必须要在 manifest 文件中声明需要的权限,当程序运行时,它必须要向用户请求授权每个所需的敏感权限。用户可以允许或拒绝每个权限,并且程序可以依赖用户已经授权的权限继续运行。
关于compileSdk、minSdk、targetSdk的相关知识,建议看一下这篇文章:http://www.jianshu.com/p/544d9f72883d
影响
-
在公司的项目开发过程中确实遇到了不少的坑,由于公司项目集成某家第三方库,导致公司的项目 targetSdkVersion 版本为 25 ,在 Adnroid 出现了动态权限管理后,发现项目在高版本的手机上调用权限崩溃的问题。最后参考其他其他一些应用,想出一个解决方案,为了防止用户随意改变应用权限导致程序崩溃,我们在 Base 层加了检查权限方法,检测到应用没有开启清单文件中的权限,就给出提示并且让用户跳转到应用权限管理页面。
-
大功告成发给测试,然而新的坑又出现了。前面说到的问题在不同版本的操作系统或不同的 target SDK level 中的结果是不同的。下面我们来总结一下都出现了那些坑。
- 检测权限的代码在一台 Android 5.0 的华为手机一直返回 true , 原因很简单 Android 5.0 它检查这些权限如果你在清单文件里注册过了,那就表明这个应用有了这个权限。但是这台手机在安装后手机厂商提示系统权限授权,测试人员选择了拒绝。检测权限代码并没有成效。
-
在 Android 6.0 以上的手机检测权限代码没有问题,但是之前的解决方案是跳转到应用权限管理页面,后来发现只要在安装后拒绝了厂商的系统权限,我在应用授权管理页面每次打开这个权限后,再次查看都是无法打开的。这就很尴尬了,厂商的应用系统权限大于应用本身的权限。
解决方案
- 当然每次没有好的决方式时,我第一想到的就是看看微信、QQ等应用是如何处理这样的问题的,分别在我的测试机华为(5.0) 、小米(7.0)上安装了很多应用,参考参考大神们是如何处理这些问题的。
- 经过试验发现微信的处理方式是很优好的,在小米(7.0)手机上关闭微信访问权限和系统应用权限。再启动微信的时候程序给出了提示,缺少相应权限让用户去设置。在华为(5.0)上我关闭了微信系统应用权限(定位、摄像头的权限)也是检测不到没有任何的提示,但是在我使用到定位、获取摄像头功能时他会通过代码判断应用是否有这个功能的权限,然后提示给用户,让用户自己去系统的授权管理里面去开启相应的权限,要不然不能使用该功能。
- 下面我举个例子说明一下,我在项目中是如何判断应用没有录音权限的,检测语音录制权限方法很多但是经过测试发现一定要通过获取语音流的方式,如果获取不到则说明没有权限。这种方式是可以兼容所有手机的,代码如下:
public boolean isHasAudioRecordingPermission(Context context) {
isHasPermission = false;
count = 0;
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
SAMPLE_RATE_IN_HZ, AudioFormat.CHANNEL_IN_DEFAULT,
AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE);
if (mAudioRecord == null) {
MyLog.e("sound", "mAudioRecord初始化失败");
}
isGetVoiceRun = true;
try {
mAudioRecord.startRecording();
short[] buffer = new short[BUFFER_SIZE];
while (isGetVoiceRun) {
count++;
if (count++ > 10) {
isGetVoiceRun = false;
}
//r是实际读取的数据长度,一般而言r会小于buffersize
int r = mAudioRecord.read(buffer, 0, BUFFER_SIZE);
long v = 0;
// 将 buffer 内容取出,进行平方和运算
for (short aBuffer : buffer) {
v += aBuffer * aBuffer;
}
// 平方和除以数据总长度,得到音量大小。
double mean = v / (double) r;
double volume = 10 * Math.log10(mean);
MyLog.d(TAG, "-------分贝值:" + volume + "----v" + v + "------r" + r);
if (v > 0 && r > 0) {
// 有录音
isHasPermission = true;
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
return isHasPermission;
}
// 大概一秒十次
synchronized (mLock) {
try {
mLock.wait(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
} catch (Exception e) {
e.printStackTrace();
}
return isHasPermission;
}
- 所以接下就是在项目中每个调用的权限的地方都加上判断,给用户提示去系统授权中打开相应的权限。
- 下面是整理了一些不同手机跳转到对应的权限管理页面的方法(仅供参照,不排除个别厂家有作更改,希望小伙伴们多来指正)
switch (Build.MANUFACTURER) {
case Constants.ROM_HUAWEI: // 华为
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.huawei.systemmanager",
"com.huawei.permissionmanager.ui.MainActivity");
intent.setComponent(comp);
startActivity(intent);
break;
case Constants.ROM_MEIZU: // 魅族
intent.setAction("com.meizu.safe.security.SHOW_APPSEC");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
startActivity(intent);
break;
case Constants.ROM_XIAOMI: // 小米
intent.setAction("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter",
"com.miui.permcenter.permissions.PermissionsEditorActivity");
intent.putExtra("extra_pkgname", getPackageName());
startActivity(intent);
break;
case Constants.ROM_SONY: // 索尼
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.sonymobile.cta",
"com.sonymobile.cta.SomcCTAMainActivity");
intent.setComponent(comp);
startActivity(intent);
break;
case Constants.ROM_OPPO: // oppo
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.color.safecenter",
"com.color.safecenter.permission.PermissionManagerActivity");
intent.setComponent(comp);
startActivity(intent);
break;
case Constants.ROM_LG: // LG
intent.setAction("android.intent.action.MAIN");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.android.settings",
"com.android.settings.Settings$AccessLockSummaryActivity");
intent.setComponent(comp);
startActivity(intent);
break;
case Constants.ROM_LETV: // 乐视
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
comp = new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps");
intent.setComponent(comp);
startActivity(intent);
break;
default:
// 跳转权限设置界面
intent.setAction(Settings.ACTION_SETTINGS);
startActivity(intent);
break;
}