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