permission - android get app usage statistics
¿Cómo verificar si se otorga el permiso "android.permission.PACKAGE_USAGE_STATS"? (4)
El segundo argumento de
checkOpNoThrow
es
uid
, no
pid
.
Cambiar el código para reflejar eso parece solucionar los problemas que otros tenían:
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;
Antecedentes
Estoy tratando de obtener estadísticas lanzadas por la aplicación, y en Lollipop es posible usando la clase UsageStatsManager , como tal (publicación original here ):
manifiesto:
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
abriendo la actividad que le permitirá al usuario confirmar que le da este permiso:
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
obteniendo las estadísticas, agregadas:
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);
El problema
Parece que no puedo verificar si el permiso que necesita ("android.permission.PACKAGE_USAGE_STATS") está otorgado. Todo lo que he intentado hasta ahora siempre devuelve que se niega.
El código funciona, pero la verificación de permisos no funciona bien.
Lo que he intentado
puede verificar si se otorga un permiso usando esto:
String permission = "android.permission.PACKAGE_USAGE_STATS";
boolean granted=getContext().checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
o esto:
String permission = "android.permission.PACKAGE_USAGE_STATS";
boolean granted=getPackageManager().checkPermission(permission,getPackageName())== PackageManager.PERMISSION_GRANTED;
Ambos siempre respondieron que se le había denegado (incluso cuando otorgué el permiso como usuario).
Mirando el código de UsageStatsManager, he intentado encontrar esta solución:
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;
Funcionó, pero sigue siendo una solución.
La pregunta
¿Cómo es que no obtengo el resultado correcto de la verificación de permisos?
¿Qué se debe hacer para verificarlo mejor?
La verificación adicional en la answer es innecesaria a menos que esté desarrollando una aplicación de sistema .
Cuando una aplicación se instala por primera vez y el usuario no ha tocado el permiso de acceso de uso de la aplicación,
checkOpNoThrow()
devuelve
MODE_DEFAULT
.
Chris dice que debemos verificar si la aplicación tiene el permiso de manifiesto
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);
}
Sin embargo, a menos que su aplicación sea una aplicación del sistema, la verificación de este permiso siempre devuelve
PERMISSION_DENIED
porque
es un permiso de firma
.
Entonces puede simplificar el código a esto:
if (mode == AppOpsManager.MODE_DEFAULT) {
granted = false;
} else {
granted = (mode == AppOpsManager.MODE_ALLOWED);
}
Que luego puede simplificar a esto:
granted = (mode == AppOpsManager.MODE_ALLOWED);
Lo que nos lleva de vuelta a la solución original y más simple:
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);
Probado y verificado en Android 5, 8.1 y 9.
Según nuestra investigación: si MODE es predeterminado (MODE_DEFAULT), se necesita una verificación de permiso adicional. Gracias al esfuerzo de examen de 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);
}
AppOpsManager , que se agregó en Android 4.4, maneja los permisos especiales que otorga el usuario en la configuración del sistema (acceso a estadísticas de uso, acceso a notificaciones, ...).
Tenga en cuenta que, además de que el usuario le concede acceso en la configuración del sistema, generalmente necesita un permiso en el manifiesto de Android (o algún componente), sin que su aplicación ni siquiera se muestre en la configuración del sistema.
Para las estadísticas de uso necesitas el permiso
android.permission.PACKAGE_USAGE_STATS
.
No hay mucha documentación al respecto, pero siempre puedes consultar las fuentes de Android.
La solución puede parecer un poco confusa porque algunas constantes se agregaron más tarde al
AppOpsManager
, y algunas constantes (por ejemplo, para verificar diferentes permisos) todavía están ocultas en las API privadas.
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;
Esto le indica si el permiso fue otorgado por el usuario.
Tenga en cuenta que desde el nivel 21 de API hay
AppOpsManager.OPSTR_GET_USAGE_STATS = "android:get_usage_stats"
.
Probé esta verificación en Lollipop (incluido 5.1.1) y funciona como se esperaba.
Me dice si el usuario dio el permiso explícito sin ningún bloqueo.
También hay un método
appOps.checkOp()
que podría
appOps.checkOp()
una
SecurityException
.