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