10a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani/**
20a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Copyright (C) 2015 The Android Open Source Project
30a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani *
40a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Licensed under the Apache License, Version 2.0 (the "License"); you may not
50a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * use this file except in compliance with the License. You may obtain a copy
60a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * of the License at
70a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani *
80a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * http://www.apache.org/licenses/LICENSE-2.0
90a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani *
100a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Unless required by applicable law or agreed to in writing, software
110a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
120a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
130a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * License for the specific language governing permissions and limitations
140a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * under the License.
150a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani */
160a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
170a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasanipackage com.android.server.usage;
180a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
19119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
20119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
21119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
22119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
23119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
24119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
25172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
26172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
27172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
28bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasaniimport static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
29172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani
30868bde247c76e309232b1e3e6a873bde725acc22Suprabh Shuklaimport android.app.usage.AppStandbyInfo;
31afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasaniimport android.app.usage.UsageStatsManager;
32a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.SystemClock;
330a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.ArrayMap;
34a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.AtomicFile;
35a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Slog;
360a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.SparseArray;
37a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.TimeUtils;
38a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Xml;
390a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
40a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.annotations.VisibleForTesting;
41a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.util.FastXmlSerializer;
420a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport com.android.internal.util.IndentingPrintWriter;
430a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
44a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport libcore.io.IoUtils;
45a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
46a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParser;
47a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParserException;
48a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
49a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedOutputStream;
50a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedReader;
51a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.File;
52a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileInputStream;
53a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileOutputStream;
54a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileReader;
55a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.IOException;
56a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.nio.charset.StandardCharsets;
57868bde247c76e309232b1e3e6a873bde725acc22Suprabh Shuklaimport java.util.ArrayList;
58a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
590a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani/**
600a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Keeps track of recent active state changes in apps.
610a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Access should be guarded by a lock by the caller.
620a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani */
630a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasanipublic class AppIdleHistory {
640a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
65a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String TAG = "AppIdleHistory";
66a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
6717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private static final boolean DEBUG = AppStandbyController.DEBUG;
6817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
69a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // History for all users and all packages
7017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
710a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani    private static final long ONE_MINUTE = 60 * 1000;
720a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
733154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani    private static final int STANDBY_BUCKET_UNKNOWN = -1;
743154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani
75a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    @VisibleForTesting
76a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
77a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String TAG_PACKAGES = "packages";
78a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String TAG_PACKAGE = "package";
79a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String ATTR_NAME = "name";
80a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // Screen on timebase time when app was last used
81a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
82a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // Elapsed timebase time when app was last used
83a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
84bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    // Elapsed timebase time when the app bucket was last predicted externally
85bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime";
86bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    // The standby bucket for the app
8717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private static final String ATTR_CURRENT_BUCKET = "appLimitBucket";
88bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    // The reason the app was put in the above bucket
8917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private static final String ATTR_BUCKETING_REASON = "bucketReason";
9053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    // The last time a job was run for this app
9153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime";
9253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    // The time when the forced active state can be overridden.
93bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani    private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
94bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani    // The time when the forced working_set state can be overridden.
95bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani    private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
9617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
97a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
98a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
99a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mElapsedDuration; // Total device on duration since device was "born"
100a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
101a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot)
102a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
103a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mScreenOnDuration; // Total screen on duration since device was "born"
104a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
105a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private final File mStorageDir;
106a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
107a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private boolean mScreenOn;
108a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
109bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    static class AppUsageHistory {
110bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Last used time using elapsed timebase
111a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        long lastUsedElapsedTime;
112bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Last used time using screen_on timebase
113a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        long lastUsedScreenTime;
114bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Last predicted time using elapsed timebase
115bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        long lastPredictedTime;
1163154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani        // Last predicted bucket
1173154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani        @UsageStatsManager.StandbyBuckets
1183154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani        int lastPredictedBucket = STANDBY_BUCKET_UNKNOWN;
119bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Standby bucket
120afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani        @UsageStatsManager.StandbyBuckets
121afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani        int currentBucket;
122119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        // Reason for setting the standby bucket. The value here is a combination of
123119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        // one of UsageStatsManager.REASON_MAIN_* and one (or none) of
124119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        // UsageStatsManager.REASON_SUB_*. Also see REASON_MAIN_MASK and REASON_SUB_MASK.
125119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        int bucketingReason;
126bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // In-memory only, last bucket for which the listeners were informed
12784cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani        int lastInformedBucket;
12853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // The last time a job was run for this app, using elapsed timebase
12953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        long lastJobRunTime;
130bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        // When should the bucket active state timeout, in elapsed timebase, if greater than
13153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // lastUsedElapsedTime.
13253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // This is used to keep the app in a high bucket regardless of other timeouts and
13353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // predictions.
134bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        long bucketActiveTimeoutTime;
135bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        // If there's a forced working_set state, this is when it times out. This can be sitting
136bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        // under any active state timeout, so that it becomes applicable after the active state
137bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        // timeout expires.
138bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        long bucketWorkingSetTimeoutTime;
139a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
140a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
141a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    AppIdleHistory(File storageDir, long elapsedRealtime) {
142a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mElapsedSnapshot = elapsedRealtime;
143a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mScreenOnSnapshot = elapsedRealtime;
144a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mStorageDir = storageDir;
14561d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        readScreenOnTime();
146a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
147a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
14861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void updateDisplay(boolean screenOn, long elapsedRealtime) {
149a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (screenOn == mScreenOn) return;
150a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
151a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mScreenOn = screenOn;
152a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (mScreenOn) {
153a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mScreenOnSnapshot = elapsedRealtime;
154a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } else {
155a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
156a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
157a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mElapsedSnapshot = elapsedRealtime;
158a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
15917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) Slog.d(TAG, "mScreenOnSnapshot=" + mScreenOnSnapshot
16017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + ", mScreenOnDuration=" + mScreenOnDuration
16117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + ", mScreenOn=" + mScreenOn);
162a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
163a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
16461d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public long getScreenOnTime(long elapsedRealtime) {
165a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        long screenOnTime = mScreenOnDuration;
166a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (mScreenOn) {
167a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            screenOnTime += elapsedRealtime - mScreenOnSnapshot;
168a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
169a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return screenOnTime;
170a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
171a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
172a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    @VisibleForTesting
173a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    File getScreenOnTimeFile() {
174a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return new File(mStorageDir, "screen_on_time");
175a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
176a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
17761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    private void readScreenOnTime() {
178a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        File screenOnTimeFile = getScreenOnTimeFile();
179a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (screenOnTimeFile.exists()) {
180a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            try {
181a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
182a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                mScreenOnDuration = Long.parseLong(reader.readLine());
183a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                mElapsedDuration = Long.parseLong(reader.readLine());
184a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                reader.close();
185a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            } catch (IOException | NumberFormatException e) {
186a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
187a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } else {
18861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani            writeScreenOnTime();
189a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
190a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
191a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
19261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    private void writeScreenOnTime() {
193a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
194a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        FileOutputStream fos = null;
195a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        try {
196a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fos = screenOnTimeFile.startWrite();
197a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fos.write((Long.toString(mScreenOnDuration) + "\n"
198a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    + Long.toString(mElapsedDuration) + "\n").getBytes());
199a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            screenOnTimeFile.finishWrite(fos);
200a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } catch (IOException ioe) {
201a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            screenOnTimeFile.failWrite(fos);
202a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
203a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
204a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
205a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    /**
206a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani     * To be called periodically to keep track of elapsed time when app idle times are written
207a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani     */
20861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void writeAppIdleDurations() {
209a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        final long elapsedRealtime = SystemClock.elapsedRealtime();
210a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        // Only bump up and snapshot the elapsed time. Don't change screen on duration.
211a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
212a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mElapsedSnapshot = elapsedRealtime;
21361d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        writeScreenOnTime();
214a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
215a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
21653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    /**
21753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * Mark the app as used and update the bucket if necessary. If there is a timeout specified
21853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * that's in the future, then the usage event is temporary and keeps the app in the specified
21953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * bucket at least until the timeout is reached. This can be used to keep the app in an
22053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * elevated bucket for a while until some important task gets to run.
221d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * @param appUsageHistory the usage record for the app being updated
222d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * @param packageName name of the app being updated, for logging purposes
223d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * @param newBucket the bucket to set the app to
224119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani     * @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
22553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param elapsedRealtime mark as used time if non-zero
226bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani     * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
227bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani     *                with bucket values of ACTIVE and WORKING_SET.
22853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @return
22953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     */
230d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate    public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
231119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            int newBucket, int usageReason, long elapsedRealtime, long timeout) {
232bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        // Set the timeout if applicable
233bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        if (timeout > elapsedRealtime) {
234bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani            // Convert to elapsed timebase
235bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani            final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
236bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani            if (newBucket == STANDBY_BUCKET_ACTIVE) {
237bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
238bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                        appUsageHistory.bucketActiveTimeoutTime);
239bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani            } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
240bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
241bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                        appUsageHistory.bucketWorkingSetTimeoutTime);
242bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani            } else {
243bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                throw new IllegalArgumentException("Cannot set a timeout on bucket=" +
244bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                        newBucket);
245bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani            }
246bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani        }
247bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani
24853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        if (elapsedRealtime != 0) {
24953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            appUsageHistory.lastUsedElapsedTime = mElapsedDuration
25053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                    + (elapsedRealtime - mElapsedSnapshot);
25153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
252803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani        }
253803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani
254d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate        if (appUsageHistory.currentBucket > newBucket) {
255d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate            appUsageHistory.currentBucket = newBucket;
256803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani            if (DEBUG) {
25753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
25853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                        .currentBucket
259119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                        + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
260803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani            }
26117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
262119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
263803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani
264d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate        return appUsageHistory;
265d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate    }
266d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate
267d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate    /**
268d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * Mark the app as used and update the bucket if necessary. If there is a timeout specified
269d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * that's in the future, then the usage event is temporary and keeps the app in the specified
270d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * bucket at least until the timeout is reached. This can be used to keep the app in an
271d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * elevated bucket for a while until some important task gets to run.
272d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * @param packageName
273d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * @param userId
274d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * @param newBucket the bucket to set the app to
275119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani     * @param usageReason sub reason for usage
276119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani     * @param nowElapsed mark as used time if non-zero
277bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani     * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
278bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani     *                with bucket values of ACTIVE and WORKING_SET.
279d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     * @return
280d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate     */
281d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate    public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
282119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            int usageReason, long nowElapsed, long timeout) {
283d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
284d117b293aec3fc2de0f6508cb8975af4dbd6a6a3Christopher Tate        AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
285119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
286a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
287a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
28817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
28917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
2906776849dc5ff851a225745393f082b702754e278Amith Yamasani        if (userHistory == null) {
2916776849dc5ff851a225745393f082b702754e278Amith Yamasani            userHistory = new ArrayMap<>();
2926776849dc5ff851a225745393f082b702754e278Amith Yamasani            mIdleHistory.put(userId, userHistory);
29361d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani            readAppIdleTimes(userId, userHistory);
2946776849dc5ff851a225745393f082b702754e278Amith Yamasani        }
2956776849dc5ff851a225745393f082b702754e278Amith Yamasani        return userHistory;
2966776849dc5ff851a225745393f082b702754e278Amith Yamasani    }
2976776849dc5ff851a225745393f082b702754e278Amith Yamasani
29817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory,
29917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            String packageName, long elapsedRealtime, boolean create) {
30017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = userHistory.get(packageName);
30117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (appUsageHistory == null && create) {
30217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            appUsageHistory = new AppUsageHistory();
30317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
30417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
305bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            appUsageHistory.lastPredictedTime = getElapsedTime(0);
306172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
307119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
308afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani            appUsageHistory.lastInformedBucket = -1;
30953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
31017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            userHistory.put(packageName, appUsageHistory);
3116776849dc5ff851a225745393f082b702754e278Amith Yamasani        }
31217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return appUsageHistory;
3136776849dc5ff851a225745393f082b702754e278Amith Yamasani    }
3146776849dc5ff851a225745393f082b702754e278Amith Yamasani
315a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    public void onUserRemoved(int userId) {
3166776849dc5ff851a225745393f082b702754e278Amith Yamasani        mIdleHistory.remove(userId);
3176776849dc5ff851a225745393f082b702754e278Amith Yamasani    }
3186776849dc5ff851a225745393f082b702754e278Amith Yamasani
31961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public boolean isIdle(String packageName, int userId, long elapsedRealtime) {
32017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
32117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
32217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
32317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (appUsageHistory == null) {
324a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            return false; // Default to not idle
325a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } else {
326172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
32717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            // Whether or not it's passed will now be externally calculated and the
32817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            // bucket will be pushed to the history using setAppStandbyBucket()
32917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
33017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
33117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
33217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
333bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    public AppUsageHistory getAppUsageHistory(String packageName, int userId,
334bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            long elapsedRealtime) {
335bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
336bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        AppUsageHistory appUsageHistory =
337bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
338bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        return appUsageHistory;
339bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    }
340bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani
34117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
342119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            int bucket, int reason) {
343a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki        setAppStandbyBucket(packageName, userId, elapsedRealtime, bucket, reason, false);
344a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki    }
345a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki
346a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki    public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
347a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki            int bucket, int reason, boolean resetTimeout) {
34817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
34917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
35017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
35117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        appUsageHistory.currentBucket = bucket;
35217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        appUsageHistory.bucketingReason = reason;
353a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki
354a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki        final long elapsed = getElapsedTime(elapsedRealtime);
355a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki
356119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
357a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki            appUsageHistory.lastPredictedTime = elapsed;
3583154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani            appUsageHistory.lastPredictedBucket = bucket;
359bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        }
360a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki        if (resetTimeout) {
361a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki            appUsageHistory.bucketActiveTimeoutTime = elapsed;
362a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki            appUsageHistory.bucketWorkingSetTimeoutTime = elapsed;
363a0058b47e3e3ba816abd50ad279175b5aa1e9841Makoto Onuki        }
36417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) {
36517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
366119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                    + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
367a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
368a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
369a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
37053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    /**
3713154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani     * Update the prediction for the app but don't change the actual bucket
3723154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani     * @param app The app for which the prediction was made
3733154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani     * @param elapsedTimeAdjusted The elapsed time in the elapsed duration timebase
3743154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani     * @param bucket The predicted bucket
3753154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani     */
3763154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani    public void updateLastPrediction(AppUsageHistory app, long elapsedTimeAdjusted, int bucket) {
3773154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani        app.lastPredictedTime = elapsedTimeAdjusted;
3783154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani        app.lastPredictedBucket = bucket;
3793154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani    }
3803154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani
3813154dcf94a4f451d2b32607b30cdd070e61bd2aeAmith Yamasani    /**
38253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * Marks the last time a job was run, with the given elapsedRealtime. The time stored is
38353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * based on the elapsed timebase.
38453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param packageName
38553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param userId
38653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param elapsedRealtime
38753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     */
38853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
38953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
39053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        AppUsageHistory appUsageHistory =
39153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
39253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        appUsageHistory.lastJobRunTime = getElapsedTime(elapsedRealtime);
39353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    }
39453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani
39553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    /**
39653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * Returns the time since the last job was run for this app. This can be larger than the
39753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * current elapsedRealtime, in case it happened before boot or a really large value if no jobs
39853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * were ever run.
39953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param packageName
40053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param userId
40153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param elapsedRealtime
40253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @return
40353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     */
40453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) {
40553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
40653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        AppUsageHistory appUsageHistory =
40753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
40853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // Don't adjust the default, else it'll wrap around to a positive value
40953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        if (appUsageHistory.lastJobRunTime == Long.MIN_VALUE) return Long.MAX_VALUE;
41053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
41153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    }
41253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani
41317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
41417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
41517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
41617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
41717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return appUsageHistory.currentBucket;
41817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
41917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
420868bde247c76e309232b1e3e6a873bde725acc22Suprabh Shukla    public ArrayList<AppStandbyInfo> getAppStandbyBuckets(int userId, boolean appIdleEnabled) {
421e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
422e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        int size = userHistory.size();
423868bde247c76e309232b1e3e6a873bde725acc22Suprabh Shukla        ArrayList<AppStandbyInfo> buckets = new ArrayList<>(size);
424e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        for (int i = 0; i < size; i++) {
425868bde247c76e309232b1e3e6a873bde725acc22Suprabh Shukla            buckets.add(new AppStandbyInfo(userHistory.keyAt(i),
426868bde247c76e309232b1e3e6a873bde725acc22Suprabh Shukla                    appIdleEnabled ? userHistory.valueAt(i).currentBucket : STANDBY_BUCKET_ACTIVE));
427e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        }
428e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        return buckets;
429e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani    }
430e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani
431119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani    public int getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
43217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
43317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
43417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, false);
435119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        return appUsageHistory != null ? appUsageHistory.bucketingReason : 0;
43617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
43717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
438bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    public long getElapsedTime(long elapsedRealtime) {
439a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
440a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
441a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
442a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate    /* Returns the new standby bucket the app is assigned to */
443a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate    public int setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) {
44417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
44517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
44617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                elapsedRealtime, true);
44717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (idle) {
448172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
449119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            appUsageHistory.bucketingReason = REASON_MAIN_FORCED;
45017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        } else {
451172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
45217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            // This is to pretend that the app was just used, don't freeze the state anymore.
453119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
45417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
455a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate        return appUsageHistory.currentBucket;
456a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
457a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
45861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void clearUsage(String packageName, int userId) {
45917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
460bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani        userHistory.remove(packageName);
461bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani    }
462bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani
46317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    boolean shouldInformListeners(String packageName, int userId,
46484cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani            long elapsedRealtime, int bucket) {
46517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
46617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
46717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                elapsedRealtime, true);
46884cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani        if (appUsageHistory.lastInformedBucket != bucket) {
46984cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani            appUsageHistory.lastInformedBucket = bucket;
47017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            return true;
47117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
47217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return false;
47317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
47417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
47517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    /**
47617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds
47717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * that corresponds to how long since the app was used.
47817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param packageName
47917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param userId
48017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param elapsedRealtime current time
48117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0
48217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0
48317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @return The index whose values the app's used time exceeds (in both arrays)
48417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     */
48517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    int getThresholdIndex(String packageName, int userId, long elapsedRealtime,
48617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            long[] screenTimeThresholds, long[] elapsedTimeThresholds) {
48717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
48817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
48917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                elapsedRealtime, false);
49017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        // If we don't have any state for the app, assume never used
49117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (appUsageHistory == null) return screenTimeThresholds.length - 1;
49217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
49317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime;
49417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime;
49517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
49617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) Slog.d(TAG, packageName
49717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime
49817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + " lastUsedElapsed=" + appUsageHistory.lastUsedElapsedTime);
49917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta
50017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + ", elapsed=" + elapsedDelta);
50117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        for (int i = screenTimeThresholds.length - 1; i >= 0; i--) {
50217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            if (screenOnDelta >= screenTimeThresholds[i]
50317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                && elapsedDelta >= elapsedTimeThresholds[i]) {
50417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                return i;
50517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            }
50617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
50717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return 0;
508a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
509a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
51017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    @VisibleForTesting
51117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    File getUserFile(int userId) {
512a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return new File(new File(new File(mStorageDir, "users"),
513a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                Integer.toString(userId)), APP_IDLE_FILENAME);
514a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
515a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
51617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private void readAppIdleTimes(int userId, ArrayMap<String, AppUsageHistory> userHistory) {
517a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        FileInputStream fis = null;
518a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        try {
519a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
520a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fis = appIdleFile.openRead();
521a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            XmlPullParser parser = Xml.newPullParser();
522a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            parser.setInput(fis, StandardCharsets.UTF_8.name());
523a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
524a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            int type;
525a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            while ((type = parser.next()) != XmlPullParser.START_TAG
526a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    && type != XmlPullParser.END_DOCUMENT) {
527a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                // Skip
528a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
529a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
530a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            if (type != XmlPullParser.START_TAG) {
531a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                Slog.e(TAG, "Unable to read app idle file for user " + userId);
532a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                return;
533a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
534a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            if (!parser.getName().equals(TAG_PACKAGES)) {
535a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                return;
536a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
537a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
538a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                if (type == XmlPullParser.START_TAG) {
539a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    final String name = parser.getName();
540a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    if (name.equals(TAG_PACKAGE)) {
541a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                        final String packageName = parser.getAttributeValue(null, ATTR_NAME);
54217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        AppUsageHistory appUsageHistory = new AppUsageHistory();
54317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        appUsageHistory.lastUsedElapsedTime =
544a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                                Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
54517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        appUsageHistory.lastUsedScreenTime =
546a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                                Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
54753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                        appUsageHistory.lastPredictedTime = getLongValue(parser,
54853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                                ATTR_LAST_PREDICTED_TIME, 0L);
54917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        String currentBucketString = parser.getAttributeValue(null,
55017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                                ATTR_CURRENT_BUCKET);
55117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        appUsageHistory.currentBucket = currentBucketString == null
552172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani                                ? STANDBY_BUCKET_ACTIVE
55317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                                : Integer.parseInt(currentBucketString);
554119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                        String bucketingReason =
55517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                                parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
55653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                        appUsageHistory.lastJobRunTime = getLongValue(parser,
55753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                                ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
558bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                        appUsageHistory.bucketActiveTimeoutTime = getLongValue(parser,
559bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                                ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
560bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                        appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
561bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                                ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
562119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                        appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
563119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                        if (bucketingReason != null) {
564119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                            try {
565119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                                appUsageHistory.bucketingReason =
566119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                                        Integer.parseInt(bucketingReason, 16);
567119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                            } catch (NumberFormatException nfe) {
568119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                            }
56917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        }
570bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                        appUsageHistory.lastInformedBucket = -1;
57117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        userHistory.put(packageName, appUsageHistory);
572a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    }
573a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                }
574a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
575a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } catch (IOException | XmlPullParserException e) {
576a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            Slog.e(TAG, "Unable to read app idle file for user " + userId);
577a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } finally {
578a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            IoUtils.closeQuietly(fis);
579a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
580a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
581a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
58253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    private long getLongValue(XmlPullParser parser, String attrName, long defValue) {
58353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        String value = parser.getAttributeValue(null, attrName);
58453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        if (value == null) return defValue;
58553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        return Long.parseLong(value);
58653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    }
58753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani
58861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void writeAppIdleTimes(int userId) {
589a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        FileOutputStream fos = null;
590a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
591a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        try {
592a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fos = appIdleFile.startWrite();
593a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            final BufferedOutputStream bos = new BufferedOutputStream(fos);
594a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
595a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            FastXmlSerializer xml = new FastXmlSerializer();
596a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.setOutput(bos, StandardCharsets.UTF_8.name());
597a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.startDocument(null, true);
598a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
599a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
600a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.startTag(null, TAG_PACKAGES);
601a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
60217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId);
603a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            final int N = userHistory.size();
604a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            for (int i = 0; i < N; i++) {
605a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                String packageName = userHistory.keyAt(i);
60617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                AppUsageHistory history = userHistory.valueAt(i);
607a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.startTag(null, TAG_PACKAGE);
608a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.attribute(null, ATTR_NAME, packageName);
609a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.attribute(null, ATTR_ELAPSED_IDLE,
610a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                        Long.toString(history.lastUsedElapsedTime));
611a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.attribute(null, ATTR_SCREEN_IDLE,
612a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                        Long.toString(history.lastUsedScreenTime));
613bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                xml.attribute(null, ATTR_LAST_PREDICTED_TIME,
614bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                        Long.toString(history.lastPredictedTime));
61517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                xml.attribute(null, ATTR_CURRENT_BUCKET,
61617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        Integer.toString(history.currentBucket));
617119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                xml.attribute(null, ATTR_BUCKETING_REASON,
618119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                        Integer.toHexString(history.bucketingReason));
619bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                if (history.bucketActiveTimeoutTime > 0) {
620bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                    xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
621bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                            .bucketActiveTimeoutTime));
622bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                }
623bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                if (history.bucketWorkingSetTimeoutTime > 0) {
624bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                    xml.attribute(null, ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, Long.toString(history
625bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                            .bucketWorkingSetTimeoutTime));
62653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                }
62753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                if (history.lastJobRunTime != Long.MIN_VALUE) {
62853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                    xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
62953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                            .lastJobRunTime));
63053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                }
631a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.endTag(null, TAG_PACKAGE);
632a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
633a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
634a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.endTag(null, TAG_PACKAGES);
635a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.endDocument();
636a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            appIdleFile.finishWrite(fos);
637a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } catch (Exception e) {
638a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            appIdleFile.failWrite(fos);
639a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            Slog.e(TAG, "Error writing app idle file for user " + userId);
640a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
6410a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani    }
6420a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
643c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn    public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
644119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani        idpw.println("App Standby States:");
645a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.increaseIndent();
64617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
647a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        final long elapsedRealtime = SystemClock.elapsedRealtime();
64861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        final long totalElapsedTime = getElapsedTime(elapsedRealtime);
64961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        final long screenOnTime = getScreenOnTime(elapsedRealtime);
650a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (userHistory == null) return;
651a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        final int P = userHistory.size();
652a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        for (int p = 0; p < P; p++) {
653a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            final String packageName = userHistory.keyAt(p);
65417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            final AppUsageHistory appUsageHistory = userHistory.valueAt(p);
655c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn            if (pkg != null && !pkg.equals(packageName)) {
656c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn                continue;
657c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn            }
658a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            idpw.print("package=" + packageName);
659119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" u=" + userId);
660119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" bucket=" + appUsageHistory.currentBucket
661119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                    + " reason="
662119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani                    + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
663119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" used=");
66417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
665119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" usedScr=");
66617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
667119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" lastPred=");
668bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
669119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" activeLeft=");
670119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
671bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                    idpw);
672119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" wsLeft=");
673119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
674bbbad9cc0fc1def37bcec1fc8626e3c0ab6e3491Amith Yamasani                    idpw);
675119be9a5fc4033eba570ec94b94862401ee84570Amith Yamasani            idpw.print(" lastJob=");
67653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
67761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani            idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
678a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            idpw.println();
679a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
680a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.println();
681a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.print("totalElapsedTime=");
68261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        TimeUtils.formatDuration(getElapsedTime(elapsedRealtime), idpw);
683a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.println();
684a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.print("totalScreenOnTime=");
68561d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        TimeUtils.formatDuration(getScreenOnTime(elapsedRealtime), idpw);
686a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.println();
687a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.decreaseIndent();
688a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
689a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani}
690