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