• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.res.Configuration;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.util.Arrays;
26 import java.util.List;
27 
28 /**
29  * A result returned from {@link android.app.usage.UsageStatsManager#queryEvents(long, long)}
30  * from which to read {@link android.app.usage.UsageEvents.Event} objects.
31  */
32 public final class UsageEvents implements Parcelable {
33 
34     /** @hide */
35     public static final String INSTANT_APP_PACKAGE_NAME = "android.instant_app";
36 
37     /** @hide */
38     public static final String INSTANT_APP_CLASS_NAME = "android.instant_class";
39 
40     /**
41      * An event representing a state change for a component.
42      */
43     public static final class Event {
44 
45         /**
46          * No event type.
47          */
48         public static final int NONE = 0;
49 
50         /**
51          * An event type denoting that a component moved to the foreground.
52          */
53         public static final int MOVE_TO_FOREGROUND = 1;
54 
55         /**
56          * An event type denoting that a component moved to the background.
57          */
58         public static final int MOVE_TO_BACKGROUND = 2;
59 
60         /**
61          * An event type denoting that a component was in the foreground when the stats
62          * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}.
63          * {@hide}
64          */
65         public static final int END_OF_DAY = 3;
66 
67         /**
68          * An event type denoting that a component was in the foreground the previous day.
69          * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}.
70          * {@hide}
71          */
72         public static final int CONTINUE_PREVIOUS_DAY = 4;
73 
74         /**
75          * An event type denoting that the device configuration has changed.
76          */
77         public static final int CONFIGURATION_CHANGE = 5;
78 
79         /**
80          * An event type denoting that a package was interacted with in some way by the system.
81          * @hide
82          */
83         public static final int SYSTEM_INTERACTION = 6;
84 
85         /**
86          * An event type denoting that a package was interacted with in some way by the user.
87          */
88         public static final int USER_INTERACTION = 7;
89 
90         /**
91          * An event type denoting that an action equivalent to a ShortcutInfo is taken by the user.
92          *
93          * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
94          */
95         public static final int SHORTCUT_INVOCATION = 8;
96 
97         /**
98          * An event type denoting that a package was selected by the user for ChooserActivity.
99          * @hide
100          */
101         public static final int CHOOSER_ACTION = 9;
102 
103         /** @hide */
104         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
105 
106         /** @hide */
107         @IntDef(flag = true,
108                 value = {
109                         FLAG_IS_PACKAGE_INSTANT_APP,
110                 })
111         @Retention(RetentionPolicy.SOURCE)
112         public @interface EventFlags {}
113 
114         /**
115          * {@hide}
116          */
117         public String mPackage;
118 
119         /**
120          * {@hide}
121          */
122         public String mClass;
123 
124         /**
125          * {@hide}
126          */
127         public long mTimeStamp;
128 
129         /**
130          * {@hide}
131          */
132         public int mEventType;
133 
134         /**
135          * Only present for {@link #CONFIGURATION_CHANGE} event types.
136          * {@hide}
137          */
138         public Configuration mConfiguration;
139 
140         /**
141          * ID of the shortcut.
142          * Only present for {@link #SHORTCUT_INVOCATION} event types.
143          * {@hide}
144          */
145         public String mShortcutId;
146 
147         /**
148          * Action type passed to ChooserActivity
149          * Only present for {@link #CHOOSER_ACTION} event types.
150          * {@hide}
151          */
152         public String mAction;
153 
154         /**
155          * Content type passed to ChooserActivity.
156          * Only present for {@link #CHOOSER_ACTION} event types.
157          * {@hide}
158          */
159         public String mContentType;
160 
161         /**
162          * Content annotations passed to ChooserActivity.
163          * Only present for {@link #CHOOSER_ACTION} event types.
164          * {@hide}
165          */
166         public String[] mContentAnnotations;
167 
168         /** @hide */
169         @EventFlags
170         public int mFlags;
171 
Event()172         public Event() {
173         }
174 
175         /** @hide */
Event(Event orig)176         public Event(Event orig) {
177             mPackage = orig.mPackage;
178             mClass = orig.mClass;
179             mTimeStamp = orig.mTimeStamp;
180             mEventType = orig.mEventType;
181             mConfiguration = orig.mConfiguration;
182             mShortcutId = orig.mShortcutId;
183             mAction = orig.mAction;
184             mContentType = orig.mContentType;
185             mContentAnnotations = orig.mContentAnnotations;
186             mFlags = orig.mFlags;
187         }
188 
189         /**
190          * The package name of the source of this event.
191          */
getPackageName()192         public String getPackageName() {
193             return mPackage;
194         }
195 
196         /**
197          * The class name of the source of this event. This may be null for
198          * certain events.
199          */
getClassName()200         public String getClassName() {
201             return mClass;
202         }
203 
204         /**
205          * The time at which this event occurred, measured in milliseconds since the epoch.
206          * <p/>
207          * See {@link System#currentTimeMillis()}.
208          */
getTimeStamp()209         public long getTimeStamp() {
210             return mTimeStamp;
211         }
212 
213         /**
214          * The event type.
215          *
216          * See {@link #MOVE_TO_BACKGROUND}
217          * See {@link #MOVE_TO_FOREGROUND}
218          */
getEventType()219         public int getEventType() {
220             return mEventType;
221         }
222 
223         /**
224          * Returns a {@link Configuration} for this event if the event is of type
225          * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
226          */
getConfiguration()227         public Configuration getConfiguration() {
228             return mConfiguration;
229         }
230 
231         /**
232          * Returns the ID of a {@link android.content.pm.ShortcutInfo} for this event
233          * if the event is of type {@link #SHORTCUT_INVOCATION}, otherwise it returns null.
234          *
235          * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
236          */
getShortcutId()237         public String getShortcutId() {
238             return mShortcutId;
239         }
240 
241         /** @hide */
getObfuscatedIfInstantApp()242         public Event getObfuscatedIfInstantApp() {
243             if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
244                 return this;
245             }
246             final Event ret = new Event(this);
247             ret.mPackage = INSTANT_APP_PACKAGE_NAME;
248             ret.mClass = INSTANT_APP_CLASS_NAME;
249 
250             // Note there are other string fields too, but they're for app shortcuts and choosers,
251             // which instant apps can't use anyway, so there's no need to hide them.
252             return ret;
253         }
254     }
255 
256     // Only used when creating the resulting events. Not used for reading/unparceling.
257     private List<Event> mEventsToWrite = null;
258 
259     // Only used for reading/unparceling events.
260     private Parcel mParcel = null;
261     private final int mEventCount;
262 
263     private int mIndex = 0;
264 
265     /*
266      * In order to save space, since ComponentNames will be duplicated everywhere,
267      * we use a map and index into it.
268      */
269     private String[] mStringPool;
270 
271     /**
272      * Construct the iterator from a parcel.
273      * {@hide}
274      */
UsageEvents(Parcel in)275     public UsageEvents(Parcel in) {
276         mEventCount = in.readInt();
277         mIndex = in.readInt();
278         if (mEventCount > 0) {
279             mStringPool = in.createStringArray();
280 
281             final int listByteLength = in.readInt();
282             final int positionInParcel = in.readInt();
283             mParcel = Parcel.obtain();
284             mParcel.setDataPosition(0);
285             mParcel.appendFrom(in, in.dataPosition(), listByteLength);
286             mParcel.setDataSize(mParcel.dataPosition());
287             mParcel.setDataPosition(positionInParcel);
288         }
289     }
290 
291     /**
292      * Create an empty iterator.
293      * {@hide}
294      */
UsageEvents()295     UsageEvents() {
296         mEventCount = 0;
297     }
298 
299     /**
300      * Construct the iterator in preparation for writing it to a parcel.
301      * {@hide}
302      */
UsageEvents(List<Event> events, String[] stringPool)303     public UsageEvents(List<Event> events, String[] stringPool) {
304         mStringPool = stringPool;
305         mEventCount = events.size();
306         mEventsToWrite = events;
307     }
308 
309     /**
310      * Returns whether or not there are more events to read using
311      * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
312      *
313      * @return true if there are more events, false otherwise.
314      */
hasNextEvent()315     public boolean hasNextEvent() {
316         return mIndex < mEventCount;
317     }
318 
319     /**
320      * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
321      * resulting data into {@code eventOut}.
322      *
323      * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
324      *                 next event data.
325      * @return true if an event was available, false if there are no more events.
326      */
getNextEvent(Event eventOut)327     public boolean getNextEvent(Event eventOut) {
328         if (mIndex >= mEventCount) {
329             return false;
330         }
331 
332         readEventFromParcel(mParcel, eventOut);
333 
334         mIndex++;
335         if (mIndex >= mEventCount) {
336             mParcel.recycle();
337             mParcel = null;
338         }
339         return true;
340     }
341 
342     /**
343      * Resets the collection so that it can be iterated over from the beginning.
344      *
345      * @hide When this object is iterated to completion, the parcel is destroyed and
346      * so resetToStart doesn't work.
347      */
resetToStart()348     public void resetToStart() {
349         mIndex = 0;
350         if (mParcel != null) {
351             mParcel.setDataPosition(0);
352         }
353     }
354 
findStringIndex(String str)355     private int findStringIndex(String str) {
356         final int index = Arrays.binarySearch(mStringPool, str);
357         if (index < 0) {
358             throw new IllegalStateException("String '" + str + "' is not in the string pool");
359         }
360         return index;
361     }
362 
363     /**
364      * Writes a single event to the parcel. Modify this when updating {@link Event}.
365      */
writeEventToParcel(Event event, Parcel p, int flags)366     private void writeEventToParcel(Event event, Parcel p, int flags) {
367         final int packageIndex;
368         if (event.mPackage != null) {
369             packageIndex = findStringIndex(event.mPackage);
370         } else {
371             packageIndex = -1;
372         }
373 
374         final int classIndex;
375         if (event.mClass != null) {
376             classIndex = findStringIndex(event.mClass);
377         } else {
378             classIndex = -1;
379         }
380         p.writeInt(packageIndex);
381         p.writeInt(classIndex);
382         p.writeInt(event.mEventType);
383         p.writeLong(event.mTimeStamp);
384 
385         switch (event.mEventType) {
386             case Event.CONFIGURATION_CHANGE:
387                 event.mConfiguration.writeToParcel(p, flags);
388                 break;
389             case Event.SHORTCUT_INVOCATION:
390                 p.writeString(event.mShortcutId);
391                 break;
392             case Event.CHOOSER_ACTION:
393                 p.writeString(event.mAction);
394                 p.writeString(event.mContentType);
395                 p.writeStringArray(event.mContentAnnotations);
396                 break;
397         }
398     }
399 
400     /**
401      * Reads a single event from the parcel. Modify this when updating {@link Event}.
402      */
readEventFromParcel(Parcel p, Event eventOut)403     private void readEventFromParcel(Parcel p, Event eventOut) {
404         final int packageIndex = p.readInt();
405         if (packageIndex >= 0) {
406             eventOut.mPackage = mStringPool[packageIndex];
407         } else {
408             eventOut.mPackage = null;
409         }
410 
411         final int classIndex = p.readInt();
412         if (classIndex >= 0) {
413             eventOut.mClass = mStringPool[classIndex];
414         } else {
415             eventOut.mClass = null;
416         }
417         eventOut.mEventType = p.readInt();
418         eventOut.mTimeStamp = p.readLong();
419 
420         // Fill out the event-dependant fields.
421         eventOut.mConfiguration = null;
422         eventOut.mShortcutId = null;
423         eventOut.mAction = null;
424         eventOut.mContentType = null;
425         eventOut.mContentAnnotations = null;
426 
427         switch (eventOut.mEventType) {
428             case Event.CONFIGURATION_CHANGE:
429                 // Extract the configuration for configuration change events.
430                 eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
431                 break;
432             case Event.SHORTCUT_INVOCATION:
433                 eventOut.mShortcutId = p.readString();
434                 break;
435             case Event.CHOOSER_ACTION:
436                 eventOut.mAction = p.readString();
437                 eventOut.mContentType = p.readString();
438                 eventOut.mContentAnnotations = p.createStringArray();
439                 break;
440         }
441     }
442 
443     @Override
describeContents()444     public int describeContents() {
445         return 0;
446     }
447 
448     @Override
writeToParcel(Parcel dest, int flags)449     public void writeToParcel(Parcel dest, int flags) {
450         dest.writeInt(mEventCount);
451         dest.writeInt(mIndex);
452         if (mEventCount > 0) {
453             dest.writeStringArray(mStringPool);
454 
455             if (mEventsToWrite != null) {
456                 // Write out the events
457                 Parcel p = Parcel.obtain();
458                 try {
459                     p.setDataPosition(0);
460                     for (int i = 0; i < mEventCount; i++) {
461                         final Event event = mEventsToWrite.get(i);
462                         writeEventToParcel(event, p, flags);
463                     }
464 
465                     final int listByteLength = p.dataPosition();
466 
467                     // Write the total length of the data.
468                     dest.writeInt(listByteLength);
469 
470                     // Write our current position into the data.
471                     dest.writeInt(0);
472 
473                     // Write the data.
474                     dest.appendFrom(p, 0, listByteLength);
475                 } finally {
476                     p.recycle();
477                 }
478 
479             } else if (mParcel != null) {
480                 // Write the total length of the data.
481                 dest.writeInt(mParcel.dataSize());
482 
483                 // Write out current position into the data.
484                 dest.writeInt(mParcel.dataPosition());
485 
486                 // Write the data.
487                 dest.appendFrom(mParcel, 0, mParcel.dataSize());
488             } else {
489                 throw new IllegalStateException(
490                         "Either mParcel or mEventsToWrite must not be null");
491             }
492         }
493     }
494 
495     public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
496         @Override
497         public UsageEvents createFromParcel(Parcel source) {
498             return new UsageEvents(source);
499         }
500 
501         @Override
502         public UsageEvents[] newArray(int size) {
503             return new UsageEvents[size];
504         }
505     };
506 }
507