blob: 6b573e99f1fab66b71d6165a3a5723215abe6449 [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
Makoto Onukiad623012017-05-15 09:29:34 -070018import android.annotation.IntDef;
Amith Yamasani7ec89412018-02-07 08:48:49 -080019import android.annotation.SystemApi;
Adam Lesinski7f61e962014-09-02 16:43:52 -070020import android.content.res.Configuration;
Adam Lesinski35168002014-07-21 15:25:30 -070021import android.os.Parcel;
22import android.os.Parcelable;
23
Makoto Onukiad623012017-05-15 09:29:34 -070024import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
Adam Lesinski35168002014-07-21 15:25:30 -070026import java.util.Arrays;
27import java.util.List;
28
29/**
30 * A result returned from {@link android.app.usage.UsageStatsManager#queryEvents(long, long)}
31 * from which to read {@link android.app.usage.UsageEvents.Event} objects.
32 */
33public final class UsageEvents implements Parcelable {
34
Makoto Onukiad623012017-05-15 09:29:34 -070035 /** @hide */
36 public static final String INSTANT_APP_PACKAGE_NAME = "android.instant_app";
37
38 /** @hide */
39 public static final String INSTANT_APP_CLASS_NAME = "android.instant_class";
40
Adam Lesinski35168002014-07-21 15:25:30 -070041 /**
42 * An event representing a state change for a component.
43 */
44 public static final class Event {
45
46 /**
47 * No event type.
48 */
49 public static final int NONE = 0;
50
51 /**
52 * An event type denoting that a component moved to the foreground.
53 */
54 public static final int MOVE_TO_FOREGROUND = 1;
55
56 /**
57 * An event type denoting that a component moved to the background.
58 */
59 public static final int MOVE_TO_BACKGROUND = 2;
60
61 /**
62 * An event type denoting that a component was in the foreground when the stats
63 * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}.
64 * {@hide}
65 */
66 public static final int END_OF_DAY = 3;
67
68 /**
69 * An event type denoting that a component was in the foreground the previous day.
70 * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}.
71 * {@hide}
72 */
73 public static final int CONTINUE_PREVIOUS_DAY = 4;
74
75 /**
Adam Lesinski7f61e962014-09-02 16:43:52 -070076 * An event type denoting that the device configuration has changed.
77 */
78 public static final int CONFIGURATION_CHANGE = 5;
79
80 /**
Adam Lesinskic8e87292015-06-10 15:33:45 -070081 * An event type denoting that a package was interacted with in some way by the system.
82 * @hide
Adam Lesinski978a1ed2015-03-02 11:37:24 -080083 */
Adam Lesinskic8e87292015-06-10 15:33:45 -070084 public static final int SYSTEM_INTERACTION = 6;
85
86 /**
87 * An event type denoting that a package was interacted with in some way by the user.
88 */
89 public static final int USER_INTERACTION = 7;
Adam Lesinski978a1ed2015-03-02 11:37:24 -080090
91 /**
Makoto Onukiac042502016-05-20 16:39:42 -070092 * An event type denoting that an action equivalent to a ShortcutInfo is taken by the user.
93 *
94 * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
95 */
96 public static final int SHORTCUT_INVOCATION = 8;
97
98 /**
Kang Li53b43142016-11-14 14:38:25 -080099 * An event type denoting that a package was selected by the user for ChooserActivity.
100 * @hide
101 */
102 public static final int CHOOSER_ACTION = 9;
103
Amith Yamasani803eab692017-11-09 17:47:04 -0800104 /**
105 * An event type denoting that a notification was viewed by the user.
106 * @hide
107 */
Amith Yamasani7ec89412018-02-07 08:48:49 -0800108 @SystemApi
Amith Yamasani803eab692017-11-09 17:47:04 -0800109 public static final int NOTIFICATION_SEEN = 10;
110
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800111 /**
112 * An event type denoting a change in App Standby Bucket.
113 * @hide
114 */
Amith Yamasani7ec89412018-02-07 08:48:49 -0800115 @SystemApi
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800116 public static final int STANDBY_BUCKET_CHANGED = 11;
117
Makoto Onukiad623012017-05-15 09:29:34 -0700118 /** @hide */
119 public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
120
121 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700122 @IntDef(flag = true, prefix = { "FLAG_" }, value = {
123 FLAG_IS_PACKAGE_INSTANT_APP,
124 })
Makoto Onukiad623012017-05-15 09:29:34 -0700125 @Retention(RetentionPolicy.SOURCE)
126 public @interface EventFlags {}
127
Kang Li53b43142016-11-14 14:38:25 -0800128 /**
Adam Lesinski35168002014-07-21 15:25:30 -0700129 * {@hide}
130 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700131 public String mPackage;
132
133 /**
134 * {@hide}
135 */
136 public String mClass;
Adam Lesinski35168002014-07-21 15:25:30 -0700137
138 /**
139 * {@hide}
140 */
141 public long mTimeStamp;
142
143 /**
144 * {@hide}
145 */
146 public int mEventType;
147
148 /**
Adam Lesinski7f61e962014-09-02 16:43:52 -0700149 * Only present for {@link #CONFIGURATION_CHANGE} event types.
150 * {@hide}
151 */
152 public Configuration mConfiguration;
153
154 /**
Makoto Onukiac042502016-05-20 16:39:42 -0700155 * ID of the shortcut.
156 * Only present for {@link #SHORTCUT_INVOCATION} event types.
157 * {@hide}
158 */
159 public String mShortcutId;
160
161 /**
Kang Li53b43142016-11-14 14:38:25 -0800162 * Action type passed to ChooserActivity
163 * Only present for {@link #CHOOSER_ACTION} event types.
164 * {@hide}
165 */
166 public String mAction;
167
168 /**
169 * Content type passed to ChooserActivity.
170 * Only present for {@link #CHOOSER_ACTION} event types.
171 * {@hide}
172 */
173 public String mContentType;
174
175 /**
176 * Content annotations passed to ChooserActivity.
177 * Only present for {@link #CHOOSER_ACTION} event types.
178 * {@hide}
179 */
180 public String[] mContentAnnotations;
181
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800182 /**
183 * The app standby bucket assigned.
184 * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
185 * {@hide}
186 */
187 public int mBucket;
188
Makoto Onukiad623012017-05-15 09:29:34 -0700189 /** @hide */
190 @EventFlags
191 public int mFlags;
192
193 public Event() {
194 }
195
196 /** @hide */
197 public Event(Event orig) {
198 mPackage = orig.mPackage;
199 mClass = orig.mClass;
200 mTimeStamp = orig.mTimeStamp;
201 mEventType = orig.mEventType;
202 mConfiguration = orig.mConfiguration;
203 mShortcutId = orig.mShortcutId;
204 mAction = orig.mAction;
205 mContentType = orig.mContentType;
206 mContentAnnotations = orig.mContentAnnotations;
207 mFlags = orig.mFlags;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800208 mBucket = orig.mBucket;
Makoto Onukiad623012017-05-15 09:29:34 -0700209 }
210
Kang Li53b43142016-11-14 14:38:25 -0800211 /**
Adam Lesinski9d960752014-08-25 14:48:12 -0700212 * The package name of the source of this event.
213 */
214 public String getPackageName() {
215 return mPackage;
216 }
217
218 /**
219 * The class name of the source of this event. This may be null for
220 * certain events.
221 */
222 public String getClassName() {
223 return mClass;
Adam Lesinski35168002014-07-21 15:25:30 -0700224 }
225
226 /**
Adam Lesinskicc562a82014-08-27 11:52:52 -0700227 * The time at which this event occurred, measured in milliseconds since the epoch.
228 * <p/>
229 * See {@link System#currentTimeMillis()}.
Adam Lesinski35168002014-07-21 15:25:30 -0700230 */
231 public long getTimeStamp() {
232 return mTimeStamp;
233 }
234
235 /**
236 * The event type.
237 *
238 * See {@link #MOVE_TO_BACKGROUND}
239 * See {@link #MOVE_TO_FOREGROUND}
240 */
241 public int getEventType() {
242 return mEventType;
243 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700244
245 /**
246 * Returns a {@link Configuration} for this event if the event is of type
247 * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
248 */
249 public Configuration getConfiguration() {
250 return mConfiguration;
251 }
Makoto Onukiac042502016-05-20 16:39:42 -0700252
253 /**
254 * Returns the ID of a {@link android.content.pm.ShortcutInfo} for this event
255 * if the event is of type {@link #SHORTCUT_INVOCATION}, otherwise it returns null.
256 *
257 * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
258 */
259 public String getShortcutId() {
260 return mShortcutId;
261 }
Makoto Onukiad623012017-05-15 09:29:34 -0700262
Amith Yamasani7ec89412018-02-07 08:48:49 -0800263 /**
264 * Returns the standby bucket of the app, if the event is of type
265 * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
266 * @return the standby bucket associated with the event.
267 * @hide
268 */
269 @SystemApi
270 public int getStandbyBucket() {
271 return mBucket;
272 }
273
Makoto Onukiad623012017-05-15 09:29:34 -0700274 /** @hide */
275 public Event getObfuscatedIfInstantApp() {
276 if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
277 return this;
278 }
279 final Event ret = new Event(this);
280 ret.mPackage = INSTANT_APP_PACKAGE_NAME;
281 ret.mClass = INSTANT_APP_CLASS_NAME;
282
283 // Note there are other string fields too, but they're for app shortcuts and choosers,
284 // which instant apps can't use anyway, so there's no need to hide them.
285 return ret;
286 }
Adam Lesinski35168002014-07-21 15:25:30 -0700287 }
288
289 // Only used when creating the resulting events. Not used for reading/unparceling.
290 private List<Event> mEventsToWrite = null;
291
292 // Only used for reading/unparceling events.
293 private Parcel mParcel = null;
294 private final int mEventCount;
295
296 private int mIndex = 0;
297
298 /*
299 * In order to save space, since ComponentNames will be duplicated everywhere,
300 * we use a map and index into it.
301 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700302 private String[] mStringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700303
304 /**
305 * Construct the iterator from a parcel.
306 * {@hide}
307 */
308 public UsageEvents(Parcel in) {
309 mEventCount = in.readInt();
310 mIndex = in.readInt();
311 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700312 mStringPool = in.createStringArray();
Adam Lesinski35168002014-07-21 15:25:30 -0700313
314 final int listByteLength = in.readInt();
315 final int positionInParcel = in.readInt();
316 mParcel = Parcel.obtain();
317 mParcel.setDataPosition(0);
318 mParcel.appendFrom(in, in.dataPosition(), listByteLength);
319 mParcel.setDataSize(mParcel.dataPosition());
320 mParcel.setDataPosition(positionInParcel);
321 }
322 }
323
324 /**
325 * Create an empty iterator.
326 * {@hide}
327 */
328 UsageEvents() {
329 mEventCount = 0;
330 }
331
332 /**
333 * Construct the iterator in preparation for writing it to a parcel.
334 * {@hide}
335 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700336 public UsageEvents(List<Event> events, String[] stringPool) {
337 mStringPool = stringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700338 mEventCount = events.size();
339 mEventsToWrite = events;
340 }
341
342 /**
343 * Returns whether or not there are more events to read using
344 * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
345 *
346 * @return true if there are more events, false otherwise.
347 */
348 public boolean hasNextEvent() {
349 return mIndex < mEventCount;
350 }
351
352 /**
353 * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
354 * resulting data into {@code eventOut}.
355 *
356 * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
357 * next event data.
358 * @return true if an event was available, false if there are no more events.
359 */
360 public boolean getNextEvent(Event eventOut) {
361 if (mIndex >= mEventCount) {
362 return false;
363 }
364
Adam Lesinski7f61e962014-09-02 16:43:52 -0700365 readEventFromParcel(mParcel, eventOut);
Adam Lesinski9d960752014-08-25 14:48:12 -0700366
Adam Lesinski35168002014-07-21 15:25:30 -0700367 mIndex++;
Adam Lesinski35168002014-07-21 15:25:30 -0700368 if (mIndex >= mEventCount) {
369 mParcel.recycle();
370 mParcel = null;
371 }
372 return true;
373 }
374
375 /**
376 * Resets the collection so that it can be iterated over from the beginning.
Adam Lesinski54e064b2014-10-08 12:33:16 -0700377 *
378 * @hide When this object is iterated to completion, the parcel is destroyed and
379 * so resetToStart doesn't work.
Adam Lesinski35168002014-07-21 15:25:30 -0700380 */
381 public void resetToStart() {
382 mIndex = 0;
383 if (mParcel != null) {
384 mParcel.setDataPosition(0);
385 }
386 }
387
Adam Lesinski9d960752014-08-25 14:48:12 -0700388 private int findStringIndex(String str) {
389 final int index = Arrays.binarySearch(mStringPool, str);
390 if (index < 0) {
391 throw new IllegalStateException("String '" + str + "' is not in the string pool");
392 }
393 return index;
394 }
395
Adam Lesinski7f61e962014-09-02 16:43:52 -0700396 /**
397 * Writes a single event to the parcel. Modify this when updating {@link Event}.
398 */
399 private void writeEventToParcel(Event event, Parcel p, int flags) {
400 final int packageIndex;
401 if (event.mPackage != null) {
402 packageIndex = findStringIndex(event.mPackage);
403 } else {
404 packageIndex = -1;
405 }
406
407 final int classIndex;
408 if (event.mClass != null) {
409 classIndex = findStringIndex(event.mClass);
410 } else {
411 classIndex = -1;
412 }
413 p.writeInt(packageIndex);
414 p.writeInt(classIndex);
415 p.writeInt(event.mEventType);
416 p.writeLong(event.mTimeStamp);
417
Makoto Onukiac042502016-05-20 16:39:42 -0700418 switch (event.mEventType) {
419 case Event.CONFIGURATION_CHANGE:
420 event.mConfiguration.writeToParcel(p, flags);
421 break;
422 case Event.SHORTCUT_INVOCATION:
423 p.writeString(event.mShortcutId);
424 break;
Kang Li53b43142016-11-14 14:38:25 -0800425 case Event.CHOOSER_ACTION:
426 p.writeString(event.mAction);
427 p.writeString(event.mContentType);
428 p.writeStringArray(event.mContentAnnotations);
429 break;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800430 case Event.STANDBY_BUCKET_CHANGED:
431 p.writeInt(event.mBucket);
432 break;
Adam Lesinski7f61e962014-09-02 16:43:52 -0700433 }
434 }
435
436 /**
437 * Reads a single event from the parcel. Modify this when updating {@link Event}.
438 */
439 private void readEventFromParcel(Parcel p, Event eventOut) {
440 final int packageIndex = p.readInt();
441 if (packageIndex >= 0) {
442 eventOut.mPackage = mStringPool[packageIndex];
443 } else {
444 eventOut.mPackage = null;
445 }
446
447 final int classIndex = p.readInt();
448 if (classIndex >= 0) {
449 eventOut.mClass = mStringPool[classIndex];
450 } else {
451 eventOut.mClass = null;
452 }
453 eventOut.mEventType = p.readInt();
454 eventOut.mTimeStamp = p.readLong();
455
Makoto Onukiac042502016-05-20 16:39:42 -0700456 // Fill out the event-dependant fields.
457 eventOut.mConfiguration = null;
458 eventOut.mShortcutId = null;
Kang Li53b43142016-11-14 14:38:25 -0800459 eventOut.mAction = null;
460 eventOut.mContentType = null;
461 eventOut.mContentAnnotations = null;
Makoto Onukiac042502016-05-20 16:39:42 -0700462
463 switch (eventOut.mEventType) {
464 case Event.CONFIGURATION_CHANGE:
465 // Extract the configuration for configuration change events.
466 eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
467 break;
468 case Event.SHORTCUT_INVOCATION:
469 eventOut.mShortcutId = p.readString();
470 break;
Kang Li53b43142016-11-14 14:38:25 -0800471 case Event.CHOOSER_ACTION:
472 eventOut.mAction = p.readString();
473 eventOut.mContentType = p.readString();
474 eventOut.mContentAnnotations = p.createStringArray();
475 break;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800476 case Event.STANDBY_BUCKET_CHANGED:
477 eventOut.mBucket = p.readInt();
478 break;
Adam Lesinski7f61e962014-09-02 16:43:52 -0700479 }
480 }
481
482 @Override
483 public int describeContents() {
484 return 0;
485 }
486
Adam Lesinski35168002014-07-21 15:25:30 -0700487 @Override
488 public void writeToParcel(Parcel dest, int flags) {
489 dest.writeInt(mEventCount);
490 dest.writeInt(mIndex);
491 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700492 dest.writeStringArray(mStringPool);
Adam Lesinski35168002014-07-21 15:25:30 -0700493
494 if (mEventsToWrite != null) {
495 // Write out the events
496 Parcel p = Parcel.obtain();
497 try {
498 p.setDataPosition(0);
499 for (int i = 0; i < mEventCount; i++) {
500 final Event event = mEventsToWrite.get(i);
Adam Lesinski7f61e962014-09-02 16:43:52 -0700501 writeEventToParcel(event, p, flags);
Adam Lesinski35168002014-07-21 15:25:30 -0700502 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700503
Adam Lesinski35168002014-07-21 15:25:30 -0700504 final int listByteLength = p.dataPosition();
505
506 // Write the total length of the data.
507 dest.writeInt(listByteLength);
508
509 // Write our current position into the data.
510 dest.writeInt(0);
511
512 // Write the data.
513 dest.appendFrom(p, 0, listByteLength);
514 } finally {
515 p.recycle();
516 }
517
518 } else if (mParcel != null) {
519 // Write the total length of the data.
520 dest.writeInt(mParcel.dataSize());
521
522 // Write out current position into the data.
523 dest.writeInt(mParcel.dataPosition());
524
525 // Write the data.
526 dest.appendFrom(mParcel, 0, mParcel.dataSize());
527 } else {
528 throw new IllegalStateException(
529 "Either mParcel or mEventsToWrite must not be null");
530 }
531 }
532 }
533
534 public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
535 @Override
536 public UsageEvents createFromParcel(Parcel source) {
537 return new UsageEvents(source);
538 }
539
540 @Override
541 public UsageEvents[] newArray(int size) {
542 return new UsageEvents[size];
543 }
544 };
Adam Lesinski35168002014-07-21 15:25:30 -0700545}