首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么安卓8中的Settings.canDrawOverlays()方法在用户授予绘制覆盖并返回到应用程序的权限时返回"false“?

为什么安卓8中的Settings.canDrawOverlays()方法在用户授予绘制覆盖并返回到应用程序的权限时返回"false“?
EN

Stack Overflow用户
提问于 2017-09-12 10:00:46
回答 8查看 16.4K关注 0票数 29

我正在为家长开发MDM应用程序来控制孩子的设备,如果执行了禁止的操作,它将使用权限SYSTEM_ALERT_WINDOW在设备上显示警告。在安装过程中,在使用SDK 23+ (Android6.0)的设备上,应用程序使用以下方法检查权限:

代码语言:javascript
运行
复制
Settings.canDrawOverlays(getApplicationContext()) 

如果此方法返回false,则应用程序将打开系统对话框,用户可以在该对话框中授予权限:

代码语言:javascript
运行
复制
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);

但是在使用SDK 26 (Android8.0)的设备上,当用户成功地授予许可并按下“后退”按钮返回到应用程序时,方法canDrawOverlays()仍然返回false,直到用户不关闭该应用程序并再次启动它,或者只是在最近的“应用程序”对话框中选择它。我在Android中用Android 8在最新版本的虚拟设备上测试了它,因为我没有真正的设备。

我做了一些研究,另外还检查了AppOpsManager的权限:

代码语言:javascript
运行
复制
AppOpsManager appOpsMgr = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), getPackageName());
Log.d(TAG, "android:system_alert_window: mode=" + mode);

因此:

  • 当应用程序没有此权限时,用户的模式为"2“(MODE_ERRORED) (canDrawOverlays()返回false)
  • 授予权限并返回到应用程序时,模式为"1“(MODE_IGNORED) (canDrawOverlays()返回false)
  • 如果现在重新启动应用程序,模式为"0“(MODE_ALLOWED) (canDrawOverlays()返回true)

拜托,有人能跟我解释一下这种行为吗?我可以依赖mode == 1的操作"android:system_alert_window"并假设用户已经授予了权限吗?

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2017-09-19 13:36:09

我也发现了checkOp的这个问题。在我的例子中,我有一个流,它只允许在未设置权限时重定向到设置。并且,只有在重定向到设置时才会设置AppOps。

假设AppOps回调只在更改时调用,并且只有一个开关可以更改。这意味着,如果调用回调,则用户必须授予该权限。

代码语言:javascript
运行
复制
if (VERSION.SDK_INT >= VERSION_CODES.O &&
                (AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW.equals(op) && 
                     packageName.equals(mContext.getPackageName()))) {
    // proceed to back to your app
}

在应用程序恢复后,使用canDrawOverlays()启动的检查对我来说是有效的。当然,我重新启动应用程序,并检查是否通过标准方式授予许可。

这绝对不是一个完美的解决方案,但它应该是可行的,直到我们从谷歌了解更多这方面的信息。

编辑:我问google:https://issuetracker.google.com/issues/66072795

编辑2:谷歌修复了这个问题。但安卓O版本似乎仍将受到影响。

票数 7
EN

Stack Overflow用户

发布于 2017-09-12 11:09:09

我也遇到了同样的问题。我使用了一个解决方法,试图添加一个不可见的覆盖。如果抛出异常,则不授予权限。这可能不是最好的解决办法,但有效。我不能告诉您任何关于AppOps解决方案的信息,但是它看起来是可靠的。

编辑2020年10月:正如注释中提到的那样,当抛出WindowManager时,可能会在SecurityException中出现内存泄漏,导致解决方案视图不会被删除(即使是对removeView的显式调用)。对于正常使用来说,这不是什么大问题,但是要避免在同一个应用程序会话中运行太多的检查(如果不对其进行测试,我认为任何低于100的都可以)。

代码语言:javascript
运行
复制
/**
 * Workaround for Android O
 */
public static boolean canDrawOverlays(Context context) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;
    else if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
        return Settings.canDrawOverlays(context);
    } else {
        if (Settings.canDrawOverlays(context)) return true;
        try {
            WindowManager mgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            if (mgr == null) return false; //getSystemService might return null
            View viewToAdd = new View(context);
            WindowManager.LayoutParams params = new WindowManager.LayoutParams(0, 0, android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
            viewToAdd.setLayoutParams(params);
            mgr.addView(viewToAdd, params);
            mgr.removeView(viewToAdd);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}
票数 21
EN

Stack Overflow用户

发布于 2018-07-09 16:02:32

这里是我的全部解决方案,这是一个其他的组合,但在大多数情况下服务良好。

首先,根据Android使用标准检查

第二个检查是使用AppOpsManager

第三,也是最后一次检查,如果其他所有的失败都是试图显示一个覆盖,如果失败,它肯定不会工作;)

代码语言:javascript
运行
复制
static boolean canDrawOverlays(Context context) {

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M && Settings.canDrawOverlays(context)) return true;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {//USING APP OPS MANAGER
        AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        if (manager != null) {
            try {
                int result = manager.checkOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, Binder.getCallingUid(), context.getPackageName());
                return result == AppOpsManager.MODE_ALLOWED;
            } catch (Exception ignore) {
            }
        }
    }

    try {//IF This Fails, we definitely can't do it
        WindowManager mgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        if (mgr == null) return false; //getSystemService might return null
        View viewToAdd = new View(context);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(0, 0, android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
        viewToAdd.setLayoutParams(params);
        mgr.addView(viewToAdd, params);
        mgr.removeView(viewToAdd);
        return true;
    } catch (Exception ignore) {
    }
    return false;

}
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46173460

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档