blob: 372ec981df02a2af8f62a83395bb9f4b455a8dfa [file] [log] [blame]
Amith Yamasani0a11e692015-05-08 16:36:21 -07001/**
2 * Copyright (C) 2015 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 com.android.server.usage;
18
Amith Yamasani119be9a2018-02-18 22:23:00 -080019import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
Kweku Adamsc182d5e2020-01-08 18:37:26 -080020import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
Amith Yamasani119be9a2018-02-18 22:23:00 -080021import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
22import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
23import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
Kweku Adams12752132020-02-18 15:36:48 -080024import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
Amith Yamasani119be9a2018-02-18 22:23:00 -080025import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
Amith Yamasani172612c2017-12-15 10:51:53 -080026import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
27import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
28import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
Kweku Adamsc6a9b342020-01-08 18:37:26 -080029import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -080030import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
Amith Yamasani172612c2017-12-15 10:51:53 -080031
Kweku Adamsc6a9b342020-01-08 18:37:26 -080032import static com.android.server.usage.AppStandbyController.isUserUsage;
33
Suprabh Shukla868bde22018-02-20 20:59:52 -080034import android.app.usage.AppStandbyInfo;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080035import android.app.usage.UsageStatsManager;
Amith Yamasania93542f2016-02-03 18:02:06 -080036import android.os.SystemClock;
Amith Yamasani0a11e692015-05-08 16:36:21 -070037import android.util.ArrayMap;
Amith Yamasania93542f2016-02-03 18:02:06 -080038import android.util.AtomicFile;
39import android.util.Slog;
Amith Yamasani0a11e692015-05-08 16:36:21 -070040import android.util.SparseArray;
Amith Yamasania93542f2016-02-03 18:02:06 -080041import android.util.TimeUtils;
42import android.util.Xml;
Amith Yamasani0a11e692015-05-08 16:36:21 -070043
Amith Yamasania93542f2016-02-03 18:02:06 -080044import com.android.internal.annotations.VisibleForTesting;
Sudheer Shankab497dd42020-02-16 22:23:24 -080045import com.android.internal.util.CollectionUtils;
Amith Yamasania93542f2016-02-03 18:02:06 -080046import com.android.internal.util.FastXmlSerializer;
Kweku Adams12752132020-02-18 15:36:48 -080047import com.android.internal.util.FrameworkStatsLog;
Amith Yamasani0a11e692015-05-08 16:36:21 -070048import com.android.internal.util.IndentingPrintWriter;
49
Amith Yamasania93542f2016-02-03 18:02:06 -080050import libcore.io.IoUtils;
51
52import org.xmlpull.v1.XmlPullParser;
53import org.xmlpull.v1.XmlPullParserException;
54
55import java.io.BufferedOutputStream;
56import java.io.BufferedReader;
57import java.io.File;
58import java.io.FileInputStream;
59import java.io.FileOutputStream;
60import java.io.FileReader;
61import java.io.IOException;
62import java.nio.charset.StandardCharsets;
Suprabh Shukla868bde22018-02-20 20:59:52 -080063import java.util.ArrayList;
Sudheer Shankab497dd42020-02-16 22:23:24 -080064import java.util.List;
Amith Yamasania93542f2016-02-03 18:02:06 -080065
Amith Yamasani0a11e692015-05-08 16:36:21 -070066/**
67 * Keeps track of recent active state changes in apps.
68 * Access should be guarded by a lock by the caller.
69 */
70public class AppIdleHistory {
71
Amith Yamasania93542f2016-02-03 18:02:06 -080072 private static final String TAG = "AppIdleHistory";
73
Amith Yamasani17fffee2017-09-29 13:17:43 -070074 private static final boolean DEBUG = AppStandbyController.DEBUG;
75
Amith Yamasania93542f2016-02-03 18:02:06 -080076 // History for all users and all packages
Amith Yamasani17fffee2017-09-29 13:17:43 -070077 private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
Amith Yamasani0a11e692015-05-08 16:36:21 -070078 private static final long ONE_MINUTE = 60 * 1000;
Amith Yamasani0a11e692015-05-08 16:36:21 -070079
Amith Yamasani3154dcf2018-03-27 18:24:04 -070080 private static final int STANDBY_BUCKET_UNKNOWN = -1;
81
Kweku Adams95cd9522020-05-08 09:56:53 -070082 /**
83 * The bucket beyond which apps are considered idle. Any apps in this bucket or lower are
84 * considered idle while those in higher buckets are not considered idle.
85 */
86 static final int IDLE_BUCKET_CUTOFF = STANDBY_BUCKET_RARE;
87
Amith Yamasania93542f2016-02-03 18:02:06 -080088 @VisibleForTesting
89 static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
90 private static final String TAG_PACKAGES = "packages";
91 private static final String TAG_PACKAGE = "package";
92 private static final String ATTR_NAME = "name";
93 // Screen on timebase time when app was last used
94 private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
95 // Elapsed timebase time when app was last used
96 private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
Kweku Adamsc6a9b342020-01-08 18:37:26 -080097 // Elapsed timebase time when app was last used by the user
98 private static final String ATTR_LAST_USED_BY_USER_ELAPSED = "lastUsedByUserElapsedTime";
Amith Yamasanibd7b3022017-12-06 17:40:25 -080099 // Elapsed timebase time when the app bucket was last predicted externally
100 private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime";
101 // The standby bucket for the app
Amith Yamasani17fffee2017-09-29 13:17:43 -0700102 private static final String ATTR_CURRENT_BUCKET = "appLimitBucket";
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800103 // The reason the app was put in the above bucket
Amith Yamasani17fffee2017-09-29 13:17:43 -0700104 private static final String ATTR_BUCKETING_REASON = "bucketReason";
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800105 // The last time a job was run for this app
106 private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime";
107 // The time when the forced active state can be overridden.
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800108 private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
109 // The time when the forced working_set state can be overridden.
110 private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800111 // Elapsed timebase time when the app was last marked for restriction.
112 private static final String ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED =
113 "lastRestrictionAttemptElapsedTime";
114 // Reason why the app was last marked for restriction.
115 private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON =
116 "lastRestrictionAttemptReason";
Amith Yamasani17fffee2017-09-29 13:17:43 -0700117
Amith Yamasania93542f2016-02-03 18:02:06 -0800118 // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
119 private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
120 private long mElapsedDuration; // Total device on duration since device was "born"
121
122 // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot)
123 private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
124 private long mScreenOnDuration; // Total screen on duration since device was "born"
125
Amith Yamasania93542f2016-02-03 18:02:06 -0800126 private final File mStorageDir;
127
128 private boolean mScreenOn;
129
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800130 static class AppUsageHistory {
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800131 // Last used time (including system usage), using elapsed timebase
Amith Yamasania93542f2016-02-03 18:02:06 -0800132 long lastUsedElapsedTime;
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800133 // Last time the user used the app, using elapsed timebase
134 long lastUsedByUserElapsedTime;
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800135 // Last used time using screen_on timebase
Amith Yamasania93542f2016-02-03 18:02:06 -0800136 long lastUsedScreenTime;
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800137 // Last predicted time using elapsed timebase
138 long lastPredictedTime;
Amith Yamasani3154dcf2018-03-27 18:24:04 -0700139 // Last predicted bucket
140 @UsageStatsManager.StandbyBuckets
141 int lastPredictedBucket = STANDBY_BUCKET_UNKNOWN;
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800142 // Standby bucket
Amith Yamasaniafbccb72017-11-27 10:44:24 -0800143 @UsageStatsManager.StandbyBuckets
144 int currentBucket;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800145 // Reason for setting the standby bucket. The value here is a combination of
146 // one of UsageStatsManager.REASON_MAIN_* and one (or none) of
147 // UsageStatsManager.REASON_SUB_*. Also see REASON_MAIN_MASK and REASON_SUB_MASK.
148 int bucketingReason;
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800149 // In-memory only, last bucket for which the listeners were informed
Amith Yamasani84cd7b72017-11-07 13:59:37 -0800150 int lastInformedBucket;
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800151 // The last time a job was run for this app, using elapsed timebase
152 long lastJobRunTime;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800153 // When should the bucket active state timeout, in elapsed timebase, if greater than
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800154 // lastUsedElapsedTime.
155 // This is used to keep the app in a high bucket regardless of other timeouts and
156 // predictions.
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800157 long bucketActiveTimeoutTime;
158 // If there's a forced working_set state, this is when it times out. This can be sitting
159 // under any active state timeout, so that it becomes applicable after the active state
160 // timeout expires.
161 long bucketWorkingSetTimeoutTime;
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800162 // The last time an agent attempted to put the app into the RESTRICTED bucket.
163 long lastRestrictAttemptElapsedTime;
164 // The last reason the app was marked to be put into the RESTRICTED bucket.
165 int lastRestrictReason;
Amith Yamasania93542f2016-02-03 18:02:06 -0800166 }
167
Amith Yamasania93542f2016-02-03 18:02:06 -0800168 AppIdleHistory(File storageDir, long elapsedRealtime) {
169 mElapsedSnapshot = elapsedRealtime;
170 mScreenOnSnapshot = elapsedRealtime;
171 mStorageDir = storageDir;
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800172 readScreenOnTime();
Amith Yamasania93542f2016-02-03 18:02:06 -0800173 }
174
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800175 public void updateDisplay(boolean screenOn, long elapsedRealtime) {
Amith Yamasania93542f2016-02-03 18:02:06 -0800176 if (screenOn == mScreenOn) return;
177
178 mScreenOn = screenOn;
179 if (mScreenOn) {
180 mScreenOnSnapshot = elapsedRealtime;
181 } else {
182 mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
183 mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
Amith Yamasania93542f2016-02-03 18:02:06 -0800184 mElapsedSnapshot = elapsedRealtime;
185 }
Amith Yamasani17fffee2017-09-29 13:17:43 -0700186 if (DEBUG) Slog.d(TAG, "mScreenOnSnapshot=" + mScreenOnSnapshot
187 + ", mScreenOnDuration=" + mScreenOnDuration
188 + ", mScreenOn=" + mScreenOn);
Amith Yamasania93542f2016-02-03 18:02:06 -0800189 }
190
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800191 public long getScreenOnTime(long elapsedRealtime) {
Amith Yamasania93542f2016-02-03 18:02:06 -0800192 long screenOnTime = mScreenOnDuration;
193 if (mScreenOn) {
194 screenOnTime += elapsedRealtime - mScreenOnSnapshot;
195 }
196 return screenOnTime;
197 }
198
199 @VisibleForTesting
200 File getScreenOnTimeFile() {
201 return new File(mStorageDir, "screen_on_time");
202 }
203
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800204 private void readScreenOnTime() {
Amith Yamasania93542f2016-02-03 18:02:06 -0800205 File screenOnTimeFile = getScreenOnTimeFile();
206 if (screenOnTimeFile.exists()) {
207 try {
208 BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
209 mScreenOnDuration = Long.parseLong(reader.readLine());
210 mElapsedDuration = Long.parseLong(reader.readLine());
211 reader.close();
212 } catch (IOException | NumberFormatException e) {
213 }
214 } else {
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800215 writeScreenOnTime();
Amith Yamasania93542f2016-02-03 18:02:06 -0800216 }
217 }
218
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800219 private void writeScreenOnTime() {
Amith Yamasania93542f2016-02-03 18:02:06 -0800220 AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
221 FileOutputStream fos = null;
222 try {
223 fos = screenOnTimeFile.startWrite();
224 fos.write((Long.toString(mScreenOnDuration) + "\n"
225 + Long.toString(mElapsedDuration) + "\n").getBytes());
226 screenOnTimeFile.finishWrite(fos);
227 } catch (IOException ioe) {
228 screenOnTimeFile.failWrite(fos);
229 }
230 }
231
232 /**
233 * To be called periodically to keep track of elapsed time when app idle times are written
234 */
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800235 public void writeAppIdleDurations() {
Amith Yamasania93542f2016-02-03 18:02:06 -0800236 final long elapsedRealtime = SystemClock.elapsedRealtime();
237 // Only bump up and snapshot the elapsed time. Don't change screen on duration.
238 mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
239 mElapsedSnapshot = elapsedRealtime;
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800240 writeScreenOnTime();
Amith Yamasania93542f2016-02-03 18:02:06 -0800241 }
242
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800243 /**
244 * Mark the app as used and update the bucket if necessary. If there is a timeout specified
245 * that's in the future, then the usage event is temporary and keeps the app in the specified
246 * bucket at least until the timeout is reached. This can be used to keep the app in an
247 * elevated bucket for a while until some important task gets to run.
Christopher Tated117b292018-01-05 17:32:36 -0800248 * @param appUsageHistory the usage record for the app being updated
249 * @param packageName name of the app being updated, for logging purposes
250 * @param newBucket the bucket to set the app to
Amith Yamasani119be9a2018-02-18 22:23:00 -0800251 * @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800252 * @param elapsedRealtime mark as used time if non-zero
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800253 * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
254 * with bucket values of ACTIVE and WORKING_SET.
Kweku Adams12752132020-02-18 15:36:48 -0800255 * @return {@code appUsageHistory}
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800256 */
Kweku Adams12752132020-02-18 15:36:48 -0800257 AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -0800258 int newBucket, int usageReason, long elapsedRealtime, long timeout) {
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800259 int bucketingReason = REASON_MAIN_USAGE | usageReason;
260 final boolean isUserUsage = isUserUsage(bucketingReason);
261
262 if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) {
263 // Only user usage should bring an app out of the RESTRICTED bucket.
264 newBucket = STANDBY_BUCKET_RESTRICTED;
265 bucketingReason = appUsageHistory.bucketingReason;
266 } else {
267 // Set the timeout if applicable
268 if (timeout > elapsedRealtime) {
269 // Convert to elapsed timebase
270 final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
271 if (newBucket == STANDBY_BUCKET_ACTIVE) {
272 appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
273 appUsageHistory.bucketActiveTimeoutTime);
274 } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
275 appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
276 appUsageHistory.bucketWorkingSetTimeoutTime);
277 } else {
278 throw new IllegalArgumentException("Cannot set a timeout on bucket="
279 + newBucket);
280 }
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800281 }
282 }
283
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800284 if (elapsedRealtime != 0) {
285 appUsageHistory.lastUsedElapsedTime = mElapsedDuration
286 + (elapsedRealtime - mElapsedSnapshot);
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800287 if (isUserUsage) {
288 appUsageHistory.lastUsedByUserElapsedTime = appUsageHistory.lastUsedElapsedTime;
289 }
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800290 appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
291 }
Amith Yamasania93542f2016-02-03 18:02:06 -0800292
Christopher Tated117b292018-01-05 17:32:36 -0800293 if (appUsageHistory.currentBucket > newBucket) {
294 appUsageHistory.currentBucket = newBucket;
Kweku Adams12752132020-02-18 15:36:48 -0800295 logAppStandbyBucketChanged(packageName, userId, newBucket, bucketingReason);
Amith Yamasani17fffee2017-09-29 13:17:43 -0700296 }
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800297 appUsageHistory.bucketingReason = bucketingReason;
Amith Yamasani803eab692017-11-09 17:47:04 -0800298
Christopher Tated117b292018-01-05 17:32:36 -0800299 return appUsageHistory;
300 }
301
302 /**
303 * Mark the app as used and update the bucket if necessary. If there is a timeout specified
304 * that's in the future, then the usage event is temporary and keeps the app in the specified
305 * bucket at least until the timeout is reached. This can be used to keep the app in an
306 * elevated bucket for a while until some important task gets to run.
307 * @param packageName
308 * @param userId
309 * @param newBucket the bucket to set the app to
Amith Yamasani119be9a2018-02-18 22:23:00 -0800310 * @param usageReason sub reason for usage
311 * @param nowElapsed mark as used time if non-zero
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800312 * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
313 * with bucket values of ACTIVE and WORKING_SET.
Christopher Tated117b292018-01-05 17:32:36 -0800314 * @return
315 */
316 public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
Amith Yamasani119be9a2018-02-18 22:23:00 -0800317 int usageReason, long nowElapsed, long timeout) {
Christopher Tated117b292018-01-05 17:32:36 -0800318 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
319 AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
Kweku Adams12752132020-02-18 15:36:48 -0800320 return reportUsage(history, packageName, userId, newBucket, usageReason, nowElapsed,
321 timeout);
Amith Yamasani803eab692017-11-09 17:47:04 -0800322 }
323
Amith Yamasani17fffee2017-09-29 13:17:43 -0700324 private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
325 ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
Amith Yamasani67768492015-06-09 12:23:58 -0700326 if (userHistory == null) {
327 userHistory = new ArrayMap<>();
328 mIdleHistory.put(userId, userHistory);
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800329 readAppIdleTimes(userId, userHistory);
Amith Yamasani67768492015-06-09 12:23:58 -0700330 }
331 return userHistory;
332 }
333
Amith Yamasani17fffee2017-09-29 13:17:43 -0700334 private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory,
335 String packageName, long elapsedRealtime, boolean create) {
336 AppUsageHistory appUsageHistory = userHistory.get(packageName);
337 if (appUsageHistory == null && create) {
338 appUsageHistory = new AppUsageHistory();
339 appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
340 appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800341 appUsageHistory.lastPredictedTime = getElapsedTime(0);
Amith Yamasani172612c2017-12-15 10:51:53 -0800342 appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800343 appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
Amith Yamasaniafbccb72017-11-27 10:44:24 -0800344 appUsageHistory.lastInformedBucket = -1;
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800345 appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
Amith Yamasani17fffee2017-09-29 13:17:43 -0700346 userHistory.put(packageName, appUsageHistory);
Amith Yamasani67768492015-06-09 12:23:58 -0700347 }
Amith Yamasani17fffee2017-09-29 13:17:43 -0700348 return appUsageHistory;
Amith Yamasani67768492015-06-09 12:23:58 -0700349 }
350
Amith Yamasania93542f2016-02-03 18:02:06 -0800351 public void onUserRemoved(int userId) {
Amith Yamasani67768492015-06-09 12:23:58 -0700352 mIdleHistory.remove(userId);
353 }
354
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800355 public boolean isIdle(String packageName, int userId, long elapsedRealtime) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700356 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
357 AppUsageHistory appUsageHistory =
358 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
Kweku Adams95cd9522020-05-08 09:56:53 -0700359 return appUsageHistory.currentBucket >= IDLE_BUCKET_CUTOFF;
Amith Yamasania93542f2016-02-03 18:02:06 -0800360 }
361
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800362 public AppUsageHistory getAppUsageHistory(String packageName, int userId,
363 long elapsedRealtime) {
364 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
365 AppUsageHistory appUsageHistory =
366 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
367 return appUsageHistory;
368 }
369
Amith Yamasani17fffee2017-09-29 13:17:43 -0700370 public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
Amith Yamasani119be9a2018-02-18 22:23:00 -0800371 int bucket, int reason) {
Makoto Onukia0058b42018-05-22 16:32:23 -0700372 setAppStandbyBucket(packageName, userId, elapsedRealtime, bucket, reason, false);
373 }
374
375 public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
376 int bucket, int reason, boolean resetTimeout) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700377 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
378 AppUsageHistory appUsageHistory =
379 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
Kweku Adams12752132020-02-18 15:36:48 -0800380 final boolean changed = appUsageHistory.currentBucket != bucket;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700381 appUsageHistory.currentBucket = bucket;
382 appUsageHistory.bucketingReason = reason;
Makoto Onukia0058b42018-05-22 16:32:23 -0700383
384 final long elapsed = getElapsedTime(elapsedRealtime);
385
Amith Yamasani119be9a2018-02-18 22:23:00 -0800386 if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
Makoto Onukia0058b42018-05-22 16:32:23 -0700387 appUsageHistory.lastPredictedTime = elapsed;
Amith Yamasani3154dcf2018-03-27 18:24:04 -0700388 appUsageHistory.lastPredictedBucket = bucket;
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800389 }
Makoto Onukia0058b42018-05-22 16:32:23 -0700390 if (resetTimeout) {
391 appUsageHistory.bucketActiveTimeoutTime = elapsed;
392 appUsageHistory.bucketWorkingSetTimeoutTime = elapsed;
393 }
Kweku Adams12752132020-02-18 15:36:48 -0800394 if (changed) {
395 logAppStandbyBucketChanged(packageName, userId, bucket, reason);
Amith Yamasani17fffee2017-09-29 13:17:43 -0700396 }
397 }
398
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800399 /**
Amith Yamasani3154dcf2018-03-27 18:24:04 -0700400 * Update the prediction for the app but don't change the actual bucket
401 * @param app The app for which the prediction was made
402 * @param elapsedTimeAdjusted The elapsed time in the elapsed duration timebase
403 * @param bucket The predicted bucket
404 */
405 public void updateLastPrediction(AppUsageHistory app, long elapsedTimeAdjusted, int bucket) {
406 app.lastPredictedTime = elapsedTimeAdjusted;
407 app.lastPredictedBucket = bucket;
408 }
409
410 /**
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800411 * Marks the last time a job was run, with the given elapsedRealtime. The time stored is
412 * based on the elapsed timebase.
413 * @param packageName
414 * @param userId
415 * @param elapsedRealtime
416 */
417 public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
418 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
419 AppUsageHistory appUsageHistory =
420 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
421 appUsageHistory.lastJobRunTime = getElapsedTime(elapsedRealtime);
422 }
423
424 /**
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800425 * Notes an attempt to put the app in the {@link UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
426 * bucket.
427 *
428 * @param packageName The package name of the app that is being restricted
429 * @param userId The ID of the user in which the app is being restricted
430 * @param elapsedRealtime The time the attempt was made, in the (unadjusted) elapsed realtime
431 * timebase
432 * @param reason The reason for the restriction attempt
433 */
434 void noteRestrictionAttempt(String packageName, int userId, long elapsedRealtime, int reason) {
435 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
436 AppUsageHistory appUsageHistory =
437 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
438 appUsageHistory.lastRestrictAttemptElapsedTime = getElapsedTime(elapsedRealtime);
439 appUsageHistory.lastRestrictReason = reason;
440 }
441
442 /**
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800443 * Returns the time since the last job was run for this app. This can be larger than the
444 * current elapsedRealtime, in case it happened before boot or a really large value if no jobs
445 * were ever run.
446 * @param packageName
447 * @param userId
448 * @param elapsedRealtime
449 * @return
450 */
451 public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) {
452 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
453 AppUsageHistory appUsageHistory =
Varun Shah7609b752018-10-15 15:07:47 -0700454 getPackageHistory(userHistory, packageName, elapsedRealtime, false);
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800455 // Don't adjust the default, else it'll wrap around to a positive value
Varun Shah7609b752018-10-15 15:07:47 -0700456 if (appUsageHistory == null || appUsageHistory.lastJobRunTime == Long.MIN_VALUE) {
457 return Long.MAX_VALUE;
458 }
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800459 return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
460 }
461
Amith Yamasani17fffee2017-09-29 13:17:43 -0700462 public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
463 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
464 AppUsageHistory appUsageHistory =
Varun Shah7609b752018-10-15 15:07:47 -0700465 getPackageHistory(userHistory, packageName, elapsedRealtime, false);
466 return appUsageHistory == null ? STANDBY_BUCKET_NEVER : appUsageHistory.currentBucket;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700467 }
468
Suprabh Shukla868bde22018-02-20 20:59:52 -0800469 public ArrayList<AppStandbyInfo> getAppStandbyBuckets(int userId, boolean appIdleEnabled) {
Amith Yamasanie8789312017-12-10 14:34:26 -0800470 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
471 int size = userHistory.size();
Suprabh Shukla868bde22018-02-20 20:59:52 -0800472 ArrayList<AppStandbyInfo> buckets = new ArrayList<>(size);
Amith Yamasanie8789312017-12-10 14:34:26 -0800473 for (int i = 0; i < size; i++) {
Suprabh Shukla868bde22018-02-20 20:59:52 -0800474 buckets.add(new AppStandbyInfo(userHistory.keyAt(i),
475 appIdleEnabled ? userHistory.valueAt(i).currentBucket : STANDBY_BUCKET_ACTIVE));
Amith Yamasanie8789312017-12-10 14:34:26 -0800476 }
477 return buckets;
478 }
479
Amith Yamasani119be9a2018-02-18 22:23:00 -0800480 public int getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700481 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
482 AppUsageHistory appUsageHistory =
483 getPackageHistory(userHistory, packageName, elapsedRealtime, false);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800484 return appUsageHistory != null ? appUsageHistory.bucketingReason : 0;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700485 }
486
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800487 public long getElapsedTime(long elapsedRealtime) {
Amith Yamasania93542f2016-02-03 18:02:06 -0800488 return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
489 }
490
Christopher Tatea732f012017-10-26 17:26:53 -0700491 /* Returns the new standby bucket the app is assigned to */
492 public int setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) {
Kweku Adams12752132020-02-18 15:36:48 -0800493 final int newBucket;
494 final int reason;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700495 if (idle) {
Kweku Adams95cd9522020-05-08 09:56:53 -0700496 newBucket = IDLE_BUCKET_CUTOFF;
Kweku Adams12752132020-02-18 15:36:48 -0800497 reason = REASON_MAIN_FORCED_BY_USER;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700498 } else {
Kweku Adams12752132020-02-18 15:36:48 -0800499 newBucket = STANDBY_BUCKET_ACTIVE;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700500 // This is to pretend that the app was just used, don't freeze the state anymore.
Kweku Adams12752132020-02-18 15:36:48 -0800501 reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700502 }
Kweku Adams12752132020-02-18 15:36:48 -0800503 setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason, false);
504
505 return newBucket;
Amith Yamasania93542f2016-02-03 18:02:06 -0800506 }
507
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800508 public void clearUsage(String packageName, int userId) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700509 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
Amith Yamasanibdda1e02016-03-14 11:55:38 -0700510 userHistory.remove(packageName);
511 }
512
Amith Yamasani17fffee2017-09-29 13:17:43 -0700513 boolean shouldInformListeners(String packageName, int userId,
Amith Yamasani84cd7b72017-11-07 13:59:37 -0800514 long elapsedRealtime, int bucket) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700515 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
516 AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
517 elapsedRealtime, true);
Amith Yamasani84cd7b72017-11-07 13:59:37 -0800518 if (appUsageHistory.lastInformedBucket != bucket) {
519 appUsageHistory.lastInformedBucket = bucket;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700520 return true;
521 }
522 return false;
Amith Yamasania93542f2016-02-03 18:02:06 -0800523 }
524
Amith Yamasani17fffee2017-09-29 13:17:43 -0700525 /**
526 * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds
527 * that corresponds to how long since the app was used.
528 * @param packageName
529 * @param userId
530 * @param elapsedRealtime current time
531 * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0
532 * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0
533 * @return The index whose values the app's used time exceeds (in both arrays)
534 */
535 int getThresholdIndex(String packageName, int userId, long elapsedRealtime,
536 long[] screenTimeThresholds, long[] elapsedTimeThresholds) {
537 ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
538 AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
539 elapsedRealtime, false);
540 // If we don't have any state for the app, assume never used
541 if (appUsageHistory == null) return screenTimeThresholds.length - 1;
542
543 long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime;
544 long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime;
545
546 if (DEBUG) Slog.d(TAG, packageName
547 + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime
548 + " lastUsedElapsed=" + appUsageHistory.lastUsedElapsedTime);
549 if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta
550 + ", elapsed=" + elapsedDelta);
551 for (int i = screenTimeThresholds.length - 1; i >= 0; i--) {
552 if (screenOnDelta >= screenTimeThresholds[i]
553 && elapsedDelta >= elapsedTimeThresholds[i]) {
554 return i;
555 }
556 }
557 return 0;
558 }
559
Kweku Adams12752132020-02-18 15:36:48 -0800560 /**
561 * Log a standby bucket change to statsd, and also logcat if debug logging is enabled.
562 */
563 private void logAppStandbyBucketChanged(String packageName, int userId, int bucket,
564 int reason) {
565 FrameworkStatsLog.write(
566 FrameworkStatsLog.APP_STANDBY_BUCKET_CHANGED,
567 packageName, userId, bucket,
568 (reason & REASON_MAIN_MASK), (reason & REASON_SUB_MASK));
569 if (DEBUG) {
570 Slog.d(TAG, "Moved " + packageName + " to bucket=" + bucket
571 + ", reason=0x0" + Integer.toHexString(reason));
572 }
573 }
574
Amith Yamasani17fffee2017-09-29 13:17:43 -0700575 @VisibleForTesting
576 File getUserFile(int userId) {
Amith Yamasania93542f2016-02-03 18:02:06 -0800577 return new File(new File(new File(mStorageDir, "users"),
578 Integer.toString(userId)), APP_IDLE_FILENAME);
579 }
580
Michael Wachenschwanzd1d8aa62019-02-28 16:38:37 -0800581 /**
582 * Check if App Idle File exists on disk
583 * @param userId
584 * @return true if file exists
585 */
586 public boolean userFileExists(int userId) {
587 return getUserFile(userId).exists();
588 }
589
Amith Yamasani17fffee2017-09-29 13:17:43 -0700590 private void readAppIdleTimes(int userId, ArrayMap<String, AppUsageHistory> userHistory) {
Amith Yamasania93542f2016-02-03 18:02:06 -0800591 FileInputStream fis = null;
592 try {
593 AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
594 fis = appIdleFile.openRead();
595 XmlPullParser parser = Xml.newPullParser();
596 parser.setInput(fis, StandardCharsets.UTF_8.name());
597
598 int type;
599 while ((type = parser.next()) != XmlPullParser.START_TAG
600 && type != XmlPullParser.END_DOCUMENT) {
601 // Skip
602 }
603
604 if (type != XmlPullParser.START_TAG) {
605 Slog.e(TAG, "Unable to read app idle file for user " + userId);
606 return;
607 }
608 if (!parser.getName().equals(TAG_PACKAGES)) {
609 return;
610 }
611 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
612 if (type == XmlPullParser.START_TAG) {
613 final String name = parser.getName();
614 if (name.equals(TAG_PACKAGE)) {
615 final String packageName = parser.getAttributeValue(null, ATTR_NAME);
Amith Yamasani17fffee2017-09-29 13:17:43 -0700616 AppUsageHistory appUsageHistory = new AppUsageHistory();
617 appUsageHistory.lastUsedElapsedTime =
Amith Yamasania93542f2016-02-03 18:02:06 -0800618 Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800619 appUsageHistory.lastUsedByUserElapsedTime = getLongValue(parser,
620 ATTR_LAST_USED_BY_USER_ELAPSED,
621 appUsageHistory.lastUsedElapsedTime);
Amith Yamasani17fffee2017-09-29 13:17:43 -0700622 appUsageHistory.lastUsedScreenTime =
Amith Yamasania93542f2016-02-03 18:02:06 -0800623 Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800624 appUsageHistory.lastPredictedTime = getLongValue(parser,
625 ATTR_LAST_PREDICTED_TIME, 0L);
Amith Yamasani17fffee2017-09-29 13:17:43 -0700626 String currentBucketString = parser.getAttributeValue(null,
627 ATTR_CURRENT_BUCKET);
628 appUsageHistory.currentBucket = currentBucketString == null
Amith Yamasani172612c2017-12-15 10:51:53 -0800629 ? STANDBY_BUCKET_ACTIVE
Amith Yamasani17fffee2017-09-29 13:17:43 -0700630 : Integer.parseInt(currentBucketString);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800631 String bucketingReason =
Amith Yamasani17fffee2017-09-29 13:17:43 -0700632 parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800633 appUsageHistory.lastJobRunTime = getLongValue(parser,
634 ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800635 appUsageHistory.bucketActiveTimeoutTime = getLongValue(parser,
636 ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
637 appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
638 ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800639 appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
640 if (bucketingReason != null) {
641 try {
642 appUsageHistory.bucketingReason =
643 Integer.parseInt(bucketingReason, 16);
644 } catch (NumberFormatException nfe) {
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800645 Slog.wtf(TAG, "Unable to read bucketing reason", nfe);
646 }
647 }
648 appUsageHistory.lastRestrictAttemptElapsedTime =
649 getLongValue(parser, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED, 0);
650 String lastRestrictReason = parser.getAttributeValue(
651 null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON);
652 if (lastRestrictReason != null) {
653 try {
654 appUsageHistory.lastRestrictReason =
655 Integer.parseInt(lastRestrictReason, 16);
656 } catch (NumberFormatException nfe) {
657 Slog.wtf(TAG, "Unable to read last restrict reason", nfe);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800658 }
Amith Yamasani17fffee2017-09-29 13:17:43 -0700659 }
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800660 appUsageHistory.lastInformedBucket = -1;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700661 userHistory.put(packageName, appUsageHistory);
Amith Yamasania93542f2016-02-03 18:02:06 -0800662 }
663 }
664 }
665 } catch (IOException | XmlPullParserException e) {
Michael Wachenschwanz16b2f2b2019-10-09 15:32:55 -0700666 Slog.e(TAG, "Unable to read app idle file for user " + userId, e);
Amith Yamasania93542f2016-02-03 18:02:06 -0800667 } finally {
668 IoUtils.closeQuietly(fis);
669 }
670 }
671
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800672 private long getLongValue(XmlPullParser parser, String attrName, long defValue) {
673 String value = parser.getAttributeValue(null, attrName);
674 if (value == null) return defValue;
675 return Long.parseLong(value);
676 }
677
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800678 public void writeAppIdleTimes(int userId) {
Amith Yamasania93542f2016-02-03 18:02:06 -0800679 FileOutputStream fos = null;
680 AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
681 try {
682 fos = appIdleFile.startWrite();
683 final BufferedOutputStream bos = new BufferedOutputStream(fos);
684
685 FastXmlSerializer xml = new FastXmlSerializer();
686 xml.setOutput(bos, StandardCharsets.UTF_8.name());
687 xml.startDocument(null, true);
688 xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
689
690 xml.startTag(null, TAG_PACKAGES);
691
Amith Yamasani17fffee2017-09-29 13:17:43 -0700692 ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId);
Amith Yamasania93542f2016-02-03 18:02:06 -0800693 final int N = userHistory.size();
694 for (int i = 0; i < N; i++) {
695 String packageName = userHistory.keyAt(i);
Michael Wachenschwanz16b2f2b2019-10-09 15:32:55 -0700696 // Skip any unexpected null package names
697 if (packageName == null) {
698 Slog.w(TAG, "Skipping App Idle write for unexpected null package");
699 continue;
700 }
Amith Yamasani17fffee2017-09-29 13:17:43 -0700701 AppUsageHistory history = userHistory.valueAt(i);
Amith Yamasania93542f2016-02-03 18:02:06 -0800702 xml.startTag(null, TAG_PACKAGE);
703 xml.attribute(null, ATTR_NAME, packageName);
704 xml.attribute(null, ATTR_ELAPSED_IDLE,
705 Long.toString(history.lastUsedElapsedTime));
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800706 xml.attribute(null, ATTR_LAST_USED_BY_USER_ELAPSED,
707 Long.toString(history.lastUsedByUserElapsedTime));
Amith Yamasania93542f2016-02-03 18:02:06 -0800708 xml.attribute(null, ATTR_SCREEN_IDLE,
709 Long.toString(history.lastUsedScreenTime));
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800710 xml.attribute(null, ATTR_LAST_PREDICTED_TIME,
711 Long.toString(history.lastPredictedTime));
Amith Yamasani17fffee2017-09-29 13:17:43 -0700712 xml.attribute(null, ATTR_CURRENT_BUCKET,
713 Integer.toString(history.currentBucket));
Amith Yamasani119be9a2018-02-18 22:23:00 -0800714 xml.attribute(null, ATTR_BUCKETING_REASON,
715 Integer.toHexString(history.bucketingReason));
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800716 if (history.bucketActiveTimeoutTime > 0) {
717 xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
718 .bucketActiveTimeoutTime));
719 }
720 if (history.bucketWorkingSetTimeoutTime > 0) {
721 xml.attribute(null, ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, Long.toString(history
722 .bucketWorkingSetTimeoutTime));
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800723 }
724 if (history.lastJobRunTime != Long.MIN_VALUE) {
725 xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
726 .lastJobRunTime));
727 }
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800728 if (history.lastRestrictAttemptElapsedTime > 0) {
729 xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED,
730 Long.toString(history.lastRestrictAttemptElapsedTime));
731 }
732 xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON,
733 Integer.toHexString(history.lastRestrictReason));
Amith Yamasania93542f2016-02-03 18:02:06 -0800734 xml.endTag(null, TAG_PACKAGE);
735 }
736
737 xml.endTag(null, TAG_PACKAGES);
738 xml.endDocument();
739 appIdleFile.finishWrite(fos);
740 } catch (Exception e) {
741 appIdleFile.failWrite(fos);
Michael Wachenschwanz16b2f2b2019-10-09 15:32:55 -0700742 Slog.e(TAG, "Error writing app idle file for user " + userId, e);
Amith Yamasania93542f2016-02-03 18:02:06 -0800743 }
Amith Yamasani0a11e692015-05-08 16:36:21 -0700744 }
745
Sudheer Shankab497dd42020-02-16 22:23:24 -0800746 public void dump(IndentingPrintWriter idpw, int userId, List<String> pkgs) {
Amith Yamasani119be9a2018-02-18 22:23:00 -0800747 idpw.println("App Standby States:");
Amith Yamasania93542f2016-02-03 18:02:06 -0800748 idpw.increaseIndent();
Amith Yamasani17fffee2017-09-29 13:17:43 -0700749 ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
Amith Yamasania93542f2016-02-03 18:02:06 -0800750 final long elapsedRealtime = SystemClock.elapsedRealtime();
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800751 final long totalElapsedTime = getElapsedTime(elapsedRealtime);
752 final long screenOnTime = getScreenOnTime(elapsedRealtime);
Amith Yamasani0a11e692015-05-08 16:36:21 -0700753 if (userHistory == null) return;
754 final int P = userHistory.size();
755 for (int p = 0; p < P; p++) {
756 final String packageName = userHistory.keyAt(p);
Amith Yamasani17fffee2017-09-29 13:17:43 -0700757 final AppUsageHistory appUsageHistory = userHistory.valueAt(p);
Sudheer Shankab497dd42020-02-16 22:23:24 -0800758 if (!CollectionUtils.isEmpty(pkgs) && !pkgs.contains(packageName)) {
Dianne Hackbornc81983a2017-10-20 16:16:32 -0700759 continue;
760 }
Amith Yamasania93542f2016-02-03 18:02:06 -0800761 idpw.print("package=" + packageName);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800762 idpw.print(" u=" + userId);
763 idpw.print(" bucket=" + appUsageHistory.currentBucket
764 + " reason="
765 + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
766 idpw.print(" used=");
Amith Yamasani17fffee2017-09-29 13:17:43 -0700767 TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800768 idpw.print(" usedByUser=");
769 TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedByUserElapsedTime,
770 idpw);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800771 idpw.print(" usedScr=");
Amith Yamasani17fffee2017-09-29 13:17:43 -0700772 TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800773 idpw.print(" lastPred=");
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800774 TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800775 idpw.print(" activeLeft=");
776 TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800777 idpw);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800778 idpw.print(" wsLeft=");
779 TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800780 idpw);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800781 idpw.print(" lastJob=");
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800782 TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800783 if (appUsageHistory.lastRestrictAttemptElapsedTime > 0) {
784 idpw.print(" lastRestrictAttempt=");
785 TimeUtils.formatDuration(
786 totalElapsedTime - appUsageHistory.lastRestrictAttemptElapsedTime, idpw);
787 idpw.print(" lastRestrictReason="
788 + UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason));
789 }
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800790 idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
Amith Yamasania93542f2016-02-03 18:02:06 -0800791 idpw.println();
792 }
793 idpw.println();
794 idpw.print("totalElapsedTime=");
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800795 TimeUtils.formatDuration(getElapsedTime(elapsedRealtime), idpw);
Amith Yamasania93542f2016-02-03 18:02:06 -0800796 idpw.println();
797 idpw.print("totalScreenOnTime=");
Amith Yamasani61d5fd72017-02-24 11:02:07 -0800798 TimeUtils.formatDuration(getScreenOnTime(elapsedRealtime), idpw);
Amith Yamasania93542f2016-02-03 18:02:06 -0800799 idpw.println();
800 idpw.decreaseIndent();
801 }
Amith Yamasania93542f2016-02-03 18:02:06 -0800802}