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