小米MIUI NFCWIFI权限排查踩坑
Posted 夏至的稻穗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小米MIUI NFCWIFI权限排查踩坑相关的知识,希望对你有一定的参考价值。
背景: 小米手机使用NFC时,会提示是否使用NFC弹窗,如果点击拒绝,则下次碰 NFC 无反应。
一. 现象
NFC 的使用非常简单,只需要在 androidManifest.xml 上 注册权限即可:
<uses-permission android:name="android.permission.NFC" />
但 MIUI 可能考虑安全问题,NFC 有使用权限限制,如:
经排查,安卓官网并没有对 NFC 使用有特殊权限申请,其他手机使用 NFC 无该情况。
二. 问题排查
既然涉及权限问题,可能 MIUI 需要申请权限,抱着疑问使用了:
boolean isHasNfcPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.NFC)
== PackageManager.PERMISSION_GRANTED;
无论关闭还是打开NFC,结果都是 PERMISSION_GRANTED ,看来这里行不通。
权限问题,除了 ActivityCompat.checkSelfPermission() , 还有 AppOpsManager 权限管理类;
进到源码,查看 op 值 ,从数组来看,并发现并没有跟 NFC 相关的权限。
这个时候就有点蛋疼了。。。
三. 解决问题
后来网上发现了一遍文章,https://blog.csdn.net/GuangkuoDing/article/details/100324162 ,说是小米官网给的,“后台弹出页面” op 值为 10021 ,但没说 NFC 是多少。
不过可以看到改值还是比较大的,原生的数组长度为 90 ,估计 MIUI 取了个比较靠后的用来测试。
所以,我大概取了个范围,从 10000 到 10030,进行对比测试:
for (int op = 10000; op <= 10030; op++) {
try {
AppOpsManager appOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
Method checkOpNoThrow = appOpsManager.getClass().getDeclaredMethod("checkOpNoThrow",
int.class, int.class, String.class);
checkOpNoThrow.setAccessible(true);
int result = (int) checkOpNoThrow.invoke(appOpsManager, op, Process.myUid(),
getPackageName());
Log.d(TAG, "zsr op 值: "+op+" / 结果: "+result);
} catch (Exception e) {
RLog.e(TAG, "hasMIUIPermission: " + e.toString());
e.printStackTrace();
Log.d(TAG, "zsr 无效op: "+op);
}
}
控制变量为 : NFC 的开关
发现有意外收获。
得到的测试结果如下图,可以看到 10016 是有变动: 0 → 1
通过反复验证,得到 NFC 的 op 值为 10016,WiFi 的 op 值为 10001。故 AppOpsManager 是可行的,只是值得自己去排查。
四. 工具
知道权限,还得跳转该当前页面,通过命令 adb shell dumpsys activity top | grep ACTIVITY 可以拿到授权的MIUI包名为:
com.miui.securitycenter/com.miui.permcenter.permissions.PermissionsEditorActivity
而不同平台的MIUI 是不一样的,所以写了个MIUI的工具类,方便开发:
public class MiuiUtils {
private static final String TAG = "MiuiUtils";
private static final int OPS_NFC = 10016;
private static final int OPS_WIFI = 10001;
private static Context context = MaxhubApplication.getContext().getApplicationContext();
/**
* 跳转到MIUI应用权限设置页面
*
* @param context context
*/
private static void jumpToPermissionsEditorActivity(Context context) {
try {
// MIUI 8
Intent localIntent = new Intent();
localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
localIntent.putExtra("extra_pkgname", context.getPackageName());
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(localIntent);
} catch (Exception e) {
RLog.i(TAG, "jumpToPermissionsEditorActivity: "+e.toString());
try {
// MIUI 5/6/7
Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR");
localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
localIntent.putExtra("extra_pkgname", context.getPackageName());
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(localIntent);
} catch (Exception e1) {
RLog.i(TAG, "jumpToPermissionsEditorActivity2: "+e.toString());
// 否则跳转到应用详情
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", context.getPackageName(), null);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(uri);
context.startActivity(intent);
}
}
}
/**
* 检察小米的权限,其他手机没有此魔改
*
* @param context
* @return
*/
private static boolean isHasPermission(Context context, int ops) {
if (!isMIUI()){
return true;
}
try {
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
Method checkOpNoThrow = appOpsManager.getClass().getDeclaredMethod("checkOpNoThrow",
int.class, int.class, String.class);
checkOpNoThrow.setAccessible(true);
int result = (int) checkOpNoThrow.invoke(appOpsManager, ops, Process.myUid(),
context.getPackageName());
return AppOpsManager.MODE_IGNORED != result;
} catch (Exception e) {
RLog.e(TAG, "hasMIUIPermission: " + e.toString());
e.printStackTrace();
return false;
}
}
/**
* 判断是否是MIUI
*/
private static boolean isMIUI() {
String manufacturer = Build.MANUFACTURER;
if ("xiaomi".equalsIgnoreCase(manufacturer)) {
return true;
}
return false;
}
/**
* NFC使用,需要使用权限
* @return
*/
public static boolean hasMIUINfcPermission(){
return MiuiUtils.isHasPermission(context, OPS_NFC);
}
/**
* WiFi使用,需要使用权限
* @return
*/
public static boolean hasMIUIWifiPermission(){
return MiuiUtils.isHasPermission(context, OPS_WIFI);
}
/**
* 去掉 MIUI 权限详情页
* @return
*/
public static void gotoMIUIPermissionAct(){
MiuiUtils.jumpToPermissionsEditorActivity(context);
}
}
以上是关于小米MIUI NFCWIFI权限排查踩坑的主要内容,如果未能解决你的问题,请参考以下文章