AppIdleHistory.java revision 53f06eae611ffa559f80d9efaddba17544eb7819
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
19172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_DEFAULT;
20172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_FORCED;
21172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_PREDICTED;
22172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.REASON_USAGE;
23172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
24172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
25172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasaniimport static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
26172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani
27afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasaniimport android.app.usage.UsageStatsManager;
28a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.SystemClock;
290a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.ArrayMap;
30a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.AtomicFile;
31a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Slog;
320a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.SparseArray;
33a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.TimeUtils;
34a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Xml;
350a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
36a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.annotations.VisibleForTesting;
37a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.util.FastXmlSerializer;
380a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport com.android.internal.util.IndentingPrintWriter;
390a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
40a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport libcore.io.IoUtils;
41a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
42a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParser;
43a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParserException;
44a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
45a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedOutputStream;
46a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedReader;
47a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.File;
48a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileInputStream;
49a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileOutputStream;
50a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileReader;
51a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.IOException;
52a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.nio.charset.StandardCharsets;
53e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasaniimport java.util.HashMap;
54e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasaniimport java.util.Map;
55a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
560a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani/**
570a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Keeps track of recent active state changes in apps.
580a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Access should be guarded by a lock by the caller.
590a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani */
600a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasanipublic class AppIdleHistory {
610a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
62a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String TAG = "AppIdleHistory";
63a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
6417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private static final boolean DEBUG = AppStandbyController.DEBUG;
6517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
66a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // History for all users and all packages
6717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
680a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani    private static final long ONE_MINUTE = 60 * 1000;
690a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
70a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    @VisibleForTesting
71a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
72a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String TAG_PACKAGES = "packages";
73a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String TAG_PACKAGE = "package";
74a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String ATTR_NAME = "name";
75a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // Screen on timebase time when app was last used
76a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
77a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // Elapsed timebase time when app was last used
78a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
79bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    // Elapsed timebase time when the app bucket was last predicted externally
80bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime";
81bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    // The standby bucket for the app
8217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private static final String ATTR_CURRENT_BUCKET = "appLimitBucket";
83bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    // The reason the app was put in the above bucket
8417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private static final String ATTR_BUCKETING_REASON = "bucketReason";
8553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    // The last time a job was run for this app
8653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime";
8753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    // The time when the forced active state can be overridden.
8853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    private static final String ATTR_BUCKET_TIMEOUT_TIME = "bucketTimeoutTime";
8917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
90a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
91a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
92a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mElapsedDuration; // Total device on duration since device was "born"
93a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
94a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot)
95a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
96a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private long mScreenOnDuration; // Total screen on duration since device was "born"
97a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
98a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private final File mStorageDir;
99a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
100a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    private boolean mScreenOn;
101a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
102bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    static class AppUsageHistory {
103bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Last used time using elapsed timebase
104a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        long lastUsedElapsedTime;
105bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Last used time using screen_on timebase
106a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        long lastUsedScreenTime;
107bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Last predicted time using elapsed timebase
108bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        long lastPredictedTime;
109bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Standby bucket
110afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani        @UsageStatsManager.StandbyBuckets
111afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani        int currentBucket;
112bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // Reason for setting the standby bucket. TODO: Switch to int.
11317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        String bucketingReason;
114bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        // In-memory only, last bucket for which the listeners were informed
11584cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani        int lastInformedBucket;
11653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // The last time a job was run for this app, using elapsed timebase
11753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        long lastJobRunTime;
11853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // When should the bucket state timeout, in elapsed timebase, if greater than
11953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // lastUsedElapsedTime.
12053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // This is used to keep the app in a high bucket regardless of other timeouts and
12153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // predictions.
12253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        long bucketTimeoutTime;
123a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
124a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
125a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    AppIdleHistory(File storageDir, long elapsedRealtime) {
126a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mElapsedSnapshot = elapsedRealtime;
127a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mScreenOnSnapshot = elapsedRealtime;
128a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mStorageDir = storageDir;
12961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        readScreenOnTime();
130a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
131a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
13261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void updateDisplay(boolean screenOn, long elapsedRealtime) {
133a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (screenOn == mScreenOn) return;
134a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
135a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mScreenOn = screenOn;
136a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (mScreenOn) {
137a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mScreenOnSnapshot = elapsedRealtime;
138a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } else {
139a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
140a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
141a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            mElapsedSnapshot = elapsedRealtime;
142a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
14317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) Slog.d(TAG, "mScreenOnSnapshot=" + mScreenOnSnapshot
14417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + ", mScreenOnDuration=" + mScreenOnDuration
14517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + ", mScreenOn=" + mScreenOn);
146a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
147a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
14861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public long getScreenOnTime(long elapsedRealtime) {
149a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        long screenOnTime = mScreenOnDuration;
150a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (mScreenOn) {
151a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            screenOnTime += elapsedRealtime - mScreenOnSnapshot;
152a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
153a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return screenOnTime;
154a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
155a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
156a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    @VisibleForTesting
157a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    File getScreenOnTimeFile() {
158a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return new File(mStorageDir, "screen_on_time");
159a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
160a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
16161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    private void readScreenOnTime() {
162a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        File screenOnTimeFile = getScreenOnTimeFile();
163a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (screenOnTimeFile.exists()) {
164a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            try {
165a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
166a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                mScreenOnDuration = Long.parseLong(reader.readLine());
167a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                mElapsedDuration = Long.parseLong(reader.readLine());
168a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                reader.close();
169a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            } catch (IOException | NumberFormatException e) {
170a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
171a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } else {
17261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani            writeScreenOnTime();
173a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
174a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
175a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
17661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    private void writeScreenOnTime() {
177a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
178a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        FileOutputStream fos = null;
179a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        try {
180a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fos = screenOnTimeFile.startWrite();
181a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fos.write((Long.toString(mScreenOnDuration) + "\n"
182a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    + Long.toString(mElapsedDuration) + "\n").getBytes());
183a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            screenOnTimeFile.finishWrite(fos);
184a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } catch (IOException ioe) {
185a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            screenOnTimeFile.failWrite(fos);
186a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
187a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
188a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
189a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    /**
190a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani     * To be called periodically to keep track of elapsed time when app idle times are written
191a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani     */
19261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void writeAppIdleDurations() {
193a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        final long elapsedRealtime = SystemClock.elapsedRealtime();
194a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        // Only bump up and snapshot the elapsed time. Don't change screen on duration.
195a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
196a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        mElapsedSnapshot = elapsedRealtime;
19761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        writeScreenOnTime();
198a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
199a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
20053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    /**
20153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * Mark the app as used and update the bucket if necessary. If there is a timeout specified
20253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * that's in the future, then the usage event is temporary and keeps the app in the specified
20353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * bucket at least until the timeout is reached. This can be used to keep the app in an
20453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * elevated bucket for a while until some important task gets to run.
20553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param packageName
20653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param userId
20753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param bucket the bucket to set the app to
20853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param elapsedRealtime mark as used time if non-zero
20953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param timeout set the timeout of the specified bucket, if non-zero
21053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @return
21153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     */
21253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    public int reportUsage(String packageName, int userId, int bucket, long elapsedRealtime,
21353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            long timeout) {
21417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
21517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
21617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                elapsedRealtime, true);
217a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
21853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        if (elapsedRealtime != 0) {
21953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            appUsageHistory.lastUsedElapsedTime = mElapsedDuration
22053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                    + (elapsedRealtime - mElapsedSnapshot);
22153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
222803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani        }
223803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani
22453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        if (appUsageHistory.currentBucket > bucket) {
22553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            appUsageHistory.currentBucket = bucket;
226803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani            if (DEBUG) {
22753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
22853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                        .currentBucket
229803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani                        + ", reason=" + appUsageHistory.bucketingReason);
230803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani            }
23153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            if (timeout > elapsedRealtime) {
23253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                // Convert to elapsed timebase
23353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                appUsageHistory.bucketTimeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
23453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            }
23517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
236172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani        appUsageHistory.bucketingReason = REASON_USAGE;
237803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani
238803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani        return appUsageHistory.currentBucket;
239a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
240a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
24117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
24217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
2436776849dc5ff851a225745393f082b702754e278Amith Yamasani        if (userHistory == null) {
2446776849dc5ff851a225745393f082b702754e278Amith Yamasani            userHistory = new ArrayMap<>();
2456776849dc5ff851a225745393f082b702754e278Amith Yamasani            mIdleHistory.put(userId, userHistory);
24661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani            readAppIdleTimes(userId, userHistory);
2476776849dc5ff851a225745393f082b702754e278Amith Yamasani        }
2486776849dc5ff851a225745393f082b702754e278Amith Yamasani        return userHistory;
2496776849dc5ff851a225745393f082b702754e278Amith Yamasani    }
2506776849dc5ff851a225745393f082b702754e278Amith Yamasani
25117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory,
25217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            String packageName, long elapsedRealtime, boolean create) {
25317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = userHistory.get(packageName);
25417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (appUsageHistory == null && create) {
25517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            appUsageHistory = new AppUsageHistory();
25617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
25717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
258bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            appUsageHistory.lastPredictedTime = getElapsedTime(0);
259172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
260172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.bucketingReason = REASON_DEFAULT;
261afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani            appUsageHistory.lastInformedBucket = -1;
26253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
26317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            userHistory.put(packageName, appUsageHistory);
2646776849dc5ff851a225745393f082b702754e278Amith Yamasani        }
26517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return appUsageHistory;
2666776849dc5ff851a225745393f082b702754e278Amith Yamasani    }
2676776849dc5ff851a225745393f082b702754e278Amith Yamasani
268a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    public void onUserRemoved(int userId) {
2696776849dc5ff851a225745393f082b702754e278Amith Yamasani        mIdleHistory.remove(userId);
2706776849dc5ff851a225745393f082b702754e278Amith Yamasani    }
2716776849dc5ff851a225745393f082b702754e278Amith Yamasani
27261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public boolean isIdle(String packageName, int userId, long elapsedRealtime) {
27317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
27417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
27517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
27617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (appUsageHistory == null) {
277a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            return false; // Default to not idle
278a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } else {
279172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
28017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            // Whether or not it's passed will now be externally calculated and the
28117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            // bucket will be pushed to the history using setAppStandbyBucket()
28217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
28317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
28417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
28517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
286bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    public AppUsageHistory getAppUsageHistory(String packageName, int userId,
287bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            long elapsedRealtime) {
288bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
289bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        AppUsageHistory appUsageHistory =
290bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
291bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        return appUsageHistory;
292bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    }
293bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani
29417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
29517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            int bucket, String reason) {
29617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
29717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
29817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
29917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        appUsageHistory.currentBucket = bucket;
30017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        appUsageHistory.bucketingReason = reason;
301172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani        if (reason.startsWith(REASON_PREDICTED)) {
302bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
303bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani        }
30417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) {
30517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
30617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                    + ", reason=" + appUsageHistory.bucketingReason);
307a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
308a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
309a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
31053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    /**
31153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * Marks the last time a job was run, with the given elapsedRealtime. The time stored is
31253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * based on the elapsed timebase.
31353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param packageName
31453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param userId
31553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param elapsedRealtime
31653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     */
31753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
31853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
31953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        AppUsageHistory appUsageHistory =
32053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
32153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        appUsageHistory.lastJobRunTime = getElapsedTime(elapsedRealtime);
32253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    }
32353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani
32453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    /**
32553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * Returns the time since the last job was run for this app. This can be larger than the
32653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * current elapsedRealtime, in case it happened before boot or a really large value if no jobs
32753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * were ever run.
32853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param packageName
32953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param userId
33053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @param elapsedRealtime
33153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     * @return
33253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani     */
33353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) {
33453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
33553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        AppUsageHistory appUsageHistory =
33653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
33753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        // Don't adjust the default, else it'll wrap around to a positive value
33853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        if (appUsageHistory.lastJobRunTime == Long.MIN_VALUE) return Long.MAX_VALUE;
33953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
34053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    }
34153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani
34217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
34317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
34417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
34517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
34617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return appUsageHistory.currentBucket;
34717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
34817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
349172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani    public Map<String, Integer> getAppStandbyBuckets(int userId, long elapsedRealtime,
350172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            boolean appIdleEnabled) {
351e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
352e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        int size = userHistory.size();
353e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        HashMap<String, Integer> buckets = new HashMap<>(size);
354e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        for (int i = 0; i < size; i++) {
355172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            buckets.put(userHistory.keyAt(i),
356172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani                    appIdleEnabled ? userHistory.valueAt(i).currentBucket : STANDBY_BUCKET_ACTIVE);
357e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        }
358e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani        return buckets;
359e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani    }
360e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani
36117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
36217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
36317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory =
36417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                getPackageHistory(userHistory, packageName, elapsedRealtime, false);
36517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
36617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
36717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
368bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani    public long getElapsedTime(long elapsedRealtime) {
369a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
370a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
371a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
372a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate    /* Returns the new standby bucket the app is assigned to */
373a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate    public int setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) {
37417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
37517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
37617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                elapsedRealtime, true);
37717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (idle) {
378172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
379172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.bucketingReason = REASON_FORCED;
38017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        } else {
381172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
38217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            // This is to pretend that the app was just used, don't freeze the state anymore.
383172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani            appUsageHistory.bucketingReason = REASON_USAGE;
38417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
385a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate        return appUsageHistory.currentBucket;
386a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
387a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
38861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void clearUsage(String packageName, int userId) {
38917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
390bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani        userHistory.remove(packageName);
391bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani    }
392bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani
39317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    boolean shouldInformListeners(String packageName, int userId,
39484cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani            long elapsedRealtime, int bucket) {
39517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
39617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
39717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                elapsedRealtime, true);
39884cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani        if (appUsageHistory.lastInformedBucket != bucket) {
39984cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani            appUsageHistory.lastInformedBucket = bucket;
40017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            return true;
40117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
40217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return false;
40317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    }
40417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
40517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    /**
40617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds
40717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * that corresponds to how long since the app was used.
40817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param packageName
40917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param userId
41017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param elapsedRealtime current time
41117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0
41217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0
41317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     * @return The index whose values the app's used time exceeds (in both arrays)
41417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani     */
41517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    int getThresholdIndex(String packageName, int userId, long elapsedRealtime,
41617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            long[] screenTimeThresholds, long[] elapsedTimeThresholds) {
41717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
41817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
41917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                elapsedRealtime, false);
42017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        // If we don't have any state for the app, assume never used
42117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (appUsageHistory == null) return screenTimeThresholds.length - 1;
42217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
42317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime;
42417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime;
42517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani
42617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) Slog.d(TAG, packageName
42717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime
42817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + " lastUsedElapsed=" + appUsageHistory.lastUsedElapsedTime);
42917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta
43017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                + ", elapsed=" + elapsedDelta);
43117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        for (int i = screenTimeThresholds.length - 1; i >= 0; i--) {
43217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            if (screenOnDelta >= screenTimeThresholds[i]
43317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                && elapsedDelta >= elapsedTimeThresholds[i]) {
43417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                return i;
43517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            }
43617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        }
43717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        return 0;
438a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
439a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
44017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    @VisibleForTesting
44117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    File getUserFile(int userId) {
442a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        return new File(new File(new File(mStorageDir, "users"),
443a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                Integer.toString(userId)), APP_IDLE_FILENAME);
444a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
445a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
44617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani    private void readAppIdleTimes(int userId, ArrayMap<String, AppUsageHistory> userHistory) {
447a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        FileInputStream fis = null;
448a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        try {
449a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
450a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fis = appIdleFile.openRead();
451a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            XmlPullParser parser = Xml.newPullParser();
452a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            parser.setInput(fis, StandardCharsets.UTF_8.name());
453a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
454a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            int type;
455a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            while ((type = parser.next()) != XmlPullParser.START_TAG
456a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    && type != XmlPullParser.END_DOCUMENT) {
457a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                // Skip
458a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
459a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
460a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            if (type != XmlPullParser.START_TAG) {
461a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                Slog.e(TAG, "Unable to read app idle file for user " + userId);
462a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                return;
463a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
464a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            if (!parser.getName().equals(TAG_PACKAGES)) {
465a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                return;
466a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
467a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
468a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                if (type == XmlPullParser.START_TAG) {
469a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    final String name = parser.getName();
470a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    if (name.equals(TAG_PACKAGE)) {
471a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                        final String packageName = parser.getAttributeValue(null, ATTR_NAME);
47217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        AppUsageHistory appUsageHistory = new AppUsageHistory();
47317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        appUsageHistory.lastUsedElapsedTime =
474a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                                Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
47517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        appUsageHistory.lastUsedScreenTime =
476a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                                Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
47753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                        appUsageHistory.lastPredictedTime = getLongValue(parser,
47853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                                ATTR_LAST_PREDICTED_TIME, 0L);
47917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        String currentBucketString = parser.getAttributeValue(null,
48017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                                ATTR_CURRENT_BUCKET);
48117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        appUsageHistory.currentBucket = currentBucketString == null
482172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani                                ? STANDBY_BUCKET_ACTIVE
48317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                                : Integer.parseInt(currentBucketString);
48417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        appUsageHistory.bucketingReason =
48517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                                parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
48653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                        appUsageHistory.lastJobRunTime = getLongValue(parser,
48753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                                ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
48853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                        appUsageHistory.bucketTimeoutTime = getLongValue(parser,
48953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                                ATTR_BUCKET_TIMEOUT_TIME, 0L);
49017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        if (appUsageHistory.bucketingReason == null) {
491172612c8919638ad57ac0b67f2acec3e144fc26cAmith Yamasani                            appUsageHistory.bucketingReason = REASON_DEFAULT;
49217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        }
493bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                        appUsageHistory.lastInformedBucket = -1;
49417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        userHistory.put(packageName, appUsageHistory);
495a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                    }
496a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                }
497a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
498a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } catch (IOException | XmlPullParserException e) {
499a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            Slog.e(TAG, "Unable to read app idle file for user " + userId);
500a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } finally {
501a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            IoUtils.closeQuietly(fis);
502a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
503a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
504a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
50553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    private long getLongValue(XmlPullParser parser, String attrName, long defValue) {
50653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        String value = parser.getAttributeValue(null, attrName);
50753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        if (value == null) return defValue;
50853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani        return Long.parseLong(value);
50953f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani    }
51053f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani
51161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani    public void writeAppIdleTimes(int userId) {
512a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        FileOutputStream fos = null;
513a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
514a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        try {
515a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            fos = appIdleFile.startWrite();
516a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            final BufferedOutputStream bos = new BufferedOutputStream(fos);
517a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
518a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            FastXmlSerializer xml = new FastXmlSerializer();
519a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.setOutput(bos, StandardCharsets.UTF_8.name());
520a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.startDocument(null, true);
521a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
522a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
523a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.startTag(null, TAG_PACKAGES);
524a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
52517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId);
526a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            final int N = userHistory.size();
527a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            for (int i = 0; i < N; i++) {
528a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                String packageName = userHistory.keyAt(i);
52917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                AppUsageHistory history = userHistory.valueAt(i);
530a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.startTag(null, TAG_PACKAGE);
531a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.attribute(null, ATTR_NAME, packageName);
532a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.attribute(null, ATTR_ELAPSED_IDLE,
533a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                        Long.toString(history.lastUsedElapsedTime));
534a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.attribute(null, ATTR_SCREEN_IDLE,
535a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                        Long.toString(history.lastUsedScreenTime));
536bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                xml.attribute(null, ATTR_LAST_PREDICTED_TIME,
537bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani                        Long.toString(history.lastPredictedTime));
53817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                xml.attribute(null, ATTR_CURRENT_BUCKET,
53917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                        Integer.toString(history.currentBucket));
54017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
54153f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                if (history.bucketTimeoutTime > 0) {
54253f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                    xml.attribute(null, ATTR_BUCKET_TIMEOUT_TIME, Long.toString(history
54353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                            .bucketTimeoutTime));
54453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                }
54553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                if (history.lastJobRunTime != Long.MIN_VALUE) {
54653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                    xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
54753f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                            .lastJobRunTime));
54853f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani                }
549a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani                xml.endTag(null, TAG_PACKAGE);
550a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            }
551a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani
552a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.endTag(null, TAG_PACKAGES);
553a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            xml.endDocument();
554a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            appIdleFile.finishWrite(fos);
555a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        } catch (Exception e) {
556a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            appIdleFile.failWrite(fos);
557a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            Slog.e(TAG, "Error writing app idle file for user " + userId);
558a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
5590a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani    }
5600a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani
561c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn    public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
562a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.println("Package idle stats:");
563a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.increaseIndent();
56417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani        ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
565a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        final long elapsedRealtime = SystemClock.elapsedRealtime();
56661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        final long totalElapsedTime = getElapsedTime(elapsedRealtime);
56761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        final long screenOnTime = getScreenOnTime(elapsedRealtime);
568a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        if (userHistory == null) return;
569a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        final int P = userHistory.size();
570a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        for (int p = 0; p < P; p++) {
571a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            final String packageName = userHistory.keyAt(p);
57217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            final AppUsageHistory appUsageHistory = userHistory.valueAt(p);
573c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn            if (pkg != null && !pkg.equals(packageName)) {
574c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn                continue;
575c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn            }
576a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            idpw.print("package=" + packageName);
577a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            idpw.print(" lastUsedElapsed=");
57817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
579a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            idpw.print(" lastUsedScreenOn=");
58017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
581bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            idpw.print(" lastPredictedTime=");
582bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
58353f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            idpw.print(" bucketTimeoutTime=");
58453f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketTimeoutTime, idpw);
58553f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            idpw.print(" lastJobRunTime=");
58653f06eae611ffa559f80d9efaddba17544eb7819Amith Yamasani            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
58761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani            idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
58817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani            idpw.print(" bucket=" + appUsageHistory.currentBucket
58917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani                    + " reason=" + appUsageHistory.bucketingReason);
590a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani            idpw.println();
591a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        }
592a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.println();
593a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.print("totalElapsedTime=");
59461d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        TimeUtils.formatDuration(getElapsedTime(elapsedRealtime), idpw);
595a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.println();
596a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.print("totalScreenOnTime=");
59761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani        TimeUtils.formatDuration(getScreenOnTime(elapsedRealtime), idpw);
598a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.println();
599a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani        idpw.decreaseIndent();
600a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani    }
601a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani}
602