AppIdleHistory.java revision bd7b302f91f225f2dd2367cc37db9d2b75aec521
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 19afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasaniimport android.app.usage.UsageStatsManager; 20a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.SystemClock; 210a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.ArrayMap; 22a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.AtomicFile; 23a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Slog; 240a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.SparseArray; 25a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.TimeUtils; 26a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Xml; 270a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 28a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.annotations.VisibleForTesting; 29a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.util.FastXmlSerializer; 300a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport com.android.internal.util.IndentingPrintWriter; 310a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 32a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport libcore.io.IoUtils; 33a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 34a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParser; 35a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParserException; 36a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 37a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedOutputStream; 38a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedReader; 39a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.File; 40a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileInputStream; 41a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileOutputStream; 42a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileReader; 43a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.IOException; 44a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.nio.charset.StandardCharsets; 45a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 460a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani/** 470a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Keeps track of recent active state changes in apps. 480a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Access should be guarded by a lock by the caller. 490a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani */ 500a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasanipublic class AppIdleHistory { 510a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 52a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG = "AppIdleHistory"; 53a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 5417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final boolean DEBUG = AppStandbyController.DEBUG; 5517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 56a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // History for all users and all packages 5717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>(); 58a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mLastPeriod = 0; 590a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final long ONE_MINUTE = 60 * 1000; 600a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int HISTORY_SIZE = 100; 610a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_LAST_STATE = 2; 620a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_PARTIAL_ACTIVE = 1; 636776849dc5ff851a225745393f082b702754e278Amith Yamasani private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE 640a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani : 60 * ONE_MINUTE; 650a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 66a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 67a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; 68a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGES = "packages"; 69a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGE = "package"; 70a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_NAME = "name"; 71a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Screen on timebase time when app was last used 72a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; 73a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Elapsed timebase time when app was last used 74a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; 75bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Elapsed timebase time when the app bucket was last predicted externally 76bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime"; 77bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // The standby bucket for the app 7817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; 79bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // The reason the app was put in the above bucket 8017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final String ATTR_BUCKETING_REASON = "bucketReason"; 8117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 82a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) 83a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration 84a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedDuration; // Total device on duration since device was "born" 85a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 86a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot) 87a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration 88a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnDuration; // Total screen on duration since device was "born" 89a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 90a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private final File mStorageDir; 91a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 92a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private boolean mScreenOn; 93a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 94bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani static class AppUsageHistory { 95bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Debug 96a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] recent = new byte[HISTORY_SIZE]; 97bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Last used time using elapsed timebase 98a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedElapsedTime; 99bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Last used time using screen_on timebase 100a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedScreenTime; 101bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Last predicted time using elapsed timebase 102bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani long lastPredictedTime; 103bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Standby bucket 104afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani @UsageStatsManager.StandbyBuckets 105afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani int currentBucket; 106bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Reason for setting the standby bucket. TODO: Switch to int. 10717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String bucketingReason; 108bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // In-memory only, last bucket for which the listeners were informed 10984cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani int lastInformedBucket; 110a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 111a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 112a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AppIdleHistory(File storageDir, long elapsedRealtime) { 113a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 114a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnSnapshot = elapsedRealtime; 115a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mStorageDir = storageDir; 11661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani readScreenOnTime(); 117a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 118a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 11961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void updateDisplay(boolean screenOn, long elapsedRealtime) { 120a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (screenOn == mScreenOn) return; 121a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 122a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOn = screenOn; 123a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mScreenOn) { 124a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnSnapshot = elapsedRealtime; 125a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 126a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot; 127a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration += elapsedRealtime - mElapsedSnapshot; 128a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 129a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 13017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, "mScreenOnSnapshot=" + mScreenOnSnapshot 13117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", mScreenOnDuration=" + mScreenOnDuration 13217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", mScreenOn=" + mScreenOn); 133a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 134a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 13561d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public long getScreenOnTime(long elapsedRealtime) { 136a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long screenOnTime = mScreenOnDuration; 137a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mScreenOn) { 138a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTime += elapsedRealtime - mScreenOnSnapshot; 139a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 140a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return screenOnTime; 141a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 142a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 143a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 144a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani File getScreenOnTimeFile() { 145a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return new File(mStorageDir, "screen_on_time"); 146a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 147a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 14861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani private void readScreenOnTime() { 149a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani File screenOnTimeFile = getScreenOnTimeFile(); 150a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (screenOnTimeFile.exists()) { 151a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 152a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile)); 153a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnDuration = Long.parseLong(reader.readLine()); 154a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration = Long.parseLong(reader.readLine()); 155a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani reader.close(); 156a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException | NumberFormatException e) { 157a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 158a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 15961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani writeScreenOnTime(); 160a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 161a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 162a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 16361d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani private void writeScreenOnTime() { 164a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile()); 165a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileOutputStream fos = null; 166a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 167a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos = screenOnTimeFile.startWrite(); 168a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos.write((Long.toString(mScreenOnDuration) + "\n" 169a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani + Long.toString(mElapsedDuration) + "\n").getBytes()); 170a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTimeFile.finishWrite(fos); 171a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException ioe) { 172a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTimeFile.failWrite(fos); 173a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 174a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 175a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 176a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani /** 177a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani * To be called periodically to keep track of elapsed time when app idle times are written 178a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani */ 17961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void writeAppIdleDurations() { 180a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 181a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Only bump up and snapshot the elapsed time. Don't change screen on duration. 182a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration += elapsedRealtime - mElapsedSnapshot; 183a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 18461d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani writeScreenOnTime(); 185a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 186a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 187803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani public int reportUsage(String packageName, int userId, long elapsedRealtime) { 18817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 18917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 19017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 191a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 192a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani shiftHistoryToNow(userHistory, elapsedRealtime); 193a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 19417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = mElapsedDuration 195a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani + (elapsedRealtime - mElapsedSnapshot); 19617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); 19717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; 198afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) { 199afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE; 200803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani if (DEBUG) { 201803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 202803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 203803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 204803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 205afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE; 206803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani 207803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani return appUsageHistory.currentBucket; 208803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 209803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani 210803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani public int reportMildUsage(String packageName, int userId, long elapsedRealtime) { 211803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 212803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 213803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani elapsedRealtime, true); 214afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) { 215afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET; 216803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani if (DEBUG) { 217803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 218803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 219803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 22017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 221803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani // TODO: Should this be a different reason for partial usage? 222afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE; 223803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani 224803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani return appUsageHistory.currentBucket; 225a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 226a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 227a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void setIdle(String packageName, int userId, long elapsedRealtime) { 22817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 22917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 23017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 231a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 232a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani shiftHistoryToNow(userHistory, elapsedRealtime); 2336776849dc5ff851a225745393f082b702754e278Amith Yamasani 23417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; 235a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 236a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 23717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private void shiftHistoryToNow(ArrayMap<String, AppUsageHistory> userHistory, 238a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long elapsedRealtime) { 239a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long thisPeriod = elapsedRealtime / PERIOD_DURATION; 2400a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Has the period switched over? Slide all users' package histories 241a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mLastPeriod != 0 && mLastPeriod < thisPeriod 242a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) { 243a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int diff = (int) (thisPeriod - mLastPeriod); 2446776849dc5ff851a225745393f082b702754e278Amith Yamasani final int NUSERS = mIdleHistory.size(); 2450a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int u = 0; u < NUSERS; u++) { 2466776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = mIdleHistory.valueAt(u); 24717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani for (AppUsageHistory idleState : userHistory.values()) { 2480a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Shift left 249a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani System.arraycopy(idleState.recent, diff, idleState.recent, 0, 250a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani HISTORY_SIZE - diff); 2510a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Replicate last state across the diff 2520a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < diff; i++) { 253a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idleState.recent[HISTORY_SIZE - i - 1] = 254a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); 2550a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2560a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2570a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2580a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 259a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mLastPeriod = thisPeriod; 2600a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2610a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 26217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { 26317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 2646776849dc5ff851a225745393f082b702754e278Amith Yamasani if (userHistory == null) { 2656776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = new ArrayMap<>(); 2666776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.put(userId, userHistory); 26761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani readAppIdleTimes(userId, userHistory); 2686776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2696776849dc5ff851a225745393f082b702754e278Amith Yamasani return userHistory; 2706776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2716776849dc5ff851a225745393f082b702754e278Amith Yamasani 27217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory, 27317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String packageName, long elapsedRealtime, boolean create) { 27417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = userHistory.get(packageName); 27517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null && create) { 27617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory = new AppUsageHistory(); 27717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime); 27817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); 279bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastPredictedTime = getElapsedTime(0); 280afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER; 281afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; 282afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.lastInformedBucket = -1; 28317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani userHistory.put(packageName, appUsageHistory); 2846776849dc5ff851a225745393f082b702754e278Amith Yamasani } 28517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory; 2866776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2876776849dc5ff851a225745393f082b702754e278Amith Yamasani 288a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void onUserRemoved(int userId) { 2896776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.remove(userId); 2906776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2916776849dc5ff851a225745393f082b702754e278Amith Yamasani 29261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public boolean isIdle(String packageName, int userId, long elapsedRealtime) { 29317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 29417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 29517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 29617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null) { 297a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return false; // Default to not idle 298a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 299afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani return appUsageHistory.currentBucket >= UsageStatsManager.STANDBY_BUCKET_RARE; 30017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // Whether or not it's passed will now be externally calculated and the 30117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // bucket will be pushed to the history using setAppStandbyBucket() 30217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani //return hasPassedThresholds(appUsageHistory, elapsedRealtime); 30317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 30417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 30517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 306bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani public AppUsageHistory getAppUsageHistory(String packageName, int userId, 307bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani long elapsedRealtime) { 308bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 309bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani AppUsageHistory appUsageHistory = 310bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 311bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani return appUsageHistory; 312bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani } 313bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani 31417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime, 31517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int bucket, String reason) { 31617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 31717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 31817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 31917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = bucket; 32017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = reason; 321bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) { 322bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime); 323bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani } 32417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) { 32517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 32617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 327a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 328a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 329a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 33017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) { 33117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 33217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 33317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 33417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory.currentBucket; 33517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 33617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 33717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) { 33817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 33917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 34017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, false); 34117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory != null ? appUsageHistory.bucketingReason : null; 34217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 34317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 344bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani public long getElapsedTime(long elapsedRealtime) { 345a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); 346a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 347a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 348a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate /* Returns the new standby bucket the app is assigned to */ 349a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate public int setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) { 35017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 35117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 35217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 35317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (idle) { 354afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_RARE; 355afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_FORCED; 35617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } else { 357afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE; 35817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // This is to pretend that the app was just used, don't freeze the state anymore. 359afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE; 36017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 361a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate return appUsageHistory.currentBucket; 362a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 363a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 36461d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void clearUsage(String packageName, int userId) { 36517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 366bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani userHistory.remove(packageName); 367bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani } 368bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani 36917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani boolean shouldInformListeners(String packageName, int userId, 37084cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani long elapsedRealtime, int bucket) { 37117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 37217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 37317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 37484cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani if (appUsageHistory.lastInformedBucket != bucket) { 37584cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani appUsageHistory.lastInformedBucket = bucket; 37617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return true; 37717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 37817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return false; 37917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 38017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 38117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani /** 38217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds 38317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * that corresponds to how long since the app was used. 38417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param packageName 38517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param userId 38617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param elapsedRealtime current time 38717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0 38817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0 38917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @return The index whose values the app's used time exceeds (in both arrays) 39017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani */ 39117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int getThresholdIndex(String packageName, int userId, long elapsedRealtime, 39217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long[] screenTimeThresholds, long[] elapsedTimeThresholds) { 39317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 39417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 39517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, false); 39617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // If we don't have any state for the app, assume never used 39717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null) return screenTimeThresholds.length - 1; 39817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 39917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime; 40017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime; 40117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 40217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, packageName 40317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime 40417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " lastUsedElapsed=" + appUsageHistory.lastUsedElapsedTime); 40517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta 40617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", elapsed=" + elapsedDelta); 40717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani for (int i = screenTimeThresholds.length - 1; i >= 0; i--) { 40817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (screenOnDelta >= screenTimeThresholds[i] 40917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani && elapsedDelta >= elapsedTimeThresholds[i]) { 41017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return i; 41117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 41217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 41317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return 0; 414a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 415a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 41617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani @VisibleForTesting 41717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani File getUserFile(int userId) { 418a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return new File(new File(new File(mStorageDir, "users"), 419a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Integer.toString(userId)), APP_IDLE_FILENAME); 420a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 421a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 42217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private void readAppIdleTimes(int userId, ArrayMap<String, AppUsageHistory> userHistory) { 423a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileInputStream fis = null; 424a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 425a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 426a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fis = appIdleFile.openRead(); 427a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani XmlPullParser parser = Xml.newPullParser(); 428a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani parser.setInput(fis, StandardCharsets.UTF_8.name()); 429a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 430a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int type; 431a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.START_TAG 432a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && type != XmlPullParser.END_DOCUMENT) { 433a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Skip 434a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 435a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 436a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type != XmlPullParser.START_TAG) { 437a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 438a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 439a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 440a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (!parser.getName().equals(TAG_PACKAGES)) { 441a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 442a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 443a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 444a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type == XmlPullParser.START_TAG) { 445a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String name = parser.getName(); 446a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (name.equals(TAG_PACKAGE)) { 447a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = parser.getAttributeValue(null, ATTR_NAME); 44817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = new AppUsageHistory(); 44917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = 450a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); 45117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = 452a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); 453bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani String lastPredictedTimeString = parser.getAttributeValue(null, 454bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani ATTR_LAST_PREDICTED_TIME); 455bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani if (lastPredictedTimeString != null) { 456bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastPredictedTime = 457bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani Long.parseLong(lastPredictedTimeString); 458bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani } 45917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String currentBucketString = parser.getAttributeValue(null, 46017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ATTR_CURRENT_BUCKET); 46117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = currentBucketString == null 462afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani ? UsageStatsManager.STANDBY_BUCKET_ACTIVE 46317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani : Integer.parseInt(currentBucketString); 46417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = 46517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani parser.getAttributeValue(null, ATTR_BUCKETING_REASON); 46617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory.bucketingReason == null) { 467afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; 46817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 469bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastInformedBucket = -1; 47017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani userHistory.put(packageName, appUsageHistory); 471a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 472a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 473a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 474a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException | XmlPullParserException e) { 475a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 476a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } finally { 477a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani IoUtils.closeQuietly(fis); 478a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 479a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 480a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 48161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void writeAppIdleTimes(int userId) { 482a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileOutputStream fos = null; 483a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 484a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 485a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos = appIdleFile.startWrite(); 486a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final BufferedOutputStream bos = new BufferedOutputStream(fos); 487a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 488a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FastXmlSerializer xml = new FastXmlSerializer(); 489a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setOutput(bos, StandardCharsets.UTF_8.name()); 490a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startDocument(null, true); 491a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 492a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 493a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGES); 494a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 49517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId); 496a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int N = userHistory.size(); 497a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int i = 0; i < N; i++) { 498a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani String packageName = userHistory.keyAt(i); 49917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory history = userHistory.valueAt(i); 500a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGE); 501a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_NAME, packageName); 502a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_ELAPSED_IDLE, 503a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedElapsedTime)); 504a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_SCREEN_IDLE, 505a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedScreenTime)); 506bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani xml.attribute(null, ATTR_LAST_PREDICTED_TIME, 507bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani Long.toString(history.lastPredictedTime)); 50817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani xml.attribute(null, ATTR_CURRENT_BUCKET, 50917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani Integer.toString(history.currentBucket)); 51017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); 511a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGE); 512a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 513a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 514a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGES); 515a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endDocument(); 516a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.finishWrite(fos); 517a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (Exception e) { 518a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.failWrite(fos); 519a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Error writing app idle file for user " + userId); 520a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 5210a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 5220a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 523c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn public void dump(IndentingPrintWriter idpw, int userId, String pkg) { 524a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println("Package idle stats:"); 525a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.increaseIndent(); 52617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 527a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 52861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani final long totalElapsedTime = getElapsedTime(elapsedRealtime); 52961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani final long screenOnTime = getScreenOnTime(elapsedRealtime); 530a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (userHistory == null) return; 531a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int P = userHistory.size(); 532a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int p = 0; p < P; p++) { 533a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = userHistory.keyAt(p); 53417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani final AppUsageHistory appUsageHistory = userHistory.valueAt(p); 535c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn if (pkg != null && !pkg.equals(packageName)) { 536c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn continue; 537c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn } 538a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("package=" + packageName); 539a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedElapsed="); 54017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); 541a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedScreenOn="); 54217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); 543bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani idpw.print(" lastPredictedTime="); 544bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); 54561d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); 54617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani idpw.print(" bucket=" + appUsageHistory.currentBucket 54717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " reason=" + appUsageHistory.bucketingReason); 548a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 549a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 550a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 551a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalElapsedTime="); 55261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani TimeUtils.formatDuration(getElapsedTime(elapsedRealtime), idpw); 553a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 554a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalScreenOnTime="); 55561d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani TimeUtils.formatDuration(getScreenOnTime(elapsedRealtime), idpw); 556a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 557a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.decreaseIndent(); 558a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 559a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 560a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void dumpHistory(IndentingPrintWriter idpw, int userId) { 56117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 562a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 5630a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani if (userHistory == null) return; 5640a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final int P = userHistory.size(); 5650a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int p = 0; p < P; p++) { 5660a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final String packageName = userHistory.keyAt(p); 567a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] history = userHistory.valueAt(p).recent; 5680a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < HISTORY_SIZE; i++) { 5690a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(history[i] == 0 ? '.' : 'A'); 5700a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 57161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); 5720a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(" " + packageName); 5730a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.println(); 5740a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 5750a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 576a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani} 577