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