63

Background

Google has deprecated the function "getRecentTasks" of "ActivityManager" class. Now all it does is to get the list of apps that the current app has opened.

I've even written a post about it here on StackOverflow, but I noticed it's impossible.

The problem

I've made a post about it (here, and another, similar one created by someone else, here) and requested to re-consider it, and Google decided to make a new class, that seem to provide a similar functionality (more like statistics, but might also be useful), but I can't find out how to use it.

The class is called "UsageStatsManager", and my guess is that the function "queryUsageStats" does the job.

Also, it seems it has a new permission ("android.permission.PACKAGE_USAGE_STATS"), which is a system permission, but it's written that:

declaring the permission implies intention to use the API and the user of the device can grant permission through the Settings application.

Here's another link about this new functionality.

What I've found

I've looked at the code of Android, and noticed that "Context" has USAGE_STATS_SERVICE , which in the JavaDocs say the next thing:

/**
 * Use with {@link #getSystemService} to retrieve a {@link
 * android.app.UsageStatsManager} for interacting with the status bar.
 *
 * @see #getSystemService
 * @see android.app.UsageStatsManager
 * @hide
 */
public static final String USAGE_STATS_SERVICE = "usagestats";

The weird thing is that not only it says "status bar", but also the packageName doesn't match (should be "android.app.usage.UsageStatsManager" instead) .

I've also added the correct permission:

<uses-permission
    android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions" />

and here's the code I use:

  final UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService("usagestats");// Context.USAGE_STATS_SERVICE);
  final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
  final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);

In the emulator itself, I went to "Settings"->"security"->"apps with usage access" , and enabled my app.

However, when running the code, all I get is an empty list...

The question

How do you use UsageStatsManager ?

Also, how do you let the user to grant the permission in the easiest way possible? Or is it automatically done, as soon as the app tries to get the needed information?

What happens when trying to use this class yet the user hasn't confirmed it yet?

How can I make the code return me a real list of apps?

Prags
  • 2,457
  • 2
  • 21
  • 38
android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • "how do you let the user to grant the permission in the easiest way possible?" -- I see "Apps with usage access" in the Settings > Security screen. Presumably, apps requesting this permission show up there. IMHO, they should have used ``. – CommonsWare Oct 17 '14 at 19:10
  • @CommonsWare Yes, I've noticed that, but I ask this as a developer. What should the developer do with it? I don't even know how to initialize the whole thing. It's also a bit weird that it's a system permission and yet it's possible to use it... Is there such a thing on a different case? Maybe it would be a similar handling... – android developer Oct 17 '14 at 19:14
  • "What should the developer do with it?" -- tell the user to go visit that screen, I would imagine. "Is there such a thing on a different case?" -- not that I am aware of. – CommonsWare Oct 17 '14 at 19:16
  • @CommonsWare I mean, how does it work? How can I even initialize or reach this class? It doesn't have a CTOR, and it's not available via Context.getSystemService (not in the docs and in the SDK, at least) ... Also, what happens when I try to call this function? will it go to the needed settings screen, or should I do it myself? I need to wish how to work with this new class... – android developer Oct 17 '14 at 19:19
  • My guess is that it is supposed to be via `getSystemService()` and that there is a documentation bug. As for the rest, I have no idea. The source code for it does not seem to have been released yet. – CommonsWare Oct 17 '14 at 19:22
  • @CommonsWare Maybe I should post about it back to Google... BTW, here's another clue: https://developer.android.com/about/versions/android-5.0.html#System – android developer Oct 17 '14 at 19:25
  • ok, I've posted about it here: https://code.google.com/p/android/issues/detail?id=77648 . – android developer Oct 17 '14 at 19:37
  • @CommonsWare I've found something interesting. I've looked at the code of Android, and noticed that "Context" has USAGE_STATS_SERVICE , which in the JavaDocs say "Use with getSystemService to retrieve a android.app.UsageStatsManager for interacting with the status bar." . The weird thing is that not only it says "status bar", but also the packageName doesn't match (should be "android.app.usage.UsageStatsManager" instead) . – android developer Oct 18 '14 at 22:51
  • @CommonsWare Updated the question again, as I've found some more clues. – android developer Oct 19 '14 at 17:39
  • The data is located here `/data/system/usagestats/{user_id_}`, where `{user_id}` is id of user account on the device. – TheRealChx101 Oct 30 '16 at 20:58
  • 1
    found this [repo](https://github.com/TheBotBox/AppsUsageMonitorAPI) doing exactly what you have asked. – bhanu kaushik May 14 '19 at 10:41

7 Answers7

36

I think the documentation was just short hand for the Calendar stuff. I don't think it actually works with just 2014; however I can be wrong.

In order to access the actually list of UsageStats, you would need to create a Calendar object with the correct month,day, and year. Exactly how MRK said in the other answer. I copied and corrected the errors in MRK's code so anyone who sees it in the future can see it.

Calendar beginCal = Calendar.getInstance();
beginCal.set(Calendar.DATE, 1);
beginCal.set(Calendar.MONTH, 0);
beginCal.set(Calendar.YEAR, 2012);

Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.DATE, 1);
endCal.set(Calendar.MONTH, 0);
endCal.set(Calendar.YEAR, 2014);

final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginCal.getTimeInMillis(), endCal.getTimeInMillis());

-Credit MRK; corrected by me (he accidentally just put cal instead of beginCal and endCal)

The code for the usage access settings is below. :)

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
Atish
  • 1,239
  • 1
  • 12
  • 10
  • How did I miss that? Maybe they've added it after I looked at it. Anyway, is there a way to reach to exactly the app to enable/disable? so that the user will just have to confirm? – android developer Oct 29 '14 at 12:53
  • I don't think its possible. If you look at the Input under [ACTION_USAGE_ACCESS_SETTINGS](http://developer.android.com/reference/android/provider/Settings.html#ACTION_USAGE_ACCESS_SETTINGS) , you'll notice it says nothing. Therefore, I believe there is no way to specify a certain app. – Atish Oct 29 '14 at 21:20
  • Too bad. Now I don't know what to do with the answer to my question, as both you and MRK answered different parts of it. Only thing that's missing for me is an explanation of how to work with it, and what's the "interval" exactly. I also wonder if it will work on normal devices and not just the emulator, as it was seem as if it's a system-permission. – android developer Oct 29 '14 at 21:49
  • It'll work on devices. You can just check the amount of time a package has been active to decide if an activity is being used or i think you can use queryEvents() to see if an app was moved to the foreground or background. Honestly, its really going to discourage users to use your app. Going and giving special permission makes users feel scared/weird if its the first time they are using your app. – Atish Oct 30 '14 at 01:57
  • I fixed MRK's code and put it into one to make it easier for future reference. He had forgotten to use beginCal and endCal instead of just cal. – Atish Oct 30 '14 at 02:02
  • I don't intend on forcing users to give this permission, but I will give a warning dialog about it. The app's main purpose is far from it, so it should be fine: https://play.google.com/store/apps/details?id=com.lb.app_manager . Anyway, thank you. – android developer Oct 30 '14 at 19:54
29

I've created a sample of how to use UsageStats on my Github. Hopefully it can be of help to someone

https://github.com/ColeMurray/UsageStatsSample

Cole Murray
  • 546
  • 6
  • 12
  • Thank you . you get +1 for the effort. :) – android developer Mar 02 '15 at 22:27
  • 1
    the code is a bit wrong about assuming when you get the required permission, as it assumes that if the result is empty, you don't have permission. I've tried using those solutions: http://stackoverflow.com/q/7203668/878126 , but all of them returned the same result (denied), no matter what happens. I've written a post about it here: http://stackoverflow.com/q/28921136/878126 – android developer Mar 07 '15 at 23:10
  • Could this be due to it being a hidden permission?^ – Cole Murray Mar 14 '15 at 01:02
9

Answering your last question "How can I make the code return me a real list of apps?". queryUsageStats takes begin time and end time in milliseconds, not the value of the year in int.

Calendar beginCal = Calendar.getInstance();
beginCal.set(Calendar.DATE, 1);
beginCal.set(Calendar.MONTH, 0);
beginCal.set(Calendar.YEAR, 2012);

Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.DATE, 1);
endCal.set(Calendar.MONTH, 0);
endCal.set(Calendar.YEAR, 2014);

final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginCal.getTimeInMillis(), endCal.getTimeInMillis());

This should return a list of UsageStats for the years 2012 and 2013 (keep in mind the end time is exclusive of the end result time range).

Usman Rana
  • 2,067
  • 1
  • 21
  • 32
MRK
  • 721
  • 1
  • 8
  • 15
  • That's weird, as the documentation says : beginTime = 2013 endTime = 2015 . Seems to work now. Why do they need the interval? I give them the range anyway... Also, do you know perhaps how to start the intent of asking the user for this permission ? It seems it's a permission that's manually enabled by the user. – android developer Oct 28 '14 at 19:24
  • 1
    Yes, the documentation is not clear at all. It's not very clear why they need an interval along with the start and end time, but from what I can deduce the UsageStatsManger stores data in daily, weekly, monthly and yearly buckets which have a start and end time of their own. Eg, the daily bucket starts at 5:30 AM. So if you provide a daily interval along with the start time of 10/30/2014 00:00 and end time of 10/30/2014 23:59, queryUsageStats will return data for 10/29 and 10/30. I may be wrong but this is what I understood testing on an emulator. – MRK Oct 29 '14 at 19:32
  • I didn't understand your understanding . :( – android developer Oct 29 '14 at 20:09
9

There is actually an example app included in AOSP sample code: developers/samples/android/system/AppUsageStatistics/

It includes all the bits necessary to use UsageStats in an app:

  1. Declaring the permission in AndroidManifest.xml

    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>

  2. Show settings to grant permission to access UsageStatsManager

    startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

  3. Query UsageStatsManager for statistics.

    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.YEAR, -1);
    List<UsageStats> queryUsageStats = mUsageStatsManager
            .queryUsageStats(intervalType, cal.getTimeInMillis(),
                    System.currentTimeMillis());
    
  4. Creating a list of apps based on UsageStats

To summarize and apply to your example:

  • You seem to correctly ask for and grant permissions to access usage stats.
  • You correctly get the UsageStats system service.
  • However, the time period you query for is way too short: Arguments beginTime and endTime are measured in milliseconds since the epoch. Calendar instances can give you this value with getTimeinMillis(). What you erroneously do is to only give the year numbers (2015 and2017 if you would run the program today). These values are interpreted as milliseconds since the epoch and thus the interval is only 2 milliseconds long and is some time in 1970.

Instead of the following code snippet that you posted, you should copy the example I posted below:

Wrong:

final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);

Correct:

final long currentTime = System.currentTimeMillis(); // Get current time in milliseconds

final Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -2); // Set year to beginning of desired period.
final long beginTime = cal.getTimeInMillis(); // Get begin time in milliseconds

final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginTime, currentTime);
justfortherec
  • 1,590
  • 1
  • 13
  • 17
  • What is exactly the "intervalType" , if you've already set the time to be a year before the current one? – android developer Mar 02 '17 at 18:35
  • @android-developer: `intervalType` is the granularity statistics get aggregated for. [Official documentation](https://developer.android.com/reference/android/app/usage/UsageStatsManager.html#queryUsageStats(int,%20long,%20long)) has more details. There is also a convenience method [`queryAndAggregateUsageStats(long, long)`](https://developer.android.com/reference/android/app/usage/UsageStatsManager.html#queryAndAggregateUsageStats(long,%20long)) that tries to automatically find the best interval type. – justfortherec Mar 03 '17 at 08:20
  • I don't understand. What is there to aggregate here? Doesn't it just show usage statistics? I've looked in UsageStats docs (here: https://developer.android.com/reference/android/app/usage/UsageStats.html ) and it seems quite simple to me... What would it mean, for example, if I chose a month to aggregate? Would "getLastTimeUsed" return a time only in the range of the last month, and for the second one of the month before it? – android developer Mar 03 '17 at 15:05
  • @android-developer, better look at`queryUsageStats()` in [UsageStatsManager](https://developer.android.com/reference/android/app/usage/UsageStatsManager.html#queryUsageStats(int,%20long,%20long)) which you need to query for stats. There you see that `UsageStats` are returned for each package in each of the intervals. Say you query for a year and want monthly intervals. That means the list could contain 12 `UsageStats` elements for the very same package (if the app is used in each of the 12 months in the interval. – justfortherec Mar 04 '17 at 21:06
  • So it is as I wrote? – android developer Mar 05 '17 at 08:58
  • @android-developer, no, it isn't. It depends on the values that you pass for `beginTime` and `endTime` as well as the instance of `UsageStats` that you look at. Please post a new question if you need more clarification of the documentation of [`UsageStatsManager.queryUsageStats()`](https://developer.android.com/reference/android/app/usage/UsageStatsManager.html#queryUsageStats(int,%20long,%20long)). These comments are the wrong place to do that. I'd be happy to elaborate in the larger space of proper answer. – justfortherec Mar 05 '17 at 13:29
  • But that's the question I've posted "How to use UsageStatsManager" . – android developer Mar 05 '17 at 20:57
  • @android-developer, I have added some more information regarding your code example. It seems you pass the wrong values for `beginTime` and `endTime`. Hopefully, with the new information you can solve your problem. Let me know if anything stays unclear. – justfortherec Mar 06 '17 at 08:59
  • One other thing: As mentioned already: using `queryAndAggregateUsageStats(long, long)` might actually be the better solution for you because it gives you results for the entire two years for each package. – justfortherec Mar 06 '17 at 11:22
  • I need the stats of, say, the whole recent year. I want to sort the apps by recent-time-of-launch. For example, if I launched app "A" 3 hours ago, and then app "B" 2 hours ago, and then again "A" 1 hour ago, it means "A will be most recently launched app, and then "B". – android developer Mar 06 '17 at 15:49
  • That is exactly what the [sample app that I linked](https://android.googlesource.com/platform/developers/samples/android/+/master/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java) does. Does [this screenshot](http://imgur.com/a/xTvUt) look promising to you? Please have a look at the source of the example app. – justfortherec Mar 06 '17 at 16:06
  • I don't understand how to import this sample app. The screenshot shows something that I don't need (time span - just one day?) and something that I do (recently launched time). In any case, will the code you've provided here suffice for this? This is the code I use today: https://postimg.org/image/fu3bcw1lb/ – android developer Mar 06 '17 at 21:52
  • @android-developer, the screenshot shows "daily" in a dropdown menu which lists different time intervals. This is apparent from the example code I linked in my answer. Please have a look at the example, it should be useful even if you cannot get it to compile. I do not believe that comments are the right place for a code review. That said, the code example you linked looks like it should work. If you cannot get it to work, please open a new question with your (new) specific problems. Happy coding! – justfortherec Mar 07 '17 at 15:48
  • I don't know your exact specifications and it is hard to judge in isolation, but the snippet seems to do what you described above. – justfortherec Mar 08 '17 at 09:49
  • It's just what I wrote recently. I want to get apps by recently launched time, over a specific duration – android developer Mar 08 '17 at 12:02
  • As mentioned before: If anything is still unclear, please post a new question or modify your question with the code you currently use. – justfortherec Mar 09 '17 at 10:51
  • I didn't understand well? – android developer Mar 09 '17 at 12:59
  • My point is that obviously the comments are not the right place to continue this conversation, since we have failed to solve your issue for many comments. Creating a new question is probably best because the question you have posed initially seems to be solved and you seem to be stuck at a new problem. – justfortherec Mar 09 '17 at 13:23
  • But it's the same problem. I want to know get the stats of apps (mainly the recently launched time of apps), and I'm not sure what's the purpose of the aggregation . – android developer Mar 10 '17 at 10:13
  • Aggregation gets rid of duplicate list entries for packages by combining information from all entries with the same package name into one entry of the `Map`. How do you not understand how to get recently launched time? You do exactly that in the code snippet you posted a screenshot of. [Here is an example](https://android.googlesource.com/platform/developers/samples/android/+/master/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/UsageListAdapter.java#82) how to get return value of `getLastTimeUsed()` and format it in a human readable way. – justfortherec Mar 13 '17 at 11:06
  • Can't I just aggregate to the whole range I provide? – android developer Mar 13 '17 at 13:40
  • That's exactly what `queryAndAggregateUsageStats()` does ... and you use it already. I will stop responding to comments in this thread unless something new comes up. I believe that your question will be resolved once you read and really tried to understand [the example](https://android.googlesource.com/platform/developers/samples/android/+/master/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics) and [documentation of `UsageStatsManager`](https://developer.android.com/reference/android/app/usage/UsageStatsManager.html). – justfortherec Mar 13 '17 at 14:14
7

Use UsageStats will get the wrong information when user opens the notification drawer or on a locked screen. You have to use UsageStatsManager.queryEvents() and look for the latest event with MOVE_TO_FOREGROUND event type.

Sam Lu
  • 3,448
  • 1
  • 27
  • 39
  • Why is it wrong? And can you please show code of how it should be, to demonstrate how it's better? – android developer Dec 31 '17 at 11:53
  • I just tested out the problem on a Samsung Galaxy S5 6.0.1 and Motorola Moto G5 Plus 8.1.0. When the screen was locked, the latest `UsageStats` had a package of `android` rather than the last-used app. Is that the problem you're referring to? – Sam Feb 23 '19 at 06:17
1

If you want to see usage statistics of a specific time period, you have to first calculate the length of time in milliseconds of the start and end of your time period since the epoch. (epoch is the number of seconds that have elapsed since 00:00:00 UTC, Thursday 1, 1970.) Or, as I will show in the following sample code, an easy way is to calculate backwards from the current time in milliseconds.

For example, if you want a usage statistic of the past 4 days, you can use the following code:

UsageStatsManager mUsageStatsManager = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);    
List<UsageStats> queryUsageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
                    (System.currentTimeMillis() - 345600000), System.currentTimeMillis());

The number 345600000 is the number of milliseconds in 4 days.

And for the INTERVAL_TYPE this article explains it well.

The system collects and aggregates the data over 4 different intervals and they are: INTERVAL_DAILY, INTERVAL_WEEKLY, INTERVAL_MONTHLY and INTERVAL_YEARLY. The system records are limited in time, so you’ll be able to retrieve app usage data for up to 7 days for interval daily, up to 4 weeks for interval weekly, up to 6 months for monthly and finally up to 2 years for yearly.

There’s a fifth option to mention: INTERVAL_BEST will choose the best fitting interval between the four above based on the timespan you’ve chosen.

Community
  • 1
  • 1
Msgun
  • 156
  • 8
  • But you used INTERVAL_TYPE – android developer Aug 19 '18 at 23:18
  • So the max we can use is of 6 months? I don't get what's the point of the interval type, if we already give it an interval in exact time... – android developer Aug 20 '18 at 14:35
  • The INTERVAL_TYPE is to help you see stats for your interval of selection. For the above snippet that I used, the stat will show me daily app usage of packages in the last 4 days. If you just want to see usage stats of the interval you specified (the begin and end time) please look into queryAndAggregateUsageStats(long, long). Otherwise, the INTERVAL_TYPE will produce usage stat in one of the 4 interval types (daily, weekly, monthly or yearly). – Msgun Aug 20 '18 at 19:07
  • Again, I don't get why they had to add to add the extra parameter. why couldn't they just have a single parameter for time, which tells in which period of time to fetch the data from, and that's it. And if there are some restrictions, tell about them... – android developer Aug 21 '18 at 17:37
  • Might be unrelated, but could you please tell why you invoked `#getSystemService` on `this` instead of directly calling it? – fsljfke Aug 29 '20 at 23:41
0

you can do like this

//noinspection ResourceType
final UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService("usagestats");// Context.USAGE_STATS_SERVICE);
final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);
Abdul Rehman Janjua
  • 1,461
  • 14
  • 19