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