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.clone();
191         mForegroundServices = new ArrayMap<>(stats.mForegroundServices);
192         mChooserCounts = new ArrayMap<>(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 last time the package was used - defined by the latest of
297      * mLastTimeUsed, mLastTimeVisible, mLastTimeForegroundServiceUsed, or mLastTimeComponentUsed.
298      * @hide
299      */
getLastTimePackageUsed()300     public long getLastTimePackageUsed() {
301         return Math.max(mLastTimeUsed,
302                         Math.max(mLastTimeVisible,
303                                  Math.max(mLastTimeForegroundServiceUsed, mLastTimeComponentUsed)));
304     }
305 
306     /**
307      * Returns the number of times the app was launched as an activity from outside of the app.
308      * Excludes intra-app activity transitions.
309      * @hide
310      */
311     @SystemApi
getAppLaunchCount()312     public int getAppLaunchCount() {
313         return mAppLaunchCount;
314     }
315 
mergeEventMap(SparseIntArray left, SparseIntArray right)316     private void mergeEventMap(SparseIntArray left, SparseIntArray right) {
317         final int size = right.size();
318         for (int i = 0; i < size; i++) {
319             final int instanceId = right.keyAt(i);
320             final int event = right.valueAt(i);
321             final int index = left.indexOfKey(instanceId);
322             if (index >= 0) {
323                 left.put(instanceId, Math.max(left.valueAt(index), event));
324             } else {
325                 left.put(instanceId, event);
326             }
327         }
328     }
329 
mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right)330     private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) {
331         final int size = right.size();
332         for (int i = 0; i < size; i++) {
333             final String className = right.keyAt(i);
334             final Integer event = right.valueAt(i);
335             if (left.containsKey(className)) {
336                 left.put(className, Math.max(left.get(className), event));
337             } else {
338                 left.put(className, event);
339             }
340         }
341     }
342 
343     /**
344      * Add the statistics from the right {@link UsageStats} to the left. The package name for
345      * both {@link UsageStats} objects must be the same.
346      * @param right The {@link UsageStats} object to merge into this one.
347      * @throws java.lang.IllegalArgumentException if the package names of the two
348      *         {@link UsageStats} objects are different.
349      */
add(UsageStats right)350     public void add(UsageStats right) {
351         if (!mPackageName.equals(right.mPackageName)) {
352             throw new IllegalArgumentException("Can't merge UsageStats for package '" +
353                     mPackageName + "' with UsageStats for package '" + right.mPackageName + "'.");
354         }
355 
356         // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
357         // regards to their mEndTimeStamp.
358         if (right.mBeginTimeStamp > mBeginTimeStamp) {
359             // Even though incoming UsageStat begins after this one, its last time used fields
360             // may somehow be empty or chronologically preceding the older UsageStat.
361             mergeEventMap(mActivities, right.mActivities);
362             mergeEventMap(mForegroundServices, right.mForegroundServices);
363             mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
364             mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible);
365             mLastTimeComponentUsed = Math.max(mLastTimeComponentUsed, right.mLastTimeComponentUsed);
366             mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed,
367                     right.mLastTimeForegroundServiceUsed);
368         }
369         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
370         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
371         mTotalTimeInForeground += right.mTotalTimeInForeground;
372         mTotalTimeVisible += right.mTotalTimeVisible;
373         mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed;
374         mLaunchCount += right.mLaunchCount;
375         mAppLaunchCount += right.mAppLaunchCount;
376         if (mChooserCounts == null) {
377             mChooserCounts = right.mChooserCounts;
378         } else if (right.mChooserCounts != null) {
379             final int chooserCountsSize = right.mChooserCounts.size();
380             for (int i = 0; i < chooserCountsSize; i++) {
381                 String action = right.mChooserCounts.keyAt(i);
382                 ArrayMap<String, Integer> counts = right.mChooserCounts.valueAt(i);
383                 if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) {
384                     mChooserCounts.put(action, counts);
385                     continue;
386                 }
387                 final int annotationSize = counts.size();
388                 for (int j = 0; j < annotationSize; j++) {
389                     String key = counts.keyAt(j);
390                     int rightValue = counts.valueAt(j);
391                     int leftValue = mChooserCounts.get(action).getOrDefault(key, 0);
392                     mChooserCounts.get(action).put(key, leftValue + rightValue);
393                 }
394             }
395         }
396     }
397 
398     /**
399      * Tell if any activity is in foreground.
400      * @return
401      */
hasForegroundActivity()402     private boolean hasForegroundActivity() {
403         final int size = mActivities.size();
404         for (int i = 0; i < size; i++) {
405             if (mActivities.valueAt(i) == ACTIVITY_RESUMED) {
406                 return true;
407             }
408         }
409         return false;
410     }
411 
412     /**
413      * Tell if any activity is visible.
414      * @return
415      */
hasVisibleActivity()416     private boolean hasVisibleActivity() {
417         final int size = mActivities.size();
418         for (int i = 0; i < size; i++) {
419             final int type = mActivities.valueAt(i);
420             if (type == ACTIVITY_RESUMED
421                     || type == ACTIVITY_PAUSED) {
422                 return true;
423             }
424         }
425         return false;
426     }
427 
428     /**
429      * Tell if any foreground service is started.
430      * @return
431      */
anyForegroundServiceStarted()432     private boolean anyForegroundServiceStarted() {
433         return !mForegroundServices.isEmpty();
434     }
435 
436     /**
437      * Increment total time in foreground and update last time in foreground.
438      * @param timeStamp current timestamp.
439      */
incrementTimeUsed(long timeStamp)440     private void incrementTimeUsed(long timeStamp) {
441         if (timeStamp > mLastTimeUsed) {
442             mTotalTimeInForeground += timeStamp - mLastTimeUsed;
443             mLastTimeUsed = timeStamp;
444         }
445     }
446 
447     /**
448      * Increment total time visible and update last time visible.
449      * @param timeStamp current timestmap.
450      */
incrementTimeVisible(long timeStamp)451     private void incrementTimeVisible(long timeStamp) {
452         if (timeStamp > mLastTimeVisible) {
453             mTotalTimeVisible += timeStamp - mLastTimeVisible;
454             mLastTimeVisible = timeStamp;
455         }
456     }
457 
458     /**
459      * Increment total time foreground service is used and update last time foreground service is
460      * used.
461      * @param timeStamp current timestamp.
462      */
incrementServiceTimeUsed(long timeStamp)463     private void incrementServiceTimeUsed(long timeStamp) {
464         if (timeStamp > mLastTimeForegroundServiceUsed) {
465             mTotalTimeForegroundServiceUsed +=
466                     timeStamp - mLastTimeForegroundServiceUsed;
467             mLastTimeForegroundServiceUsed = timeStamp;
468         }
469     }
470 
471     /**
472      * Update by an event of an activity.
473      * @param className className of the activity.
474      * @param timeStamp timeStamp of the event.
475      * @param eventType type of the event.
476      * @param instanceId hashCode of the ActivityRecord's appToken.
477      * @hide
478      */
updateActivity(String className, long timeStamp, int eventType, int instanceId)479     private void updateActivity(String className, long timeStamp, int eventType, int instanceId) {
480         if (eventType != ACTIVITY_RESUMED
481                 && eventType != ACTIVITY_PAUSED
482                 && eventType != ACTIVITY_STOPPED
483                 && eventType != ACTIVITY_DESTROYED) {
484             return;
485         }
486 
487         // update usage.
488         final int index = mActivities.indexOfKey(instanceId);
489         if (index >= 0) {
490             final int lastEvent = mActivities.valueAt(index);
491             switch (lastEvent) {
492                 case ACTIVITY_RESUMED:
493                     incrementTimeUsed(timeStamp);
494                     incrementTimeVisible(timeStamp);
495                     break;
496                 case ACTIVITY_PAUSED:
497                     incrementTimeVisible(timeStamp);
498                     break;
499                 default:
500                     break;
501             }
502         }
503 
504         // update current event.
505         switch(eventType) {
506             case ACTIVITY_RESUMED:
507                 if (!hasVisibleActivity()) {
508                     // this is the first visible activity.
509                     mLastTimeUsed = timeStamp;
510                     mLastTimeVisible = timeStamp;
511                 } else if (!hasForegroundActivity()) {
512                     // this is the first foreground activity.
513                     mLastTimeUsed = timeStamp;
514                 }
515                 mActivities.put(instanceId, eventType);
516                 break;
517             case ACTIVITY_PAUSED:
518                 if (!hasVisibleActivity()) {
519                     // this is the first visible activity.
520                     mLastTimeVisible = timeStamp;
521                 }
522                 mActivities.put(instanceId, eventType);
523                 break;
524             case ACTIVITY_STOPPED:
525             case ACTIVITY_DESTROYED:
526                 // remove activity from the map.
527                 mActivities.delete(instanceId);
528                 break;
529             default:
530                 break;
531         }
532     }
533 
534     /**
535      * Update by an event of an foreground service.
536      * @param className className of the foreground service.
537      * @param timeStamp timeStamp of the event.
538      * @param eventType type of the event.
539      * @hide
540      */
updateForegroundService(String className, long timeStamp, int eventType)541     private void updateForegroundService(String className, long timeStamp, int eventType) {
542         if (eventType != FOREGROUND_SERVICE_STOP
543                 && eventType != FOREGROUND_SERVICE_START) {
544             return;
545         }
546         final Integer lastEvent = mForegroundServices.get(className);
547         // update usage.
548         if (lastEvent != null) {
549             switch (lastEvent) {
550                 case FOREGROUND_SERVICE_START:
551                 case CONTINUING_FOREGROUND_SERVICE:
552                     incrementServiceTimeUsed(timeStamp);
553                     break;
554                 default:
555                     break;
556             }
557         }
558 
559         // update current event.
560         switch (eventType) {
561             case FOREGROUND_SERVICE_START:
562                 if (!anyForegroundServiceStarted()) {
563                     mLastTimeForegroundServiceUsed = timeStamp;
564                 }
565                 mForegroundServices.put(className, eventType);
566                 break;
567             case FOREGROUND_SERVICE_STOP:
568                 mForegroundServices.remove(className);
569                 break;
570             default:
571                 break;
572         }
573     }
574 
575     /**
576      * Update the UsageStats by a activity or foreground service event.
577      * @param className class name of a activity or foreground service, could be null to if this
578      *                  is sent to all activities/services in this package.
579      * @param timeStamp Epoch timestamp in milliseconds.
580      * @param eventType event type as in {@link UsageEvents.Event}
581      * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken.
582      *                 if className is not an activity, instanceId is not used.
583      * @hide
584      */
update(String className, long timeStamp, int eventType, int instanceId)585     public void update(String className, long timeStamp, int eventType, int instanceId) {
586         switch(eventType) {
587             case ACTIVITY_RESUMED:
588             case ACTIVITY_PAUSED:
589             case ACTIVITY_STOPPED:
590             case ACTIVITY_DESTROYED:
591                 updateActivity(className, timeStamp, eventType, instanceId);
592                 break;
593             case END_OF_DAY:
594                 // END_OF_DAY updates all activities.
595                 if (hasForegroundActivity()) {
596                     incrementTimeUsed(timeStamp);
597                 }
598                 if (hasVisibleActivity()) {
599                     incrementTimeVisible(timeStamp);
600                 }
601                 break;
602             case FOREGROUND_SERVICE_START:
603             case FOREGROUND_SERVICE_STOP:
604                 updateForegroundService(className, timeStamp, eventType);
605                 break;
606             case ROLLOVER_FOREGROUND_SERVICE:
607                 // ROLLOVER_FOREGROUND_SERVICE updates all foreground services.
608                 if (anyForegroundServiceStarted()) {
609                     incrementServiceTimeUsed(timeStamp);
610                 }
611                 break;
612             case CONTINUING_FOREGROUND_SERVICE:
613                 mLastTimeForegroundServiceUsed = timeStamp;
614                 mForegroundServices.put(className, eventType);
615                 break;
616             case DEVICE_SHUTDOWN:
617             case FLUSH_TO_DISK:
618                 // update usage of all active activities/services.
619                 if (hasForegroundActivity()) {
620                     incrementTimeUsed(timeStamp);
621                 }
622                 if (hasVisibleActivity()) {
623                     incrementTimeVisible(timeStamp);
624                 }
625                 if (anyForegroundServiceStarted()) {
626                     incrementServiceTimeUsed(timeStamp);
627                 }
628                 break;
629             case USER_INTERACTION:
630                 if (hasForegroundActivity()) {
631                     incrementTimeUsed(timeStamp);
632                 } else {
633                     mLastTimeUsed = timeStamp;
634                 }
635                 if (hasVisibleActivity()) {
636                     incrementTimeVisible(timeStamp);
637                 } else {
638                     mLastTimeVisible = timeStamp;
639                 }
640                 break;
641             case APP_COMPONENT_USED:
642                 mLastTimeComponentUsed = timeStamp;
643                 break;
644             default:
645                 break;
646         }
647         mEndTimeStamp = timeStamp;
648 
649         if (eventType == ACTIVITY_RESUMED) {
650             mLaunchCount += 1;
651         }
652     }
653 
654     @Override
describeContents()655     public int describeContents() {
656         return 0;
657     }
658 
659     @Override
writeToParcel(Parcel dest, int flags)660     public void writeToParcel(Parcel dest, int flags) {
661         dest.writeString(mPackageName);
662         dest.writeLong(mBeginTimeStamp);
663         dest.writeLong(mEndTimeStamp);
664         dest.writeLong(mLastTimeUsed);
665         dest.writeLong(mLastTimeVisible);
666         dest.writeLong(mLastTimeComponentUsed);
667         dest.writeLong(mLastTimeForegroundServiceUsed);
668         dest.writeLong(mTotalTimeInForeground);
669         dest.writeLong(mTotalTimeVisible);
670         dest.writeLong(mTotalTimeForegroundServiceUsed);
671         dest.writeInt(mLaunchCount);
672         dest.writeInt(mAppLaunchCount);
673         dest.writeInt(mLastEvent);
674         Bundle allCounts = new Bundle();
675         if (mChooserCounts != null) {
676             final int chooserCountSize = mChooserCounts.size();
677             for (int i = 0; i < chooserCountSize; i++) {
678                 String action = mChooserCounts.keyAt(i);
679                 ArrayMap<String, Integer> counts = mChooserCounts.valueAt(i);
680                 Bundle currentCounts = new Bundle();
681                 final int annotationSize = counts.size();
682                 for (int j = 0; j < annotationSize; j++) {
683                     currentCounts.putInt(counts.keyAt(j), counts.valueAt(j));
684                 }
685                 allCounts.putBundle(action, currentCounts);
686             }
687         }
688         dest.writeBundle(allCounts);
689 
690         writeSparseIntArray(dest, mActivities);
691         dest.writeBundle(eventMapToBundle(mForegroundServices));
692     }
693 
writeSparseIntArray(Parcel dest, SparseIntArray arr)694     private void writeSparseIntArray(Parcel dest, SparseIntArray arr) {
695         final int size = arr.size();
696         dest.writeInt(size);
697         for (int i = 0; i < size; i++) {
698             dest.writeInt(arr.keyAt(i));
699             dest.writeInt(arr.valueAt(i));
700         }
701     }
702 
eventMapToBundle(ArrayMap<String, Integer> eventMap)703     private Bundle eventMapToBundle(ArrayMap<String, Integer> eventMap) {
704         final Bundle bundle = new Bundle();
705         final int size = eventMap.size();
706         for (int i = 0; i < size; i++) {
707             bundle.putInt(eventMap.keyAt(i), eventMap.valueAt(i));
708         }
709         return bundle;
710     }
711 
712     public static final @android.annotation.NonNull Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
713         @Override
714         public UsageStats createFromParcel(Parcel in) {
715             UsageStats stats = new UsageStats();
716             stats.mPackageName = in.readString();
717             stats.mBeginTimeStamp = in.readLong();
718             stats.mEndTimeStamp = in.readLong();
719             stats.mLastTimeUsed = in.readLong();
720             stats.mLastTimeVisible = in.readLong();
721             stats.mLastTimeComponentUsed = in.readLong();
722             stats.mLastTimeForegroundServiceUsed = in.readLong();
723             stats.mTotalTimeInForeground = in.readLong();
724             stats.mTotalTimeVisible = in.readLong();
725             stats.mTotalTimeForegroundServiceUsed = in.readLong();
726             stats.mLaunchCount = in.readInt();
727             stats.mAppLaunchCount = in.readInt();
728             stats.mLastEvent = in.readInt();
729             Bundle allCounts = in.readBundle();
730             if (allCounts != null) {
731                 stats.mChooserCounts = new ArrayMap<>();
732                 for (String action : allCounts.keySet()) {
733                     if (!stats.mChooserCounts.containsKey(action)) {
734                         ArrayMap<String, Integer> newCounts = new ArrayMap<>();
735                         stats.mChooserCounts.put(action, newCounts);
736                     }
737                     Bundle currentCounts = allCounts.getBundle(action);
738                     if (currentCounts != null) {
739                         for (String key : currentCounts.keySet()) {
740                             int value = currentCounts.getInt(key);
741                             if (value > 0) {
742                                 stats.mChooserCounts.get(action).put(key, value);
743                             }
744                         }
745                     }
746                 }
747             }
748             readSparseIntArray(in, stats.mActivities);
749             readBundleToEventMap(in.readBundle(), stats.mForegroundServices);
750             return stats;
751         }
752 
753         private void readSparseIntArray(Parcel in, SparseIntArray arr) {
754             final int size = in.readInt();
755             for (int i = 0; i < size; i++) {
756                 final int key = in.readInt();
757                 final int value = in.readInt();
758                 arr.put(key, value);
759             }
760         }
761 
762         private void readBundleToEventMap(Bundle bundle, ArrayMap<String, Integer> eventMap) {
763             if (bundle != null) {
764                 for (String className : bundle.keySet()) {
765                     final int event = bundle.getInt(className);
766                     eventMap.put(className, event);
767                 }
768             }
769         }
770 
771         @Override
772         public UsageStats[] newArray(int size) {
773             return new UsageStats[size];
774         }
775     };
776 
777     /** @hide */
778     // This class is used by the mainline test suite, so we have to keep these APIs around across
779     // releases. Consider making this class public to help external developers to write tests as
780     // well.
781     @TestApi
782     public static final class Builder {
783         private final UsageStats mUsageStats = new UsageStats();
784 
785         @NonNull
build()786         public UsageStats build() {
787             return mUsageStats;
788         }
789 
790         @NonNull
setPackageName(@ullable String packageName)791         public Builder setPackageName(@Nullable String packageName) {
792             mUsageStats.mPackageName = packageName;
793             return this;
794         }
795 
796         @NonNull
setFirstTimeStamp(long firstTimeStamp)797         public Builder setFirstTimeStamp(long firstTimeStamp) {
798             mUsageStats.mBeginTimeStamp = firstTimeStamp;
799             return this;
800         }
801 
802         @NonNull
setLastTimeStamp(long lastTimeStamp)803         public Builder setLastTimeStamp(long lastTimeStamp) {
804             mUsageStats.mEndTimeStamp = lastTimeStamp;
805             return this;
806         }
807 
808         @NonNull
setTotalTimeInForeground(long totalTimeInForeground)809         public Builder setTotalTimeInForeground(long totalTimeInForeground) {
810             mUsageStats.mTotalTimeInForeground = totalTimeInForeground;
811             return this;
812         }
813 
814         @NonNull
setLastTimeUsed(long lastTimeUsed)815         public Builder setLastTimeUsed(long lastTimeUsed) {
816             mUsageStats.mLastTimeUsed = lastTimeUsed;
817             return this;
818         }
819     }
820 }
821