UsageStats.java revision bc813eb26e3027856114a26312e36e4bad86bd86
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
17package android.app.usage;
18
19import android.annotation.SystemApi;
20import android.os.Bundle;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.util.ArrayMap;
24
25/**
26 * Contains usage statistics for an app package for a specific
27 * time range.
28 */
29public 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     */
80    public UsageStats() {
81    }
82
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     */
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
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     */
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     */
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     */
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     */
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
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     */
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
205    public int describeContents() {
206        return 0;
207    }
208
209    @Override
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