UsageEvents.java revision ff1575f0596791a1eeba37f52408331c29cb0193
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 */ 16package android.app.usage; 17 18import android.annotation.IntDef; 19import android.annotation.SystemApi; 20import android.content.res.Configuration; 21import android.os.Parcel; 22import android.os.Parcelable; 23 24import java.lang.annotation.Retention; 25import java.lang.annotation.RetentionPolicy; 26import java.util.Arrays; 27import 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 */ 33public 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 mEventCount = in.readInt(); 403 mIndex = in.readInt(); 404 if (mEventCount > 0) { 405 mStringPool = in.createStringArray(); 406 407 final int listByteLength = in.readInt(); 408 final int positionInParcel = in.readInt(); 409 mParcel = Parcel.obtain(); 410 mParcel.setDataPosition(0); 411 mParcel.appendFrom(in, in.dataPosition(), listByteLength); 412 mParcel.setDataSize(mParcel.dataPosition()); 413 mParcel.setDataPosition(positionInParcel); 414 } 415 } 416 417 /** 418 * Create an empty iterator. 419 * {@hide} 420 */ 421 UsageEvents() { 422 mEventCount = 0; 423 } 424 425 /** 426 * Construct the iterator in preparation for writing it to a parcel. 427 * {@hide} 428 */ 429 public UsageEvents(List<Event> events, String[] stringPool) { 430 mStringPool = stringPool; 431 mEventCount = events.size(); 432 mEventsToWrite = events; 433 } 434 435 /** 436 * Returns whether or not there are more events to read using 437 * {@link #getNextEvent(android.app.usage.UsageEvents.Event)}. 438 * 439 * @return true if there are more events, false otherwise. 440 */ 441 public boolean hasNextEvent() { 442 return mIndex < mEventCount; 443 } 444 445 /** 446 * Retrieve the next {@link android.app.usage.UsageEvents.Event} from the collection and put the 447 * resulting data into {@code eventOut}. 448 * 449 * @param eventOut The {@link android.app.usage.UsageEvents.Event} object that will receive the 450 * next event data. 451 * @return true if an event was available, false if there are no more events. 452 */ 453 public boolean getNextEvent(Event eventOut) { 454 if (mIndex >= mEventCount) { 455 return false; 456 } 457 458 readEventFromParcel(mParcel, eventOut); 459 460 mIndex++; 461 if (mIndex >= mEventCount) { 462 mParcel.recycle(); 463 mParcel = null; 464 } 465 return true; 466 } 467 468 /** 469 * Resets the collection so that it can be iterated over from the beginning. 470 * 471 * @hide When this object is iterated to completion, the parcel is destroyed and 472 * so resetToStart doesn't work. 473 */ 474 public void resetToStart() { 475 mIndex = 0; 476 if (mParcel != null) { 477 mParcel.setDataPosition(0); 478 } 479 } 480 481 private int findStringIndex(String str) { 482 final int index = Arrays.binarySearch(mStringPool, str); 483 if (index < 0) { 484 throw new IllegalStateException("String '" + str + "' is not in the string pool"); 485 } 486 return index; 487 } 488 489 /** 490 * Writes a single event to the parcel. Modify this when updating {@link Event}. 491 */ 492 private void writeEventToParcel(Event event, Parcel p, int flags) { 493 final int packageIndex; 494 if (event.mPackage != null) { 495 packageIndex = findStringIndex(event.mPackage); 496 } else { 497 packageIndex = -1; 498 } 499 500 final int classIndex; 501 if (event.mClass != null) { 502 classIndex = findStringIndex(event.mClass); 503 } else { 504 classIndex = -1; 505 } 506 p.writeInt(packageIndex); 507 p.writeInt(classIndex); 508 p.writeInt(event.mEventType); 509 p.writeLong(event.mTimeStamp); 510 511 switch (event.mEventType) { 512 case Event.CONFIGURATION_CHANGE: 513 event.mConfiguration.writeToParcel(p, flags); 514 break; 515 case Event.SHORTCUT_INVOCATION: 516 p.writeString(event.mShortcutId); 517 break; 518 case Event.CHOOSER_ACTION: 519 p.writeString(event.mAction); 520 p.writeString(event.mContentType); 521 p.writeStringArray(event.mContentAnnotations); 522 break; 523 case Event.STANDBY_BUCKET_CHANGED: 524 p.writeInt(event.mBucketAndReason); 525 break; 526 case Event.NOTIFICATION_INTERRUPTION: 527 p.writeString(event.mNotificationChannelId); 528 break; 529 } 530 } 531 532 /** 533 * Reads a single event from the parcel. Modify this when updating {@link Event}. 534 */ 535 private void readEventFromParcel(Parcel p, Event eventOut) { 536 final int packageIndex = p.readInt(); 537 if (packageIndex >= 0) { 538 eventOut.mPackage = mStringPool[packageIndex]; 539 } else { 540 eventOut.mPackage = null; 541 } 542 543 final int classIndex = p.readInt(); 544 if (classIndex >= 0) { 545 eventOut.mClass = mStringPool[classIndex]; 546 } else { 547 eventOut.mClass = null; 548 } 549 eventOut.mEventType = p.readInt(); 550 eventOut.mTimeStamp = p.readLong(); 551 552 // Fill out the event-dependant fields. 553 eventOut.mConfiguration = null; 554 eventOut.mShortcutId = null; 555 eventOut.mAction = null; 556 eventOut.mContentType = null; 557 eventOut.mContentAnnotations = null; 558 eventOut.mNotificationChannelId = null; 559 560 switch (eventOut.mEventType) { 561 case Event.CONFIGURATION_CHANGE: 562 // Extract the configuration for configuration change events. 563 eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p); 564 break; 565 case Event.SHORTCUT_INVOCATION: 566 eventOut.mShortcutId = p.readString(); 567 break; 568 case Event.CHOOSER_ACTION: 569 eventOut.mAction = p.readString(); 570 eventOut.mContentType = p.readString(); 571 eventOut.mContentAnnotations = p.createStringArray(); 572 break; 573 case Event.STANDBY_BUCKET_CHANGED: 574 eventOut.mBucketAndReason = p.readInt(); 575 break; 576 case Event.NOTIFICATION_INTERRUPTION: 577 eventOut.mNotificationChannelId = p.readString(); 578 break; 579 } 580 } 581 582 @Override 583 public int describeContents() { 584 return 0; 585 } 586 587 @Override 588 public void writeToParcel(Parcel dest, int flags) { 589 dest.writeInt(mEventCount); 590 dest.writeInt(mIndex); 591 if (mEventCount > 0) { 592 dest.writeStringArray(mStringPool); 593 594 if (mEventsToWrite != null) { 595 // Write out the events 596 Parcel p = Parcel.obtain(); 597 try { 598 p.setDataPosition(0); 599 for (int i = 0; i < mEventCount; i++) { 600 final Event event = mEventsToWrite.get(i); 601 writeEventToParcel(event, p, flags); 602 } 603 604 final int listByteLength = p.dataPosition(); 605 606 // Write the total length of the data. 607 dest.writeInt(listByteLength); 608 609 // Write our current position into the data. 610 dest.writeInt(0); 611 612 // Write the data. 613 dest.appendFrom(p, 0, listByteLength); 614 } finally { 615 p.recycle(); 616 } 617 618 } else if (mParcel != null) { 619 // Write the total length of the data. 620 dest.writeInt(mParcel.dataSize()); 621 622 // Write out current position into the data. 623 dest.writeInt(mParcel.dataPosition()); 624 625 // Write the data. 626 dest.appendFrom(mParcel, 0, mParcel.dataSize()); 627 } else { 628 throw new IllegalStateException( 629 "Either mParcel or mEventsToWrite must not be null"); 630 } 631 } 632 } 633 634 public static final Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() { 635 @Override 636 public UsageEvents createFromParcel(Parcel source) { 637 return new UsageEvents(source); 638 } 639 640 @Override 641 public UsageEvents[] newArray(int size) { 642 return new UsageEvents[size]; 643 } 644 }; 645} 646