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.content.Context;
20import android.content.pm.ParceledListSlice;
21import android.os.RemoteException;
22import android.util.ArrayMap;
23
24import java.util.Collections;
25import java.util.List;
26import java.util.Map;
27
28/**
29 * Provides access to device usage history and statistics. Usage data is aggregated into
30 * time intervals: days, weeks, months, and years.
31 * <p />
32 * When requesting usage data since a particular time, the request might look something like this:
33 * <pre>
34 * PAST                   REQUEST_TIME                    TODAY                   FUTURE
35 * ————————————————————————————||———————————————————————————¦-----------------------|
36 *                        YEAR ||                           ¦                       |
37 * ————————————————————————————||———————————————————————————¦-----------------------|
38 *  MONTH            |         ||                MONTH      ¦                       |
39 * ——————————————————|—————————||———————————————————————————¦-----------------------|
40 *   |      WEEK     |     WEEK||    |     WEEK     |     WE¦EK     |      WEEK     |
41 * ————————————————————————————||———————————————————|———————¦-----------------------|
42 *                             ||           |DAY|DAY|DAY|DAY¦DAY|DAY|DAY|DAY|DAY|DAY|
43 * ————————————————————————————||———————————————————————————¦-----------------------|
44 * </pre>
45 * A request for data in the middle of a time interval will include that interval.
46 * <p/>
47 * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS, which
48 * is a system-level permission and will not be granted to third-party apps. However, declaring
49 * the permission implies intention to use the API and the user of the device can grant permission
50 * through the Settings application.
51 */
52public final class UsageStatsManager {
53
54    /**
55     * An interval type that spans a day. See {@link #queryUsageStats(int, long, long)}.
56     */
57    public static final int INTERVAL_DAILY = 0;
58
59    /**
60     * An interval type that spans a week. See {@link #queryUsageStats(int, long, long)}.
61     */
62    public static final int INTERVAL_WEEKLY = 1;
63
64    /**
65     * An interval type that spans a month. See {@link #queryUsageStats(int, long, long)}.
66     */
67    public static final int INTERVAL_MONTHLY = 2;
68
69    /**
70     * An interval type that spans a year. See {@link #queryUsageStats(int, long, long)}.
71     */
72    public static final int INTERVAL_YEARLY = 3;
73
74    /**
75     * An interval type that will use the best fit interval for the given time range.
76     * See {@link #queryUsageStats(int, long, long)}.
77     */
78    public static final int INTERVAL_BEST = 4;
79
80    /**
81     * The number of available intervals. Does not include {@link #INTERVAL_BEST}, since it
82     * is a pseudo interval (it actually selects a real interval).
83     * {@hide}
84     */
85    public static final int INTERVAL_COUNT = 4;
86
87    private static final UsageEvents sEmptyResults = new UsageEvents();
88
89    private final Context mContext;
90    private final IUsageStatsManager mService;
91
92    /**
93     * {@hide}
94     */
95    public UsageStatsManager(Context context, IUsageStatsManager service) {
96        mContext = context;
97        mService = service;
98    }
99
100    /**
101     * Gets application usage stats for the given time range, aggregated by the specified interval.
102     * <p>The returned list will contain a {@link UsageStats} object for each package that
103     * has data for an interval that is a subset of the time range given. To illustrate:</p>
104     * <pre>
105     * intervalType = INTERVAL_YEARLY
106     * beginTime = 2013
107     * endTime = 2015 (exclusive)
108     *
109     * Results:
110     * 2013 - com.example.alpha
111     * 2013 - com.example.beta
112     * 2014 - com.example.alpha
113     * 2014 - com.example.beta
114     * 2014 - com.example.charlie
115     * </pre>
116     *
117     * @param intervalType The time interval by which the stats are aggregated.
118     * @param beginTime The inclusive beginning of the range of stats to include in the results.
119     * @param endTime The exclusive end of the range of stats to include in the results.
120     * @return A list of {@link UsageStats} or null if none are available.
121     *
122     * @see #INTERVAL_DAILY
123     * @see #INTERVAL_WEEKLY
124     * @see #INTERVAL_MONTHLY
125     * @see #INTERVAL_YEARLY
126     * @see #INTERVAL_BEST
127     */
128    public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
129        try {
130            @SuppressWarnings("unchecked")
131            ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
132                    endTime, mContext.getOpPackageName());
133            if (slice != null) {
134                return slice.getList();
135            }
136        } catch (RemoteException e) {
137            // fallthrough and return null.
138        }
139        return Collections.emptyList();
140    }
141
142    /**
143     * Gets the hardware configurations the device was in for the given time range, aggregated by
144     * the specified interval. The results are ordered as in
145     * {@link #queryUsageStats(int, long, long)}.
146     *
147     * @param intervalType The time interval by which the stats are aggregated.
148     * @param beginTime The inclusive beginning of the range of stats to include in the results.
149     * @param endTime The exclusive end of the range of stats to include in the results.
150     * @return A list of {@link ConfigurationStats} or null if none are available.
151     */
152    public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime,
153            long endTime) {
154        try {
155            @SuppressWarnings("unchecked")
156            ParceledListSlice<ConfigurationStats> slice = mService.queryConfigurationStats(
157                    intervalType, beginTime, endTime, mContext.getOpPackageName());
158            if (slice != null) {
159                return slice.getList();
160            }
161        } catch (RemoteException e) {
162            // fallthrough and return the empty list.
163        }
164        return Collections.emptyList();
165    }
166
167    /**
168     * Query for events in the given time range. Events are only kept by the system for a few
169     * days.
170     * <p />
171     * <b>NOTE:</b> The last few minutes of the event log will be truncated to prevent abuse
172     * by applications.
173     *
174     * @param beginTime The inclusive beginning of the range of events to include in the results.
175     * @param endTime The exclusive end of the range of events to include in the results.
176     * @return A {@link UsageEvents}.
177     */
178    public UsageEvents queryEvents(long beginTime, long endTime) {
179        try {
180            UsageEvents iter = mService.queryEvents(beginTime, endTime,
181                    mContext.getOpPackageName());
182            if (iter != null) {
183                return iter;
184            }
185        } catch (RemoteException e) {
186            // fallthrough and return null
187        }
188        return sEmptyResults;
189    }
190
191    /**
192     * A convenience method that queries for all stats in the given range (using the best interval
193     * for that range), merges the resulting data, and keys it by package name.
194     * See {@link #queryUsageStats(int, long, long)}.
195     *
196     * @param beginTime The inclusive beginning of the range of stats to include in the results.
197     * @param endTime The exclusive end of the range of stats to include in the results.
198     * @return A {@link java.util.Map} keyed by package name, or null if no stats are
199     *         available.
200     */
201    public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
202        List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime);
203        if (stats.isEmpty()) {
204            return Collections.emptyMap();
205        }
206
207        ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>();
208        final int statCount = stats.size();
209        for (int i = 0; i < statCount; i++) {
210            UsageStats newStat = stats.get(i);
211            UsageStats existingStat = aggregatedStats.get(newStat.getPackageName());
212            if (existingStat == null) {
213                aggregatedStats.put(newStat.mPackageName, newStat);
214            } else {
215                existingStat.add(newStat);
216            }
217        }
218        return aggregatedStats;
219    }
220}
221