blob: 1560b9e708e3bf00a1c45f52a09b840d3cb2c0dc [file] [log] [blame]
Adam Lesinski35168002014-07-21 15:25:30 -07001/**
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
Adam Lesinski3c153512014-07-23 17:34:34 -070017package com.android.server.usage;
18
Hui Yub1d243a2018-12-13 12:02:00 -080019import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
Hui Yu80a8b462019-03-05 15:22:13 -080020import static android.app.usage.UsageEvents.Event.DEVICE_STARTUP;
Hui Yu03d12402018-12-06 18:00:37 -080021import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
22import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
23import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
24import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY;
25import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY;
26import static android.app.usage.UsageStatsManager.INTERVAL_YEARLY;
27
Adam Lesinski7f61e962014-09-02 16:43:52 -070028import android.app.usage.ConfigurationStats;
Suprabh Shukla60aa35b2018-04-24 18:52:46 -070029import android.app.usage.EventList;
Dianne Hackbornced54392018-02-26 13:07:42 -080030import android.app.usage.EventStats;
Varun Shah5bef1be2019-05-07 22:35:22 -070031import android.app.usage.TimeSparseArray;
Adam Lesinski35168002014-07-21 15:25:30 -070032import android.app.usage.UsageEvents;
Hui Yu03d12402018-12-06 18:00:37 -080033import android.app.usage.UsageEvents.Event;
Adam Lesinski3c153512014-07-23 17:34:34 -070034import android.app.usage.UsageStats;
35import android.app.usage.UsageStatsManager;
Hui Yue361a232018-10-04 15:05:21 -070036import android.content.Context;
Adam Lesinski7f61e962014-09-02 16:43:52 -070037import android.content.res.Configuration;
Adam Lesinski66143fa2014-09-11 08:31:05 -070038import android.os.SystemClock;
Adam Lesinski1bb18c42014-08-18 12:21:34 -070039import android.text.format.DateUtils;
40import android.util.ArrayMap;
Adam Lesinski3c153512014-07-23 17:34:34 -070041import android.util.ArraySet;
Varun Shah5bef1be2019-05-07 22:35:22 -070042import android.util.AtomicFile;
Adam Lesinski3c153512014-07-23 17:34:34 -070043import android.util.Slog;
Hui Yu03d12402018-12-06 18:00:37 -080044import android.util.SparseIntArray;
Adam Lesinski3c153512014-07-23 17:34:34 -070045
Adam Lesinski1bb18c42014-08-18 12:21:34 -070046import com.android.internal.util.IndentingPrintWriter;
Adam Lesinski7f61e962014-09-02 16:43:52 -070047import com.android.server.usage.UsageStatsDatabase.StatCombiner;
48
Adam Lesinski3c153512014-07-23 17:34:34 -070049import java.io.File;
50import java.io.IOException;
51import java.text.SimpleDateFormat;
Adam Lesinski35168002014-07-21 15:25:30 -070052import java.util.ArrayList;
53import java.util.Arrays;
Adam Lesinski35168002014-07-21 15:25:30 -070054import java.util.List;
Adam Lesinski3c153512014-07-23 17:34:34 -070055
56/**
57 * A per-user UsageStatsService. All methods are meant to be called with the main lock held
58 * in UsageStatsService.
59 */
60class UserUsageStatsService {
61 private static final String TAG = "UsageStatsService";
62 private static final boolean DEBUG = UsageStatsService.DEBUG;
Michael Wachenschwanz1088cbb2018-03-01 12:45:16 -080063 private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Adam Lesinski1bb18c42014-08-18 12:21:34 -070064 private static final int sDateFormatFlags =
65 DateUtils.FORMAT_SHOW_DATE
66 | DateUtils.FORMAT_SHOW_TIME
67 | DateUtils.FORMAT_SHOW_YEAR
68 | DateUtils.FORMAT_NUMERIC_DATE;
Adam Lesinski3c153512014-07-23 17:34:34 -070069
Adam Lesinski1bb18c42014-08-18 12:21:34 -070070 private final Context mContext;
Adam Lesinski3c153512014-07-23 17:34:34 -070071 private final UsageStatsDatabase mDatabase;
Adam Lesinski35168002014-07-21 15:25:30 -070072 private final IntervalStats[] mCurrentStats;
Adam Lesinski3c153512014-07-23 17:34:34 -070073 private boolean mStatsChanged = false;
Adam Lesinskid26bea32014-09-03 16:49:59 -070074 private final UnixCalendar mDailyExpiryDate;
Adam Lesinski3c153512014-07-23 17:34:34 -070075 private final StatsUpdatedListener mListener;
76 private final String mLogPrefix;
Amith Yamasanibc813eb2018-03-20 19:37:46 -070077 private String mLastBackgroundedPackage;
Amith Yamasani55717a62015-04-03 17:22:36 -070078 private final int mUserId;
Varun Shah5e1613e2019-09-05 11:50:18 -070079 private long mRealTimeSnapshot;
80 private long mSystemTimeSnapshot;
Adam Lesinski3c153512014-07-23 17:34:34 -070081
Adam Lesinski7cba1d42015-08-04 16:17:37 -070082 private static final long[] INTERVAL_LENGTH = new long[] {
83 UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
84 UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
85 };
86
Adam Lesinski3c153512014-07-23 17:34:34 -070087 interface StatsUpdatedListener {
88 void onStatsUpdated();
Adam Lesinskib2d3ffa2016-01-26 18:18:19 -080089 void onStatsReloaded();
Amith Yamasania93542f2016-02-03 18:02:06 -080090 /**
91 * Callback that a system update was detected
92 * @param mUserId user that needs to be initialized
93 */
94 void onNewUpdate(int mUserId);
Adam Lesinski3c153512014-07-23 17:34:34 -070095 }
96
Amith Yamasanib0ff3222015-03-04 09:56:14 -080097 UserUsageStatsService(Context context, int userId, File usageStatsDir,
98 StatsUpdatedListener listener) {
Adam Lesinski1bb18c42014-08-18 12:21:34 -070099 mContext = context;
Adam Lesinskid26bea32014-09-03 16:49:59 -0700100 mDailyExpiryDate = new UnixCalendar(0);
Adam Lesinski3c153512014-07-23 17:34:34 -0700101 mDatabase = new UsageStatsDatabase(usageStatsDir);
Hui Yu03d12402018-12-06 18:00:37 -0800102 mCurrentStats = new IntervalStats[INTERVAL_COUNT];
Adam Lesinski3c153512014-07-23 17:34:34 -0700103 mListener = listener;
104 mLogPrefix = "User[" + Integer.toString(userId) + "] ";
Amith Yamasani55717a62015-04-03 17:22:36 -0700105 mUserId = userId;
Varun Shah5e1613e2019-09-05 11:50:18 -0700106 mRealTimeSnapshot = SystemClock.elapsedRealtime();
107 mSystemTimeSnapshot = System.currentTimeMillis();
Adam Lesinski3c153512014-07-23 17:34:34 -0700108 }
109
Amith Yamasania93542f2016-02-03 18:02:06 -0800110 void init(final long currentTimeMillis) {
Adam Lesinski66143fa2014-09-11 08:31:05 -0700111 mDatabase.init(currentTimeMillis);
Adam Lesinski3c153512014-07-23 17:34:34 -0700112
113 int nullCount = 0;
114 for (int i = 0; i < mCurrentStats.length; i++) {
115 mCurrentStats[i] = mDatabase.getLatestUsageStats(i);
116 if (mCurrentStats[i] == null) {
Adam Lesinski35168002014-07-21 15:25:30 -0700117 // Find out how many intervals we don't have data for.
118 // Ideally it should be all or none.
Adam Lesinski3c153512014-07-23 17:34:34 -0700119 nullCount++;
120 }
121 }
122
123 if (nullCount > 0) {
124 if (nullCount != mCurrentStats.length) {
125 // This is weird, but we shouldn't fail if something like this
126 // happens.
127 Slog.w(TAG, mLogPrefix + "Some stats have no latest available");
128 } else {
129 // This must be first boot.
130 }
131
132 // By calling loadActiveStats, we will
133 // generate new stats for each bucket.
Amith Yamasania93542f2016-02-03 18:02:06 -0800134 loadActiveStats(currentTimeMillis);
Adam Lesinski3c153512014-07-23 17:34:34 -0700135 } else {
136 // Set up the expiry date to be one day from the latest daily stat.
137 // This may actually be today and we will rollover on the first event
138 // that is reported.
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700139 updateRolloverDeadline();
Adam Lesinski3c153512014-07-23 17:34:34 -0700140 }
141
Hui Yub1d243a2018-12-13 12:02:00 -0800142 // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp
143 // is last time UsageStatsDatabase is persisted to disk.
Hui Yu80a8b462019-03-05 15:22:13 -0800144 // Also add a DEVICE_STARTUP event with current system timestamp.
Hui Yub1d243a2018-12-13 12:02:00 -0800145 final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
146 if (currentDailyStats != null) {
Hui Yu80a8b462019-03-05 15:22:13 -0800147 // File system timestamp only has precision of 1 second, add 1000ms to make up
148 // for the loss of round up.
149 final Event shutdownEvent =
150 new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved + 1000);
151 shutdownEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
152 currentDailyStats.addEvent(shutdownEvent);
153 final Event startupEvent = new Event(DEVICE_STARTUP, currentTimeMillis);
154 startupEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
155 currentDailyStats.addEvent(startupEvent);
Hui Yub1d243a2018-12-13 12:02:00 -0800156 }
157
Amith Yamasani55717a62015-04-03 17:22:36 -0700158 if (mDatabase.isNewUpdate()) {
Amith Yamasania93542f2016-02-03 18:02:06 -0800159 notifyNewUpdate();
Amith Yamasani55717a62015-04-03 17:22:36 -0700160 }
161 }
162
Varun Shah3a315202019-07-24 16:33:42 -0700163 void userUnlocked(long currentTimeMillis) {
164 init(currentTimeMillis);
165 }
166
167 void userStopped() {
168 // Flush events to disk immediately to guarantee persistence.
169 persistActiveStats();
170 }
171
Varun Shah5e1613e2019-09-05 11:50:18 -0700172 private void onTimeChanged(long oldTime, long newTime) {
Adam Lesinski66143fa2014-09-11 08:31:05 -0700173 persistActiveStats();
174 mDatabase.onTimeChanged(newTime - oldTime);
Amith Yamasania93542f2016-02-03 18:02:06 -0800175 loadActiveStats(newTime);
Adam Lesinski66143fa2014-09-11 08:31:05 -0700176 }
177
Varun Shah5e1613e2019-09-05 11:50:18 -0700178 /**
179 * This should be the only way to get the time from the system.
180 */
181 private long checkAndGetTimeLocked() {
182 final long actualSystemTime = System.currentTimeMillis();
183 if (!UsageStatsService.ENABLE_TIME_CHANGE_CORRECTION) {
184 return actualSystemTime;
185 }
186 final long actualRealtime = SystemClock.elapsedRealtime();
187 final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
188 final long diffSystemTime = actualSystemTime - expectedSystemTime;
189 if (Math.abs(diffSystemTime) > UsageStatsService.TIME_CHANGE_THRESHOLD_MILLIS) {
190 // The time has changed.
191 Slog.i(TAG, mLogPrefix + "Time changed in by " + (diffSystemTime / 1000) + " seconds");
192 onTimeChanged(expectedSystemTime, actualSystemTime);
193 mRealTimeSnapshot = actualRealtime;
194 mSystemTimeSnapshot = actualSystemTime;
195 }
196 return actualSystemTime;
197 }
198
199 /**
200 * Assuming the event's timestamp is measured in milliseconds since boot,
201 * convert it to a system wall time.
202 */
203 private void convertToSystemTimeLocked(Event event) {
204 event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
205 }
206
Hui Yu03d12402018-12-06 18:00:37 -0800207 void reportEvent(Event event) {
Adam Lesinski3c153512014-07-23 17:34:34 -0700208 if (DEBUG) {
Adam Lesinski9d960752014-08-25 14:48:12 -0700209 Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
Adam Lesinski7f61e962014-09-02 16:43:52 -0700210 + "[" + event.mTimeStamp + "]: "
211 + eventToString(event.mEventType));
Adam Lesinski3c153512014-07-23 17:34:34 -0700212 }
213
Varun Shah5e1613e2019-09-05 11:50:18 -0700214 checkAndGetTimeLocked();
215 convertToSystemTimeLocked(event);
216
Adam Lesinski7f61e962014-09-02 16:43:52 -0700217 if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
Adam Lesinski3c153512014-07-23 17:34:34 -0700218 // Need to rollover
Amith Yamasania93542f2016-02-03 18:02:06 -0800219 rolloverStats(event.mTimeStamp);
Adam Lesinski3c153512014-07-23 17:34:34 -0700220 }
221
Hui Yu03d12402018-12-06 18:00:37 -0800222 final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
Adam Lesinski7f61e962014-09-02 16:43:52 -0700223
224 final Configuration newFullConfig = event.mConfiguration;
Hui Yu03d12402018-12-06 18:00:37 -0800225 if (event.mEventType == Event.CONFIGURATION_CHANGE
226 && currentDailyStats.activeConfiguration != null) {
Adam Lesinski7f61e962014-09-02 16:43:52 -0700227 // Make the event configuration a delta.
228 event.mConfiguration = Configuration.generateDelta(
229 currentDailyStats.activeConfiguration, newFullConfig);
Adam Lesinski35168002014-07-21 15:25:30 -0700230 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700231
Hui Yu03d12402018-12-06 18:00:37 -0800232 if (event.mEventType != Event.SYSTEM_INTERACTION
233 // ACTIVITY_DESTROYED is a private event. If there is preceding ACTIVITY_STOPPED
234 // ACTIVITY_DESTROYED will be dropped. Otherwise it will be converted to
235 // ACTIVITY_STOPPED.
236 && event.mEventType != Event.ACTIVITY_DESTROYED
237 // FLUSH_TO_DISK is a private event.
Hui Yub1d243a2018-12-13 12:02:00 -0800238 && event.mEventType != Event.FLUSH_TO_DISK
239 // DEVICE_SHUTDOWN is added to event list after reboot.
240 && event.mEventType != Event.DEVICE_SHUTDOWN) {
Michael Wachenschwanzc90bc152018-09-10 15:17:57 -0700241 currentDailyStats.addEvent(event);
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800242 }
Adam Lesinski35168002014-07-21 15:25:30 -0700243
Amith Yamasanibc813eb2018-03-20 19:37:46 -0700244 boolean incrementAppLaunch = false;
Hui Yu03d12402018-12-06 18:00:37 -0800245 if (event.mEventType == Event.ACTIVITY_RESUMED) {
Amith Yamasanibc813eb2018-03-20 19:37:46 -0700246 if (event.mPackage != null && !event.mPackage.equals(mLastBackgroundedPackage)) {
247 incrementAppLaunch = true;
248 }
Hui Yu03d12402018-12-06 18:00:37 -0800249 } else if (event.mEventType == Event.ACTIVITY_PAUSED) {
Amith Yamasanibc813eb2018-03-20 19:37:46 -0700250 if (event.mPackage != null) {
251 mLastBackgroundedPackage = event.mPackage;
252 }
253 }
254
Adam Lesinski35168002014-07-21 15:25:30 -0700255 for (IntervalStats stats : mCurrentStats) {
Dianne Hackbornced54392018-02-26 13:07:42 -0800256 switch (event.mEventType) {
Hui Yu03d12402018-12-06 18:00:37 -0800257 case Event.CONFIGURATION_CHANGE: {
Dianne Hackbornced54392018-02-26 13:07:42 -0800258 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
259 } break;
Hui Yu03d12402018-12-06 18:00:37 -0800260 case Event.CHOOSER_ACTION: {
Dianne Hackbornced54392018-02-26 13:07:42 -0800261 stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
262 String[] annotations = event.mContentAnnotations;
263 if (annotations != null) {
264 for (String annotation : annotations) {
265 stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
266 }
Kang Li53b43142016-11-14 14:38:25 -0800267 }
Dianne Hackbornced54392018-02-26 13:07:42 -0800268 } break;
Hui Yu03d12402018-12-06 18:00:37 -0800269 case Event.SCREEN_INTERACTIVE: {
Dianne Hackbornced54392018-02-26 13:07:42 -0800270 stats.updateScreenInteractive(event.mTimeStamp);
271 } break;
Hui Yu03d12402018-12-06 18:00:37 -0800272 case Event.SCREEN_NON_INTERACTIVE: {
Dianne Hackbornced54392018-02-26 13:07:42 -0800273 stats.updateScreenNonInteractive(event.mTimeStamp);
274 } break;
Hui Yu03d12402018-12-06 18:00:37 -0800275 case Event.KEYGUARD_SHOWN: {
Dianne Hackborn3378aa92018-03-30 17:43:49 -0700276 stats.updateKeyguardShown(event.mTimeStamp);
277 } break;
Hui Yu03d12402018-12-06 18:00:37 -0800278 case Event.KEYGUARD_HIDDEN: {
Dianne Hackborn3378aa92018-03-30 17:43:49 -0700279 stats.updateKeyguardHidden(event.mTimeStamp);
280 } break;
Dianne Hackbornced54392018-02-26 13:07:42 -0800281 default: {
Hui Yue361a232018-10-04 15:05:21 -0700282 stats.update(event.mPackage, event.getClassName(),
Hui Yu03d12402018-12-06 18:00:37 -0800283 event.mTimeStamp, event.mEventType, event.mInstanceId);
Dianne Hackbornced54392018-02-26 13:07:42 -0800284 if (incrementAppLaunch) {
285 stats.incrementAppLaunchCount(event.mPackage);
286 }
287 } break;
Adam Lesinski7f61e962014-09-02 16:43:52 -0700288 }
Adam Lesinski3c153512014-07-23 17:34:34 -0700289 }
290
Amith Yamasanicf768722015-04-23 20:36:41 -0700291 notifyStatsChanged();
292 }
293
Adam Lesinski7f61e962014-09-02 16:43:52 -0700294 private static final StatCombiner<UsageStats> sUsageStatsCombiner =
295 new StatCombiner<UsageStats>() {
296 @Override
297 public void combine(IntervalStats stats, boolean mutable,
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700298 List<UsageStats> accResult) {
Adam Lesinski7f61e962014-09-02 16:43:52 -0700299 if (!mutable) {
Adam Lesinski37a46b42014-09-05 15:38:05 -0700300 accResult.addAll(stats.packageStats.values());
Adam Lesinski7f61e962014-09-02 16:43:52 -0700301 return;
302 }
303
Adam Lesinski37a46b42014-09-05 15:38:05 -0700304 final int statCount = stats.packageStats.size();
Adam Lesinski7f61e962014-09-02 16:43:52 -0700305 for (int i = 0; i < statCount; i++) {
Adam Lesinski37a46b42014-09-05 15:38:05 -0700306 accResult.add(new UsageStats(stats.packageStats.valueAt(i)));
Adam Lesinski7f61e962014-09-02 16:43:52 -0700307 }
308 }
309 };
310
311 private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
312 new StatCombiner<ConfigurationStats>() {
313 @Override
314 public void combine(IntervalStats stats, boolean mutable,
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700315 List<ConfigurationStats> accResult) {
Adam Lesinski7f61e962014-09-02 16:43:52 -0700316 if (!mutable) {
317 accResult.addAll(stats.configurations.values());
318 return;
319 }
320
321 final int configCount = stats.configurations.size();
322 for (int i = 0; i < configCount; i++) {
323 accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
324 }
325 }
326 };
327
Dianne Hackbornced54392018-02-26 13:07:42 -0800328 private static final StatCombiner<EventStats> sEventStatsCombiner =
329 new StatCombiner<EventStats>() {
330 @Override
331 public void combine(IntervalStats stats, boolean mutable,
332 List<EventStats> accResult) {
333 stats.addEventStatsTo(accResult);
334 }
335 };
336
Varun Shah5e1613e2019-09-05 11:50:18 -0700337 private static boolean validRange(long currentTime, long beginTime, long endTime) {
338 return beginTime <= currentTime && beginTime < endTime;
339 }
340
Adam Lesinski7f61e962014-09-02 16:43:52 -0700341 /**
342 * Generic query method that selects the appropriate IntervalStats for the specified time range
343 * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
344 * provided to select the stats to use from the IntervalStats object.
345 */
Adam Lesinskid26bea32014-09-03 16:49:59 -0700346 private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
Adam Lesinski7f61e962014-09-02 16:43:52 -0700347 StatCombiner<T> combiner) {
Hui Yu03d12402018-12-06 18:00:37 -0800348 if (intervalType == INTERVAL_BEST) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700349 intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
350 if (intervalType < 0) {
351 // Nothing saved to disk yet, so every stat is just as equal (no rollover has
352 // occurred.
Hui Yu03d12402018-12-06 18:00:37 -0800353 intervalType = INTERVAL_DAILY;
Adam Lesinskid26bea32014-09-03 16:49:59 -0700354 }
Adam Lesinski35168002014-07-21 15:25:30 -0700355 }
356
Adam Lesinskid26bea32014-09-03 16:49:59 -0700357 if (intervalType < 0 || intervalType >= mCurrentStats.length) {
Adam Lesinski35168002014-07-21 15:25:30 -0700358 if (DEBUG) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700359 Slog.d(TAG, mLogPrefix + "Bad intervalType used " + intervalType);
Adam Lesinski35168002014-07-21 15:25:30 -0700360 }
361 return null;
362 }
363
Adam Lesinskid26bea32014-09-03 16:49:59 -0700364 final IntervalStats currentStats = mCurrentStats[intervalType];
Adam Lesinski35168002014-07-21 15:25:30 -0700365
Adam Lesinski3c153512014-07-23 17:34:34 -0700366 if (DEBUG) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700367 Slog.d(TAG, mLogPrefix + "SELECT * FROM " + intervalType + " WHERE beginTime >= "
Adam Lesinski35168002014-07-21 15:25:30 -0700368 + beginTime + " AND endTime < " + endTime);
Adam Lesinski3c153512014-07-23 17:34:34 -0700369 }
370
Adam Lesinskid26bea32014-09-03 16:49:59 -0700371 if (beginTime >= currentStats.endTime) {
372 if (DEBUG) {
373 Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
374 + currentStats.endTime);
375 }
376 // Nothing newer available.
377 return null;
378 }
379
380 // Truncate the endTime to just before the in-memory stats. Then, we'll append the
381 // in-memory stats to the results (if necessary) so as to avoid writing to disk too
382 // often.
383 final long truncatedEndTime = Math.min(currentStats.beginTime, endTime);
384
385 // Get the stats from disk.
386 List<T> results = mDatabase.queryUsageStats(intervalType, beginTime,
387 truncatedEndTime, combiner);
Adam Lesinski3c153512014-07-23 17:34:34 -0700388 if (DEBUG) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700389 Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk");
390 Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime +
391 " endTime=" + currentStats.endTime);
392 }
393
394 // Now check if the in-memory stats match the range and add them if they do.
395 if (beginTime < currentStats.endTime && endTime > currentStats.beginTime) {
396 if (DEBUG) {
397 Slog.d(TAG, mLogPrefix + "Returning in-memory stats");
398 }
399
400 if (results == null) {
401 results = new ArrayList<>();
402 }
403 combiner.combine(currentStats, true, results);
404 }
405
406 if (DEBUG) {
407 Slog.d(TAG, mLogPrefix + "Results: " + (results != null ? results.size() : 0));
Adam Lesinski3c153512014-07-23 17:34:34 -0700408 }
409 return results;
410 }
411
Adam Lesinski7f61e962014-09-02 16:43:52 -0700412 List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
Varun Shah5e1613e2019-09-05 11:50:18 -0700413 if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
414 return null;
415 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700416 return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
417 }
418
419 List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
Varun Shah5e1613e2019-09-05 11:50:18 -0700420 if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
421 return null;
422 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700423 return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
424 }
425
Dianne Hackbornced54392018-02-26 13:07:42 -0800426 List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
Varun Shah5e1613e2019-09-05 11:50:18 -0700427 if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
428 return null;
429 }
Dianne Hackbornced54392018-02-26 13:07:42 -0800430 return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
431 }
432
Makoto Onukiad623012017-05-15 09:29:34 -0700433 UsageEvents queryEvents(final long beginTime, final long endTime,
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800434 boolean obfuscateInstantApps) {
Varun Shah5e1613e2019-09-05 11:50:18 -0700435 if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
436 return null;
437 }
Adam Lesinskid26bea32014-09-03 16:49:59 -0700438 final ArraySet<String> names = new ArraySet<>();
Hui Yu03d12402018-12-06 18:00:37 -0800439 List<Event> results = queryStats(INTERVAL_DAILY,
440 beginTime, endTime, new StatCombiner<Event>() {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700441 @Override
442 public void combine(IntervalStats stats, boolean mutable,
Hui Yu03d12402018-12-06 18:00:37 -0800443 List<Event> accumulatedResult) {
Suprabh Shukla60aa35b2018-04-24 18:52:46 -0700444 final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
Adam Lesinskid26bea32014-09-03 16:49:59 -0700445 final int size = stats.events.size();
446 for (int i = startIndex; i < size; i++) {
Suprabh Shukla60aa35b2018-04-24 18:52:46 -0700447 if (stats.events.get(i).mTimeStamp >= endTime) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700448 return;
449 }
Adam Lesinski35168002014-07-21 15:25:30 -0700450
Hui Yu03d12402018-12-06 18:00:37 -0800451 Event event = stats.events.get(i);
Makoto Onukiad623012017-05-15 09:29:34 -0700452 if (obfuscateInstantApps) {
453 event = event.getObfuscatedIfInstantApp();
454 }
Hui Yu03d12402018-12-06 18:00:37 -0800455 if (event.mPackage != null) {
456 names.add(event.mPackage);
457 }
Adam Lesinskid26bea32014-09-03 16:49:59 -0700458 if (event.mClass != null) {
459 names.add(event.mClass);
460 }
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800461 if (event.mTaskRootPackage != null) {
462 names.add(event.mTaskRootPackage);
463 }
464 if (event.mTaskRootClass != null) {
465 names.add(event.mTaskRootClass);
466 }
Adam Lesinskid26bea32014-09-03 16:49:59 -0700467 accumulatedResult.add(event);
468 }
469 }
470 });
471
472 if (results == null || results.isEmpty()) {
473 return null;
Adam Lesinski35168002014-07-21 15:25:30 -0700474 }
475
Adam Lesinskid26bea32014-09-03 16:49:59 -0700476 String[] table = names.toArray(new String[names.size()]);
477 Arrays.sort(table);
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800478 return new UsageEvents(results, table, true);
Adam Lesinski35168002014-07-21 15:25:30 -0700479 }
480
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800481 UsageEvents queryEventsForPackage(final long beginTime, final long endTime,
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800482 final String packageName, boolean includeTaskRoot) {
Varun Shah5e1613e2019-09-05 11:50:18 -0700483 if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
484 return null;
485 }
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800486 final ArraySet<String> names = new ArraySet<>();
487 names.add(packageName);
Hui Yu03d12402018-12-06 18:00:37 -0800488 final List<Event> results = queryStats(INTERVAL_DAILY,
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800489 beginTime, endTime, (stats, mutable, accumulatedResult) -> {
Suprabh Shukla60aa35b2018-04-24 18:52:46 -0700490 final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800491 final int size = stats.events.size();
492 for (int i = startIndex; i < size; i++) {
Suprabh Shukla60aa35b2018-04-24 18:52:46 -0700493 if (stats.events.get(i).mTimeStamp >= endTime) {
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800494 return;
495 }
496
Hui Yu03d12402018-12-06 18:00:37 -0800497 final Event event = stats.events.get(i);
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800498 if (!packageName.equals(event.mPackage)) {
499 continue;
500 }
501 if (event.mClass != null) {
502 names.add(event.mClass);
503 }
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800504 if (includeTaskRoot && event.mTaskRootPackage != null) {
505 names.add(event.mTaskRootPackage);
506 }
507 if (includeTaskRoot && event.mTaskRootClass != null) {
508 names.add(event.mTaskRootClass);
509 }
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800510 accumulatedResult.add(event);
511 }
512 });
513
514 if (results == null || results.isEmpty()) {
515 return null;
516 }
517
518 final String[] table = names.toArray(new String[names.size()]);
519 Arrays.sort(table);
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800520 return new UsageEvents(results, table, includeTaskRoot);
Suprabh Shukla217ccda2018-02-23 17:57:12 -0800521 }
522
Adam Lesinski3c153512014-07-23 17:34:34 -0700523 void persistActiveStats() {
524 if (mStatsChanged) {
525 Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
526 try {
527 for (int i = 0; i < mCurrentStats.length; i++) {
528 mDatabase.putUsageStats(i, mCurrentStats[i]);
529 }
530 mStatsChanged = false;
531 } catch (IOException e) {
532 Slog.e(TAG, mLogPrefix + "Failed to persist active stats", e);
533 }
534 }
535 }
536
Amith Yamasania93542f2016-02-03 18:02:06 -0800537 private void rolloverStats(final long currentTimeMillis) {
Adam Lesinski66143fa2014-09-11 08:31:05 -0700538 final long startTime = SystemClock.elapsedRealtime();
Adam Lesinski3c153512014-07-23 17:34:34 -0700539 Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
540
Hui Yue361a232018-10-04 15:05:21 -0700541 // Finish any ongoing events with an END_OF_DAY or ROLLOVER_FOREGROUND_SERVICE event.
542 // Make a note of which components need a new CONTINUE_PREVIOUS_DAY or
543 // CONTINUING_FOREGROUND_SERVICE entry.
Adam Lesinski7f61e962014-09-02 16:43:52 -0700544 final Configuration previousConfig =
Hui Yu03d12402018-12-06 18:00:37 -0800545 mCurrentStats[INTERVAL_DAILY].activeConfiguration;
546 ArraySet<String> continuePkgs = new ArraySet<>();
547 ArrayMap<String, SparseIntArray> continueActivity =
Hui Yue361a232018-10-04 15:05:21 -0700548 new ArrayMap<>();
Hui Yu03d12402018-12-06 18:00:37 -0800549 ArrayMap<String, ArrayMap<String, Integer>> continueForegroundService =
Hui Yue361a232018-10-04 15:05:21 -0700550 new ArrayMap<>();
Adam Lesinski35168002014-07-21 15:25:30 -0700551 for (IntervalStats stat : mCurrentStats) {
Adam Lesinski37a46b42014-09-05 15:38:05 -0700552 final int pkgCount = stat.packageStats.size();
Adam Lesinski3c153512014-07-23 17:34:34 -0700553 for (int i = 0; i < pkgCount; i++) {
Hui Yue361a232018-10-04 15:05:21 -0700554 final UsageStats pkgStats = stat.packageStats.valueAt(i);
Hui Yu03d12402018-12-06 18:00:37 -0800555 if (pkgStats.mActivities.size() > 0
556 || !pkgStats.mForegroundServices.isEmpty()) {
557 if (pkgStats.mActivities.size() > 0) {
558 continueActivity.put(pkgStats.mPackageName,
559 pkgStats.mActivities);
Hui Yue361a232018-10-04 15:05:21 -0700560 stat.update(pkgStats.mPackageName, null,
561 mDailyExpiryDate.getTimeInMillis() - 1,
Hui Yu03d12402018-12-06 18:00:37 -0800562 Event.END_OF_DAY, 0);
Hui Yue361a232018-10-04 15:05:21 -0700563 }
Hui Yu03d12402018-12-06 18:00:37 -0800564 if (!pkgStats.mForegroundServices.isEmpty()) {
565 continueForegroundService.put(pkgStats.mPackageName,
566 pkgStats.mForegroundServices);
Hui Yue361a232018-10-04 15:05:21 -0700567 stat.update(pkgStats.mPackageName, null,
568 mDailyExpiryDate.getTimeInMillis() - 1,
Hui Yu03d12402018-12-06 18:00:37 -0800569 Event.ROLLOVER_FOREGROUND_SERVICE, 0);
Hui Yue361a232018-10-04 15:05:21 -0700570 }
Hui Yu03d12402018-12-06 18:00:37 -0800571 continuePkgs.add(pkgStats.mPackageName);
Adam Lesinski66143fa2014-09-11 08:31:05 -0700572 notifyStatsChanged();
Adam Lesinski3c153512014-07-23 17:34:34 -0700573 }
574 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700575
Hui Yue361a232018-10-04 15:05:21 -0700576 stat.updateConfigurationStats(null,
577 mDailyExpiryDate.getTimeInMillis() - 1);
Dianne Hackbornced54392018-02-26 13:07:42 -0800578 stat.commitTime(mDailyExpiryDate.getTimeInMillis() - 1);
Adam Lesinski3c153512014-07-23 17:34:34 -0700579 }
580
581 persistActiveStats();
Adam Lesinski66143fa2014-09-11 08:31:05 -0700582 mDatabase.prune(currentTimeMillis);
Amith Yamasania93542f2016-02-03 18:02:06 -0800583 loadActiveStats(currentTimeMillis);
Adam Lesinski3c153512014-07-23 17:34:34 -0700584
Hui Yu03d12402018-12-06 18:00:37 -0800585 final int continueCount = continuePkgs.size();
Adam Lesinski3c153512014-07-23 17:34:34 -0700586 for (int i = 0; i < continueCount; i++) {
Hui Yu03d12402018-12-06 18:00:37 -0800587 String pkgName = continuePkgs.valueAt(i);
588 final long beginTime = mCurrentStats[INTERVAL_DAILY].beginTime;
Adam Lesinski35168002014-07-21 15:25:30 -0700589 for (IntervalStats stat : mCurrentStats) {
Hui Yu03d12402018-12-06 18:00:37 -0800590 if (continueActivity.containsKey(pkgName)) {
591 final SparseIntArray eventMap =
592 continueActivity.get(pkgName);
593 final int size = eventMap.size();
Hui Yue361a232018-10-04 15:05:21 -0700594 for (int j = 0; j < size; j++) {
Hui Yu03d12402018-12-06 18:00:37 -0800595 stat.update(pkgName, null, beginTime,
596 eventMap.valueAt(j), eventMap.keyAt(j));
Hui Yue361a232018-10-04 15:05:21 -0700597 }
598 }
Hui Yu03d12402018-12-06 18:00:37 -0800599 if (continueForegroundService.containsKey(pkgName)) {
600 final ArrayMap<String, Integer> eventMap =
601 continueForegroundService.get(pkgName);
602 final int size = eventMap.size();
Hui Yue361a232018-10-04 15:05:21 -0700603 for (int j = 0; j < size; j++) {
Hui Yu03d12402018-12-06 18:00:37 -0800604 stat.update(pkgName, eventMap.keyAt(j), beginTime,
605 eventMap.valueAt(j), 0);
Hui Yue361a232018-10-04 15:05:21 -0700606 }
607 }
Adam Lesinski7f61e962014-09-02 16:43:52 -0700608 stat.updateConfigurationStats(previousConfig, beginTime);
Adam Lesinski66143fa2014-09-11 08:31:05 -0700609 notifyStatsChanged();
Adam Lesinski3c153512014-07-23 17:34:34 -0700610 }
611 }
612 persistActiveStats();
613
Adam Lesinski66143fa2014-09-11 08:31:05 -0700614 final long totalTime = SystemClock.elapsedRealtime() - startTime;
Adam Lesinski3c153512014-07-23 17:34:34 -0700615 Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
616 + " milliseconds");
617 }
618
619 private void notifyStatsChanged() {
620 if (!mStatsChanged) {
621 mStatsChanged = true;
622 mListener.onStatsUpdated();
623 }
624 }
625
Amith Yamasania93542f2016-02-03 18:02:06 -0800626 private void notifyNewUpdate() {
627 mListener.onNewUpdate(mUserId);
628 }
629
630 private void loadActiveStats(final long currentTimeMillis) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700631 for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700632 final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
Hui Yub21d59f2019-01-28 16:33:32 -0800633 if (stats != null
634 && currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) {
Adam Lesinski35168002014-07-21 15:25:30 -0700635 if (DEBUG) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700636 Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700637 sDateFormat.format(stats.beginTime) + "(" + stats.beginTime +
Adam Lesinskid26bea32014-09-03 16:49:59 -0700638 ") for interval " + intervalType);
Adam Lesinski35168002014-07-21 15:25:30 -0700639 }
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700640 mCurrentStats[intervalType] = stats;
Adam Lesinski3c153512014-07-23 17:34:34 -0700641 } else {
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700642 // No good fit remains.
Adam Lesinski35168002014-07-21 15:25:30 -0700643 if (DEBUG) {
Adam Lesinskid26bea32014-09-03 16:49:59 -0700644 Slog.d(TAG, "Creating new stats @ " +
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700645 sDateFormat.format(currentTimeMillis) + "(" +
646 currentTimeMillis + ") for interval " + intervalType);
Adam Lesinski35168002014-07-21 15:25:30 -0700647 }
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700648
Adam Lesinskid26bea32014-09-03 16:49:59 -0700649 mCurrentStats[intervalType] = new IntervalStats();
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700650 mCurrentStats[intervalType].beginTime = currentTimeMillis;
651 mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
Adam Lesinski3c153512014-07-23 17:34:34 -0700652 }
653 }
Adam Lesinskida4a3772016-01-07 18:24:53 -0800654
Adam Lesinski3c153512014-07-23 17:34:34 -0700655 mStatsChanged = false;
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700656 updateRolloverDeadline();
Adam Lesinskib2d3ffa2016-01-26 18:18:19 -0800657
658 // Tell the listener that the stats reloaded, which may have changed idle states.
659 mListener.onStatsReloaded();
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700660 }
661
662 private void updateRolloverDeadline() {
663 mDailyExpiryDate.setTimeInMillis(
Hui Yu03d12402018-12-06 18:00:37 -0800664 mCurrentStats[INTERVAL_DAILY].beginTime);
Adam Lesinskid26bea32014-09-03 16:49:59 -0700665 mDailyExpiryDate.addDays(1);
Adam Lesinskid26bea32014-09-03 16:49:59 -0700666 Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
667 sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
Adam Lesinski7cba1d42015-08-04 16:17:37 -0700668 mDailyExpiryDate.getTimeInMillis() + ")");
Adam Lesinski3c153512014-07-23 17:34:34 -0700669 }
670
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700671 //
672 // -- DUMP related methods --
673 //
674
Amith Yamasania93542f2016-02-03 18:02:06 -0800675 void checkin(final IndentingPrintWriter pw) {
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700676 mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
677 @Override
678 public boolean checkin(IntervalStats stats) {
Michael Wachenschwanz1088cbb2018-03-01 12:45:16 -0800679 printIntervalStats(pw, stats, false, false, null);
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700680 return true;
681 }
682 });
683 }
684
Dianne Hackbornc81983a2017-10-20 16:16:32 -0700685 void dump(IndentingPrintWriter pw, String pkg) {
Michael Wachenschwanz1088cbb2018-03-01 12:45:16 -0800686 dump(pw, pkg, false);
687 }
688 void dump(IndentingPrintWriter pw, String pkg, boolean compact) {
689 printLast24HrEvents(pw, !compact, pkg);
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700690 for (int interval = 0; interval < mCurrentStats.length; interval++) {
691 pw.print("In-memory ");
692 pw.print(intervalToString(interval));
693 pw.println(" stats");
Michael Wachenschwanz1088cbb2018-03-01 12:45:16 -0800694 printIntervalStats(pw, mCurrentStats[interval], !compact, true, pkg);
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700695 }
Hui Yu0818df02019-02-21 13:55:00 -0800696 mDatabase.dump(pw, compact);
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700697 }
698
Varun Shah5bef1be2019-05-07 22:35:22 -0700699 void dumpDatabaseInfo(IndentingPrintWriter ipw) {
700 mDatabase.dump(ipw, false);
701 }
702
703 void dumpFile(IndentingPrintWriter ipw, String[] args) {
704 if (args == null || args.length == 0) {
705 // dump all files for every interval for specified user
706 final int numIntervals = mDatabase.mSortedStatFiles.length;
707 for (int interval = 0; interval < numIntervals; interval++) {
708 ipw.println("interval=" + intervalToString(interval));
709 ipw.increaseIndent();
710 dumpFileDetailsForInterval(ipw, interval);
711 ipw.decreaseIndent();
712 }
713 } else {
714 final int interval;
715 try {
716 final int intervalValue = stringToInterval(args[0]);
717 if (intervalValue == -1) {
718 interval = Integer.valueOf(args[0]);
719 } else {
720 interval = intervalValue;
721 }
722 } catch (NumberFormatException nfe) {
723 ipw.println("invalid interval specified.");
724 return;
725 }
726 if (interval < 0 || interval >= mDatabase.mSortedStatFiles.length) {
727 ipw.println("the specified interval does not exist.");
728 return;
729 }
730 if (args.length == 1) {
731 // dump all files in the specified interval
732 dumpFileDetailsForInterval(ipw, interval);
733 } else {
734 // dump details only for the specified filename
735 final long filename;
736 try {
737 filename = Long.valueOf(args[1]);
738 } catch (NumberFormatException nfe) {
739 ipw.println("invalid filename specified.");
740 return;
741 }
742 final IntervalStats stats = mDatabase.readIntervalStatsForFile(interval, filename);
743 if (stats == null) {
744 ipw.println("the specified filename does not exist.");
745 return;
746 }
747 dumpFileDetails(ipw, stats, Long.valueOf(args[1]));
748 }
749 }
750 }
751
752 private void dumpFileDetailsForInterval(IndentingPrintWriter ipw, int interval) {
753 final TimeSparseArray<AtomicFile> files = mDatabase.mSortedStatFiles[interval];
754 final int numFiles = files.size();
755 for (int i = 0; i < numFiles; i++) {
756 final long filename = files.keyAt(i);
757 final IntervalStats stats = mDatabase.readIntervalStatsForFile(interval, filename);
758 dumpFileDetails(ipw, stats, filename);
759 ipw.println();
760 }
761 }
762
763 private void dumpFileDetails(IndentingPrintWriter ipw, IntervalStats stats, long filename) {
764 ipw.println("file=" + filename);
765 ipw.increaseIndent();
766 printIntervalStats(ipw, stats, false, false, null);
767 ipw.decreaseIndent();
768 }
769
Hui Yu0818df02019-02-21 13:55:00 -0800770 static String formatDateTime(long dateTime, boolean pretty) {
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700771 if (pretty) {
Michael Wachenschwanz1088cbb2018-03-01 12:45:16 -0800772 return "\"" + sDateFormat.format(dateTime)+ "\"";
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700773 }
774 return Long.toString(dateTime);
775 }
776
777 private String formatElapsedTime(long elapsedTime, boolean pretty) {
778 if (pretty) {
779 return "\"" + DateUtils.formatElapsedTime(elapsedTime / 1000) + "\"";
780 }
781 return Long.toString(elapsedTime);
782 }
783
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800784
Hui Yu03d12402018-12-06 18:00:37 -0800785 void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800786 pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
787 pw.printPair("type", eventToString(event.mEventType));
788 pw.printPair("package", event.mPackage);
789 if (event.mClass != null) {
790 pw.printPair("class", event.mClass);
791 }
792 if (event.mConfiguration != null) {
793 pw.printPair("config", Configuration.resourceQualifierString(event.mConfiguration));
794 }
795 if (event.mShortcutId != null) {
796 pw.printPair("shortcutId", event.mShortcutId);
797 }
Hui Yu03d12402018-12-06 18:00:37 -0800798 if (event.mEventType == Event.STANDBY_BUCKET_CHANGED) {
Amith Yamasani119be9a2018-02-18 22:23:00 -0800799 pw.printPair("standbyBucket", event.getStandbyBucket());
800 pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
Hui Yu03d12402018-12-06 18:00:37 -0800801 } else if (event.mEventType == Event.ACTIVITY_RESUMED
802 || event.mEventType == Event.ACTIVITY_PAUSED
803 || event.mEventType == Event.ACTIVITY_STOPPED) {
804 pw.printPair("instanceId", event.getInstanceId());
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800805 }
Hui Yu03d12402018-12-06 18:00:37 -0800806
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800807 if (event.getTaskRootPackageName() != null) {
808 pw.printPair("taskRootPackage", event.getTaskRootPackageName());
809 }
810
811 if (event.getTaskRootClassName() != null) {
812 pw.printPair("taskRootClass", event.getTaskRootClassName());
813 }
814
Julia Reynolds5dc1edf2018-06-12 10:19:02 -0400815 if (event.mNotificationChannelId != null) {
816 pw.printPair("channelId", event.mNotificationChannelId);
817 }
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800818 pw.printHexPair("flags", event.mFlags);
819 pw.println();
820 }
821
822 void printLast24HrEvents(IndentingPrintWriter pw, boolean prettyDates, final String pkg) {
823 final long endTime = System.currentTimeMillis();
824 UnixCalendar yesterday = new UnixCalendar(endTime);
825 yesterday.addDays(-1);
826
827 final long beginTime = yesterday.getTimeInMillis();
828
Hui Yu03d12402018-12-06 18:00:37 -0800829 List<Event> events = queryStats(INTERVAL_DAILY,
830 beginTime, endTime, new StatCombiner<Event>() {
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800831 @Override
832 public void combine(IntervalStats stats, boolean mutable,
Hui Yu03d12402018-12-06 18:00:37 -0800833 List<Event> accumulatedResult) {
Suprabh Shukla60aa35b2018-04-24 18:52:46 -0700834 final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800835 final int size = stats.events.size();
836 for (int i = startIndex; i < size; i++) {
Suprabh Shukla60aa35b2018-04-24 18:52:46 -0700837 if (stats.events.get(i).mTimeStamp >= endTime) {
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800838 return;
839 }
840
Hui Yu03d12402018-12-06 18:00:37 -0800841 Event event = stats.events.get(i);
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800842 if (pkg != null && !pkg.equals(event.mPackage)) {
843 continue;
844 }
845 accumulatedResult.add(event);
846 }
847 }
848 });
849
850 pw.print("Last 24 hour events (");
851 if (prettyDates) {
852 pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
853 beginTime, endTime, sDateFormatFlags) + "\"");
854 } else {
855 pw.printPair("beginTime", beginTime);
856 pw.printPair("endTime", endTime);
857 }
858 pw.println(")");
Michael Wachenschwanz78646e52018-02-27 15:54:27 -0800859 if (events != null) {
860 pw.increaseIndent();
Hui Yu03d12402018-12-06 18:00:37 -0800861 for (Event event : events) {
Michael Wachenschwanz78646e52018-02-27 15:54:27 -0800862 printEvent(pw, event, prettyDates);
863 }
864 pw.decreaseIndent();
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800865 }
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800866 }
867
Dianne Hackborn3378aa92018-03-30 17:43:49 -0700868 void printEventAggregation(IndentingPrintWriter pw, String label,
869 IntervalStats.EventTracker tracker, boolean prettyDates) {
870 if (tracker.count != 0 || tracker.duration != 0) {
Dianne Hackbornced54392018-02-26 13:07:42 -0800871 pw.print(label);
872 pw.print(": ");
Dianne Hackborn3378aa92018-03-30 17:43:49 -0700873 pw.print(tracker.count);
Dianne Hackbornced54392018-02-26 13:07:42 -0800874 pw.print("x for ");
Dianne Hackborn3378aa92018-03-30 17:43:49 -0700875 pw.print(formatElapsedTime(tracker.duration, prettyDates));
876 if (tracker.curStartTime != 0) {
877 pw.print(" (now running, started at ");
878 formatDateTime(tracker.curStartTime, prettyDates);
879 pw.print(")");
880 }
Dianne Hackbornced54392018-02-26 13:07:42 -0800881 pw.println();
882 }
883 }
884
Amith Yamasania93542f2016-02-03 18:02:06 -0800885 void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
Michael Wachenschwanz1088cbb2018-03-01 12:45:16 -0800886 boolean prettyDates, boolean skipEvents, String pkg) {
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700887 if (prettyDates) {
888 pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
889 stats.beginTime, stats.endTime, sDateFormatFlags) + "\"");
890 } else {
891 pw.printPair("beginTime", stats.beginTime);
892 pw.printPair("endTime", stats.endTime);
893 }
894 pw.println();
895 pw.increaseIndent();
896 pw.println("packages");
897 pw.increaseIndent();
898 final ArrayMap<String, UsageStats> pkgStats = stats.packageStats;
899 final int pkgCount = pkgStats.size();
900 for (int i = 0; i < pkgCount; i++) {
901 final UsageStats usageStats = pkgStats.valueAt(i);
Dianne Hackbornc81983a2017-10-20 16:16:32 -0700902 if (pkg != null && !pkg.equals(usageStats.mPackageName)) {
903 continue;
904 }
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700905 pw.printPair("package", usageStats.mPackageName);
Hui Yu03d12402018-12-06 18:00:37 -0800906 pw.printPair("totalTimeUsed",
Adam Lesinskic8e87292015-06-10 15:33:45 -0700907 formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
Hui Yu03d12402018-12-06 18:00:37 -0800908 pw.printPair("lastTimeUsed", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
909 pw.printPair("totalTimeVisible",
910 formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates));
911 pw.printPair("lastTimeVisible",
912 formatDateTime(usageStats.mLastTimeVisible, prettyDates));
913 pw.printPair("totalTimeFS",
914 formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates));
915 pw.printPair("lastTimeFS",
916 formatDateTime(usageStats.mLastTimeForegroundServiceUsed, prettyDates));
Amith Yamasanibc813eb2018-03-20 19:37:46 -0700917 pw.printPair("appLaunchCount", usageStats.mAppLaunchCount);
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700918 pw.println();
919 }
920 pw.decreaseIndent();
921
Kang Li53b43142016-11-14 14:38:25 -0800922 pw.println();
Kang Li53b43142016-11-14 14:38:25 -0800923 pw.println("ChooserCounts");
924 pw.increaseIndent();
925 for (UsageStats usageStats : pkgStats.values()) {
Dianne Hackbornc81983a2017-10-20 16:16:32 -0700926 if (pkg != null && !pkg.equals(usageStats.mPackageName)) {
927 continue;
928 }
Kang Li53b43142016-11-14 14:38:25 -0800929 pw.printPair("package", usageStats.mPackageName);
930 if (usageStats.mChooserCounts != null) {
931 final int chooserCountSize = usageStats.mChooserCounts.size();
932 for (int i = 0; i < chooserCountSize; i++) {
933 final String action = usageStats.mChooserCounts.keyAt(i);
934 final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
935 final int annotationSize = counts.size();
936 for (int j = 0; j < annotationSize; j++) {
937 final String key = counts.keyAt(j);
938 final int count = counts.valueAt(j);
939 if (count != 0) {
940 pw.printPair("ChooserCounts", action + ":" + key + " is " +
941 Integer.toString(count));
942 pw.println();
943 }
944 }
945 }
946 }
947 pw.println();
948 }
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800949 pw.decreaseIndent();
Kang Li53b43142016-11-14 14:38:25 -0800950
Dianne Hackbornc81983a2017-10-20 16:16:32 -0700951 if (pkg == null) {
952 pw.println("configurations");
953 pw.increaseIndent();
954 final ArrayMap<Configuration, ConfigurationStats> configStats = stats.configurations;
955 final int configCount = configStats.size();
956 for (int i = 0; i < configCount; i++) {
957 final ConfigurationStats config = configStats.valueAt(i);
958 pw.printPair("config", Configuration.resourceQualifierString(
959 config.mConfiguration));
960 pw.printPair("totalTime", formatElapsedTime(config.mTotalTimeActive, prettyDates));
961 pw.printPair("lastTime", formatDateTime(config.mLastTimeActive, prettyDates));
962 pw.printPair("count", config.mActivationCount);
963 pw.println();
964 }
965 pw.decreaseIndent();
Dianne Hackbornced54392018-02-26 13:07:42 -0800966 pw.println("event aggregations");
967 pw.increaseIndent();
Dianne Hackborn3378aa92018-03-30 17:43:49 -0700968 printEventAggregation(pw, "screen-interactive", stats.interactiveTracker,
969 prettyDates);
970 printEventAggregation(pw, "screen-non-interactive", stats.nonInteractiveTracker,
971 prettyDates);
972 printEventAggregation(pw, "keyguard-shown", stats.keyguardShownTracker,
973 prettyDates);
974 printEventAggregation(pw, "keyguard-hidden", stats.keyguardHiddenTracker,
975 prettyDates);
Dianne Hackbornced54392018-02-26 13:07:42 -0800976 pw.decreaseIndent();
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700977 }
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700978
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800979 // The last 24 hours of events is already printed in the non checkin dump
980 // No need to repeat here.
Michael Wachenschwanz1088cbb2018-03-01 12:45:16 -0800981 if (!skipEvents) {
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800982 pw.println("events");
983 pw.increaseIndent();
Suprabh Shukla60aa35b2018-04-24 18:52:46 -0700984 final EventList events = stats.events;
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800985 final int eventCount = events != null ? events.size() : 0;
986 for (int i = 0; i < eventCount; i++) {
Hui Yu03d12402018-12-06 18:00:37 -0800987 final Event event = events.get(i);
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800988 if (pkg != null && !pkg.equals(event.mPackage)) {
989 continue;
990 }
991 printEvent(pw, event, prettyDates);
Dianne Hackbornc81983a2017-10-20 16:16:32 -0700992 }
Michael Wachenschwanz06e49402018-02-20 23:39:09 -0800993 pw.decreaseIndent();
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700994 }
995 pw.decreaseIndent();
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700996 }
997
Hui Yu0818df02019-02-21 13:55:00 -0800998 public static String intervalToString(int interval) {
Adam Lesinski1bb18c42014-08-18 12:21:34 -0700999 switch (interval) {
Hui Yu03d12402018-12-06 18:00:37 -08001000 case INTERVAL_DAILY:
Adam Lesinski1bb18c42014-08-18 12:21:34 -07001001 return "daily";
Hui Yu03d12402018-12-06 18:00:37 -08001002 case INTERVAL_WEEKLY:
Adam Lesinski1bb18c42014-08-18 12:21:34 -07001003 return "weekly";
Hui Yu03d12402018-12-06 18:00:37 -08001004 case INTERVAL_MONTHLY:
Adam Lesinski1bb18c42014-08-18 12:21:34 -07001005 return "monthly";
Hui Yu03d12402018-12-06 18:00:37 -08001006 case INTERVAL_YEARLY:
Adam Lesinski1bb18c42014-08-18 12:21:34 -07001007 return "yearly";
1008 default:
1009 return "?";
1010 }
1011 }
Adam Lesinski3c153512014-07-23 17:34:34 -07001012
Varun Shah5bef1be2019-05-07 22:35:22 -07001013 private static int stringToInterval(String interval) {
1014 switch (interval.toLowerCase()) {
1015 case "daily":
1016 return INTERVAL_DAILY;
1017 case "weekly":
1018 return INTERVAL_WEEKLY;
1019 case "monthly":
1020 return INTERVAL_MONTHLY;
1021 case "yearly":
1022 return INTERVAL_YEARLY;
1023 default:
1024 return -1;
1025 }
1026 }
1027
Adam Lesinski3c153512014-07-23 17:34:34 -07001028 private static String eventToString(int eventType) {
1029 switch (eventType) {
Hui Yu03d12402018-12-06 18:00:37 -08001030 case Event.NONE:
Adam Lesinski3c153512014-07-23 17:34:34 -07001031 return "NONE";
Hui Yu03d12402018-12-06 18:00:37 -08001032 case Event.ACTIVITY_PAUSED:
1033 return "ACTIVITY_PAUSED";
1034 case Event.ACTIVITY_RESUMED:
1035 return "ACTIVITY_RESUMED";
1036 case Event.FOREGROUND_SERVICE_START:
Hui Yue361a232018-10-04 15:05:21 -07001037 return "FOREGROUND_SERVICE_START";
Hui Yu03d12402018-12-06 18:00:37 -08001038 case Event.FOREGROUND_SERVICE_STOP:
Hui Yue361a232018-10-04 15:05:21 -07001039 return "FOREGROUND_SERVICE_STOP";
Hui Yu03d12402018-12-06 18:00:37 -08001040 case Event.ACTIVITY_STOPPED:
1041 return "ACTIVITY_STOPPED";
1042 case Event.END_OF_DAY:
Adam Lesinski3c153512014-07-23 17:34:34 -07001043 return "END_OF_DAY";
Hui Yu03d12402018-12-06 18:00:37 -08001044 case Event.ROLLOVER_FOREGROUND_SERVICE:
Hui Yue361a232018-10-04 15:05:21 -07001045 return "ROLLOVER_FOREGROUND_SERVICE";
Hui Yu03d12402018-12-06 18:00:37 -08001046 case Event.CONTINUE_PREVIOUS_DAY:
Adam Lesinski3c153512014-07-23 17:34:34 -07001047 return "CONTINUE_PREVIOUS_DAY";
Hui Yu03d12402018-12-06 18:00:37 -08001048 case Event.CONTINUING_FOREGROUND_SERVICE:
Hui Yue361a232018-10-04 15:05:21 -07001049 return "CONTINUING_FOREGROUND_SERVICE";
Hui Yu03d12402018-12-06 18:00:37 -08001050 case Event.CONFIGURATION_CHANGE:
Adam Lesinski7f61e962014-09-02 16:43:52 -07001051 return "CONFIGURATION_CHANGE";
Hui Yu03d12402018-12-06 18:00:37 -08001052 case Event.SYSTEM_INTERACTION:
Adam Lesinskic8e87292015-06-10 15:33:45 -07001053 return "SYSTEM_INTERACTION";
Hui Yu03d12402018-12-06 18:00:37 -08001054 case Event.USER_INTERACTION:
Adam Lesinskic8e87292015-06-10 15:33:45 -07001055 return "USER_INTERACTION";
Hui Yu03d12402018-12-06 18:00:37 -08001056 case Event.SHORTCUT_INVOCATION:
Makoto Onukiac042502016-05-20 16:39:42 -07001057 return "SHORTCUT_INVOCATION";
Hui Yu03d12402018-12-06 18:00:37 -08001058 case Event.CHOOSER_ACTION:
Kang Li53b43142016-11-14 14:38:25 -08001059 return "CHOOSER_ACTION";
Hui Yu03d12402018-12-06 18:00:37 -08001060 case Event.NOTIFICATION_SEEN:
Amith Yamasani803eab692017-11-09 17:47:04 -08001061 return "NOTIFICATION_SEEN";
Hui Yu03d12402018-12-06 18:00:37 -08001062 case Event.STANDBY_BUCKET_CHANGED:
Amith Yamasanibfc4bf52018-01-19 06:55:08 -08001063 return "STANDBY_BUCKET_CHANGED";
Hui Yu03d12402018-12-06 18:00:37 -08001064 case Event.NOTIFICATION_INTERRUPTION:
Julia Reynolds1fac86e2018-03-07 08:30:37 -05001065 return "NOTIFICATION_INTERRUPTION";
Hui Yu03d12402018-12-06 18:00:37 -08001066 case Event.SLICE_PINNED:
Jason Monk1918ef72018-03-14 09:20:39 -04001067 return "SLICE_PINNED";
Hui Yu03d12402018-12-06 18:00:37 -08001068 case Event.SLICE_PINNED_PRIV:
Jason Monk1918ef72018-03-14 09:20:39 -04001069 return "SLICE_PINNED_PRIV";
Hui Yu03d12402018-12-06 18:00:37 -08001070 case Event.SCREEN_INTERACTIVE:
Dianne Hackbornced54392018-02-26 13:07:42 -08001071 return "SCREEN_INTERACTIVE";
Hui Yu03d12402018-12-06 18:00:37 -08001072 case Event.SCREEN_NON_INTERACTIVE:
Dianne Hackbornced54392018-02-26 13:07:42 -08001073 return "SCREEN_NON_INTERACTIVE";
Hui Yub1d243a2018-12-13 12:02:00 -08001074 case Event.KEYGUARD_SHOWN:
Esteban Talavera917a71d2018-11-13 07:55:08 +00001075 return "KEYGUARD_SHOWN";
Hui Yub1d243a2018-12-13 12:02:00 -08001076 case Event.KEYGUARD_HIDDEN:
Esteban Talavera917a71d2018-11-13 07:55:08 +00001077 return "KEYGUARD_HIDDEN";
Hui Yub1d243a2018-12-13 12:02:00 -08001078 case Event.DEVICE_SHUTDOWN:
1079 return "DEVICE_SHUTDOWN";
Hui Yu80a8b462019-03-05 15:22:13 -08001080 case Event.DEVICE_STARTUP:
1081 return "DEVICE_STARTUP";
Adam Lesinski3c153512014-07-23 17:34:34 -07001082 default:
Esteban Talavera917a71d2018-11-13 07:55:08 +00001083 return "UNKNOWN_TYPE_" + eventType;
Adam Lesinski3c153512014-07-23 17:34:34 -07001084 }
1085 }
Ritesh Reddy8a6ce2c2015-12-17 17:03:54 +00001086
1087 byte[] getBackupPayload(String key){
Varun Shah5e1613e2019-09-05 11:50:18 -07001088 checkAndGetTimeLocked();
Ritesh Reddy8a6ce2c2015-12-17 17:03:54 +00001089 return mDatabase.getBackupPayload(key);
1090 }
1091
1092 void applyRestoredPayload(String key, byte[] payload){
Varun Shah5e1613e2019-09-05 11:50:18 -07001093 checkAndGetTimeLocked();
Ritesh Reddy8a6ce2c2015-12-17 17:03:54 +00001094 mDatabase.applyRestoredPayload(key, payload);
1095 }
Adam Lesinski3c153512014-07-23 17:34:34 -07001096}