小米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权限排查踩坑的主要内容,如果未能解决你的问题,请参考以下文章

小米MIUI NFCWIFI权限排查踩坑

小米root权限怎么开启miui12

如何获得删除小米(MIUI)设备中联系人的权限?

小米手机MIUI安装APK时自动获取安装权限

小米手机怎么打开root权限管理

miui稳定版怎么破解root权限