美文网首页
AppOpsManager源码探析及检测悬浮窗权限

AppOpsManager源码探析及检测悬浮窗权限

作者: InnerNight | 来源:发表于2017-03-08 16:53 被阅读2435次

在开发悬浮窗过程中,我们会遇到的很大一个问题就是权限问题。在6.0引入动态权限之后,权限被分为了一般权限和危险权限。一般权限只要在清单文件中注册可使用,危险权限可以通过动态获取来获得(比如获取联系人)。而有一些权限必须要通过指定Intent才能获得(比如录屏)。但像悬浮窗权限,是属于默认关闭的权限,必须要用户手动打开。
那如何检测用户是否同意给了悬浮窗权限呢?这里要用到Android中的一个服务叫做AppOpsManager,这个api在4.4之后引入,设计意图是统一管理系统的权限。但是后来一直也不怎么成功,直到6.0引入了动态权限,这套机制的作用更小了。但是这并不影响我们这里用它来判断悬浮窗权限。
关于AppOpsManager可以参考这篇文章
checkOpNoThrow()这个方法
比如我们要检测悬浮窗权限,可以这样用:

private boolean checkAlertWindowAllowed() {
    AppOpsManager manager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    return AppOpsManager.MODE_ALLOWED == manager.checkOpNoThrow(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW,
            Binder.getCallingUid(), getPackageName());
}

这上面使用的checkOpNoThrow()在没有权限时不会像checkOp那样抛出SecurityException,而是返回错误值,所以可以直接判断。
很简单吧!
但是!
但是!
但是!
你以为这么简单吗?如果你的app只要支持6.0以上的机型,那么的确是这么简单。但是!如果你要支持其它4.4以上的机型,就会遇到这样一个问题:6.0以下找不到这个权限AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW
似乎是因为这个权限对应的常量在6.0以上android才单独拿出来,果然坑爹。
通过查看checkOp的源码我们可以发现,其实这个方法最后会调用一个参数不同的checkOp方法:

public int checkOp(String op, int uid, String packageName) {
    return checkOp(strOpToOp(op), uid, packageName);
}

在strOpToOp方法中,我们传入的权限string被转成对应的int值。而在6.0以下机型,也是这个方法抛出的异常:

public static int strOpToOp(String op) {
    Integer val = sOpStrToOp.get(op);
    if (val == null) {
        throw new IllegalArgumentException("Unknown operation string: " + op);
    }
    return val;
}

而悬浮窗对应的常量值,我们在源文件里也可以找到,问题是这个常量被hide了:

/** @hide */
public static final int OP_SYSTEM_ALERT_WINDOW = 24;

那你说,我直接hardcode 24不可以么?当然可以。但问题是,下面这个方法它也是hide的:

/**
 * Do a quick check for whether an application might be able to perform an operation.
 * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)}
 * or {@link #startOp(int, int, String)} for your actual security checks, which also
 * ensure that the given uid and package name are consistent.  This function can just be
 * used for a quick check to see if an operation has been disabled for the application,
 * as an early reject of some work.  This does not modify the time stamp or other data
 * about the operation.
 * @param op The operation to check.  One of the OP_* constants.
 * @param uid The user id of the application attempting to perform the operation.
 * @param packageName The name of the application attempting to perform the operation.
 * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
 * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
 * causing the app to crash).
 * @throws SecurityException If the app has been configured to crash on this op.
 * @hide
 */
public int checkOp(int op, int uid, String packageName) {
    try {
        int mode = mService.checkOperation(op, uid, packageName);
        if (mode == MODE_ERRORED) {
            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
        }
        return mode;
    } catch (RemoteException e) {
    }
    return MODE_IGNORED;
}

所以只好使用最后的杀招:反射。代码如下:

private boolean isAlertWindowAllowed() {
    try {
        Object object = getSystemService(Context.APP_OPS_SERVICE);
        if (object == null) {
            return false;
        }
        Class localClass = object.getClass();
        Class[] arrayOfClass = new Class[3];
        arrayOfClass[0] = Integer.TYPE;
        arrayOfClass[1] = Integer.TYPE;
        arrayOfClass[2] = String.class;
        Method method = localClass.getMethod("checkOp", arrayOfClass);

        if (method == null) {
            return false;
        }
        Object[] arrayOfObject = new Object[3];
        arrayOfObject[0] = Integer.valueOf(24);
        arrayOfObject[1] = Integer.valueOf(Binder.getCallingUid());
        arrayOfObject[2] = getPackageName();
        int m = ((Integer) method.invoke(object, arrayOfObject)).intValue();
        return m == AppOpsManager.MODE_ALLOWED;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

附上一些参考和拓展文章:
AppOpsManager介绍
Android无需权限显示悬浮窗

相关文章

  • AppOpsManager源码探析及检测悬浮窗权限

    在开发悬浮窗过程中,我们会遇到的很大一个问题就是权限问题。在6.0引入动态权限之后,权限被分为了一般权限和危险权限...

  • Android权限适配(二)

    本文接 Android权限适配(一) 悬浮窗权限 悬浮窗权限同样属于上文中说到的特殊权限。 悬浮窗代码的设置 要使...

  • AppOpsManager权限检测适配

    导语 android碎片化相信是每一个android开发者的痛。机型适配也是难以绕过去的坎。这其中Android动...

  • 悬浮窗上线

    [ 参考点: 悬浮窗(权限启动申请) || 悬浮窗( 权限使用时申请 )] -------------------...

  • android 悬浮窗

    安卓悬浮窗的书写,我们分为几个步骤: 1.添加悬浮窗权限 2.书写悬浮窗代码,搭建悬浮窗布局 3.判断悬浮窗权限是...

  • 使用教程

    小米&华为&魅族打开悬浮窗权限 OPPO&VIVO打开悬浮权限 其他手机打开悬浮权限

  • Android 悬浮窗及权限

    引言:需要实现一个视频悬浮播放的功能,功能实现后发现悬浮权限的检测与申请并没有想象中那样简单。时间:2017年04...

  • Android悬浮窗探究

    需求:学习悬浮窗使用以及type参数影响 悬浮窗使用 在获得系统悬浮窗权限(SYSTEM_ALERT_WINDOW...

  • 悬浮窗权限

    权限 1、注册权限 2、动态申请权限 API19以后需要动态申请权限,API23以前默认是开放的,但是个别厂商自己...

  • Android 悬浮窗-开箱即用

    开箱即用的 Android 悬浮窗 开箱即用的 Android 悬浮窗 FloatWindowX 1. 需要权限 ...

网友评论

      本文标题:AppOpsManager源码探析及检测悬浮窗权限

      本文链接:https://www.haomeiwen.com/subject/zmemgttx.html