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 package android.app.usage;
17 
18 import android.annotation.IntDef;
19 import android.annotation.SystemApi;
20 import android.content.res.Configuration;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 import java.util.Arrays;
27 import 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  */
33 public final class UsageEvents implements Parcelable {
34 
35     /** @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 
41     /**
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         /**
76          * An event type denoting that the device configuration has changed.
77          */
78         public static final int CONFIGURATION_CHANGE = 5;
79 
80         /**
81          * An event type denoting that a package was interacted with in some way by the system.
82          * @hide
83          */
84         @SystemApi
85         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;
91 
92         /**
93          * 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         /**
100          * 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 
105         /**
106          * An event type denoting that a notification was viewed by the user.
107          * @hide
108          */
109         @SystemApi
110         public static final int NOTIFICATION_SEEN = 10;
111 
112         /**
113          * An event type denoting a change in App Standby Bucket. The new bucket can be
114          * retrieved by calling {@link #getAppStandbyBucket()}.
115          *
116          * @see UsageStatsManager#getAppStandbyBucket()
117          */
118         public static final int STANDBY_BUCKET_CHANGED = 11;
119 
120         /**
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 
128         /**
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 
142         /**
143          * An event type denoting that the screen has gone in to an interactive state (turned
144          * on for full user interaction, not ambient display or other non-interactive state).
145          */
146         public static final int SCREEN_INTERACTIVE = 15;
147 
148         /**
149          * An event type denoting that the screen has gone in to a non-interactive state
150          * (completely turned off or turned on only in a non-interactive state like ambient
151          * display).
152          */
153         public static final int SCREEN_NON_INTERACTIVE = 16;
154 
155         /**
156          * An event type denoting that the screen's keyguard has been shown, whether or not
157          * the screen is off.
158          */
159         public static final int KEYGUARD_SHOWN = 17;
160 
161         /**
162          * An event type denoting that the screen's keyguard has been hidden.  This typically
163          * happens when the user unlocks their phone after turning it on.
164          */
165         public static final int KEYGUARD_HIDDEN = 18;
166 
167         /** @hide */
168         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
169 
170         /** @hide */
171         @IntDef(flag = true, prefix = { "FLAG_" }, value = {
172                 FLAG_IS_PACKAGE_INSTANT_APP,
173         })
174         @Retention(RetentionPolicy.SOURCE)
175         public @interface EventFlags {}
176 
177         /**
178          * {@hide}
179          */
180         public String mPackage;
181 
182         /**
183          * {@hide}
184          */
185         public String mClass;
186 
187         /**
188          * {@hide}
189          */
190         public long mTimeStamp;
191 
192         /**
193          * {@hide}
194          */
195         public int mEventType;
196 
197         /**
198          * Only present for {@link #CONFIGURATION_CHANGE} event types.
199          * {@hide}
200          */
201         public Configuration mConfiguration;
202 
203         /**
204          * ID of the shortcut.
205          * Only present for {@link #SHORTCUT_INVOCATION} event types.
206          * {@hide}
207          */
208         public String mShortcutId;
209 
210         /**
211          * Action type passed to ChooserActivity
212          * Only present for {@link #CHOOSER_ACTION} event types.
213          * {@hide}
214          */
215         public String mAction;
216 
217         /**
218          * Content type passed to ChooserActivity.
219          * Only present for {@link #CHOOSER_ACTION} event types.
220          * {@hide}
221          */
222         public String mContentType;
223 
224         /**
225          * Content annotations passed to ChooserActivity.
226          * Only present for {@link #CHOOSER_ACTION} event types.
227          * {@hide}
228          */
229         public String[] mContentAnnotations;
230 
231         /**
232          * The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
233          * is the low order 16 bits.
234          * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
235          * {@hide}
236          */
237         public int mBucketAndReason;
238 
239         /**
240          * The id of the {@link android.app.NotificationChannel} to which an interruptive
241          * notification was posted.
242          * Only present for {@link #NOTIFICATION_INTERRUPTION} event types.
243          * {@hide}
244          */
245         public String mNotificationChannelId;
246 
247         /** @hide */
248         @EventFlags
249         public int mFlags;
250 
Event()251         public Event() {
252         }
253 
254         /** @hide */
Event(Event orig)255         public Event(Event orig) {
256             mPackage = orig.mPackage;
257             mClass = orig.mClass;
258             mTimeStamp = orig.mTimeStamp;
259             mEventType = orig.mEventType;
260             mConfiguration = orig.mConfiguration;
261             mShortcutId = orig.mShortcutId;
262             mAction = orig.mAction;
263             mContentType = orig.mContentType;
264             mContentAnnotations = orig.mContentAnnotations;
265             mFlags = orig.mFlags;
266             mBucketAndReason = orig.mBucketAndReason;
267             mNotificationChannelId = orig.mNotificationChannelId;
268         }
269 
270         /**
271          * The package name of the source of this event.
272          */
getPackageName()273         public String getPackageName() {
274             return mPackage;
275         }
276 
277         /**
278          * The class name of the source of this event. This may be null for
279          * certain events.
280          */
getClassName()281         public String getClassName() {
282             return mClass;
283         }
284 
285         /**
286          * The time at which this event occurred, measured in milliseconds since the epoch.
287          * <p/>
288          * See {@link System#currentTimeMillis()}.
289          */
getTimeStamp()290         public long getTimeStamp() {
291             return mTimeStamp;
292         }
293 
294         /**
295          * The event type.
296          *
297          * @see #MOVE_TO_BACKGROUND
298          * @see #MOVE_TO_FOREGROUND
299          * @see #CONFIGURATION_CHANGE
300          * @see #USER_INTERACTION
301          * @see #STANDBY_BUCKET_CHANGED
302          */
getEventType()303         public int getEventType() {
304             return mEventType;
305         }
306 
307         /**
308          * Returns a {@link Configuration} for this event if the event is of type
309          * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
310          */
getConfiguration()311         public Configuration getConfiguration() {
312             return mConfiguration;
313         }
314 
315         /**
316          * Returns the ID of a {@link android.content.pm.ShortcutInfo} for this event
317          * if the event is of type {@link #SHORTCUT_INVOCATION}, otherwise it returns null.
318          *
319          * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
320          */
getShortcutId()321         public String getShortcutId() {
322             return mShortcutId;
323         }
324 
325         /**
326          * Returns the standby bucket of the app, if the event is of type
327          * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
328          * @return the standby bucket associated with the event.
329          * @hide
330          */
getStandbyBucket()331         public int getStandbyBucket() {
332             return (mBucketAndReason & 0xFFFF0000) >>> 16;
333         }
334 
335         /**
336          * Returns the standby bucket of the app, if the event is of type
337          * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
338          * @return the standby bucket associated with the event.
339          *
340          */
getAppStandbyBucket()341         public int getAppStandbyBucket() {
342             return (mBucketAndReason & 0xFFFF0000) >>> 16;
343         }
344 
345         /**
346          * Returns the reason for the bucketing, if the event is of type
347          * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
348          * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
349          * are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
350          * is REASON_MAIN_USAGE.
351          * @hide
352          */
getStandbyReason()353         public int getStandbyReason() {
354             return mBucketAndReason & 0x0000FFFF;
355         }
356 
357         /**
358          * Returns the ID of the {@link android.app.NotificationChannel} for this event if the
359          * event is of type {@link #NOTIFICATION_INTERRUPTION}, otherwise it returns null;
360          * @hide
361          */
362         @SystemApi
getNotificationChannelId()363         public String getNotificationChannelId() {
364             return mNotificationChannelId;
365         }
366 
367         /** @hide */
getObfuscatedIfInstantApp()368         public Event getObfuscatedIfInstantApp() {
369             if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
370                 return this;
371             }
372             final Event ret = new Event(this);
373             ret.mPackage = INSTANT_APP_PACKAGE_NAME;
374             ret.mClass = INSTANT_APP_CLASS_NAME;
375 
376             // Note there are other string fields too, but they're for app shortcuts and choosers,
377             // which instant apps can't use anyway, so there's no need to hide them.
378             return ret;
379         }
380     }
381 
382     // Only used when creating the resulting events. Not used for reading/unparceling.
383     private List<Event> mEventsToWrite = null;
384 
385     // Only used for reading/unparceling events.
386     private Parcel mParcel = null;
387     private final int mEventCount;
388 
389     private int mIndex = 0;
390 
391     /*
392      * In order to save space, since ComponentNames will be duplicated everywhere,
393      * we use a map and index into it.
394      */
395     private String[] mStringPool;
396 
397     /**
398      * Construct the iterator from a parcel.
399      * {@hide}
400      */
UsageEvents(Parcel in)401     public UsageEvents(Parcel in) {
402         byte[] bytes = in.readBlob();
403         Parcel data = Parcel.obtain();
404         data.unmarshall(bytes, 0, bytes.length);
405         data.setDataPosition(0);
406         mEventCount = data.readInt();
407         mIndex = data.readInt();
408         if (mEventCount > 0) {
409             mStringPool = data.createStringArray();
410 
411             final int listByteLength = data.readInt();
412             final int positionInParcel = data.readInt();
413             mParcel = Parcel.obtain();
414             mParcel.setDataPosition(0);
415             mParcel.appendFrom(data, data.dataPosition(), listByteLength);
416             mParcel.setDataSize(mParcel.dataPosition());
417             mParcel.setDataPosition(positionInParcel);
418         }
419     }
420 
421     /**
422      * Create an empty iterator.
423      * {@hide}
424      */
UsageEvents()425     UsageEvents() {
426         mEventCount = 0;
427     }
428 
429     /**
430      * Construct the iterator in preparation for writing it to a parcel.
431      * {@hide}
432      */
UsageEvents(List<Event> events, String[] stringPool)433     public UsageEvents(List<Event> events, String[] stringPool) {
434         mStringPool = stringPool;
435         mEventCount = events.size();
436         mEventsToWrite = events;
437     }
438 
439     /**
440      * Returns whether or not there are more events to read using
441      * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
442      *
443      * @return true if there are more events, false otherwise.
444      */
hasNextEvent()445     public boolean hasNextEvent() {
446         return mIndex < mEventCount;
447     }
448 
449     /**
450      * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
451      * resulting data into {@code eventOut}.
452      *
453      * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
454      *                 next event data.
455      * @return true if an event was available, false if there are no more events.
456      */
getNextEvent(Event eventOut)457     public boolean getNextEvent(Event eventOut) {
458         if (mIndex >= mEventCount) {
459             return false;
460         }
461 
462         readEventFromParcel(mParcel, eventOut);
463 
464         mIndex++;
465         if (mIndex >= mEventCount) {
466             mParcel.recycle();
467             mParcel = null;
468         }
469         return true;
470     }
471 
472     /**
473      * Resets the collection so that it can be iterated over from the beginning.
474      *
475      * @hide When this object is iterated to completion, the parcel is destroyed and
476      * so resetToStart doesn't work.
477      */
resetToStart()478     public void resetToStart() {
479         mIndex = 0;
480         if (mParcel != null) {
481             mParcel.setDataPosition(0);
482         }
483     }
484 
findStringIndex(String str)485     private int findStringIndex(String str) {
486         final int index = Arrays.binarySearch(mStringPool, str);
487         if (index < 0) {
488             throw new IllegalStateException("String '" + str + "' is not in the string pool");
489         }
490         return index;
491     }
492 
493     /**
494      * Writes a single event to the parcel. Modify this when updating {@link Event}.
495      */
writeEventToParcel(Event event, Parcel p, int flags)496     private void writeEventToParcel(Event event, Parcel p, int flags) {
497         final int packageIndex;
498         if (event.mPackage != null) {
499             packageIndex = findStringIndex(event.mPackage);
500         } else {
501             packageIndex = -1;
502         }
503 
504         final int classIndex;
505         if (event.mClass != null) {
506             classIndex = findStringIndex(event.mClass);
507         } else {
508             classIndex = -1;
509         }
510         p.writeInt(packageIndex);
511         p.writeInt(classIndex);
512         p.writeInt(event.mEventType);
513         p.writeLong(event.mTimeStamp);
514 
515         switch (event.mEventType) {
516             case Event.CONFIGURATION_CHANGE:
517                 event.mConfiguration.writeToParcel(p, flags);
518                 break;
519             case Event.SHORTCUT_INVOCATION:
520                 p.writeString(event.mShortcutId);
521                 break;
522             case Event.CHOOSER_ACTION:
523                 p.writeString(event.mAction);
524                 p.writeString(event.mContentType);
525                 p.writeStringArray(event.mContentAnnotations);
526                 break;
527             case Event.STANDBY_BUCKET_CHANGED:
528                 p.writeInt(event.mBucketAndReason);
529                 break;
530             case Event.NOTIFICATION_INTERRUPTION:
531                 p.writeString(event.mNotificationChannelId);
532                 break;
533         }
534     }
535 
536     /**
537      * Reads a single event from the parcel. Modify this when updating {@link Event}.
538      */
readEventFromParcel(Parcel p, Event eventOut)539     private void readEventFromParcel(Parcel p, Event eventOut) {
540         final int packageIndex = p.readInt();
541         if (packageIndex >= 0) {
542             eventOut.mPackage = mStringPool[packageIndex];
543         } else {
544             eventOut.mPackage = null;
545         }
546 
547         final int classIndex = p.readInt();
548         if (classIndex >= 0) {
549             eventOut.mClass = mStringPool[classIndex];
550         } else {
551             eventOut.mClass = null;
552         }
553         eventOut.mEventType = p.readInt();
554         eventOut.mTimeStamp = p.readLong();
555 
556         // Fill out the event-dependant fields.
557         eventOut.mConfiguration = null;
558         eventOut.mShortcutId = null;
559         eventOut.mAction = null;
560         eventOut.mContentType = null;
561         eventOut.mContentAnnotations = null;
562         eventOut.mNotificationChannelId = null;
563 
564         switch (eventOut.mEventType) {
565             case Event.CONFIGURATION_CHANGE:
566                 // Extract the configuration for configuration change events.
567                 eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
568                 break;
569             case Event.SHORTCUT_INVOCATION:
570                 eventOut.mShortcutId = p.readString();
571                 break;
572             case Event.CHOOSER_ACTION:
573                 eventOut.mAction = p.readString();
574                 eventOut.mContentType = p.readString();
575                 eventOut.mContentAnnotations = p.createStringArray();
576                 break;
577             case Event.STANDBY_BUCKET_CHANGED:
578                 eventOut.mBucketAndReason = p.readInt();
579                 break;
580             case Event.NOTIFICATION_INTERRUPTION:
581                 eventOut.mNotificationChannelId = p.readString();
582                 break;
583         }
584     }
585 
586     @Override
describeContents()587     public int describeContents() {
588         return 0;
589     }
590 
591     @Override
writeToParcel(Parcel dest, int flags)592     public void writeToParcel(Parcel dest, int flags) {
593         Parcel data = Parcel.obtain();
594         data.writeInt(mEventCount);
595         data.writeInt(mIndex);
596         if (mEventCount > 0) {
597             data.writeStringArray(mStringPool);
598 
599             if (mEventsToWrite != null) {
600                 // Write out the events
601                 Parcel p = Parcel.obtain();
602                 try {
603                     p.setDataPosition(0);
604                     for (int i = 0; i < mEventCount; i++) {
605                         final Event event = mEventsToWrite.get(i);
606                         writeEventToParcel(event, p, flags);
607                     }
608 
609                     final int listByteLength = p.dataPosition();
610 
611                     // Write the total length of the data.
612                     data.writeInt(listByteLength);
613 
614                     // Write our current position into the data.
615                     data.writeInt(0);
616 
617                     // Write the data.
618                     data.appendFrom(p, 0, listByteLength);
619                 } finally {
620                     p.recycle();
621                 }
622 
623             } else if (mParcel != null) {
624                 // Write the total length of the data.
625                 data.writeInt(mParcel.dataSize());
626 
627                 // Write out current position into the data.
628                 data.writeInt(mParcel.dataPosition());
629 
630                 // Write the data.
631                 data.appendFrom(mParcel, 0, mParcel.dataSize());
632             } else {
633                 throw new IllegalStateException(
634                         "Either mParcel or mEventsToWrite must not be null");
635             }
636         }
637         // Data can be too large for a transact. Write the data as a Blob, which will be written to
638         // ashmem if too large.
639         dest.writeBlob(data.marshall());
640     }
641 
642     public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
643         @Override
644         public UsageEvents createFromParcel(Parcel source) {
645             return new UsageEvents(source);
646         }
647 
648         @Override
649         public UsageEvents[] newArray(int size) {
650             return new UsageEvents[size];
651         }
652     };
653 }
654