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