Home | History | Annotate | Download | only in usage
      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          */
    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          */
    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          */
    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          */
    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          */
    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      */
    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      */
    189     UsageEvents() {
    190         mEventCount = 0;
    191     }
    192 
    193     /**
    194      * Construct the iterator in preparation for writing it to a parcel.
    195      * {@hide}
    196      */
    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      */
    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      */
    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      */
    242     public void resetToStart() {
    243         mIndex = 0;
    244         if (mParcel != null) {
    245             mParcel.setDataPosition(0);
    246         }
    247     }
    248 
    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      */
    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      */
    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
    313     public int describeContents() {
    314         return 0;
    315     }
    316 
    317     @Override
    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