blob: b354e8122a980a3ac0deb1ee0e2464496944d0af [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 */
Michael Wachenschwanzf0acb022018-03-15 20:17:22 -070084 @SystemApi
Adam Lesinskic8e87292015-06-10 15:33:45 -070085 public static final int SYSTEM_INTERACTION = 6;
86
87 /**
88 * An event type denoting that a package was interacted with in some way by the user.
89 */
90 public static final int USER_INTERACTION = 7;
Adam Lesinski978a1ed2015-03-02 11:37:24 -080091
92 /**
Makoto Onukiac042502016-05-20 16:39:42 -070093 * An event type denoting that an action equivalent to a ShortcutInfo is taken by the user.
94 *
95 * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
96 */
97 public static final int SHORTCUT_INVOCATION = 8;
98
99 /**
Kang Li53b43142016-11-14 14:38:25 -0800100 * An event type denoting that a package was selected by the user for ChooserActivity.
101 * @hide
102 */
103 public static final int CHOOSER_ACTION = 9;
104
Amith Yamasani803eab692017-11-09 17:47:04 -0800105 /**
106 * An event type denoting that a notification was viewed by the user.
107 * @hide
108 */
Amith Yamasani7ec89412018-02-07 08:48:49 -0800109 @SystemApi
Amith Yamasani803eab692017-11-09 17:47:04 -0800110 public static final int NOTIFICATION_SEEN = 10;
111
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800112 /**
Suprabh Shukla4e12de82018-03-08 18:34:15 -0800113 * An event type denoting a change in App Standby Bucket. The new bucket can be
114 * retrieved by calling {@link #getStandbyBucket()}.
115 *
116 * @see UsageStatsManager#getAppStandbyBucket()
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800117 */
118 public static final int STANDBY_BUCKET_CHANGED = 11;
119
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500120 /**
121 * An event type denoting that an app posted an interruptive notification. Visual and
122 * audible interruptions are included.
123 * @hide
124 */
125 @SystemApi
126 public static final int NOTIFICATION_INTERRUPTION = 12;
127
Jason Monk1918ef72018-03-14 09:20:39 -0400128 /**
129 * A Slice was pinned by the default launcher or the default assistant.
130 * @hide
131 */
132 @SystemApi
133 public static final int SLICE_PINNED_PRIV = 13;
134
135 /**
136 * A Slice was pinned by an app.
137 * @hide
138 */
139 @SystemApi
140 public static final int SLICE_PINNED = 14;
141
Makoto Onukiad623012017-05-15 09:29:34 -0700142 /** @hide */
143 public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
144
145 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700146 @IntDef(flag = true, prefix = { "FLAG_" }, value = {
147 FLAG_IS_PACKAGE_INSTANT_APP,
148 })
Makoto Onukiad623012017-05-15 09:29:34 -0700149 @Retention(RetentionPolicy.SOURCE)
150 public @interface EventFlags {}
151
Kang Li53b43142016-11-14 14:38:25 -0800152 /**
Adam Lesinski35168002014-07-21 15:25:30 -0700153 * {@hide}
154 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700155 public String mPackage;
156
157 /**
158 * {@hide}
159 */
160 public String mClass;
Adam Lesinski35168002014-07-21 15:25:30 -0700161
162 /**
163 * {@hide}
164 */
165 public long mTimeStamp;
166
167 /**
168 * {@hide}
169 */
170 public int mEventType;
171
172 /**
Adam Lesinski7f61e962014-09-02 16:43:52 -0700173 * Only present for {@link #CONFIGURATION_CHANGE} event types.
174 * {@hide}
175 */
176 public Configuration mConfiguration;
177
178 /**
Makoto Onukiac042502016-05-20 16:39:42 -0700179 * ID of the shortcut.
180 * Only present for {@link #SHORTCUT_INVOCATION} event types.
181 * {@hide}
182 */
183 public String mShortcutId;
184
185 /**
Kang Li53b43142016-11-14 14:38:25 -0800186 * Action type passed to ChooserActivity
187 * Only present for {@link #CHOOSER_ACTION} event types.
188 * {@hide}
189 */
190 public String mAction;
191
192 /**
193 * Content type passed to ChooserActivity.
194 * Only present for {@link #CHOOSER_ACTION} event types.
195 * {@hide}
196 */
197 public String mContentType;
198
199 /**
200 * Content annotations passed to ChooserActivity.
201 * Only present for {@link #CHOOSER_ACTION} event types.
202 * {@hide}
203 */
204 public String[] mContentAnnotations;
205
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800206 /**
Amith Yamasani119be9a2018-02-18 22:23:00 -0800207 * The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
208 * is the low order 16 bits.
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800209 * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
210 * {@hide}
211 */
Amith Yamasani119be9a2018-02-18 22:23:00 -0800212 public int mBucketAndReason;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800213
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500214 /**
215 * The id of the {@link android.app.NotificationChannel} to which an interruptive
216 * notification was posted.
217 * Only present for {@link #NOTIFICATION_INTERRUPTION} event types.
218 * {@hide}
219 */
220 public String mNotificationChannelId;
221
Makoto Onukiad623012017-05-15 09:29:34 -0700222 /** @hide */
223 @EventFlags
224 public int mFlags;
225
226 public Event() {
227 }
228
229 /** @hide */
230 public Event(Event orig) {
231 mPackage = orig.mPackage;
232 mClass = orig.mClass;
233 mTimeStamp = orig.mTimeStamp;
234 mEventType = orig.mEventType;
235 mConfiguration = orig.mConfiguration;
236 mShortcutId = orig.mShortcutId;
237 mAction = orig.mAction;
238 mContentType = orig.mContentType;
239 mContentAnnotations = orig.mContentAnnotations;
240 mFlags = orig.mFlags;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800241 mBucketAndReason = orig.mBucketAndReason;
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500242 mNotificationChannelId = orig.mNotificationChannelId;
Makoto Onukiad623012017-05-15 09:29:34 -0700243 }
244
Kang Li53b43142016-11-14 14:38:25 -0800245 /**
Adam Lesinski9d960752014-08-25 14:48:12 -0700246 * The package name of the source of this event.
247 */
248 public String getPackageName() {
249 return mPackage;
250 }
251
252 /**
253 * The class name of the source of this event. This may be null for
254 * certain events.
255 */
256 public String getClassName() {
257 return mClass;
Adam Lesinski35168002014-07-21 15:25:30 -0700258 }
259
260 /**
Adam Lesinskicc562a82014-08-27 11:52:52 -0700261 * The time at which this event occurred, measured in milliseconds since the epoch.
262 * <p/>
263 * See {@link System#currentTimeMillis()}.
Adam Lesinski35168002014-07-21 15:25:30 -0700264 */
265 public long getTimeStamp() {
266 return mTimeStamp;
267 }
268
269 /**
270 * The event type.
271 *
Suprabh Shukla4e12de82018-03-08 18:34:15 -0800272 * @see #MOVE_TO_BACKGROUND
273 * @see #MOVE_TO_FOREGROUND
274 * @see #CONFIGURATION_CHANGE
275 * @see #USER_INTERACTION
276 * @see #STANDBY_BUCKET_CHANGED
Adam Lesinski35168002014-07-21 15:25:30 -0700277 */
278 public int getEventType() {
279 return mEventType;
280 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700281
282 /**
283 * Returns a {@link Configuration} for this event if the event is of type
284 * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
285 */
286 public Configuration getConfiguration() {
287 return mConfiguration;
288 }
Makoto Onukiac042502016-05-20 16:39:42 -0700289
290 /**
291 * Returns the ID of a {@link android.content.pm.ShortcutInfo} for this event
292 * if the event is of type {@link #SHORTCUT_INVOCATION}, otherwise it returns null.
293 *
294 * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
295 */
296 public String getShortcutId() {
297 return mShortcutId;
298 }
Makoto Onukiad623012017-05-15 09:29:34 -0700299
Amith Yamasani7ec89412018-02-07 08:48:49 -0800300 /**
301 * Returns the standby bucket of the app, if the event is of type
302 * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
303 * @return the standby bucket associated with the event.
Suprabh Shukla4e12de82018-03-08 18:34:15 -0800304 *
Amith Yamasani7ec89412018-02-07 08:48:49 -0800305 */
Amith Yamasani7ec89412018-02-07 08:48:49 -0800306 public int getStandbyBucket() {
Amith Yamasani119be9a2018-02-18 22:23:00 -0800307 return (mBucketAndReason & 0xFFFF0000) >>> 16;
308 }
309
310 /**
311 * Returns the reason for the bucketing, if the event is of type
312 * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
313 * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
314 * are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
315 * is REASON_MAIN_USAGE.
316 * @hide
317 */
318 public int getStandbyReason() {
319 return mBucketAndReason & 0x0000FFFF;
Amith Yamasani7ec89412018-02-07 08:48:49 -0800320 }
321
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500322 /**
323 * Returns the ID of the {@link android.app.NotificationChannel} for this event if the
324 * event is of type {@link #NOTIFICATION_INTERRUPTION}, otherwise it returns null;
325 * @hide
326 */
327 @SystemApi
328 public String getNotificationChannelId() {
329 return mNotificationChannelId;
330 }
331
Makoto Onukiad623012017-05-15 09:29:34 -0700332 /** @hide */
333 public Event getObfuscatedIfInstantApp() {
334 if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
335 return this;
336 }
337 final Event ret = new Event(this);
338 ret.mPackage = INSTANT_APP_PACKAGE_NAME;
339 ret.mClass = INSTANT_APP_CLASS_NAME;
340
341 // Note there are other string fields too, but they're for app shortcuts and choosers,
342 // which instant apps can't use anyway, so there's no need to hide them.
343 return ret;
344 }
Adam Lesinski35168002014-07-21 15:25:30 -0700345 }
346
347 // Only used when creating the resulting events. Not used for reading/unparceling.
348 private List<Event> mEventsToWrite = null;
349
350 // Only used for reading/unparceling events.
351 private Parcel mParcel = null;
352 private final int mEventCount;
353
354 private int mIndex = 0;
355
356 /*
357 * In order to save space, since ComponentNames will be duplicated everywhere,
358 * we use a map and index into it.
359 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700360 private String[] mStringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700361
362 /**
363 * Construct the iterator from a parcel.
364 * {@hide}
365 */
366 public UsageEvents(Parcel in) {
367 mEventCount = in.readInt();
368 mIndex = in.readInt();
369 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700370 mStringPool = in.createStringArray();
Adam Lesinski35168002014-07-21 15:25:30 -0700371
372 final int listByteLength = in.readInt();
373 final int positionInParcel = in.readInt();
374 mParcel = Parcel.obtain();
375 mParcel.setDataPosition(0);
376 mParcel.appendFrom(in, in.dataPosition(), listByteLength);
377 mParcel.setDataSize(mParcel.dataPosition());
378 mParcel.setDataPosition(positionInParcel);
379 }
380 }
381
382 /**
383 * Create an empty iterator.
384 * {@hide}
385 */
386 UsageEvents() {
387 mEventCount = 0;
388 }
389
390 /**
391 * Construct the iterator in preparation for writing it to a parcel.
392 * {@hide}
393 */
Adam Lesinski9d960752014-08-25 14:48:12 -0700394 public UsageEvents(List<Event> events, String[] stringPool) {
395 mStringPool = stringPool;
Adam Lesinski35168002014-07-21 15:25:30 -0700396 mEventCount = events.size();
397 mEventsToWrite = events;
398 }
399
400 /**
401 * Returns whether or not there are more events to read using
402 * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
403 *
404 * @return true if there are more events, false otherwise.
405 */
406 public boolean hasNextEvent() {
407 return mIndex < mEventCount;
408 }
409
410 /**
411 * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
412 * resulting data into {@code eventOut}.
413 *
414 * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
415 * next event data.
416 * @return true if an event was available, false if there are no more events.
417 */
418 public boolean getNextEvent(Event eventOut) {
419 if (mIndex >= mEventCount) {
420 return false;
421 }
422
Adam Lesinski7f61e962014-09-02 16:43:52 -0700423 readEventFromParcel(mParcel, eventOut);
Adam Lesinski9d960752014-08-25 14:48:12 -0700424
Adam Lesinski35168002014-07-21 15:25:30 -0700425 mIndex++;
Adam Lesinski35168002014-07-21 15:25:30 -0700426 if (mIndex >= mEventCount) {
427 mParcel.recycle();
428 mParcel = null;
429 }
430 return true;
431 }
432
433 /**
434 * Resets the collection so that it can be iterated over from the beginning.
Adam Lesinski54e064b2014-10-08 12:33:16 -0700435 *
436 * @hide When this object is iterated to completion, the parcel is destroyed and
437 * so resetToStart doesn't work.
Adam Lesinski35168002014-07-21 15:25:30 -0700438 */
439 public void resetToStart() {
440 mIndex = 0;
441 if (mParcel != null) {
442 mParcel.setDataPosition(0);
443 }
444 }
445
Adam Lesinski9d960752014-08-25 14:48:12 -0700446 private int findStringIndex(String str) {
447 final int index = Arrays.binarySearch(mStringPool, str);
448 if (index < 0) {
449 throw new IllegalStateException("String '" + str + "' is not in the string pool");
450 }
451 return index;
452 }
453
Adam Lesinski7f61e962014-09-02 16:43:52 -0700454 /**
455 * Writes a single event to the parcel. Modify this when updating {@link Event}.
456 */
457 private void writeEventToParcel(Event event, Parcel p, int flags) {
458 final int packageIndex;
459 if (event.mPackage != null) {
460 packageIndex = findStringIndex(event.mPackage);
461 } else {
462 packageIndex = -1;
463 }
464
465 final int classIndex;
466 if (event.mClass != null) {
467 classIndex = findStringIndex(event.mClass);
468 } else {
469 classIndex = -1;
470 }
471 p.writeInt(packageIndex);
472 p.writeInt(classIndex);
473 p.writeInt(event.mEventType);
474 p.writeLong(event.mTimeStamp);
475
Makoto Onukiac042502016-05-20 16:39:42 -0700476 switch (event.mEventType) {
477 case Event.CONFIGURATION_CHANGE:
478 event.mConfiguration.writeToParcel(p, flags);
479 break;
480 case Event.SHORTCUT_INVOCATION:
481 p.writeString(event.mShortcutId);
482 break;
Kang Li53b43142016-11-14 14:38:25 -0800483 case Event.CHOOSER_ACTION:
484 p.writeString(event.mAction);
485 p.writeString(event.mContentType);
486 p.writeStringArray(event.mContentAnnotations);
487 break;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800488 case Event.STANDBY_BUCKET_CHANGED:
Amith Yamasani119be9a2018-02-18 22:23:00 -0800489 p.writeInt(event.mBucketAndReason);
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800490 break;
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500491 case Event.NOTIFICATION_INTERRUPTION:
492 p.writeString(event.mNotificationChannelId);
493 break;
Adam Lesinski7f61e962014-09-02 16:43:52 -0700494 }
495 }
496
497 /**
498 * Reads a single event from the parcel. Modify this when updating {@link Event}.
499 */
500 private void readEventFromParcel(Parcel p, Event eventOut) {
501 final int packageIndex = p.readInt();
502 if (packageIndex >= 0) {
503 eventOut.mPackage = mStringPool[packageIndex];
504 } else {
505 eventOut.mPackage = null;
506 }
507
508 final int classIndex = p.readInt();
509 if (classIndex >= 0) {
510 eventOut.mClass = mStringPool[classIndex];
511 } else {
512 eventOut.mClass = null;
513 }
514 eventOut.mEventType = p.readInt();
515 eventOut.mTimeStamp = p.readLong();
516
Makoto Onukiac042502016-05-20 16:39:42 -0700517 // Fill out the event-dependant fields.
518 eventOut.mConfiguration = null;
519 eventOut.mShortcutId = null;
Kang Li53b43142016-11-14 14:38:25 -0800520 eventOut.mAction = null;
521 eventOut.mContentType = null;
522 eventOut.mContentAnnotations = null;
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500523 eventOut.mNotificationChannelId = null;
Makoto Onukiac042502016-05-20 16:39:42 -0700524
525 switch (eventOut.mEventType) {
526 case Event.CONFIGURATION_CHANGE:
527 // Extract the configuration for configuration change events.
528 eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
529 break;
530 case Event.SHORTCUT_INVOCATION:
531 eventOut.mShortcutId = p.readString();
532 break;
Kang Li53b43142016-11-14 14:38:25 -0800533 case Event.CHOOSER_ACTION:
534 eventOut.mAction = p.readString();
535 eventOut.mContentType = p.readString();
536 eventOut.mContentAnnotations = p.createStringArray();
537 break;
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800538 case Event.STANDBY_BUCKET_CHANGED:
Amith Yamasani119be9a2018-02-18 22:23:00 -0800539 eventOut.mBucketAndReason = p.readInt();
Amith Yamasanibfc4bf52018-01-19 06:55:08 -0800540 break;
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500541 case Event.NOTIFICATION_INTERRUPTION:
542 eventOut.mNotificationChannelId = p.readString();
543 break;
Adam Lesinski7f61e962014-09-02 16:43:52 -0700544 }
545 }
546
547 @Override
548 public int describeContents() {
549 return 0;
550 }
551
Adam Lesinski35168002014-07-21 15:25:30 -0700552 @Override
553 public void writeToParcel(Parcel dest, int flags) {
554 dest.writeInt(mEventCount);
555 dest.writeInt(mIndex);
556 if (mEventCount > 0) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700557 dest.writeStringArray(mStringPool);
Adam Lesinski35168002014-07-21 15:25:30 -0700558
559 if (mEventsToWrite != null) {
560 // Write out the events
561 Parcel p = Parcel.obtain();
562 try {
563 p.setDataPosition(0);
564 for (int i = 0; i < mEventCount; i++) {
565 final Event event = mEventsToWrite.get(i);
Adam Lesinski7f61e962014-09-02 16:43:52 -0700566 writeEventToParcel(event, p, flags);
Adam Lesinski35168002014-07-21 15:25:30 -0700567 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700568
Adam Lesinski35168002014-07-21 15:25:30 -0700569 final int listByteLength = p.dataPosition();
570
571 // Write the total length of the data.
572 dest.writeInt(listByteLength);
573
574 // Write our current position into the data.
575 dest.writeInt(0);
576
577 // Write the data.
578 dest.appendFrom(p, 0, listByteLength);
579 } finally {
580 p.recycle();
581 }
582
583 } else if (mParcel != null) {
584 // Write the total length of the data.
585 dest.writeInt(mParcel.dataSize());
586
587 // Write out current position into the data.
588 dest.writeInt(mParcel.dataPosition());
589
590 // Write the data.
591 dest.appendFrom(mParcel, 0, mParcel.dataSize());
592 } else {
593 throw new IllegalStateException(
594 "Either mParcel or mEventsToWrite must not be null");
595 }
596 }
597 }
598
599 public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
600 @Override
601 public UsageEvents createFromParcel(Parcel source) {
602 return new UsageEvents(source);
603 }
604
605 @Override
606 public UsageEvents[] newArray(int size) {
607 return new UsageEvents[size];
608 }
609 };
Adam Lesinski35168002014-07-21 15:25:30 -0700610}