Check if my application has usage access enabled

24.2k views Asked by At

I'm using the new UsageStatsManager API to get current foreground application in Android 5.0 Lollipop. In order to use this API, the user must enable the application in the Settings->Security->Apps with usage access screen.

I send the user directly to this screen with this Intent:


startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

Now, I want to validate the user enabled my application. I wanted to do so like I validate the user enabled my application to use the NotificationListenerService but I have no idea what is the String key, if it even exists.


Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
// Tried Settings.ACTION_USAGE_ACCESS_SETTINGS as key but it returns null

Second approach was to query the usage stats and check if it returns results (it returns an empty array when the app is not enabled) and it works most of the times but sometimes it returns 0 results even when my app is enabled.


UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService("usagestats");
long time = System.currentTimeMillis();
List stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);

if (stats == null || stats.isEmpty()) {
    // Usage access is not enabled
}

Is there a way to check if my application has usage access enabled?

10

There are 10 answers

3
Lior Iluz On BEST ANSWER

Received a great answer by someone on Twitter, tested working:

try {
   PackageManager packageManager = context.getPackageManager();
   ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
   AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
   int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
   return (mode == AppOpsManager.MODE_ALLOWED);

} catch (PackageManager.NameNotFoundException e) {
   return false;
}
0
gphx On

If they are using an Amazon Fire tablet (and possibly other Fire OS devices) the user can download the application from a user installed Google Play Store then not have the option you want activated available in their OS. I know this because as a Fire OS user this happened to me a few minutes ago. Detecting whether a user has Fire OS and, if so, offering an option which actually exists would be fantastic for both user and dev.

0
Harsh On

try this ,

public boolean check_UsgAccs(){
    long tme = System.currentTimeMillis();
    UsageStatsManager usm = (UsageStatsManager)getApplicationContext().getSystemService(Context.USAGE_STATS_SERVICE);
    List<UsageStats> al= usm.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, tme - (1000 * 1000), tme);
        return  al.size()>0;

    }
2
Tyler On

This works down to KitKat (API 19)

    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;
0
Bao Le On

This is an alternative solutions:

AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
                    android.os.Process.myUid(), context.getPackageName());
return mode == AppOpsManager.MODE_ALLOWED;
2
Sam On

Detecting when the usage access changes

Use this class to be notified when your app is granted or revoked usage access.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class UsagePermissionMonitor {

    private final Context context;
    private final AppOpsManager appOpsManager;
    private final Handler handler;
    private boolean isListening;
    private Boolean lastValue;

    public UsagePermissionMonitor(Context context) {
        this.context = context;
        appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        handler = new Handler();
    }

    public void startListening() {
        appOpsManager.startWatchingMode(AppOpsManager.OPSTR_GET_USAGE_STATS, context.getPackageName(), usageOpListener);
        isListening = true;
    }

    public void stopListening() {
        lastValue = null;
        isListening = false;
        appOpsManager.stopWatchingMode(usageOpListener);
        handler.removeCallbacks(checkUsagePermission);
    }

    private final AppOpsManager.OnOpChangedListener usageOpListener = new AppOpsManager.OnOpChangedListener() {
        @Override
        public void onOpChanged(String op, String packageName) {
            // Android sometimes sets packageName to null
            if (packageName == null || context.getPackageName().equals(packageName)) {
                // Android actually notifies us of changes to ops other than the one we registered for, so filtering them out
                if (AppOpsManager.OPSTR_GET_USAGE_STATS.equals(op)) {
                    // We're not in main thread, so post to main thread queue
                    handler.post(checkUsagePermission);
                }
            }
        }
    };

    private final Runnable checkUsagePermission = new Runnable() {
        @Override
        public void run() {
            if (isListening) {
                int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, Process.myUid(), context.getPackageName());
                boolean enabled = mode == AppOpsManager.MODE_ALLOWED;

                // Each change to the permission results in two callbacks instead of one.
                // Filtering out the duplicates.
                if (lastValue == null || lastValue != enabled) {
                    lastValue = enabled;

                    // TODO: Do something with the result
                    Log.i(UsagePermissionMonitor.class.getSimpleName(), "Usage permission changed: " + enabled);
                }
            }
        }
    };

}

Credits

Based on code from epicality in another answer.

2
android developer On

Here's my all-around solution for this (based on similar question and answer here) :

public static PermissionStatus getUsageStatsPermissionsStatus(Context context) {
    if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
        return PermissionStatus.CANNOT_BE_GRANTED;
    AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    final int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName());
    boolean granted = mode == AppOpsManager.MODE_DEFAULT ?
            (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED)
            : (mode == AppOpsManager.MODE_ALLOWED);
    return granted ? PermissionStatus.GRANTED : PermissionStatus.DENIED;
}

public enum PermissionStatus {
    GRANTED, DENIED, CANNOT_BE_GRANTED
}
0
Markus On

I previously used the same code as Bao Le, but I've run into the problem that certain devices (e.g. VF-895N) report usage stats as enabled even when they're not. As a workaround I've modified my code like this:

public static boolean hasPermission(@NonNull final Context context) {
    // Usage Stats is theoretically available on API v19+, but official/reliable support starts with API v21.
    if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
        return false;
    }

    final AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);

    if (appOpsManager == null) {
        return false;
    }

    final int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName());
    if (mode != AppOpsManager.MODE_ALLOWED) {
        return false;
    }

    // Verify that access is possible. Some devices "lie" and return MODE_ALLOWED even when it's not.
    final long now = System.currentTimeMillis();
    final UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
    final List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, now - 1000 * 10, now);
    return (stats != null && !stats.isEmpty());
}

Successfully tested on multiple devices.

0
Sagar gujarati On

None of the answer worked for me so i made this

public boolean permissiontodetectapp(Context context) {
    try {
        ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
        return ((AppOpsManager) context.getSystemService(APP_OPS_SERVICE)).checkOpNoThrow("android:get_usage_stats", applicationInfo.uid, applicationInfo.packageName) != 0;
    } catch (PackageManager.NameNotFoundException unused) {
        return true;
    }
}
0
Manish Godhani On

this code working in lollipop and marshmallow i used this code in my app

if (Build.VERSION.SDK_INT >= 21) {
            UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
            long time = System.currentTimeMillis();
            List stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);

            if (stats == null || stats.isEmpty()) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_USAGE_ACCESS_SETTINGS);
                context.startActivity(intent);
            }
    }