blob: 521ab4edc4c561212491595a7b9a1cca072a0fc2 [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 /**
Amith Yamasani119be9a2018-02-18 22:23:00 -0800112 * An event type denoting a change in App Standby Bucket. Additional bucket information
113 * is contained in mBucketAndReason.
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800114 * @hide
115 */
Amith Yamasani7ec89412018-02-07 08:48:49 -0800116 @SystemApi
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800117 public static final int STANDBY_BUCKET_CHANGED = 11;
118
Makoto Onukiad623012017-05-15 09:29:34 -0700119 /** @hide */
120 public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
121
122 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700123 @IntDef(flag = true, prefix = { "FLAG_" }, value = {
124 FLAG_IS_PACKAGE_INSTANT_APP,
125 })
Makoto Onukiad623012017-05-15 09:29:34 -0700126 @Retention(RetentionPolicy.SOURCE)
127 public @interface EventFlags {}
128
Kang Li53b43142016-11-14 14:38:25 -0800129 /**
Adam Lesinski35168002014-07-21 15:25:30 -0700130 * {@hide}
131 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700132 public String mPackage;
133
134 /**
135 * {@hide}
136 */
137 public String mClass;
Adam Lesinski35168002014-07-21 15:25:30 -0700138
139 /**
140 * {@hide}
141 */
142 public long mTimeStamp;
143
144 /**
145 * {@hide}
146 */
147 public int mEventType;
148
149 /**
Adam Lesinski7f61e962014-09-02 16:43:52 -0700150 * Only present for {@link #CONFIGURATION_CHANGE} event types.
151 * {@hide}
152 */
153 public Configuration mConfiguration;
154
155 /**
Makoto Onukiac042502016-05-20 16:39:42 -0700156 * ID of the shortcut.
157 * Only present for {@link #SHORTCUT_INVOCATION} event types.
158 * {@hide}
159 */
160 public String mShortcutId;
161
162 /**
Kang Li53b43142016-11-14 14:38:25 -0800163 * Action type passed to ChooserActivity
164 * Only present for {@link #CHOOSER_ACTION} event types.
165 * {@hide}
166 */
167 public String mAction;
168
169 /**
170 * Content type passed to ChooserActivity.
171 * Only present for {@link #CHOOSER_ACTION} event types.
172 * {@hide}
173 */
174 public String mContentType;
175
176 /**
177 * Content annotations passed to ChooserActivity.
178 * Only present for {@link #CHOOSER_ACTION} event types.
179 * {@hide}
180 */
181 public String[] mContentAnnotations;
182
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800183 /**
Amith Yamasani119be9a2018-02-18 22:23:00 -0800184 * The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
185 * is the low order 16 bits.
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800186 * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
187 * {@hide}
188 */
Amith Yamasani119be9a2018-02-18 22:23:00 -0800189 public int mBucketAndReason;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800190
Makoto Onukiad623012017-05-15 09:29:34 -0700191 /** @hide */
192 @EventFlags
193 public int mFlags;
194
195 public Event() {
196 }
197
198 /** @hide */
199 public Event(Event orig) {
200 mPackage = orig.mPackage;
201 mClass = orig.mClass;
202 mTimeStamp = orig.mTimeStamp;
203 mEventType = orig.mEventType;
204 mConfiguration = orig.mConfiguration;
205 mShortcutId = orig.mShortcutId;
206 mAction = orig.mAction;
207 mContentType = orig.mContentType;
208 mContentAnnotations = orig.mContentAnnotations;
209 mFlags = orig.mFlags;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800210 mBucketAndReason = orig.mBucketAndReason;
Makoto Onukiad623012017-05-15 09:29:34 -0700211 }
212
Kang Li53b43142016-11-14 14:38:25 -0800213 /**
Adam Lesinski9d960752014-08-25 14:48:12 -0700214 * The package name of the source of this event.
215 */
216 public String getPackageName() {
217 return mPackage;
218 }
219
220 /**
221 * The class name of the source of this event. This may be null for
222 * certain events.
223 */
224 public String getClassName() {
225 return mClass;
Adam Lesinski35168002014-07-21 15:25:30 -0700226 }
227
228 /**
Adam Lesinskicc562a82014-08-27 11:52:52 -0700229 * The time at which this event occurred, measured in milliseconds since the epoch.
230 * <p/>
231 * See {@link System#currentTimeMillis()}.
Adam Lesinski35168002014-07-21 15:25:30 -0700232 */
233 public long getTimeStamp() {
234 return mTimeStamp;
235 }
236
237 /**
238 * The event type.
239 *
240 * See {@link #MOVE_TO_BACKGROUND}
241 * See {@link #MOVE_TO_FOREGROUND}
242 */
243 public int getEventType() {
244 return mEventType;
245 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700246
247 /**
248 * Returns a {@link Configuration} for this event if the event is of type
249 * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
250 */
251 public Configuration getConfiguration() {
252 return mConfiguration;
253 }
Makoto Onukiac042502016-05-20 16:39:42 -0700254
255 /**
256 * Returns the ID of a {@link android.content.pm.ShortcutInfo} for this event
257 * if the event is of type {@link #SHORTCUT_INVOCATION}, otherwise it returns null.
258 *
259 * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
260 */
261 public String getShortcutId() {
262 return mShortcutId;
263 }
Makoto Onukiad623012017-05-15 09:29:34 -0700264
Amith Yamasani7ec89412018-02-07 08:48:49 -0800265 /**
266 * Returns the standby bucket of the app, if the event is of type
267 * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
268 * @return the standby bucket associated with the event.
269 * @hide
270 */
271 @SystemApi
272 public int getStandbyBucket() {
Amith Yamasani119be9a2018-02-18 22:23:00 -0800273 return (mBucketAndReason & 0xFFFF0000) >>> 16;
274 }
275
276 /**
277 * Returns the reason for the bucketing, if the event is of type
278 * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
279 * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
280 * are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
281 * is REASON_MAIN_USAGE.
282 * @hide
283 */
284 public int getStandbyReason() {
285 return mBucketAndReason & 0x0000FFFF;
Amith Yamasani7ec89412018-02-07 08:48:49 -0800286 }
287
Makoto Onukiad623012017-05-15 09:29:34 -0700288 /** @hide */
289 public Event getObfuscatedIfInstantApp() {
290 if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
291 return this;
292 }
293 final Event ret = new Event(this);
294 ret.mPackage = INSTANT_APP_PACKAGE_NAME;
295 ret.mClass = INSTANT_APP_CLASS_NAME;
296
297 // Note there are other string fields too, but they're for app shortcuts and choosers,
298 // which instant apps can't use anyway, so there's no need to hide them.
299 return ret;
300 }
Adam Lesinski35168002014-07-21 15:25:30 -0700301 }
302
303 // Only used when creating the resulting events. Not used for reading/unparceling.
304 private List<Event> mEventsToWrite = null;
305
306 // Only used for reading/unparceling events.
307 private Parcel mParcel = null;
308 private final int mEventCount;
309
310 private int mIndex = 0;
311
312 /*
313 * In order to save space, since ComponentNames will be duplicated everywhere,
314 * we use a map and index into it.
315 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700316 private String[] mStringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700317
318 /**
319 * Construct the iterator from a parcel.
320 * {@hide}
321 */
322 public UsageEvents(Parcel in) {
323 mEventCount = in.readInt();
324 mIndex = in.readInt();
325 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700326 mStringPool = in.createStringArray();
Adam Lesinski35168002014-07-21 15:25:30 -0700327
328 final int listByteLength = in.readInt();
329 final int positionInParcel = in.readInt();
330 mParcel = Parcel.obtain();
331 mParcel.setDataPosition(0);
332 mParcel.appendFrom(in, in.dataPosition(), listByteLength);
333 mParcel.setDataSize(mParcel.dataPosition());
334 mParcel.setDataPosition(positionInParcel);
335 }
336 }
337
338 /**
339 * Create an empty iterator.
340 * {@hide}
341 */
342 UsageEvents() {
343 mEventCount = 0;
344 }
345
346 /**
347 * Construct the iterator in preparation for writing it to a parcel.
348 * {@hide}
349 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700350 public UsageEvents(List<Event> events, String[] stringPool) {
351 mStringPool = stringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700352 mEventCount = events.size();
353 mEventsToWrite = events;
354 }
355
356 /**
357 * Returns whether or not there are more events to read using
358 * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
359 *
360 * @return true if there are more events, false otherwise.
361 */
362 public boolean hasNextEvent() {
363 return mIndex < mEventCount;
364 }
365
366 /**
367 * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
368 * resulting data into {@code eventOut}.
369 *
370 * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
371 * next event data.
372 * @return true if an event was available, false if there are no more events.
373 */
374 public boolean getNextEvent(Event eventOut) {
375 if (mIndex >= mEventCount) {
376 return false;
377 }
378
Adam Lesinski7f61e962014-09-02 16:43:52 -0700379 readEventFromParcel(mParcel, eventOut);
Adam Lesinski9d960752014-08-25 14:48:12 -0700380
Adam Lesinski35168002014-07-21 15:25:30 -0700381 mIndex++;
Adam Lesinski35168002014-07-21 15:25:30 -0700382 if (mIndex >= mEventCount) {
383 mParcel.recycle();
384 mParcel = null;
385 }
386 return true;
387 }
388
389 /**
390 * Resets the collection so that it can be iterated over from the beginning.
Adam Lesinski54e064b2014-10-08 12:33:16 -0700391 *
392 * @hide When this object is iterated to completion, the parcel is destroyed and
393 * so resetToStart doesn't work.
Adam Lesinski35168002014-07-21 15:25:30 -0700394 */
395 public void resetToStart() {
396 mIndex = 0;
397 if (mParcel != null) {
398 mParcel.setDataPosition(0);
399 }
400 }
401
Adam Lesinski9d960752014-08-25 14:48:12 -0700402 private int findStringIndex(String str) {
403 final int index = Arrays.binarySearch(mStringPool, str);
404 if (index < 0) {
405 throw new IllegalStateException("String '" + str + "' is not in the string pool");
406 }
407 return index;
408 }
409
Adam Lesinski7f61e962014-09-02 16:43:52 -0700410 /**
411 * Writes a single event to the parcel. Modify this when updating {@link Event}.
412 */
413 private void writeEventToParcel(Event event, Parcel p, int flags) {
414 final int packageIndex;
415 if (event.mPackage != null) {
416 packageIndex = findStringIndex(event.mPackage);
417 } else {
418 packageIndex = -1;
419 }
420
421 final int classIndex;
422 if (event.mClass != null) {
423 classIndex = findStringIndex(event.mClass);
424 } else {
425 classIndex = -1;
426 }
427 p.writeInt(packageIndex);
428 p.writeInt(classIndex);
429 p.writeInt(event.mEventType);
430 p.writeLong(event.mTimeStamp);
431
Makoto Onukiac042502016-05-20 16:39:42 -0700432 switch (event.mEventType) {
433 case Event.CONFIGURATION_CHANGE:
434 event.mConfiguration.writeToParcel(p, flags);
435 break;
436 case Event.SHORTCUT_INVOCATION:
437 p.writeString(event.mShortcutId);
438 break;
Kang Li53b43142016-11-14 14:38:25 -0800439 case Event.CHOOSER_ACTION:
440 p.writeString(event.mAction);
441 p.writeString(event.mContentType);
442 p.writeStringArray(event.mContentAnnotations);
443 break;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800444 case Event.STANDBY_BUCKET_CHANGED:
Amith Yamasani119be9a2018-02-18 22:23:00 -0800445 p.writeInt(event.mBucketAndReason);
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800446 break;
Adam Lesinski7f61e962014-09-02 16:43:52 -0700447 }
448 }
449
450 /**
451 * Reads a single event from the parcel. Modify this when updating {@link Event}.
452 */
453 private void readEventFromParcel(Parcel p, Event eventOut) {
454 final int packageIndex = p.readInt();
455 if (packageIndex >= 0) {
456 eventOut.mPackage = mStringPool[packageIndex];
457 } else {
458 eventOut.mPackage = null;
459 }
460
461 final int classIndex = p.readInt();
462 if (classIndex >= 0) {
463 eventOut.mClass = mStringPool[classIndex];
464 } else {
465 eventOut.mClass = null;
466 }
467 eventOut.mEventType = p.readInt();
468 eventOut.mTimeStamp = p.readLong();
469
Makoto Onukiac042502016-05-20 16:39:42 -0700470 // Fill out the event-dependant fields.
471 eventOut.mConfiguration = null;
472 eventOut.mShortcutId = null;
Kang Li53b43142016-11-14 14:38:25 -0800473 eventOut.mAction = null;
474 eventOut.mContentType = null;
475 eventOut.mContentAnnotations = null;
Makoto Onukiac042502016-05-20 16:39:42 -0700476
477 switch (eventOut.mEventType) {
478 case Event.CONFIGURATION_CHANGE:
479 // Extract the configuration for configuration change events.
480 eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
481 break;
482 case Event.SHORTCUT_INVOCATION:
483 eventOut.mShortcutId = p.readString();
484 break;
Kang Li53b43142016-11-14 14:38:25 -0800485 case Event.CHOOSER_ACTION:
486 eventOut.mAction = p.readString();
487 eventOut.mContentType = p.readString();
488 eventOut.mContentAnnotations = p.createStringArray();
489 break;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800490 case Event.STANDBY_BUCKET_CHANGED:
Amith Yamasani119be9a2018-02-18 22:23:00 -0800491 eventOut.mBucketAndReason = p.readInt();
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800492 break;
Adam Lesinski7f61e962014-09-02 16:43:52 -0700493 }
494 }
495
496 @Override
497 public int describeContents() {
498 return 0;
499 }
500
Adam Lesinski35168002014-07-21 15:25:30 -0700501 @Override
502 public void writeToParcel(Parcel dest, int flags) {
503 dest.writeInt(mEventCount);
504 dest.writeInt(mIndex);
505 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700506 dest.writeStringArray(mStringPool);
Adam Lesinski35168002014-07-21 15:25:30 -0700507
508 if (mEventsToWrite != null) {
509 // Write out the events
510 Parcel p = Parcel.obtain();
511 try {
512 p.setDataPosition(0);
513 for (int i = 0; i < mEventCount; i++) {
514 final Event event = mEventsToWrite.get(i);
Adam Lesinski7f61e962014-09-02 16:43:52 -0700515 writeEventToParcel(event, p, flags);
Adam Lesinski35168002014-07-21 15:25:30 -0700516 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700517
Adam Lesinski35168002014-07-21 15:25:30 -0700518 final int listByteLength = p.dataPosition();
519
520 // Write the total length of the data.
521 dest.writeInt(listByteLength);
522
523 // Write our current position into the data.
524 dest.writeInt(0);
525
526 // Write the data.
527 dest.appendFrom(p, 0, listByteLength);
528 } finally {
529 p.recycle();
530 }
531
532 } else if (mParcel != null) {
533 // Write the total length of the data.
534 dest.writeInt(mParcel.dataSize());
535
536 // Write out current position into the data.
537 dest.writeInt(mParcel.dataPosition());
538
539 // Write the data.
540 dest.appendFrom(mParcel, 0, mParcel.dataSize());
541 } else {
542 throw new IllegalStateException(
543 "Either mParcel or mEventsToWrite must not be null");
544 }
545 }
546 }
547
548 public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
549 @Override
550 public UsageEvents createFromParcel(Parcel source) {
551 return new UsageEvents(source);
552 }
553
554 @Override
555 public UsageEvents[] newArray(int size) {
556 return new UsageEvents[size];
557 }
558 };
Adam Lesinski35168002014-07-21 15:25:30 -0700559}