アプリ起動の統計を取得しようとしていますが、Lollipopでは UsageStatsManager クラスを使用することで可能です(元の投稿 here ):
マニフェスト:
<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;
うまくいきましたが、まだ回避策です。
許可チェックの正しい結果が得られないのはなぜですか?
それをより良くチェックするために何をすべきですか?
調査によると、MODEがデフォルト(MODE_DEFAULT)の場合、追加の許可チェックが必要です。 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 によって処理されます。これは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を含む)でこのチェックをテストしましたが、期待どおりに機能します。ユーザーがクラッシュせずに明示的な許可を与えたかどうかを教えてくれます。 SecurityException
をスローする可能性のあるメソッドappOps.checkOp()
もあります。
checkOpNoThrow
の2番目の引数は、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 の answer の追加チェックは、systemを開発しているのでなければ不要です。アプリ。
アプリが最初にインストールされ、ユーザーがアプリの使用アクセス許可に触れていない場合、 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.でテストおよび検証済み.