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 17 package android.app.usage; 18 19 import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; 20 import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; 21 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; 22 import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; 23 import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED; 24 import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; 25 import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; 26 import static android.app.usage.UsageEvents.Event.END_OF_DAY; 27 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; 28 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; 29 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP; 30 import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE; 31 import static android.app.usage.UsageEvents.Event.USER_INTERACTION; 32 33 import android.annotation.CurrentTimeMillisLong; 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.annotation.SystemApi; 37 import android.annotation.TestApi; 38 import android.compat.annotation.UnsupportedAppUsage; 39 import android.os.Build; 40 import android.os.Bundle; 41 import android.os.Parcel; 42 import android.os.Parcelable; 43 import android.util.ArrayMap; 44 import android.util.SparseArray; 45 import android.util.SparseIntArray; 46 47 /** 48 * Contains usage statistics for an app package for a specific 49 * time range. 50 */ 51 public final class UsageStats implements Parcelable { 52 53 /** 54 * {@hide} 55 */ 56 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 57 public String mPackageName; 58 59 /** 60 * {@hide} 61 */ 62 public int mPackageToken = -1; 63 64 /** 65 * {@hide} 66 */ 67 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 68 public long mBeginTimeStamp; 69 70 /** 71 * {@hide} 72 */ 73 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 74 public long mEndTimeStamp; 75 76 /** 77 * Last time an activity is at foreground (have focus), this is corresponding to 78 * {@link android.app.usage.UsageEvents.Event#ACTIVITY_RESUMED} event. 79 * {@hide} 80 */ 81 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 82 public long mLastTimeUsed; 83 84 /** 85 * Last time an activity is visible. 86 * @hide 87 */ 88 public long mLastTimeVisible; 89 90 /** 91 * Total time this package's activity is in foreground. 92 * {@hide} 93 */ 94 @UnsupportedAppUsage 95 public long mTotalTimeInForeground; 96 97 /** 98 * Total time this package's activity is visible. 99 * {@hide} 100 */ 101 public long mTotalTimeVisible; 102 103 /** 104 * Last time foreground service is started. 105 * {@hide} 106 */ 107 public long mLastTimeForegroundServiceUsed; 108 109 /** 110 * Total time this package's foreground service is started. 111 * {@hide} 112 */ 113 public long mTotalTimeForegroundServiceUsed; 114 115 /** 116 * Last time this package's component is used by a client package, measured in milliseconds 117 * since the epoch. Note that component usage is only reported in certain cases (e.g. broadcast 118 * receiver, service, content provider). 119 * See {@link UsageEvents.Event#APP_COMPONENT_USED} 120 * @hide 121 */ 122 public long mLastTimeComponentUsed; 123 124 /** 125 * {@hide} 126 */ 127 @UnsupportedAppUsage 128 public int mLaunchCount; 129 130 /** 131 * {@hide} 132 */ 133 public int mAppLaunchCount; 134 135 /** Last activity ACTIVITY_RESUMED or ACTIVITY_PAUSED event. 136 * {@hide} 137 * @deprecated use {@link #mActivities} instead. 138 */ 139 @UnsupportedAppUsage 140 @Deprecated 141 public int mLastEvent; 142 143 /** 144 * Key is instanceId of the activity (ActivityRecode appToken hashCode).. 145 * Value is this activity's last event, one of ACTIVITY_RESUMED, ACTIVITY_PAUSED or 146 * ACTIVITY_STOPPED. 147 * {@hide} 148 */ 149 public SparseIntArray mActivities = new SparseIntArray(); 150 /** 151 * If a foreground service is started, it has one entry in this map. 152 * When a foreground service is stopped, it is removed from this set. 153 * Key is foreground service class name. 154 * Value is the foreground service's last event, it is FOREGROUND_SERVICE_START. 155 * {@hide} 156 */ 157 public ArrayMap<String, Integer> mForegroundServices = new ArrayMap<>(); 158 159 /** 160 * {@hide} 161 */ 162 public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts = new ArrayMap<>(); 163 164 /** 165 * {@hide} 166 */ 167 public SparseArray<SparseIntArray> mChooserCountsObfuscated = new SparseArray<>(); 168 169 /** 170 * {@hide} 171 */ 172 @TestApi UsageStats()173 public UsageStats() { 174 } 175 UsageStats(UsageStats stats)176 public UsageStats(UsageStats stats) { 177 mPackageName = stats.mPackageName; 178 mBeginTimeStamp = stats.mBeginTimeStamp; 179 mEndTimeStamp = stats.mEndTimeStamp; 180 mLastTimeUsed = stats.mLastTimeUsed; 181 mLastTimeVisible = stats.mLastTimeVisible; 182 mLastTimeComponentUsed = stats.mLastTimeComponentUsed; 183 mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed; 184 mTotalTimeInForeground = stats.mTotalTimeInForeground; 185 mTotalTimeVisible = stats.mTotalTimeVisible; 186 mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed; 187 mLaunchCount = stats.mLaunchCount; 188 mAppLaunchCount = stats.mAppLaunchCount; 189 mLastEvent = stats.mLastEvent; 190 mActivities = stats.mActivities; 191 mForegroundServices = stats.mForegroundServices; 192 mChooserCounts = stats.mChooserCounts; 193 } 194 195 /** 196 * {@hide} 197 */ getObfuscatedForInstantApp()198 public UsageStats getObfuscatedForInstantApp() { 199 final UsageStats ret = new UsageStats(this); 200 201 ret.mPackageName = UsageEvents.INSTANT_APP_PACKAGE_NAME; 202 203 return ret; 204 } 205 getPackageName()206 public String getPackageName() { 207 return mPackageName; 208 } 209 210 /** 211 * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents, 212 * measured in milliseconds since the epoch. 213 * <p/> 214 * See {@link System#currentTimeMillis()}. 215 */ getFirstTimeStamp()216 public long getFirstTimeStamp() { 217 return mBeginTimeStamp; 218 } 219 220 /** 221 * Get the end of the time range this {@link android.app.usage.UsageStats} represents, 222 * measured in milliseconds since the epoch. 223 * <p/> 224 * See {@link System#currentTimeMillis()}. 225 */ getLastTimeStamp()226 public long getLastTimeStamp() { 227 return mEndTimeStamp; 228 } 229 230 /** 231 * Get the last time this package's activity was used, measured in milliseconds since the epoch. 232 * <p/> 233 * See {@link System#currentTimeMillis()}. 234 */ getLastTimeUsed()235 public long getLastTimeUsed() { 236 return mLastTimeUsed; 237 } 238 239 /** 240 * Get the last time this package's activity is visible in the UI, measured in milliseconds 241 * since the epoch. 242 */ getLastTimeVisible()243 public long getLastTimeVisible() { 244 return mLastTimeVisible; 245 } 246 247 /** 248 * Get the total time this package spent in the foreground, measured in milliseconds. When in 249 * the foreground, the user is actively interacting with the app. 250 */ getTotalTimeInForeground()251 public long getTotalTimeInForeground() { 252 return mTotalTimeInForeground; 253 } 254 255 /** 256 * Get the total time this package's activity is visible in the UI, measured in milliseconds. 257 * Note: An app may be visible but not considered foreground. Apps in the foreground must be 258 * visible, so visible time includes time in the foreground. 259 */ getTotalTimeVisible()260 public long getTotalTimeVisible() { 261 return mTotalTimeVisible; 262 } 263 264 /** 265 * Get the last time this package's foreground service was used, measured in milliseconds since 266 * the epoch. 267 * <p/> 268 * See {@link System#currentTimeMillis()}. 269 */ getLastTimeForegroundServiceUsed()270 public long getLastTimeForegroundServiceUsed() { 271 return mLastTimeForegroundServiceUsed; 272 } 273 274 /** 275 * Get the total time this package's foreground services are started, measured in milliseconds. 276 */ getTotalTimeForegroundServiceUsed()277 public long getTotalTimeForegroundServiceUsed() { 278 return mTotalTimeForegroundServiceUsed; 279 } 280 281 /** 282 * Get the last time this package's component was used by a client package, measured in 283 * milliseconds since the epoch. Note that component usage is only reported for component 284 * bindings (e.g. broadcast receiver, service, content provider) and only when such a binding 285 * would cause an app to leave the stopped state. 286 * See {@link UsageEvents.Event#APP_COMPONENT_USED} 287 * @hide 288 */ 289 @SystemApi 290 @CurrentTimeMillisLong getLastTimeAnyComponentUsed()291 public long getLastTimeAnyComponentUsed() { 292 return mLastTimeComponentUsed; 293 } 294 295 /** 296 * Returns the number of times the app was launched as an activity from outside of the app. 297 * Excludes intra-app activity transitions. 298 * @hide 299 */ 300 @SystemApi getAppLaunchCount()301 public int getAppLaunchCount() { 302 return mAppLaunchCount; 303 } 304 mergeEventMap(SparseIntArray left, SparseIntArray right)305 private void mergeEventMap(SparseIntArray left, SparseIntArray right) { 306 final int size = right.size(); 307 for (int i = 0; i < size; i++) { 308 final int instanceId = right.keyAt(i); 309 final int event = right.valueAt(i); 310 final int index = left.indexOfKey(instanceId); 311 if (index >= 0) { 312 left.put(instanceId, Math.max(left.valueAt(index), event)); 313 } else { 314 left.put(instanceId, event); 315 } 316 } 317 } 318 mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right)319 private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) { 320 final int size = right.size(); 321 for (int i = 0; i < size; i++) { 322 final String className = right.keyAt(i); 323 final Integer event = right.valueAt(i); 324 if (left.containsKey(className)) { 325 left.put(className, Math.max(left.get(className), event)); 326 } else { 327 left.put(className, event); 328 } 329 } 330 } 331 332 /** 333 * Add the statistics from the right {@link UsageStats} to the left. The package name for 334 * both {@link UsageStats} objects must be the same. 335 * @param right The {@link UsageStats} object to merge into this one. 336 * @throws java.lang.IllegalArgumentException if the package names of the two 337 * {@link UsageStats} objects are different. 338 */ add(UsageStats right)339 public void add(UsageStats right) { 340 if (!mPackageName.equals(right.mPackageName)) { 341 throw new IllegalArgumentException("Can't merge UsageStats for package '" + 342 mPackageName + "' with UsageStats for package '" + right.mPackageName + "'."); 343 } 344 345 // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with 346 // regards to their mEndTimeStamp. 347 if (right.mBeginTimeStamp > mBeginTimeStamp) { 348 // Even though incoming UsageStat begins after this one, its last time used fields 349 // may somehow be empty or chronologically preceding the older UsageStat. 350 mergeEventMap(mActivities, right.mActivities); 351 mergeEventMap(mForegroundServices, right.mForegroundServices); 352 mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed); 353 mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible); 354 mLastTimeComponentUsed = Math.max(mLastTimeComponentUsed, right.mLastTimeComponentUsed); 355 mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed, 356 right.mLastTimeForegroundServiceUsed); 357 } 358 mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); 359 mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp); 360 mTotalTimeInForeground += right.mTotalTimeInForeground; 361 mTotalTimeVisible += right.mTotalTimeVisible; 362 mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed; 363 mLaunchCount += right.mLaunchCount; 364 mAppLaunchCount += right.mAppLaunchCount; 365 if (mChooserCounts == null) { 366 mChooserCounts = right.mChooserCounts; 367 } else if (right.mChooserCounts != null) { 368 final int chooserCountsSize = right.mChooserCounts.size(); 369 for (int i = 0; i < chooserCountsSize; i++) { 370 String action = right.mChooserCounts.keyAt(i); 371 ArrayMap<String, Integer> counts = right.mChooserCounts.valueAt(i); 372 if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) { 373 mChooserCounts.put(action, counts); 374 continue; 375 } 376 final int annotationSize = counts.size(); 377 for (int j = 0; j < annotationSize; j++) { 378 String key = counts.keyAt(j); 379 int rightValue = counts.valueAt(j); 380 int leftValue = mChooserCounts.get(action).getOrDefault(key, 0); 381 mChooserCounts.get(action).put(key, leftValue + rightValue); 382 } 383 } 384 } 385 } 386 387 /** 388 * Tell if any activity is in foreground. 389 * @return 390 */ hasForegroundActivity()391 private boolean hasForegroundActivity() { 392 final int size = mActivities.size(); 393 for (int i = 0; i < size; i++) { 394 if (mActivities.valueAt(i) == ACTIVITY_RESUMED) { 395 return true; 396 } 397 } 398 return false; 399 } 400 401 /** 402 * Tell if any activity is visible. 403 * @return 404 */ hasVisibleActivity()405 private boolean hasVisibleActivity() { 406 final int size = mActivities.size(); 407 for (int i = 0; i < size; i++) { 408 final int type = mActivities.valueAt(i); 409 if (type == ACTIVITY_RESUMED 410 || type == ACTIVITY_PAUSED) { 411 return true; 412 } 413 } 414 return false; 415 } 416 417 /** 418 * Tell if any foreground service is started. 419 * @return 420 */ anyForegroundServiceStarted()421 private boolean anyForegroundServiceStarted() { 422 return !mForegroundServices.isEmpty(); 423 } 424 425 /** 426 * Increment total time in foreground and update last time in foreground. 427 * @param timeStamp current timestamp. 428 */ incrementTimeUsed(long timeStamp)429 private void incrementTimeUsed(long timeStamp) { 430 if (timeStamp > mLastTimeUsed) { 431 mTotalTimeInForeground += timeStamp - mLastTimeUsed; 432 mLastTimeUsed = timeStamp; 433 } 434 } 435 436 /** 437 * Increment total time visible and update last time visible. 438 * @param timeStamp current timestmap. 439 */ incrementTimeVisible(long timeStamp)440 private void incrementTimeVisible(long timeStamp) { 441 if (timeStamp > mLastTimeVisible) { 442 mTotalTimeVisible += timeStamp - mLastTimeVisible; 443 mLastTimeVisible = timeStamp; 444 } 445 } 446 447 /** 448 * Increment total time foreground service is used and update last time foreground service is 449 * used. 450 * @param timeStamp current timestamp. 451 */ incrementServiceTimeUsed(long timeStamp)452 private void incrementServiceTimeUsed(long timeStamp) { 453 if (timeStamp > mLastTimeForegroundServiceUsed) { 454 mTotalTimeForegroundServiceUsed += 455 timeStamp - mLastTimeForegroundServiceUsed; 456 mLastTimeForegroundServiceUsed = timeStamp; 457 } 458 } 459 460 /** 461 * Update by an event of an activity. 462 * @param className className of the activity. 463 * @param timeStamp timeStamp of the event. 464 * @param eventType type of the event. 465 * @param instanceId hashCode of the ActivityRecord's appToken. 466 * @hide 467 */ updateActivity(String className, long timeStamp, int eventType, int instanceId)468 private void updateActivity(String className, long timeStamp, int eventType, int instanceId) { 469 if (eventType != ACTIVITY_RESUMED 470 && eventType != ACTIVITY_PAUSED 471 && eventType != ACTIVITY_STOPPED 472 && eventType != ACTIVITY_DESTROYED) { 473 return; 474 } 475 476 // update usage. 477 final int index = mActivities.indexOfKey(instanceId); 478 if (index >= 0) { 479 final int lastEvent = mActivities.valueAt(index); 480 switch (lastEvent) { 481 case ACTIVITY_RESUMED: 482 incrementTimeUsed(timeStamp); 483 incrementTimeVisible(timeStamp); 484 break; 485 case ACTIVITY_PAUSED: 486 incrementTimeVisible(timeStamp); 487 break; 488 default: 489 break; 490 } 491 } 492 493 // update current event. 494 switch(eventType) { 495 case ACTIVITY_RESUMED: 496 if (!hasVisibleActivity()) { 497 // this is the first visible activity. 498 mLastTimeUsed = timeStamp; 499 mLastTimeVisible = timeStamp; 500 } else if (!hasForegroundActivity()) { 501 // this is the first foreground activity. 502 mLastTimeUsed = timeStamp; 503 } 504 mActivities.put(instanceId, eventType); 505 break; 506 case ACTIVITY_PAUSED: 507 if (!hasVisibleActivity()) { 508 // this is the first visible activity. 509 mLastTimeVisible = timeStamp; 510 } 511 mActivities.put(instanceId, eventType); 512 break; 513 case ACTIVITY_STOPPED: 514 case ACTIVITY_DESTROYED: 515 // remove activity from the map. 516 mActivities.delete(instanceId); 517 break; 518 default: 519 break; 520 } 521 } 522 523 /** 524 * Update by an event of an foreground service. 525 * @param className className of the foreground service. 526 * @param timeStamp timeStamp of the event. 527 * @param eventType type of the event. 528 * @hide 529 */ updateForegroundService(String className, long timeStamp, int eventType)530 private void updateForegroundService(String className, long timeStamp, int eventType) { 531 if (eventType != FOREGROUND_SERVICE_STOP 532 && eventType != FOREGROUND_SERVICE_START) { 533 return; 534 } 535 final Integer lastEvent = mForegroundServices.get(className); 536 // update usage. 537 if (lastEvent != null) { 538 switch (lastEvent) { 539 case FOREGROUND_SERVICE_START: 540 case CONTINUING_FOREGROUND_SERVICE: 541 incrementServiceTimeUsed(timeStamp); 542 break; 543 default: 544 break; 545 } 546 } 547 548 // update current event. 549 switch (eventType) { 550 case FOREGROUND_SERVICE_START: 551 if (!anyForegroundServiceStarted()) { 552 mLastTimeForegroundServiceUsed = timeStamp; 553 } 554 mForegroundServices.put(className, eventType); 555 break; 556 case FOREGROUND_SERVICE_STOP: 557 mForegroundServices.remove(className); 558 break; 559 default: 560 break; 561 } 562 } 563 564 /** 565 * Update the UsageStats by a activity or foreground service event. 566 * @param className class name of a activity or foreground service, could be null to if this 567 * is sent to all activities/services in this package. 568 * @param timeStamp Epoch timestamp in milliseconds. 569 * @param eventType event type as in {@link UsageEvents.Event} 570 * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken. 571 * if className is not an activity, instanceId is not used. 572 * @hide 573 */ update(String className, long timeStamp, int eventType, int instanceId)574 public void update(String className, long timeStamp, int eventType, int instanceId) { 575 switch(eventType) { 576 case ACTIVITY_RESUMED: 577 case ACTIVITY_PAUSED: 578 case ACTIVITY_STOPPED: 579 case ACTIVITY_DESTROYED: 580 updateActivity(className, timeStamp, eventType, instanceId); 581 break; 582 case END_OF_DAY: 583 // END_OF_DAY updates all activities. 584 if (hasForegroundActivity()) { 585 incrementTimeUsed(timeStamp); 586 } 587 if (hasVisibleActivity()) { 588 incrementTimeVisible(timeStamp); 589 } 590 break; 591 case FOREGROUND_SERVICE_START: 592 case FOREGROUND_SERVICE_STOP: 593 updateForegroundService(className, timeStamp, eventType); 594 break; 595 case ROLLOVER_FOREGROUND_SERVICE: 596 // ROLLOVER_FOREGROUND_SERVICE updates all foreground services. 597 if (anyForegroundServiceStarted()) { 598 incrementServiceTimeUsed(timeStamp); 599 } 600 break; 601 case CONTINUING_FOREGROUND_SERVICE: 602 mLastTimeForegroundServiceUsed = timeStamp; 603 mForegroundServices.put(className, eventType); 604 break; 605 case DEVICE_SHUTDOWN: 606 case FLUSH_TO_DISK: 607 // update usage of all active activities/services. 608 if (hasForegroundActivity()) { 609 incrementTimeUsed(timeStamp); 610 } 611 if (hasVisibleActivity()) { 612 incrementTimeVisible(timeStamp); 613 } 614 if (anyForegroundServiceStarted()) { 615 incrementServiceTimeUsed(timeStamp); 616 } 617 break; 618 case USER_INTERACTION: 619 if (hasForegroundActivity()) { 620 incrementTimeUsed(timeStamp); 621 } else { 622 mLastTimeUsed = timeStamp; 623 } 624 if (hasVisibleActivity()) { 625 incrementTimeVisible(timeStamp); 626 } else { 627 mLastTimeVisible = timeStamp; 628 } 629 break; 630 case APP_COMPONENT_USED: 631 mLastTimeComponentUsed = timeStamp; 632 break; 633 default: 634 break; 635 } 636 mEndTimeStamp = timeStamp; 637 638 if (eventType == ACTIVITY_RESUMED) { 639 mLaunchCount += 1; 640 } 641 } 642 643 @Override describeContents()644 public int describeContents() { 645 return 0; 646 } 647 648 @Override writeToParcel(Parcel dest, int flags)649 public void writeToParcel(Parcel dest, int flags) { 650 dest.writeString(mPackageName); 651 dest.writeLong(mBeginTimeStamp); 652 dest.writeLong(mEndTimeStamp); 653 dest.writeLong(mLastTimeUsed); 654 dest.writeLong(mLastTimeVisible); 655 dest.writeLong(mLastTimeComponentUsed); 656 dest.writeLong(mLastTimeForegroundServiceUsed); 657 dest.writeLong(mTotalTimeInForeground); 658 dest.writeLong(mTotalTimeVisible); 659 dest.writeLong(mTotalTimeForegroundServiceUsed); 660 dest.writeInt(mLaunchCount); 661 dest.writeInt(mAppLaunchCount); 662 dest.writeInt(mLastEvent); 663 Bundle allCounts = new Bundle(); 664 if (mChooserCounts != null) { 665 final int chooserCountSize = mChooserCounts.size(); 666 for (int i = 0; i < chooserCountSize; i++) { 667 String action = mChooserCounts.keyAt(i); 668 ArrayMap<String, Integer> counts = mChooserCounts.valueAt(i); 669 Bundle currentCounts = new Bundle(); 670 final int annotationSize = counts.size(); 671 for (int j = 0; j < annotationSize; j++) { 672 currentCounts.putInt(counts.keyAt(j), counts.valueAt(j)); 673 } 674 allCounts.putBundle(action, currentCounts); 675 } 676 } 677 dest.writeBundle(allCounts); 678 679 writeSparseIntArray(dest, mActivities); 680 dest.writeBundle(eventMapToBundle(mForegroundServices)); 681 } 682 writeSparseIntArray(Parcel dest, SparseIntArray arr)683 private void writeSparseIntArray(Parcel dest, SparseIntArray arr) { 684 final int size = arr.size(); 685 dest.writeInt(size); 686 for (int i = 0; i < size; i++) { 687 dest.writeInt(arr.keyAt(i)); 688 dest.writeInt(arr.valueAt(i)); 689 } 690 } 691 eventMapToBundle(ArrayMap<String, Integer> eventMap)692 private Bundle eventMapToBundle(ArrayMap<String, Integer> eventMap) { 693 final Bundle bundle = new Bundle(); 694 final int size = eventMap.size(); 695 for (int i = 0; i < size; i++) { 696 bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i)); 697 } 698 return bundle; 699 } 700 701 public static final @android.annotation.NonNull Creator<UsageStats> CREATOR = new Creator<UsageStats>() { 702 @Override 703 public UsageStats createFromParcel(Parcel in) { 704 UsageStats stats = new UsageStats(); 705 stats.mPackageName = in.readString(); 706 stats.mBeginTimeStamp = in.readLong(); 707 stats.mEndTimeStamp = in.readLong(); 708 stats.mLastTimeUsed = in.readLong(); 709 stats.mLastTimeVisible = in.readLong(); 710 stats.mLastTimeComponentUsed = in.readLong(); 711 stats.mLastTimeForegroundServiceUsed = in.readLong(); 712 stats.mTotalTimeInForeground = in.readLong(); 713 stats.mTotalTimeVisible = in.readLong(); 714 stats.mTotalTimeForegroundServiceUsed = in.readLong(); 715 stats.mLaunchCount = in.readInt(); 716 stats.mAppLaunchCount = in.readInt(); 717 stats.mLastEvent = in.readInt(); 718 Bundle allCounts = in.readBundle(); 719 if (allCounts != null) { 720 stats.mChooserCounts = new ArrayMap<>(); 721 for (String action : allCounts.keySet()) { 722 if (!stats.mChooserCounts.containsKey(action)) { 723 ArrayMap<String, Integer> newCounts = new ArrayMap<>(); 724 stats.mChooserCounts.put(action, newCounts); 725 } 726 Bundle currentCounts = allCounts.getBundle(action); 727 if (currentCounts != null) { 728 for (String key : currentCounts.keySet()) { 729 int value = currentCounts.getInt(key); 730 if (value > 0) { 731 stats.mChooserCounts.get(action).put(key, value); 732 } 733 } 734 } 735 } 736 } 737 readSparseIntArray(in, stats.mActivities); 738 readBundleToEventMap(in.readBundle(), stats.mForegroundServices); 739 return stats; 740 } 741 742 private void readSparseIntArray(Parcel in, SparseIntArray arr) { 743 final int size = in.readInt(); 744 for (int i = 0; i < size; i++) { 745 final int key = in.readInt(); 746 final int value = in.readInt(); 747 arr.put(key, value); 748 } 749 } 750 751 private void readBundleToEventMap(Bundle bundle, ArrayMap<String, Integer> eventMap) { 752 if (bundle != null) { 753 for (String className : bundle.keySet()) { 754 final int event = bundle.getInt(className); 755 eventMap.put(className, event); 756 } 757 } 758 } 759 760 @Override 761 public UsageStats[] newArray(int size) { 762 return new UsageStats[size]; 763 } 764 }; 765 766 /** @hide */ 767 // This class is used by the mainline test suite, so we have to keep these APIs around across 768 // releases. Consider making this class public to help external developers to write tests as 769 // well. 770 @TestApi 771 public static final class Builder { 772 private final UsageStats mUsageStats = new UsageStats(); 773 774 @NonNull build()775 public UsageStats build() { 776 return mUsageStats; 777 } 778 779 @NonNull setPackageName(@ullable String packageName)780 public Builder setPackageName(@Nullable String packageName) { 781 mUsageStats.mPackageName = packageName; 782 return this; 783 } 784 785 @NonNull setFirstTimeStamp(long firstTimeStamp)786 public Builder setFirstTimeStamp(long firstTimeStamp) { 787 mUsageStats.mBeginTimeStamp = firstTimeStamp; 788 return this; 789 } 790 791 @NonNull setLastTimeStamp(long lastTimeStamp)792 public Builder setLastTimeStamp(long lastTimeStamp) { 793 mUsageStats.mEndTimeStamp = lastTimeStamp; 794 return this; 795 } 796 797 @NonNull setTotalTimeInForeground(long totalTimeInForeground)798 public Builder setTotalTimeInForeground(long totalTimeInForeground) { 799 mUsageStats.mTotalTimeInForeground = totalTimeInForeground; 800 return this; 801 } 802 803 @NonNull setLastTimeUsed(long lastTimeUsed)804 public Builder setLastTimeUsed(long lastTimeUsed) { 805 mUsageStats.mLastTimeUsed = lastTimeUsed; 806 return this; 807 } 808 } 809 } 810