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