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