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 android.annotation.SystemApi;
20 import android.os.Bundle;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.util.ArrayMap;
24 
25 /**
26  * Contains usage statistics for an app package for a specific
27  * time range.
28  */
29 public final class UsageStats implements Parcelable {
30 
31     /**
32      * {@hide}
33      */
34     public String mPackageName;
35 
36     /**
37      * {@hide}
38      */
39     public long mBeginTimeStamp;
40 
41     /**
42      * {@hide}
43      */
44     public long mEndTimeStamp;
45 
46     /**
47      * Last time used by the user with an explicit action (notification, activity launch).
48      * {@hide}
49      */
50     public long mLastTimeUsed;
51 
52     /**
53      * {@hide}
54      */
55     public long mTotalTimeInForeground;
56 
57     /**
58      * {@hide}
59      */
60     public int mLaunchCount;
61 
62     /**
63      * {@hide}
64      */
65     public int mAppLaunchCount;
66 
67     /**
68      * {@hide}
69      */
70     public int mLastEvent;
71 
72     /**
73      * {@hide}
74      */
75     public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts;
76 
77     /**
78      * {@hide}
79      */
UsageStats()80     public UsageStats() {
81     }
82 
UsageStats(UsageStats stats)83     public UsageStats(UsageStats stats) {
84         mPackageName = stats.mPackageName;
85         mBeginTimeStamp = stats.mBeginTimeStamp;
86         mEndTimeStamp = stats.mEndTimeStamp;
87         mLastTimeUsed = stats.mLastTimeUsed;
88         mTotalTimeInForeground = stats.mTotalTimeInForeground;
89         mLaunchCount = stats.mLaunchCount;
90         mAppLaunchCount = stats.mAppLaunchCount;
91         mLastEvent = stats.mLastEvent;
92         mChooserCounts = stats.mChooserCounts;
93     }
94 
95     /**
96      * {@hide}
97      */
getObfuscatedForInstantApp()98     public UsageStats getObfuscatedForInstantApp() {
99         final UsageStats ret = new UsageStats(this);
100 
101         ret.mPackageName = UsageEvents.INSTANT_APP_PACKAGE_NAME;
102 
103         return ret;
104     }
105 
getPackageName()106     public String getPackageName() {
107         return mPackageName;
108     }
109 
110     /**
111      * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents,
112      * measured in milliseconds since the epoch.
113      * <p/>
114      * See {@link System#currentTimeMillis()}.
115      */
getFirstTimeStamp()116     public long getFirstTimeStamp() {
117         return mBeginTimeStamp;
118     }
119 
120     /**
121      * Get the end of the time range this {@link android.app.usage.UsageStats} represents,
122      * measured in milliseconds since the epoch.
123      * <p/>
124      * See {@link System#currentTimeMillis()}.
125      */
getLastTimeStamp()126     public long getLastTimeStamp() {
127         return mEndTimeStamp;
128     }
129 
130     /**
131      * Get the last time this package was used, measured in milliseconds since the epoch.
132      * <p/>
133      * See {@link System#currentTimeMillis()}.
134      */
getLastTimeUsed()135     public long getLastTimeUsed() {
136         return mLastTimeUsed;
137     }
138 
139     /**
140      * Get the total time this package spent in the foreground, measured in milliseconds.
141      */
getTotalTimeInForeground()142     public long getTotalTimeInForeground() {
143         return mTotalTimeInForeground;
144     }
145 
146     /**
147      * Returns the number of times the app was launched as an activity from outside of the app.
148      * Excludes intra-app activity transitions.
149      * @hide
150      */
151     @SystemApi
getAppLaunchCount()152     public int getAppLaunchCount() {
153         return mAppLaunchCount;
154     }
155 
156     /**
157      * Add the statistics from the right {@link UsageStats} to the left. The package name for
158      * both {@link UsageStats} objects must be the same.
159      * @param right The {@link UsageStats} object to merge into this one.
160      * @throws java.lang.IllegalArgumentException if the package names of the two
161      *         {@link UsageStats} objects are different.
162      */
add(UsageStats right)163     public void add(UsageStats right) {
164         if (!mPackageName.equals(right.mPackageName)) {
165             throw new IllegalArgumentException("Can't merge UsageStats for package '" +
166                     mPackageName + "' with UsageStats for package '" + right.mPackageName + "'.");
167         }
168 
169         // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
170         // regards to their mEndTimeStamp.
171         if (right.mBeginTimeStamp > mBeginTimeStamp) {
172             // Even though incoming UsageStat begins after this one, its last time used fields
173             // may somehow be empty or chronologically preceding the older UsageStat.
174             mLastEvent = Math.max(mLastEvent, right.mLastEvent);
175             mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
176         }
177         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
178         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
179         mTotalTimeInForeground += right.mTotalTimeInForeground;
180         mLaunchCount += right.mLaunchCount;
181         mAppLaunchCount += right.mAppLaunchCount;
182         if (mChooserCounts == null) {
183             mChooserCounts = right.mChooserCounts;
184         } else if (right.mChooserCounts != null) {
185             final int chooserCountsSize = right.mChooserCounts.size();
186             for (int i = 0; i < chooserCountsSize; i++) {
187                 String action = right.mChooserCounts.keyAt(i);
188                 ArrayMap<String, Integer> counts = right.mChooserCounts.valueAt(i);
189                 if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) {
190                     mChooserCounts.put(action, counts);
191                     continue;
192                 }
193                 final int annotationSize = counts.size();
194                 for (int j = 0; j < annotationSize; j++) {
195                     String key = counts.keyAt(j);
196                     int rightValue = counts.valueAt(j);
197                     int leftValue = mChooserCounts.get(action).getOrDefault(key, 0);
198                     mChooserCounts.get(action).put(key, leftValue + rightValue);
199                 }
200             }
201         }
202     }
203 
204     @Override
describeContents()205     public int describeContents() {
206         return 0;
207     }
208 
209     @Override
writeToParcel(Parcel dest, int flags)210     public void writeToParcel(Parcel dest, int flags) {
211         dest.writeString(mPackageName);
212         dest.writeLong(mBeginTimeStamp);
213         dest.writeLong(mEndTimeStamp);
214         dest.writeLong(mLastTimeUsed);
215         dest.writeLong(mTotalTimeInForeground);
216         dest.writeInt(mLaunchCount);
217         dest.writeInt(mAppLaunchCount);
218         dest.writeInt(mLastEvent);
219         Bundle allCounts = new Bundle();
220         if (mChooserCounts != null) {
221             final int chooserCountSize = mChooserCounts.size();
222             for (int i = 0; i < chooserCountSize; i++) {
223                 String action = mChooserCounts.keyAt(i);
224                 ArrayMap<String, Integer> counts = mChooserCounts.valueAt(i);
225                 Bundle currentCounts = new Bundle();
226                 final int annotationSize = counts.size();
227                 for (int j = 0; j < annotationSize; j++) {
228                     currentCounts.putInt(counts.keyAt(j), counts.valueAt(j));
229                 }
230                 allCounts.putBundle(action, currentCounts);
231             }
232         }
233         dest.writeBundle(allCounts);
234     }
235 
236     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
237         @Override
238         public UsageStats createFromParcel(Parcel in) {
239             UsageStats stats = new UsageStats();
240             stats.mPackageName = in.readString();
241             stats.mBeginTimeStamp = in.readLong();
242             stats.mEndTimeStamp = in.readLong();
243             stats.mLastTimeUsed = in.readLong();
244             stats.mTotalTimeInForeground = in.readLong();
245             stats.mLaunchCount = in.readInt();
246             stats.mAppLaunchCount = in.readInt();
247             stats.mLastEvent = in.readInt();
248             Bundle allCounts = in.readBundle();
249             if (allCounts != null) {
250                 stats.mChooserCounts = new ArrayMap<>();
251                 for (String action : allCounts.keySet()) {
252                     if (!stats.mChooserCounts.containsKey(action)) {
253                         ArrayMap<String, Integer> newCounts = new ArrayMap<>();
254                         stats.mChooserCounts.put(action, newCounts);
255                     }
256                     Bundle currentCounts = allCounts.getBundle(action);
257                     if (currentCounts != null) {
258                         for (String key : currentCounts.keySet()) {
259                             int value = currentCounts.getInt(key);
260                             if (value > 0) {
261                                 stats.mChooserCounts.get(action).put(key, value);
262                             }
263                         }
264                     }
265                 }
266             }
267             return stats;
268         }
269 
270         @Override
271         public UsageStats[] newArray(int size) {
272             return new UsageStats[size];
273         }
274     };
275 }
276