45

背景

我正在尝试获取应用程序启动的统计信息,并且在 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;

它有效,但它仍然是一种解决方法。

问题

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

应该怎么做才能更好地检查它?

4

4 回答 4

31

用户在系统设置中授予的特殊权限(使用统计访问、通知访问等)由Android 4.4 中添加的AppOpsManager处理。

请注意,除了用户在系统设置中授予您访问权限外,您通常还需要 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.

于 2015-03-08T00:04:28.140 回答
20

根据我们的调查:如果 MODE 是默认的 (MODE_DEFAULT),则需要额外的权限检查。感谢维恩的考试努力。

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);
}
于 2017-02-22T11:51:51.203 回答
4

的第二个参数checkOpNoThrowuid,不是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;
于 2015-08-18T14:03:51.937 回答
1

除非您正在开发系统应用程序,否则不需要额外检查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);
}

但是,除非您的应用程序是系统应用程序,否则检查此权限始终会返回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 中测试和验证。

于 2019-02-24T00:48:32.913 回答