blob: 24647f388fc128a3fd1a867a10654cc1d3fa772a [file] [log] [blame]
Adam Lesinski35168002014-07-21 15:25:30 -07001/**
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 */
16package android.app.usage;
17
Adam Lesinski7f61e962014-09-02 16:43:52 -070018import android.content.res.Configuration;
Adam Lesinski35168002014-07-21 15:25:30 -070019import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.util.Arrays;
23import java.util.List;
24
25/**
26 * A result returned from {@link android.app.usage.UsageStatsManager#queryEvents(long, long)}
27 * from which to read {@link android.app.usage.UsageEvents.Event} objects.
28 */
29public final class UsageEvents implements Parcelable {
30
31 /**
32 * An event representing a state change for a component.
33 */
34 public static final class Event {
35
36 /**
37 * No event type.
38 */
39 public static final int NONE = 0;
40
41 /**
42 * An event type denoting that a component moved to the foreground.
43 */
44 public static final int MOVE_TO_FOREGROUND = 1;
45
46 /**
47 * An event type denoting that a component moved to the background.
48 */
49 public static final int MOVE_TO_BACKGROUND = 2;
50
51 /**
52 * An event type denoting that a component was in the foreground when the stats
53 * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}.
54 * {@hide}
55 */
56 public static final int END_OF_DAY = 3;
57
58 /**
59 * An event type denoting that a component was in the foreground the previous day.
60 * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}.
61 * {@hide}
62 */
63 public static final int CONTINUE_PREVIOUS_DAY = 4;
64
65 /**
Adam Lesinski7f61e962014-09-02 16:43:52 -070066 * An event type denoting that the device configuration has changed.
67 */
68 public static final int CONFIGURATION_CHANGE = 5;
69
70 /**
Adam Lesinskic8e87292015-06-10 15:33:45 -070071 * An event type denoting that a package was interacted with in some way by the system.
72 * @hide
Adam Lesinski978a1ed2015-03-02 11:37:24 -080073 */
Adam Lesinskic8e87292015-06-10 15:33:45 -070074 public static final int SYSTEM_INTERACTION = 6;
75
76 /**
77 * An event type denoting that a package was interacted with in some way by the user.
78 */
79 public static final int USER_INTERACTION = 7;
Adam Lesinski978a1ed2015-03-02 11:37:24 -080080
81 /**
Adam Lesinski35168002014-07-21 15:25:30 -070082 * {@hide}
83 */
Adam Lesinski9d960752014-08-25 14:48:12 -070084 public String mPackage;
85
86 /**
87 * {@hide}
88 */
89 public String mClass;
Adam Lesinski35168002014-07-21 15:25:30 -070090
91 /**
92 * {@hide}
93 */
94 public long mTimeStamp;
95
96 /**
97 * {@hide}
98 */
99 public int mEventType;
100
101 /**
Adam Lesinski7f61e962014-09-02 16:43:52 -0700102 * Only present for {@link #CONFIGURATION_CHANGE} event types.
103 * {@hide}
104 */
105 public Configuration mConfiguration;
106
107 /**
Adam Lesinski9d960752014-08-25 14:48:12 -0700108 * The package name of the source of this event.
109 */
110 public String getPackageName() {
111 return mPackage;
112 }
113
114 /**
115 * The class name of the source of this event. This may be null for
116 * certain events.
117 */
118 public String getClassName() {
119 return mClass;
Adam Lesinski35168002014-07-21 15:25:30 -0700120 }
121
122 /**
Adam Lesinskicc562a82014-08-27 11:52:52 -0700123 * The time at which this event occurred, measured in milliseconds since the epoch.
124 * <p/>
125 * See {@link System#currentTimeMillis()}.
Adam Lesinski35168002014-07-21 15:25:30 -0700126 */
127 public long getTimeStamp() {
128 return mTimeStamp;
129 }
130
131 /**
132 * The event type.
133 *
134 * See {@link #MOVE_TO_BACKGROUND}
135 * See {@link #MOVE_TO_FOREGROUND}
136 */
137 public int getEventType() {
138 return mEventType;
139 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700140
141 /**
142 * Returns a {@link Configuration} for this event if the event is of type
143 * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
144 */
145 public Configuration getConfiguration() {
146 return mConfiguration;
147 }
Adam Lesinski35168002014-07-21 15:25:30 -0700148 }
149
150 // Only used when creating the resulting events. Not used for reading/unparceling.
151 private List<Event> mEventsToWrite = null;
152
153 // Only used for reading/unparceling events.
154 private Parcel mParcel = null;
155 private final int mEventCount;
156
157 private int mIndex = 0;
158
159 /*
160 * In order to save space, since ComponentNames will be duplicated everywhere,
161 * we use a map and index into it.
162 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700163 private String[] mStringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700164
165 /**
166 * Construct the iterator from a parcel.
167 * {@hide}
168 */
169 public UsageEvents(Parcel in) {
170 mEventCount = in.readInt();
171 mIndex = in.readInt();
172 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700173 mStringPool = in.createStringArray();
Adam Lesinski35168002014-07-21 15:25:30 -0700174
175 final int listByteLength = in.readInt();
176 final int positionInParcel = in.readInt();
177 mParcel = Parcel.obtain();
178 mParcel.setDataPosition(0);
179 mParcel.appendFrom(in, in.dataPosition(), listByteLength);
180 mParcel.setDataSize(mParcel.dataPosition());
181 mParcel.setDataPosition(positionInParcel);
182 }
183 }
184
185 /**
186 * Create an empty iterator.
187 * {@hide}
188 */
189 UsageEvents() {
190 mEventCount = 0;
191 }
192
193 /**
194 * Construct the iterator in preparation for writing it to a parcel.
195 * {@hide}
196 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700197 public UsageEvents(List<Event> events, String[] stringPool) {
198 mStringPool = stringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700199 mEventCount = events.size();
200 mEventsToWrite = events;
201 }
202
203 /**
204 * Returns whether or not there are more events to read using
205 * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
206 *
207 * @return true if there are more events, false otherwise.
208 */
209 public boolean hasNextEvent() {
210 return mIndex < mEventCount;
211 }
212
213 /**
214 * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
215 * resulting data into {@code eventOut}.
216 *
217 * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
218 * next event data.
219 * @return true if an event was available, false if there are no more events.
220 */
221 public boolean getNextEvent(Event eventOut) {
222 if (mIndex >= mEventCount) {
223 return false;
224 }
225
Adam Lesinski7f61e962014-09-02 16:43:52 -0700226 readEventFromParcel(mParcel, eventOut);
Adam Lesinski9d960752014-08-25 14:48:12 -0700227
Adam Lesinski35168002014-07-21 15:25:30 -0700228 mIndex++;
Adam Lesinski35168002014-07-21 15:25:30 -0700229 if (mIndex >= mEventCount) {
230 mParcel.recycle();
231 mParcel = null;
232 }
233 return true;
234 }
235
236 /**
237 * Resets the collection so that it can be iterated over from the beginning.
Adam Lesinski54e064b2014-10-08 12:33:16 -0700238 *
239 * @hide When this object is iterated to completion, the parcel is destroyed and
240 * so resetToStart doesn't work.
Adam Lesinski35168002014-07-21 15:25:30 -0700241 */
242 public void resetToStart() {
243 mIndex = 0;
244 if (mParcel != null) {
245 mParcel.setDataPosition(0);
246 }
247 }
248
Adam Lesinski9d960752014-08-25 14:48:12 -0700249 private int findStringIndex(String str) {
250 final int index = Arrays.binarySearch(mStringPool, str);
251 if (index < 0) {
252 throw new IllegalStateException("String '" + str + "' is not in the string pool");
253 }
254 return index;
255 }
256
Adam Lesinski7f61e962014-09-02 16:43:52 -0700257 /**
258 * Writes a single event to the parcel. Modify this when updating {@link Event}.
259 */
260 private void writeEventToParcel(Event event, Parcel p, int flags) {
261 final int packageIndex;
262 if (event.mPackage != null) {
263 packageIndex = findStringIndex(event.mPackage);
264 } else {
265 packageIndex = -1;
266 }
267
268 final int classIndex;
269 if (event.mClass != null) {
270 classIndex = findStringIndex(event.mClass);
271 } else {
272 classIndex = -1;
273 }
274 p.writeInt(packageIndex);
275 p.writeInt(classIndex);
276 p.writeInt(event.mEventType);
277 p.writeLong(event.mTimeStamp);
278
279 if (event.mEventType == Event.CONFIGURATION_CHANGE) {
280 event.mConfiguration.writeToParcel(p, flags);
281 }
282 }
283
284 /**
285 * Reads a single event from the parcel. Modify this when updating {@link Event}.
286 */
287 private void readEventFromParcel(Parcel p, Event eventOut) {
288 final int packageIndex = p.readInt();
289 if (packageIndex >= 0) {
290 eventOut.mPackage = mStringPool[packageIndex];
291 } else {
292 eventOut.mPackage = null;
293 }
294
295 final int classIndex = p.readInt();
296 if (classIndex >= 0) {
297 eventOut.mClass = mStringPool[classIndex];
298 } else {
299 eventOut.mClass = null;
300 }
301 eventOut.mEventType = p.readInt();
302 eventOut.mTimeStamp = p.readLong();
303
304 // Extract the configuration for configuration change events.
305 if (eventOut.mEventType == Event.CONFIGURATION_CHANGE) {
306 eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
307 } else {
308 eventOut.mConfiguration = null;
309 }
310 }
311
312 @Override
313 public int describeContents() {
314 return 0;
315 }
316
Adam Lesinski35168002014-07-21 15:25:30 -0700317 @Override
318 public void writeToParcel(Parcel dest, int flags) {
319 dest.writeInt(mEventCount);
320 dest.writeInt(mIndex);
321 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700322 dest.writeStringArray(mStringPool);
Adam Lesinski35168002014-07-21 15:25:30 -0700323
324 if (mEventsToWrite != null) {
325 // Write out the events
326 Parcel p = Parcel.obtain();
327 try {
328 p.setDataPosition(0);
329 for (int i = 0; i < mEventCount; i++) {
330 final Event event = mEventsToWrite.get(i);
Adam Lesinski7f61e962014-09-02 16:43:52 -0700331 writeEventToParcel(event, p, flags);
Adam Lesinski35168002014-07-21 15:25:30 -0700332 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700333
Adam Lesinski35168002014-07-21 15:25:30 -0700334 final int listByteLength = p.dataPosition();
335
336 // Write the total length of the data.
337 dest.writeInt(listByteLength);
338
339 // Write our current position into the data.
340 dest.writeInt(0);
341
342 // Write the data.
343 dest.appendFrom(p, 0, listByteLength);
344 } finally {
345 p.recycle();
346 }
347
348 } else if (mParcel != null) {
349 // Write the total length of the data.
350 dest.writeInt(mParcel.dataSize());
351
352 // Write out current position into the data.
353 dest.writeInt(mParcel.dataPosition());
354
355 // Write the data.
356 dest.appendFrom(mParcel, 0, mParcel.dataSize());
357 } else {
358 throw new IllegalStateException(
359 "Either mParcel or mEventsToWrite must not be null");
360 }
361 }
362 }
363
364 public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
365 @Override
366 public UsageEvents createFromParcel(Parcel source) {
367 return new UsageEvents(source);
368 }
369
370 @Override
371 public UsageEvents[] newArray(int size) {
372 return new UsageEvents[size];
373 }
374 };
Adam Lesinski35168002014-07-21 15:25:30 -0700375}