1 /**
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package android.app.usage;
18 
19 import android.annotation.RequiresPermission;
20 import android.annotation.SystemApi;
21 import android.annotation.SystemService;
22 import android.content.Context;
23 import android.content.pm.ParceledListSlice;
24 import android.os.RemoteException;
25 import android.os.UserHandle;
26 import android.util.ArrayMap;
27 
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Map;
31 
32 /**
33  * Provides access to device usage history and statistics. Usage data is aggregated into
34  * time intervals: days, weeks, months, and years.
35  * <p />
36  * When requesting usage data since a particular time, the request might look something like this:
37  * <pre>
38  * PAST                   REQUEST_TIME                    TODAY                   FUTURE
39  * ————————————————————————————||———————————————————————————¦-----------------------|
40  *                        YEAR ||                           ¦                       |
41  * ————————————————————————————||———————————————————————————¦-----------------------|
42  *  MONTH            |         ||                MONTH      ¦                       |
43  * ——————————————————|—————————||———————————————————————————¦-----------------------|
44  *   |      WEEK     |     WEEK||    |     WEEK     |     WE¦EK     |      WEEK     |
45  * ————————————————————————————||———————————————————|———————¦-----------------------|
46  *                             ||           |DAY|DAY|DAY|DAY¦DAY|DAY|DAY|DAY|DAY|DAY|
47  * ————————————————————————————||———————————————————————————¦-----------------------|
48  * </pre>
49  * A request for data in the middle of a time interval will include that interval.
50  * <p/>
51  * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS, which
52  * is a system-level permission and will not be granted to third-party apps. However, declaring
53  * the permission implies intention to use the API and the user of the device can grant permission
54  * through the Settings application.
55  */
56 @SystemService(Context.USAGE_STATS_SERVICE)
57 public final class UsageStatsManager {
58 
59     /**
60      * An interval type that spans a day. See {@link #queryUsageStats(int, long, long)}.
61      */
62     public static final int INTERVAL_DAILY = 0;
63 
64     /**
65      * An interval type that spans a week. See {@link #queryUsageStats(int, long, long)}.
66      */
67     public static final int INTERVAL_WEEKLY = 1;
68 
69     /**
70      * An interval type that spans a month. See {@link #queryUsageStats(int, long, long)}.
71      */
72     public static final int INTERVAL_MONTHLY = 2;
73 
74     /**
75      * An interval type that spans a year. See {@link #queryUsageStats(int, long, long)}.
76      */
77     public static final int INTERVAL_YEARLY = 3;
78 
79     /**
80      * An interval type that will use the best fit interval for the given time range.
81      * See {@link #queryUsageStats(int, long, long)}.
82      */
83     public static final int INTERVAL_BEST = 4;
84 
85     /**
86      * The number of available intervals. Does not include {@link #INTERVAL_BEST}, since it
87      * is a pseudo interval (it actually selects a real interval).
88      * {@hide}
89      */
90     public static final int INTERVAL_COUNT = 4;
91 
92     private static final UsageEvents sEmptyResults = new UsageEvents();
93 
94     private final Context mContext;
95     private final IUsageStatsManager mService;
96 
97     /**
98      * {@hide}
99      */
UsageStatsManager(Context context, IUsageStatsManager service)100     public UsageStatsManager(Context context, IUsageStatsManager service) {
101         mContext = context;
102         mService = service;
103     }
104 
105     /**
106      * Gets application usage stats for the given time range, aggregated by the specified interval.
107      * <p>The returned list will contain a {@link UsageStats} object for each package that
108      * has data for an interval that is a subset of the time range given. To illustrate:</p>
109      * <pre>
110      * intervalType = INTERVAL_YEARLY
111      * beginTime = 2013
112      * endTime = 2015 (exclusive)
113      *
114      * Results:
115      * 2013 - com.example.alpha
116      * 2013 - com.example.beta
117      * 2014 - com.example.alpha
118      * 2014 - com.example.beta
119      * 2014 - com.example.charlie
120      * </pre>
121      *
122      * @param intervalType The time interval by which the stats are aggregated.
123      * @param beginTime The inclusive beginning of the range of stats to include in the results.
124      * @param endTime The exclusive end of the range of stats to include in the results.
125      * @return A list of {@link UsageStats} or null if none are available.
126      *
127      * @see #INTERVAL_DAILY
128      * @see #INTERVAL_WEEKLY
129      * @see #INTERVAL_MONTHLY
130      * @see #INTERVAL_YEARLY
131      * @see #INTERVAL_BEST
132      */
queryUsageStats(int intervalType, long beginTime, long endTime)133     public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
134         try {
135             @SuppressWarnings("unchecked")
136             ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
137                     endTime, mContext.getOpPackageName());
138             if (slice != null) {
139                 return slice.getList();
140             }
141         } catch (RemoteException e) {
142             // fallthrough and return null.
143         }
144         return Collections.emptyList();
145     }
146 
147     /**
148      * Gets the hardware configurations the device was in for the given time range, aggregated by
149      * the specified interval. The results are ordered as in
150      * {@link #queryUsageStats(int, long, long)}.
151      *
152      * @param intervalType The time interval by which the stats are aggregated.
153      * @param beginTime The inclusive beginning of the range of stats to include in the results.
154      * @param endTime The exclusive end of the range of stats to include in the results.
155      * @return A list of {@link ConfigurationStats} or null if none are available.
156      */
queryConfigurations(int intervalType, long beginTime, long endTime)157     public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime,
158             long endTime) {
159         try {
160             @SuppressWarnings("unchecked")
161             ParceledListSlice<ConfigurationStats> slice = mService.queryConfigurationStats(
162                     intervalType, beginTime, endTime, mContext.getOpPackageName());
163             if (slice != null) {
164                 return slice.getList();
165             }
166         } catch (RemoteException e) {
167             // fallthrough and return the empty list.
168         }
169         return Collections.emptyList();
170     }
171 
172     /**
173      * Query for events in the given time range. Events are only kept by the system for a few
174      * days.
175      * <p />
176      * <b>NOTE:</b> The last few minutes of the event log will be truncated to prevent abuse
177      * by applications.
178      *
179      * @param beginTime The inclusive beginning of the range of events to include in the results.
180      * @param endTime The exclusive end of the range of events to include in the results.
181      * @return A {@link UsageEvents}.
182      */
queryEvents(long beginTime, long endTime)183     public UsageEvents queryEvents(long beginTime, long endTime) {
184         try {
185             UsageEvents iter = mService.queryEvents(beginTime, endTime,
186                     mContext.getOpPackageName());
187             if (iter != null) {
188                 return iter;
189             }
190         } catch (RemoteException e) {
191             // fallthrough and return null
192         }
193         return sEmptyResults;
194     }
195 
196     /**
197      * A convenience method that queries for all stats in the given range (using the best interval
198      * for that range), merges the resulting data, and keys it by package name.
199      * See {@link #queryUsageStats(int, long, long)}.
200      *
201      * @param beginTime The inclusive beginning of the range of stats to include in the results.
202      * @param endTime The exclusive end of the range of stats to include in the results.
203      * @return A {@link java.util.Map} keyed by package name, or null if no stats are
204      *         available.
205      */
queryAndAggregateUsageStats(long beginTime, long endTime)206     public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
207         List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime);
208         if (stats.isEmpty()) {
209             return Collections.emptyMap();
210         }
211 
212         ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>();
213         final int statCount = stats.size();
214         for (int i = 0; i < statCount; i++) {
215             UsageStats newStat = stats.get(i);
216             UsageStats existingStat = aggregatedStats.get(newStat.getPackageName());
217             if (existingStat == null) {
218                 aggregatedStats.put(newStat.mPackageName, newStat);
219             } else {
220                 existingStat.add(newStat);
221             }
222         }
223         return aggregatedStats;
224     }
225 
226     /**
227      * Returns whether the specified app is currently considered inactive. This will be true if the
228      * app hasn't been used directly or indirectly for a period of time defined by the system. This
229      * could be of the order of several hours or days.
230      * @param packageName The package name of the app to query
231      * @return whether the app is currently considered inactive
232      */
isAppInactive(String packageName)233     public boolean isAppInactive(String packageName) {
234         try {
235             return mService.isAppInactive(packageName, UserHandle.myUserId());
236         } catch (RemoteException e) {
237             // fall through and return default
238         }
239         return false;
240     }
241 
242     /**
243      * @hide
244      */
setAppInactive(String packageName, boolean inactive)245     public void setAppInactive(String packageName, boolean inactive) {
246         try {
247             mService.setAppInactive(packageName, inactive, UserHandle.myUserId());
248         } catch (RemoteException e) {
249             // fall through
250         }
251     }
252 
253     /**
254      * {@hide}
255      * Temporarily whitelist the specified app for a short duration. This is to allow an app
256      * receiving a high priority message to be able to access the network and acquire wakelocks
257      * even if the device is in power-save mode or the app is currently considered inactive.
258      * @param packageName The package name of the app to whitelist.
259      * @param duration Duration to whitelist the app for, in milliseconds. It is recommended that
260      * this be limited to 10s of seconds. Requested duration will be clamped to a few minutes.
261      * @param user The user for whom the package should be whitelisted. Passing in a user that is
262      * not the same as the caller's process will require the INTERACT_ACROSS_USERS permission.
263      * @see #isAppInactive(String)
264      */
265     @SystemApi
266     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
whitelistAppTemporarily(String packageName, long duration, UserHandle user)267     public void whitelistAppTemporarily(String packageName, long duration, UserHandle user) {
268         try {
269             mService.whitelistAppTemporarily(packageName, duration, user.getIdentifier());
270         } catch (RemoteException re) {
271         }
272     }
273 
274     /**
275      * Inform usage stats that the carrier privileged apps access rules have changed.
276      * @hide
277      */
onCarrierPrivilegedAppsChanged()278     public void onCarrierPrivilegedAppsChanged() {
279         try {
280             mService.onCarrierPrivilegedAppsChanged();
281         } catch (RemoteException re) {
282         }
283     }
284 
285     /**
286      * Reports a Chooser action to the UsageStatsManager.
287      *
288      * @param packageName The package name of the app that is selected.
289      * @param userId The user id of who makes the selection.
290      * @param contentType The type of the content, e.g., Image, Video, App.
291      * @param annotations The annotations of the content, e.g., Game, Selfie.
292      * @param action The action type of Intent that invokes ChooserActivity.
293      * {@link UsageEvents}
294      * @hide
295      */
reportChooserSelection(String packageName, int userId, String contentType, String[] annotations, String action)296     public void reportChooserSelection(String packageName, int userId, String contentType,
297                                        String[] annotations, String action) {
298         try {
299             mService.reportChooserSelection(packageName, userId, contentType, annotations, action);
300         } catch (RemoteException re) {
301         }
302     }
303 }
304