¿Cómo verificar si se otorga el permiso “android.permission.PACKAGE_USAGE_STATS”?

Fondo

Estoy tratando de obtener estadísticas de la aplicación, y en Lollipop es posible utilizando la clase UsageStatsManager , como tal (publicación original aquí ):

manifiesto:

 

abriendo la actividad que 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 queryUsageStats=usageStatsManager.queryAndAggregateUsageStats(fromTime,toTime); 

El problema

Parece que no puedo verificar si se concede el permiso que necesita (“android.permission.PACKAGE_USAGE_STATS”). Todo lo que he intentado hasta ahora siempre devuelve que está denegado.

El código funciona, pero la verificación de permisos no funciona bien.

Lo que he intentado

puede verificar si se concede un permiso utilizando 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 denegó (incluso cuando he otorgado el permiso como usuario).

Al mirar el código de UsageStatsManager, traté de encontrar esta solución alternativa:

  UsageStatsManager usm=(UsageStatsManager)getSystemService("usagestats"); Calendar calendar=Calendar.getInstance(); long toTime=calendar.getTimeInMillis(); calendar.add(Calendar.YEAR,-1); long fromTime=calendar.getTimeInMillis(); final List 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 permiso?

¿Qué se debe hacer para verificarlo mejor?

Por nuestra investigación: si MODE está 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); } 

Los permisos especiales otorgados por el usuario en la configuración del sistema (acceso a estadísticas de uso, acceso a notificaciones, …) son manejados por AppOpsManager , que se agregó en Android 4.4.

Tenga en cuenta que además del usuario que le otorga acceso en la configuración del sistema, normalmente 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 fonts de Android. La solución puede parecer un poco hacky porque algunas constantes se agregaron más tarde al AppOpsManager , y algunas constantes (por ejemplo, para verificar diferentes permisos) aún 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 dice si el permiso fue otorgado por el usuario. Tenga en cuenta que desde API nivel 21 hay constante AppOpsManager.OPSTR_GET_USAGE_STATS = "android:get_usage_stats" .

Probé este control en Lollipop (incluido el 5.1.1) y funciona como se esperaba. Me dice si el usuario dio el permiso explícito sin ningún locking. También hay el método appOps.checkOp() que podría lanzar una SecurityException .

El segundo argumento de checkOpNoThrow es uid , no pid .

Cambiar el código para reflejar eso parece solucionar los problemas que otros estaban teniendo:

 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;