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.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 
    251         public Event() {
    252         }
    253 
    254         /** @hide */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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
    363         public String getNotificationChannelId() {
    364             return mNotificationChannelId;
    365         }
    366 
    367         /** @hide */
    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      */
    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      */
    425     UsageEvents() {
    426         mEventCount = 0;
    427     }
    428 
    429     /**
    430      * Construct the iterator in preparation for writing it to a parcel.
    431      * {@hide}
    432      */
    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      */
    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      */
    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      */
    478     public void resetToStart() {
    479         mIndex = 0;
    480         if (mParcel != null) {
    481             mParcel.setDataPosition(0);
    482         }
    483     }
    484 
    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      */
    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      */
    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
    587     public int describeContents() {
    588         return 0;
    589     }
    590 
    591     @Override
    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