如何检查是否已授予“android.permission.PACKAGE_USAGE_STATS”权限?

46

背景

我正在尝试获取应用程序启动统计信息,在Lollipop上,可以使用UsageStatsManager类来实现,如下所示(原始帖子在这里):

清单:

<uses-permission
    android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions"/>

打开一个活动,让用户确认授予您此权限:

startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

获取聚合的统计数据:

 private static final String USAGE_STATS_SERVICE ="usagestats"; // Context.USAGE_STATS_SERVICE);
 ...
 final UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService(USAGE_STATS_SERVICE);
 final Map<String,UsageStats> queryUsageStats=usageStatsManager.queryAndAggregateUsageStats(fromTime,toTime);

问题

我似乎无法检查所需的权限("android.permission.PACKAGE_USAGE_STATS")是否被授予。到目前为止,我尝试过的所有方法都显示它被拒绝了。

代码可以运行,但是权限检查效果不好。

尝试过的方法

你可以使用以下任一方法来检查权限是否已被授予:

String permission = "android.permission.PACKAGE_USAGE_STATS";
boolean granted=getContext().checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;   

或者这样:

String permission = "android.permission.PACKAGE_USAGE_STATS";
boolean granted=getPackageManager().checkPermission(permission,getPackageName())== PackageManager.PERMISSION_GRANTED;   

即使我已经作为用户授予权限,两者始终都返回被拒绝的结果。

查看UsageStatsManager的代码后,我尝试想出以下解决方法:

      UsageStatsManager usm=(UsageStatsManager)getSystemService("usagestats");
      Calendar calendar=Calendar.getInstance();
      long toTime=calendar.getTimeInMillis();
      calendar.add(Calendar.YEAR,-1);
      long fromTime=calendar.getTimeInMillis();
      final List<UsageStats> queryUsageStats=usm.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,fromTime,toTime);
      boolean granted=queryUsageStats!=null&&queryUsageStats!=Collections.EMPTY_LIST;

它起作用了,但仍然是一种变通方法。

问题

为什么我没有得到正确的权限检查结果?

应该采取什么措施来更好地进行检查?


@Sam 这已经很老了,现在说这个有点晚了,但以防万一,因为我看到这里的答案更新了,我添加了我自己在你的链接上使用的答案:https://dev59.com/Sl4d5IYBdhLWcg3wE_GP#54839499 - android developer
请问您能帮忙解决以下问题吗?https://stackoverflow.com/questions/67985653/highlight-the-menu-item-found-in-search-result-of-android-settings-app - SVK
@SVK 这里与主题无关。 - android developer
4个回答

34

用户在系统设置中授予的特殊权限(例如使用情况统计访问、通知访问等)由AppOpsManager处理,该管理器从Android 4.4开始引入。

请注意,除了用户在系统设置中授予您访问权限之外,您还通常需要在Android清单文件(或某些组件)中声明权限,否则您的应用程序甚至不会显示在系统设置中。对于使用情况统计,您需要android.permission.PACKAGE_USAGE_STATS权限。

关于这一点几乎没有文档,但您可以随时查看Android源代码。解决方案可能看起来有点取巧,因为一些常量稍后添加到了AppOpsManager中,并且某些常量(例如用于检查不同权限的常量)仍然隐藏在私有API中。

AppOpsManager appOps = (AppOpsManager) context
        .getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow("android:get_usage_stats", 
        android.os.Process.myUid(), context.getPackageName());
boolean granted = mode == AppOpsManager.MODE_ALLOWED;

这将告诉您用户是否已授予权限。请注意,自API级别21以来,常量 AppOpsManager.OPSTR_GET_USAGE_STATS = "android:get_usage_stats"

我在Lollipop(包括5.1.1)上测试了此检查,它按预期工作。它会告诉我用户是否明确授予了权限,而没有任何崩溃。还有一个方法appOps.checkOp(),可能会抛出SecurityException异常。


它抛出了什么异常? - Tomik
1
如果这篇回答对您有帮助,请考虑接受它。我查看了 Android 的源代码以寻找解决方案。如果您查看 UsageStatsService,您会发现它是如何检查权限的。它首先使用 AppOpsManager 检查显式用户同意,然后回退到标准权限检查。需要知道的是,此检查仅检查用户授予的权限,而不检查清单权限。 - Tomik
我又在Android 5.1.1上测试了一遍代码,结果出现了SecurityException异常。为什么会这样呢? - android developer
你的回答给我造成了错误,希望能得到帮助:https://stackoverflow.com/questions/51343155/usage-stats-permission-throws-error - Ruchir Baronia
appOps.checkOpNoThrow已过时 - Mubashir Murtaza
显示剩余13条评论

23

根据我们的调查:如果MODE是默认值(MODE_DEFAULT),则需要进行额外的权限检查。感谢Weien的检查工作。

boolean granted = false;
AppOpsManager appOps = (AppOpsManager) context
        .getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, 
        android.os.Process.myUid(), context.getPackageName());

if (mode == AppOpsManager.MODE_DEFAULT) {
    granted = (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
} else {
    granted = (mode == AppOpsManager.MODE_ALLOWED);
}

1
但是当我尝试使用checkCallingOrSelfPermission时,它总是写着被拒绝了,这就是为什么我在这个问题线程中问的原因(请参见“我尝试过的”部分)。你能告诉我该怎么做才能让它以不同的方式工作吗(步骤)? - android developer
应用程序在 checkOpNoThrow 上崩溃,因此我想问一下权限 "android.permission.PACKAGE_USAGE_STATS" 在 Android 5.0 以下是否有效。 - aj0822ArpitJoshi
OPSTR_GET_USAGE_STATS - 自API级别21(棒棒糖)起添加。 - Chris Li
1
我不相信当MODE_DEFAULT和权限被授予时,checkCallingOrSelfPermission会返回true。你能确认你是否真的见过这样的情况吗? - Pavel Luzhetskiy
1
这个答案中的额外检查只有在您开发系统应用程序时才需要。请参见我的解释 - Sam
显示剩余10条评论

4
< p > < code >checkOpNoThrow的第二个参数是uid,而不是pid

更改代码以反映这一点似乎可以解决其他人遇到的问题:

AppOpsManager appOps = (AppOpsManager) context
    .getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow("android:get_usage_stats", 
    android.os.Process.myUid(), context.getPackageName());
boolean granted = mode == AppOpsManager.MODE_ALLOWED;

1
我还没有足够的声望来写评论。请随意添加评论,我会删除我的回答。 - kingjason611
多么奇怪啊。我非常确定以前试过,而且没有成功。我甚至在为此制作的POC应用程序中有完全相同的代码,并向Google报告了此问题。我不知道发生了什么。也许我的ROM有问题? - android developer
你的回答给我造成了错误,希望能得到帮助:https://stackoverflow.com/questions/51343155/usage-stats-permission-throws-error - Ruchir Baronia
请问您能帮忙解决以下问题吗?https://stackoverflow.com/questions/67985653/highlight-the-menu-item-found-in-search-result-of-android-settings-app - SVK

3
Chris Li答案中的额外检查是不必要的,除非您正在开发一个系统应用程序。
当一个应用程序首次安装并且用户尚未触摸应用程序的使用访问权限时,checkOpNoThrow()会返回MODE_DEFAULT
Chris说我们需要检查应用程序是否具有PACKAGE_USAGE_STATS清单权限:
if (mode == AppOpsManager.MODE_DEFAULT) {
    granted = (context.checkCallingOrSelfPermission(Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
} else {
    granted = (mode == AppOpsManager.MODE_ALLOWED);
}

然而,除非您的应用程序是系统应用程序,否则始终会返回{{link1:PERMISSION_DENIED}}来检查此权限,因为它是一个签名权限。 因此,您可以将代码简化为以下内容:
if (mode == AppOpsManager.MODE_DEFAULT) {
    granted = false;
} else {
    granted = (mode == AppOpsManager.MODE_ALLOWED);
}

然后您可以将其简化为这个:
granted = (mode == AppOpsManager.MODE_ALLOWED);

这让我们回到最初、更简单的解决方案:
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, Process.myUid(), context.getPackageName());
boolean granted = (mode == AppOpsManager.MODE_ALLOWED);

在Android 5、8.1和9中进行了测试和验证。

由于这个答案并不涵盖所有情况,正如你所写的(不适用于系统应用程序),我认为我的答案更好:https://dev59.com/Sl4d5IYBdhLWcg3wE_GP#54839499。它将涵盖所有情况,即使用户已将应用程序转换为系统应用程序,它也可以工作。我不认为有任何意义编写一个新答案,它与现有答案相同,并且说它更好只是因为它更短,同时说它只能在某些情况下(尽管大多数情况下)起作用... - android developer
1
感谢提及我的答案。事实上,我在一个预安装的应用程序中尝试了它。 - Chris Li
请问您能帮忙解决以下问题吗?https://stackoverflow.com/questions/67985653/highlight-the-menu-item-found-in-search-result-of-android-settings-app - SVK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接