• 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.content.res.Configuration;
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 
22 import java.util.Arrays;
23 import java.util.List;
24 
25 /**
26  * A result returned from {@link android.app.usage.UsageStatsManager#queryEvents(long, long)}
27  * from which to read {@link android.app.usage.UsageEvents.Event} objects.
28  */
29 public final class UsageEvents implements Parcelable {
30 
31     /**
32      * An event representing a state change for a component.
33      */
34     public static final class Event {
35 
36         /**
37          * No event type.
38          */
39         public static final int NONE = 0;
40 
41         /**
42          * An event type denoting that a component moved to the foreground.
43          */
44         public static final int MOVE_TO_FOREGROUND = 1;
45 
46         /**
47          * An event type denoting that a component moved to the background.
48          */
49         public static final int MOVE_TO_BACKGROUND = 2;
50 
51         /**
52          * An event type denoting that a component was in the foreground when the stats
53          * rolled-over. This is effectively treated as a {@link #MOVE_TO_BACKGROUND}.
54          * {@hide}
55          */
56         public static final int END_OF_DAY = 3;
57 
58         /**
59          * An event type denoting that a component was in the foreground the previous day.
60          * This is effectively treated as a {@link #MOVE_TO_FOREGROUND}.
61          * {@hide}
62          */
63         public static final int CONTINUE_PREVIOUS_DAY = 4;
64 
65         /**
66          * An event type denoting that the device configuration has changed.
67          */
68         public static final int CONFIGURATION_CHANGE = 5;
69 
70         /**
71          * An event type denoting that a package was interacted with in some way by the system.
72          * @hide
73          */
74         public static final int SYSTEM_INTERACTION = 6;
75 
76         /**
77          * An event type denoting that a package was interacted with in some way by the user.
78          */
79         public static final int USER_INTERACTION = 7;
80 
81         /**
82          * {@hide}
83          */
84         public String mPackage;
85 
86         /**
87          * {@hide}
88          */
89         public String mClass;
90 
91         /**
92          * {@hide}
93          */
94         public long mTimeStamp;
95 
96         /**
97          * {@hide}
98          */
99         public int mEventType;
100 
101         /**
102          * Only present for {@link #CONFIGURATION_CHANGE} event types.
103          * {@hide}
104          */
105         public Configuration mConfiguration;
106 
107         /**
108          * The package name of the source of this event.
109          */
getPackageName()110         public String getPackageName() {
111             return mPackage;
112         }
113 
114         /**
115          * The class name of the source of this event. This may be null for
116          * certain events.
117          */
getClassName()118         public String getClassName() {
119             return mClass;
120         }
121 
122         /**
123          * The time at which this event occurred, measured in milliseconds since the epoch.
124          * <p/>
125          * See {@link System#currentTimeMillis()}.
126          */
getTimeStamp()127         public long getTimeStamp() {
128             return mTimeStamp;
129         }
130 
131         /**
132          * The event type.
133          *
134          * See {@link #MOVE_TO_BACKGROUND}
135          * See {@link #MOVE_TO_FOREGROUND}
136          */
getEventType()137         public int getEventType() {
138             return mEventType;
139         }
140 
141         /**
142          * Returns a {@link Configuration} for this event if the event is of type
143          * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
144          */
getConfiguration()145         public Configuration getConfiguration() {
146             return mConfiguration;
147         }
148     }
149 
150     // Only used when creating the resulting events. Not used for reading/unparceling.
151     private List<Event> mEventsToWrite = null;
152 
153     // Only used for reading/unparceling events.
154     private Parcel mParcel = null;
155     private final int mEventCount;
156 
157     private int mIndex = 0;
158 
159     /*
160      * In order to save space, since ComponentNames will be duplicated everywhere,
161      * we use a map and index into it.
162      */
163     private String[] mStringPool;
164 
165     /**
166      * Construct the iterator from a parcel.
167      * {@hide}
168      */
UsageEvents(Parcel in)169     public UsageEvents(Parcel in) {
170         mEventCount = in.readInt();
171         mIndex = in.readInt();
172         if (mEventCount > 0) {
173             mStringPool = in.createStringArray();
174 
175             final int listByteLength = in.readInt();
176             final int positionInParcel = in.readInt();
177             mParcel = Parcel.obtain();
178             mParcel.setDataPosition(0);
179             mParcel.appendFrom(in, in.dataPosition(), listByteLength);
180             mParcel.setDataSize(mParcel.dataPosition());
181             mParcel.setDataPosition(positionInParcel);
182         }
183     }
184 
185     /**
186      * Create an empty iterator.
187      * {@hide}
188      */
UsageEvents()189     UsageEvents() {
190         mEventCount = 0;
191     }
192 
193     /**
194      * Construct the iterator in preparation for writing it to a parcel.
195      * {@hide}
196      */
UsageEvents(List<Event> events, String[] stringPool)197     public UsageEvents(List<Event> events, String[] stringPool) {
198         mStringPool = stringPool;
199         mEventCount = events.size();
200         mEventsToWrite = events;
201     }
202 
203     /**
204      * Returns whether or not there are more events to read using
205      * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}.
206      *
207      * @return true if there are more events, false otherwise.
208      */
hasNextEvent()209     public boolean hasNextEvent() {
210         return mIndex < mEventCount;
211     }
212 
213     /**
214      * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the
215      * resulting data into {@code eventOut}.
216      *
217      * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the
218      *                 next event data.
219      * @return true if an event was available, false if there are no more events.
220      */
getNextEvent(Event eventOut)221     public boolean getNextEvent(Event eventOut) {
222         if (mIndex >= mEventCount) {
223             return false;
224         }
225 
226         readEventFromParcel(mParcel, eventOut);
227 
228         mIndex++;
229         if (mIndex >= mEventCount) {
230             mParcel.recycle();
231             mParcel = null;
232         }
233         return true;
234     }
235 
236     /**
237      * Resets the collection so that it can be iterated over from the beginning.
238      *
239      * @hide When this object is iterated to completion, the parcel is destroyed and
240      * so resetToStart doesn't work.
241      */
resetToStart()242     public void resetToStart() {
243         mIndex = 0;
244         if (mParcel != null) {
245             mParcel.setDataPosition(0);
246         }
247     }
248 
findStringIndex(String str)249     private int findStringIndex(String str) {
250         final int index = Arrays.binarySearch(mStringPool, str);
251         if (index < 0) {
252             throw new IllegalStateException("String '" + str + "' is not in the string pool");
253         }
254         return index;
255     }
256 
257     /**
258      * Writes a single event to the parcel. Modify this when updating {@link Event}.
259      */
writeEventToParcel(Event event, Parcel p, int flags)260     private void writeEventToParcel(Event event, Parcel p, int flags) {
261         final int packageIndex;
262         if (event.mPackage != null) {
263             packageIndex = findStringIndex(event.mPackage);
264         } else {
265             packageIndex = -1;
266         }
267 
268         final int classIndex;
269         if (event.mClass != null) {
270             classIndex = findStringIndex(event.mClass);
271         } else {
272             classIndex = -1;
273         }
274         p.writeInt(packageIndex);
275         p.writeInt(classIndex);
276         p.writeInt(event.mEventType);
277         p.writeLong(event.mTimeStamp);
278 
279         if (event.mEventType == Event.CONFIGURATION_CHANGE) {
280             event.mConfiguration.writeToParcel(p, flags);
281         }
282     }
283 
284     /**
285      * Reads a single event from the parcel. Modify this when updating {@link Event}.
286      */
readEventFromParcel(Parcel p, Event eventOut)287     private void readEventFromParcel(Parcel p, Event eventOut) {
288         final int packageIndex = p.readInt();
289         if (packageIndex >= 0) {
290             eventOut.mPackage = mStringPool[packageIndex];
291         } else {
292             eventOut.mPackage = null;
293         }
294 
295         final int classIndex = p.readInt();
296         if (classIndex >= 0) {
297             eventOut.mClass = mStringPool[classIndex];
298         } else {
299             eventOut.mClass = null;
300         }
301         eventOut.mEventType = p.readInt();
302         eventOut.mTimeStamp = p.readLong();
303 
304         // Extract the configuration for configuration change events.
305         if (eventOut.mEventType == Event.CONFIGURATION_CHANGE) {
306             eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
307         } else {
308             eventOut.mConfiguration = null;
309         }
310     }
311 
312     @Override
describeContents()313     public int describeContents() {
314         return 0;
315     }
316 
317     @Override
writeToParcel(Parcel dest, int flags)318     public void writeToParcel(Parcel dest, int flags) {
319         dest.writeInt(mEventCount);
320         dest.writeInt(mIndex);
321         if (mEventCount > 0) {
322             dest.writeStringArray(mStringPool);
323 
324             if (mEventsToWrite != null) {
325                 // Write out the events
326                 Parcel p = Parcel.obtain();
327                 try {
328                     p.setDataPosition(0);
329                     for (int i = 0; i < mEventCount; i++) {
330                         final Event event = mEventsToWrite.get(i);
331                         writeEventToParcel(event, p, flags);
332                     }
333 
334                     final int listByteLength = p.dataPosition();
335 
336                     // Write the total length of the data.
337                     dest.writeInt(listByteLength);
338 
339                     // Write our current position into the data.
340                     dest.writeInt(0);
341 
342                     // Write the data.
343                     dest.appendFrom(p, 0, listByteLength);
344                 } finally {
345                     p.recycle();
346                 }
347 
348             } else if (mParcel != null) {
349                 // Write the total length of the data.
350                 dest.writeInt(mParcel.dataSize());
351 
352                 // Write out current position into the data.
353                 dest.writeInt(mParcel.dataPosition());
354 
355                 // Write the data.
356                 dest.appendFrom(mParcel, 0, mParcel.dataSize());
357             } else {
358                 throw new IllegalStateException(
359                         "Either mParcel or mEventsToWrite must not be null");
360             }
361         }
362     }
363 
364     public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
365         @Override
366         public UsageEvents createFromParcel(Parcel source) {
367             return new UsageEvents(source);
368         }
369 
370         @Override
371         public UsageEvents[] newArray(int size) {
372             return new UsageEvents[size];
373         }
374     };
375 }
376