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.clone(); 191 mForegroundServices = new ArrayMap<>(stats.mForegroundServices); 192 mChooserCounts = new ArrayMap<>(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 last time the package was used - defined by the latest of 297 * mLastTimeUsed, mLastTimeVisible, mLastTimeForegroundServiceUsed, or mLastTimeComponentUsed. 298 * @hide 299 */ getLastTimePackageUsed()300 public long getLastTimePackageUsed() { 301 return Math.max(mLastTimeUsed, 302 Math.max(mLastTimeVisible, 303 Math.max(mLastTimeForegroundServiceUsed, mLastTimeComponentUsed))); 304 } 305 306 /** 307 * Returns the number of times the app was launched as an activity from outside of the app. 308 * Excludes intra-app activity transitions. 309 * @hide 310 */ 311 @SystemApi getAppLaunchCount()312 public int getAppLaunchCount() { 313 return mAppLaunchCount; 314 } 315 mergeEventMap(SparseIntArray left, SparseIntArray right)316 private void mergeEventMap(SparseIntArray left, SparseIntArray right) { 317 final int size = right.size(); 318 for (int i = 0; i < size; i++) { 319 final int instanceId = right.keyAt(i); 320 final int event = right.valueAt(i); 321 final int index = left.indexOfKey(instanceId); 322 if (index >= 0) { 323 left.put(instanceId, Math.max(left.valueAt(index), event)); 324 } else { 325 left.put(instanceId, event); 326 } 327 } 328 } 329 mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right)330 private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) { 331 final int size = right.size(); 332 for (int i = 0; i < size; i++) { 333 final String className = right.keyAt(i); 334 final Integer event = right.valueAt(i); 335 if (left.containsKey(className)) { 336 left.put(className, Math.max(left.get(className), event)); 337 } else { 338 left.put(className, event); 339 } 340 } 341 } 342 343 /** 344 * Add the statistics from the right {@link UsageStats} to the left. The package name for 345 * both {@link UsageStats} objects must be the same. 346 * @param right The {@link UsageStats} object to merge into this one. 347 * @throws java.lang.IllegalArgumentException if the package names of the two 348 * {@link UsageStats} objects are different. 349 */ add(UsageStats right)350 public void add(UsageStats right) { 351 if (!mPackageName.equals(right.mPackageName)) { 352 throw new IllegalArgumentException("Can't merge UsageStats for package '" + 353 mPackageName + "' with UsageStats for package '" + right.mPackageName + "'."); 354 } 355 356 // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with 357 // regards to their mEndTimeStamp. 358 if (right.mBeginTimeStamp > mBeginTimeStamp) { 359 // Even though incoming UsageStat begins after this one, its last time used fields 360 // may somehow be empty or chronologically preceding the older UsageStat. 361 mergeEventMap(mActivities, right.mActivities); 362 mergeEventMap(mForegroundServices, right.mForegroundServices); 363 mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed); 364 mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible); 365 mLastTimeComponentUsed = Math.max(mLastTimeComponentUsed, right.mLastTimeComponentUsed); 366 mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed, 367 right.mLastTimeForegroundServiceUsed); 368 } 369 mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); 370 mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp); 371 mTotalTimeInForeground += right.mTotalTimeInForeground; 372 mTotalTimeVisible += right.mTotalTimeVisible; 373 mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed; 374 mLaunchCount += right.mLaunchCount; 375 mAppLaunchCount += right.mAppLaunchCount; 376 if (mChooserCounts == null) { 377 mChooserCounts = right.mChooserCounts; 378 } else if (right.mChooserCounts != null) { 379 final int chooserCountsSize = right.mChooserCounts.size(); 380 for (int i = 0; i < chooserCountsSize; i++) { 381 String action = right.mChooserCounts.keyAt(i); 382 ArrayMap<String, Integer> counts = right.mChooserCounts.valueAt(i); 383 if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) { 384 mChooserCounts.put(action, counts); 385 continue; 386 } 387 final int annotationSize = counts.size(); 388 for (int j = 0; j < annotationSize; j++) { 389 String key = counts.keyAt(j); 390 int rightValue = counts.valueAt(j); 391 int leftValue = mChooserCounts.get(action).getOrDefault(key, 0); 392 mChooserCounts.get(action).put(key, leftValue + rightValue); 393 } 394 } 395 } 396 } 397 398 /** 399 * Tell if any activity is in foreground. 400 * @return 401 */ hasForegroundActivity()402 private boolean hasForegroundActivity() { 403 final int size = mActivities.size(); 404 for (int i = 0; i < size; i++) { 405 if (mActivities.valueAt(i) == ACTIVITY_RESUMED) { 406 return true; 407 } 408 } 409 return false; 410 } 411 412 /** 413 * Tell if any activity is visible. 414 * @return 415 */ hasVisibleActivity()416 private boolean hasVisibleActivity() { 417 final int size = mActivities.size(); 418 for (int i = 0; i < size; i++) { 419 final int type = mActivities.valueAt(i); 420 if (type == ACTIVITY_RESUMED 421 || type == ACTIVITY_PAUSED) { 422 return true; 423 } 424 } 425 return false; 426 } 427 428 /** 429 * Tell if any foreground service is started. 430 * @return 431 */ anyForegroundServiceStarted()432 private boolean anyForegroundServiceStarted() { 433 return !mForegroundServices.isEmpty(); 434 } 435 436 /** 437 * Increment total time in foreground and update last time in foreground. 438 * @param timeStamp current timestamp. 439 */ incrementTimeUsed(long timeStamp)440 private void incrementTimeUsed(long timeStamp) { 441 if (timeStamp > mLastTimeUsed) { 442 mTotalTimeInForeground += timeStamp - mLastTimeUsed; 443 mLastTimeUsed = timeStamp; 444 } 445 } 446 447 /** 448 * Increment total time visible and update last time visible. 449 * @param timeStamp current timestmap. 450 */ incrementTimeVisible(long timeStamp)451 private void incrementTimeVisible(long timeStamp) { 452 if (timeStamp > mLastTimeVisible) { 453 mTotalTimeVisible += timeStamp - mLastTimeVisible; 454 mLastTimeVisible = timeStamp; 455 } 456 } 457 458 /** 459 * Increment total time foreground service is used and update last time foreground service is 460 * used. 461 * @param timeStamp current timestamp. 462 */ incrementServiceTimeUsed(long timeStamp)463 private void incrementServiceTimeUsed(long timeStamp) { 464 if (timeStamp > mLastTimeForegroundServiceUsed) { 465 mTotalTimeForegroundServiceUsed += 466 timeStamp - mLastTimeForegroundServiceUsed; 467 mLastTimeForegroundServiceUsed = timeStamp; 468 } 469 } 470 471 /** 472 * Update by an event of an activity. 473 * @param className className of the activity. 474 * @param timeStamp timeStamp of the event. 475 * @param eventType type of the event. 476 * @param instanceId hashCode of the ActivityRecord's appToken. 477 * @hide 478 */ updateActivity(String className, long timeStamp, int eventType, int instanceId)479 private void updateActivity(String className, long timeStamp, int eventType, int instanceId) { 480 if (eventType != ACTIVITY_RESUMED 481 && eventType != ACTIVITY_PAUSED 482 && eventType != ACTIVITY_STOPPED 483 && eventType != ACTIVITY_DESTROYED) { 484 return; 485 } 486 487 // update usage. 488 final int index = mActivities.indexOfKey(instanceId); 489 if (index >= 0) { 490 final int lastEvent = mActivities.valueAt(index); 491 switch (lastEvent) { 492 case ACTIVITY_RESUMED: 493 incrementTimeUsed(timeStamp); 494 incrementTimeVisible(timeStamp); 495 break; 496 case ACTIVITY_PAUSED: 497 incrementTimeVisible(timeStamp); 498 break; 499 default: 500 break; 501 } 502 } 503 504 // update current event. 505 switch(eventType) { 506 case ACTIVITY_RESUMED: 507 if (!hasVisibleActivity()) { 508 // this is the first visible activity. 509 mLastTimeUsed = timeStamp; 510 mLastTimeVisible = timeStamp; 511 } else if (!hasForegroundActivity()) { 512 // this is the first foreground activity. 513 mLastTimeUsed = timeStamp; 514 } 515 mActivities.put(instanceId, eventType); 516 break; 517 case ACTIVITY_PAUSED: 518 if (!hasVisibleActivity()) { 519 // this is the first visible activity. 520 mLastTimeVisible = timeStamp; 521 } 522 mActivities.put(instanceId, eventType); 523 break; 524 case ACTIVITY_STOPPED: 525 case ACTIVITY_DESTROYED: 526 // remove activity from the map. 527 mActivities.delete(instanceId); 528 break; 529 default: 530 break; 531 } 532 } 533 534 /** 535 * Update by an event of an foreground service. 536 * @param className className of the foreground service. 537 * @param timeStamp timeStamp of the event. 538 * @param eventType type of the event. 539 * @hide 540 */ updateForegroundService(String className, long timeStamp, int eventType)541 private void updateForegroundService(String className, long timeStamp, int eventType) { 542 if (eventType != FOREGROUND_SERVICE_STOP 543 && eventType != FOREGROUND_SERVICE_START) { 544 return; 545 } 546 final Integer lastEvent = mForegroundServices.get(className); 547 // update usage. 548 if (lastEvent != null) { 549 switch (lastEvent) { 550 case FOREGROUND_SERVICE_START: 551 case CONTINUING_FOREGROUND_SERVICE: 552 incrementServiceTimeUsed(timeStamp); 553 break; 554 default: 555 break; 556 } 557 } 558 559 // update current event. 560 switch (eventType) { 561 case FOREGROUND_SERVICE_START: 562 if (!anyForegroundServiceStarted()) { 563 mLastTimeForegroundServiceUsed = timeStamp; 564 } 565 mForegroundServices.put(className, eventType); 566 break; 567 case FOREGROUND_SERVICE_STOP: 568 mForegroundServices.remove(className); 569 break; 570 default: 571 break; 572 } 573 } 574 575 /** 576 * Update the UsageStats by a activity or foreground service event. 577 * @param className class name of a activity or foreground service, could be null to if this 578 * is sent to all activities/services in this package. 579 * @param timeStamp Epoch timestamp in milliseconds. 580 * @param eventType event type as in {@link UsageEvents.Event} 581 * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken. 582 * if className is not an activity, instanceId is not used. 583 * @hide 584 */ update(String className, long timeStamp, int eventType, int instanceId)585 public void update(String className, long timeStamp, int eventType, int instanceId) { 586 switch(eventType) { 587 case ACTIVITY_RESUMED: 588 case ACTIVITY_PAUSED: 589 case ACTIVITY_STOPPED: 590 case ACTIVITY_DESTROYED: 591 updateActivity(className, timeStamp, eventType, instanceId); 592 break; 593 case END_OF_DAY: 594 // END_OF_DAY updates all activities. 595 if (hasForegroundActivity()) { 596 incrementTimeUsed(timeStamp); 597 } 598 if (hasVisibleActivity()) { 599 incrementTimeVisible(timeStamp); 600 } 601 break; 602 case FOREGROUND_SERVICE_START: 603 case FOREGROUND_SERVICE_STOP: 604 updateForegroundService(className, timeStamp, eventType); 605 break; 606 case ROLLOVER_FOREGROUND_SERVICE: 607 // ROLLOVER_FOREGROUND_SERVICE updates all foreground services. 608 if (anyForegroundServiceStarted()) { 609 incrementServiceTimeUsed(timeStamp); 610 } 611 break; 612 case CONTINUING_FOREGROUND_SERVICE: 613 mLastTimeForegroundServiceUsed = timeStamp; 614 mForegroundServices.put(className, eventType); 615 break; 616 case DEVICE_SHUTDOWN: 617 case FLUSH_TO_DISK: 618 // update usage of all active activities/services. 619 if (hasForegroundActivity()) { 620 incrementTimeUsed(timeStamp); 621 } 622 if (hasVisibleActivity()) { 623 incrementTimeVisible(timeStamp); 624 } 625 if (anyForegroundServiceStarted()) { 626 incrementServiceTimeUsed(timeStamp); 627 } 628 break; 629 case USER_INTERACTION: 630 if (hasForegroundActivity()) { 631 incrementTimeUsed(timeStamp); 632 } else { 633 mLastTimeUsed = timeStamp; 634 } 635 if (hasVisibleActivity()) { 636 incrementTimeVisible(timeStamp); 637 } else { 638 mLastTimeVisible = timeStamp; 639 } 640 break; 641 case APP_COMPONENT_USED: 642 mLastTimeComponentUsed = timeStamp; 643 break; 644 default: 645 break; 646 } 647 mEndTimeStamp = timeStamp; 648 649 if (eventType == ACTIVITY_RESUMED) { 650 mLaunchCount += 1; 651 } 652 } 653 654 @Override describeContents()655 public int describeContents() { 656 return 0; 657 } 658 659 @Override writeToParcel(Parcel dest, int flags)660 public void writeToParcel(Parcel dest, int flags) { 661 dest.writeString(mPackageName); 662 dest.writeLong(mBeginTimeStamp); 663 dest.writeLong(mEndTimeStamp); 664 dest.writeLong(mLastTimeUsed); 665 dest.writeLong(mLastTimeVisible); 666 dest.writeLong(mLastTimeComponentUsed); 667 dest.writeLong(mLastTimeForegroundServiceUsed); 668 dest.writeLong(mTotalTimeInForeground); 669 dest.writeLong(mTotalTimeVisible); 670 dest.writeLong(mTotalTimeForegroundServiceUsed); 671 dest.writeInt(mLaunchCount); 672 dest.writeInt(mAppLaunchCount); 673 dest.writeInt(mLastEvent); 674 Bundle allCounts = new Bundle(); 675 if (mChooserCounts != null) { 676 final int chooserCountSize = mChooserCounts.size(); 677 for (int i = 0; i < chooserCountSize; i++) { 678 String action = mChooserCounts.keyAt(i); 679 ArrayMap<String, Integer> counts = mChooserCounts.valueAt(i); 680 Bundle currentCounts = new Bundle(); 681 final int annotationSize = counts.size(); 682 for (int j = 0; j < annotationSize; j++) { 683 currentCounts.putInt(counts.keyAt(j), counts.valueAt(j)); 684 } 685 allCounts.putBundle(action, currentCounts); 686 } 687 } 688 dest.writeBundle(allCounts); 689 690 writeSparseIntArray(dest, mActivities); 691 dest.writeBundle(eventMapToBundle(mForegroundServices)); 692 } 693 writeSparseIntArray(Parcel dest, SparseIntArray arr)694 private void writeSparseIntArray(Parcel dest, SparseIntArray arr) { 695 final int size = arr.size(); 696 dest.writeInt(size); 697 for (int i = 0; i < size; i++) { 698 dest.writeInt(arr.keyAt(i)); 699 dest.writeInt(arr.valueAt(i)); 700 } 701 } 702 eventMapToBundle(ArrayMap<String, Integer> eventMap)703 private Bundle eventMapToBundle(ArrayMap<String, Integer> eventMap) { 704 final Bundle bundle = new Bundle(); 705 final int size = eventMap.size(); 706 for (int i = 0; i < size; i++) { 707 bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i)); 708 } 709 return bundle; 710 } 711 712 public static final @android.annotation.NonNull Creator<UsageStats> CREATOR = new Creator<UsageStats>() { 713 @Override 714 public UsageStats createFromParcel(Parcel in) { 715 UsageStats stats = new UsageStats(); 716 stats.mPackageName = in.readString(); 717 stats.mBeginTimeStamp = in.readLong(); 718 stats.mEndTimeStamp = in.readLong(); 719 stats.mLastTimeUsed = in.readLong(); 720 stats.mLastTimeVisible = in.readLong(); 721 stats.mLastTimeComponentUsed = in.readLong(); 722 stats.mLastTimeForegroundServiceUsed = in.readLong(); 723 stats.mTotalTimeInForeground = in.readLong(); 724 stats.mTotalTimeVisible = in.readLong(); 725 stats.mTotalTimeForegroundServiceUsed = in.readLong(); 726 stats.mLaunchCount = in.readInt(); 727 stats.mAppLaunchCount = in.readInt(); 728 stats.mLastEvent = in.readInt(); 729 Bundle allCounts = in.readBundle(); 730 if (allCounts != null) { 731 stats.mChooserCounts = new ArrayMap<>(); 732 for (String action : allCounts.keySet()) { 733 if (!stats.mChooserCounts.containsKey(action)) { 734 ArrayMap<String, Integer> newCounts = new ArrayMap<>(); 735 stats.mChooserCounts.put(action, newCounts); 736 } 737 Bundle currentCounts = allCounts.getBundle(action); 738 if (currentCounts != null) { 739 for (String key : currentCounts.keySet()) { 740 int value = currentCounts.getInt(key); 741 if (value > 0) { 742 stats.mChooserCounts.get(action).put(key, value); 743 } 744 } 745 } 746 } 747 } 748 readSparseIntArray(in, stats.mActivities); 749 readBundleToEventMap(in.readBundle(), stats.mForegroundServices); 750 return stats; 751 } 752 753 private void readSparseIntArray(Parcel in, SparseIntArray arr) { 754 final int size = in.readInt(); 755 for (int i = 0; i < size; i++) { 756 final int key = in.readInt(); 757 final int value = in.readInt(); 758 arr.put(key, value); 759 } 760 } 761 762 private void readBundleToEventMap(Bundle bundle, ArrayMap<String, Integer> eventMap) { 763 if (bundle != null) { 764 for (String className : bundle.keySet()) { 765 final int event = bundle.getInt(className); 766 eventMap.put(className, event); 767 } 768 } 769 } 770 771 @Override 772 public UsageStats[] newArray(int size) { 773 return new UsageStats[size]; 774 } 775 }; 776 777 /** @hide */ 778 // This class is used by the mainline test suite, so we have to keep these APIs around across 779 // releases. Consider making this class public to help external developers to write tests as 780 // well. 781 @TestApi 782 public static final class Builder { 783 private final UsageStats mUsageStats = new UsageStats(); 784 785 @NonNull build()786 public UsageStats build() { 787 return mUsageStats; 788 } 789 790 @NonNull setPackageName(@ullable String packageName)791 public Builder setPackageName(@Nullable String packageName) { 792 mUsageStats.mPackageName = packageName; 793 return this; 794 } 795 796 @NonNull setFirstTimeStamp(long firstTimeStamp)797 public Builder setFirstTimeStamp(long firstTimeStamp) { 798 mUsageStats.mBeginTimeStamp = firstTimeStamp; 799 return this; 800 } 801 802 @NonNull setLastTimeStamp(long lastTimeStamp)803 public Builder setLastTimeStamp(long lastTimeStamp) { 804 mUsageStats.mEndTimeStamp = lastTimeStamp; 805 return this; 806 } 807 808 @NonNull setTotalTimeInForeground(long totalTimeInForeground)809 public Builder setTotalTimeInForeground(long totalTimeInForeground) { 810 mUsageStats.mTotalTimeInForeground = totalTimeInForeground; 811 return this; 812 } 813 814 @NonNull setLastTimeUsed(long lastTimeUsed)815 public Builder setLastTimeUsed(long lastTimeUsed) { 816 mUsageStats.mLastTimeUsed = lastTimeUsed; 817 return this; 818 } 819 } 820 } 821