Как проверить, предоставлено ли разрешение "android.permission.PACKAGE_USAGE_STATS"?
Фон
Я пытаюсь получить статистику запуска приложения, и в 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;
Это сработало, но это все еще обходной путь.
Вопрос
Почему я не получил правильный результат проверки разрешений?
Что нужно сделать, чтобы проверить это лучше?
3 ответа
По нашему исследованию: если 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);
}
Специальные разрешения, предоставляемые пользователем в системных настройках (доступ к статистике использования, доступ к уведомлениям и т. Д.), Обрабатываются 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;
Дополнительная проверка в ответе Chris Li не нужна, если вы не разрабатываете системное приложение.
Когда приложение впервые установлено, и пользователь не коснулся разрешения на доступ к использованию приложения, тогда checkOpNoThrow()
возвращается MODE_DEFAULT
,
Крис говорит, что нам нужно проверить, есть ли в приложении 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.