如何检查是否给予"android.permission.PACKAGE_USAGE_STATS"权限?
How to check if "android.permission.PACKAGE_USAGE_STATS" permission is given?
背景
我正在尝试获取应用程序启动的统计信息,在 Lollipop 上可以使用 UsageStatsManager class, as such (original post here):
清单:
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
打开 activity 让用户确认授予您此权限:
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")。到目前为止,我一直都在尝试 returns 它被拒绝了。
代码有效,但权限检查效果不佳。
我试过的
您可以使用以下任一方式检查是否授予了权限:
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;
它奏效了,但它仍然是一种解决方法。
问题
为什么我没有得到正确的权限检查结果?
应该怎么做才能更好地检查?
用户在系统设置中授予的特殊权限(使用统计访问权限、通知访问权限等)由 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
.
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;
根据我们的调查:如果模式是默认的(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);
}
除非您正在开发 系统 应用程序,否则 Chris Li's 中的额外检查是不必要的。
当应用首次安装且用户未触及该应用的使用访问权限时,则checkOpNoThrow()
returns 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);
}
但是,除非您的应用是系统应用,否则始终检查此权限 returns PERMISSION_DENIED
because it's a signature permission。因此,您可以将代码简化为:
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 中进行了测试和验证。
背景
我正在尝试获取应用程序启动的统计信息,在 Lollipop 上可以使用 UsageStatsManager class, as such (original post here):
清单:
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
打开 activity 让用户确认授予您此权限:
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")。到目前为止,我一直都在尝试 returns 它被拒绝了。
代码有效,但权限检查效果不佳。
我试过的
您可以使用以下任一方式检查是否授予了权限:
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;
它奏效了,但它仍然是一种解决方法。
问题
为什么我没有得到正确的权限检查结果?
应该怎么做才能更好地检查?
用户在系统设置中授予的特殊权限(使用统计访问权限、通知访问权限等)由 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
.
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;
根据我们的调查:如果模式是默认的(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);
}
除非您正在开发 系统 应用程序,否则 Chris Li's
当应用首次安装且用户未触及该应用的使用访问权限时,则checkOpNoThrow()
returns 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);
}
但是,除非您的应用是系统应用,否则始终检查此权限 returns PERMISSION_DENIED
because it's a signature permission。因此,您可以将代码简化为:
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 中进行了测试和验证。