AppIdleHistory.java revision e878931414e46eaaf1e10e227cd50bcf5435dee8
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; 45e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasaniimport java.util.HashMap; 46e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasaniimport java.util.Map; 47a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 480a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani/** 490a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Keeps track of recent active state changes in apps. 500a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Access should be guarded by a lock by the caller. 510a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani */ 520a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasanipublic class AppIdleHistory { 530a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 54a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG = "AppIdleHistory"; 55a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 5617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final boolean DEBUG = AppStandbyController.DEBUG; 5717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 58a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // History for all users and all packages 5917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>(); 60a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mLastPeriod = 0; 610a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final long ONE_MINUTE = 60 * 1000; 620a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int HISTORY_SIZE = 100; 630a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_LAST_STATE = 2; 640a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_PARTIAL_ACTIVE = 1; 656776849dc5ff851a225745393f082b702754e278Amith Yamasani private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE 660a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani : 60 * ONE_MINUTE; 670a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 68a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 69a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; 70a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGES = "packages"; 71a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGE = "package"; 72a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_NAME = "name"; 73a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Screen on timebase time when app was last used 74a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; 75a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Elapsed timebase time when app was last used 76a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; 77bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Elapsed timebase time when the app bucket was last predicted externally 78bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime"; 79bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // The standby bucket for the app 8017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; 81bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // The reason the app was put in the above bucket 8217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final String ATTR_BUCKETING_REASON = "bucketReason"; 8317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 84a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) 85a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration 86a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedDuration; // Total device on duration since device was "born" 87a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 88a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot) 89a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration 90a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnDuration; // Total screen on duration since device was "born" 91a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 92a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private final File mStorageDir; 93a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 94a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private boolean mScreenOn; 95a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 96bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani static class AppUsageHistory { 97bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Debug 98a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] recent = new byte[HISTORY_SIZE]; 99bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Last used time using elapsed timebase 100a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedElapsedTime; 101bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Last used time using screen_on timebase 102a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedScreenTime; 103bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Last predicted time using elapsed timebase 104bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani long lastPredictedTime; 105bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Standby bucket 106afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani @UsageStatsManager.StandbyBuckets 107afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani int currentBucket; 108bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // Reason for setting the standby bucket. TODO: Switch to int. 10917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String bucketingReason; 110bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani // In-memory only, last bucket for which the listeners were informed 11184cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani int lastInformedBucket; 112a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 113a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 114a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AppIdleHistory(File storageDir, long elapsedRealtime) { 115a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 116a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnSnapshot = elapsedRealtime; 117a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mStorageDir = storageDir; 11861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani readScreenOnTime(); 119a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 120a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 12161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void updateDisplay(boolean screenOn, long elapsedRealtime) { 122a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (screenOn == mScreenOn) return; 123a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 124a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOn = screenOn; 125a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mScreenOn) { 126a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnSnapshot = elapsedRealtime; 127a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 128a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot; 129a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration += elapsedRealtime - mElapsedSnapshot; 130a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 131a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 13217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, "mScreenOnSnapshot=" + mScreenOnSnapshot 13317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", mScreenOnDuration=" + mScreenOnDuration 13417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", mScreenOn=" + mScreenOn); 135a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 136a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 13761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public long getScreenOnTime(long elapsedRealtime) { 138a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long screenOnTime = mScreenOnDuration; 139a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mScreenOn) { 140a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTime += elapsedRealtime - mScreenOnSnapshot; 141a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 142a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return screenOnTime; 143a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 144a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 145a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 146a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani File getScreenOnTimeFile() { 147a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return new File(mStorageDir, "screen_on_time"); 148a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 149a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 15061d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani private void readScreenOnTime() { 151a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani File screenOnTimeFile = getScreenOnTimeFile(); 152a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (screenOnTimeFile.exists()) { 153a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 154a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile)); 155a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnDuration = Long.parseLong(reader.readLine()); 156a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration = Long.parseLong(reader.readLine()); 157a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani reader.close(); 158a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException | NumberFormatException e) { 159a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 160a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 16161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani writeScreenOnTime(); 162a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 163a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 164a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 16561d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani private void writeScreenOnTime() { 166a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile()); 167a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileOutputStream fos = null; 168a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 169a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos = screenOnTimeFile.startWrite(); 170a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos.write((Long.toString(mScreenOnDuration) + "\n" 171a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani + Long.toString(mElapsedDuration) + "\n").getBytes()); 172a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTimeFile.finishWrite(fos); 173a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException ioe) { 174a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTimeFile.failWrite(fos); 175a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 176a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 177a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 178a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani /** 179a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani * To be called periodically to keep track of elapsed time when app idle times are written 180a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani */ 18161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void writeAppIdleDurations() { 182a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 183a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Only bump up and snapshot the elapsed time. Don't change screen on duration. 184a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration += elapsedRealtime - mElapsedSnapshot; 185a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 18661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani writeScreenOnTime(); 187a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 188a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 189803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani public int reportUsage(String packageName, int userId, long elapsedRealtime) { 19017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 19117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 19217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 193a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 194a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani shiftHistoryToNow(userHistory, elapsedRealtime); 195a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 19617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = mElapsedDuration 197a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani + (elapsedRealtime - mElapsedSnapshot); 19817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); 19917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; 200afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) { 201afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE; 202803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani if (DEBUG) { 203803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 204803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 205803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 206803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 207afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE; 208803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani 209803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani return appUsageHistory.currentBucket; 210803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 211803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani 212803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani public int reportMildUsage(String packageName, int userId, long elapsedRealtime) { 213803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 214803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 215803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani elapsedRealtime, true); 216afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) { 217afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET; 218803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani if (DEBUG) { 219803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 220803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 221803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani } 22217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 223803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani // TODO: Should this be a different reason for partial usage? 224afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE; 225803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani 226803eab6906427517b22c7fd42b21dbf57456eb72Amith Yamasani return appUsageHistory.currentBucket; 227a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 228a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 229a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void setIdle(String packageName, int userId, long elapsedRealtime) { 23017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 23117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 23217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 233a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 234a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani shiftHistoryToNow(userHistory, elapsedRealtime); 2356776849dc5ff851a225745393f082b702754e278Amith Yamasani 23617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; 237a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 238a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 23917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private void shiftHistoryToNow(ArrayMap<String, AppUsageHistory> userHistory, 240a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long elapsedRealtime) { 241a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long thisPeriod = elapsedRealtime / PERIOD_DURATION; 2420a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Has the period switched over? Slide all users' package histories 243a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mLastPeriod != 0 && mLastPeriod < thisPeriod 244a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) { 245a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int diff = (int) (thisPeriod - mLastPeriod); 2466776849dc5ff851a225745393f082b702754e278Amith Yamasani final int NUSERS = mIdleHistory.size(); 2470a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int u = 0; u < NUSERS; u++) { 2486776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = mIdleHistory.valueAt(u); 24917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani for (AppUsageHistory idleState : userHistory.values()) { 2500a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Shift left 251a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani System.arraycopy(idleState.recent, diff, idleState.recent, 0, 252a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani HISTORY_SIZE - diff); 2530a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Replicate last state across the diff 2540a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < diff; i++) { 255a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idleState.recent[HISTORY_SIZE - i - 1] = 256a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); 2570a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2580a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2590a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2600a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 261a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mLastPeriod = thisPeriod; 2620a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2630a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 26417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { 26517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 2666776849dc5ff851a225745393f082b702754e278Amith Yamasani if (userHistory == null) { 2676776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = new ArrayMap<>(); 2686776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.put(userId, userHistory); 26961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani readAppIdleTimes(userId, userHistory); 2706776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2716776849dc5ff851a225745393f082b702754e278Amith Yamasani return userHistory; 2726776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2736776849dc5ff851a225745393f082b702754e278Amith Yamasani 27417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory, 27517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String packageName, long elapsedRealtime, boolean create) { 27617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = userHistory.get(packageName); 27717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null && create) { 27817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory = new AppUsageHistory(); 27917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime); 28017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); 281bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastPredictedTime = getElapsedTime(0); 282afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER; 283afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; 284afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.lastInformedBucket = -1; 28517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani userHistory.put(packageName, appUsageHistory); 2866776849dc5ff851a225745393f082b702754e278Amith Yamasani } 28717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory; 2886776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2896776849dc5ff851a225745393f082b702754e278Amith Yamasani 290a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void onUserRemoved(int userId) { 2916776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.remove(userId); 2926776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2936776849dc5ff851a225745393f082b702754e278Amith Yamasani 29461d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public boolean isIdle(String packageName, int userId, long elapsedRealtime) { 29517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 29617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 29717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 29817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null) { 299a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return false; // Default to not idle 300a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 301afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani return appUsageHistory.currentBucket >= UsageStatsManager.STANDBY_BUCKET_RARE; 30217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // Whether or not it's passed will now be externally calculated and the 30317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // bucket will be pushed to the history using setAppStandbyBucket() 30417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani //return hasPassedThresholds(appUsageHistory, elapsedRealtime); 30517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 30617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 30717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 308bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani public AppUsageHistory getAppUsageHistory(String packageName, int userId, 309bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani long elapsedRealtime) { 310bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 311bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani AppUsageHistory appUsageHistory = 312bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 313bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani return appUsageHistory; 314bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani } 315bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani 31617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime, 31717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int bucket, String reason) { 31817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 31917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 32017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 32117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = bucket; 32217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = reason; 323bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) { 324bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime); 325bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani } 32617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) { 32717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 32817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 329a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 330a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 331a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 33217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) { 33317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 33417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 33517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 33617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory.currentBucket; 33717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 33817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 339e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani public Map<String, Integer> getAppStandbyBuckets(int userId, long elapsedRealtime) { 340e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 341e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani int size = userHistory.size(); 342e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani HashMap<String, Integer> buckets = new HashMap<>(size); 343e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani for (int i = 0; i < size; i++) { 344e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani buckets.put(userHistory.keyAt(i), userHistory.valueAt(i).currentBucket); 345e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani } 346e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani return buckets; 347e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani } 348e878931414e46eaaf1e10e227cd50bcf5435dee8Amith Yamasani 34917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) { 35017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 35117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 35217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, false); 35317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory != null ? appUsageHistory.bucketingReason : null; 35417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 35517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 356bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani public long getElapsedTime(long elapsedRealtime) { 357a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); 358a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 359a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 360a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate /* Returns the new standby bucket the app is assigned to */ 361a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate public int setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) { 36217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 36317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 36417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 36517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (idle) { 366afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_RARE; 367afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_FORCED; 36817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } else { 369afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE; 37017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // This is to pretend that the app was just used, don't freeze the state anymore. 371afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE; 37217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 373a732f014c5743af0dbb7eb2e63474a7147576f9dChristopher Tate return appUsageHistory.currentBucket; 374a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 375a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 37661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void clearUsage(String packageName, int userId) { 37717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 378bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani userHistory.remove(packageName); 379bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani } 380bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani 38117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani boolean shouldInformListeners(String packageName, int userId, 38284cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani long elapsedRealtime, int bucket) { 38317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 38417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 38517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 38684cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani if (appUsageHistory.lastInformedBucket != bucket) { 38784cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486Amith Yamasani appUsageHistory.lastInformedBucket = bucket; 38817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return true; 38917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 39017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return false; 39117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 39217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 39317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani /** 39417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds 39517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * that corresponds to how long since the app was used. 39617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param packageName 39717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param userId 39817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param elapsedRealtime current time 39917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0 40017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0 40117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @return The index whose values the app's used time exceeds (in both arrays) 40217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani */ 40317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int getThresholdIndex(String packageName, int userId, long elapsedRealtime, 40417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long[] screenTimeThresholds, long[] elapsedTimeThresholds) { 40517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 40617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 40717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, false); 40817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // If we don't have any state for the app, assume never used 40917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null) return screenTimeThresholds.length - 1; 41017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 41117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime; 41217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime; 41317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 41417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, packageName 41517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime 41617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " lastUsedElapsed=" + appUsageHistory.lastUsedElapsedTime); 41717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta 41817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", elapsed=" + elapsedDelta); 41917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani for (int i = screenTimeThresholds.length - 1; i >= 0; i--) { 42017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (screenOnDelta >= screenTimeThresholds[i] 42117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani && elapsedDelta >= elapsedTimeThresholds[i]) { 42217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return i; 42317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 42417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 42517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return 0; 426a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 427a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 42817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani @VisibleForTesting 42917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani File getUserFile(int userId) { 430a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return new File(new File(new File(mStorageDir, "users"), 431a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Integer.toString(userId)), APP_IDLE_FILENAME); 432a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 433a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 43417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private void readAppIdleTimes(int userId, ArrayMap<String, AppUsageHistory> userHistory) { 435a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileInputStream fis = null; 436a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 437a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 438a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fis = appIdleFile.openRead(); 439a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani XmlPullParser parser = Xml.newPullParser(); 440a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani parser.setInput(fis, StandardCharsets.UTF_8.name()); 441a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 442a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int type; 443a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.START_TAG 444a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && type != XmlPullParser.END_DOCUMENT) { 445a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Skip 446a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 447a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 448a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type != XmlPullParser.START_TAG) { 449a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 450a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 451a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 452a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (!parser.getName().equals(TAG_PACKAGES)) { 453a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 454a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 455a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 456a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type == XmlPullParser.START_TAG) { 457a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String name = parser.getName(); 458a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (name.equals(TAG_PACKAGE)) { 459a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = parser.getAttributeValue(null, ATTR_NAME); 46017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = new AppUsageHistory(); 46117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = 462a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); 46317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = 464a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); 465bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani String lastPredictedTimeString = parser.getAttributeValue(null, 466bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani ATTR_LAST_PREDICTED_TIME); 467bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani if (lastPredictedTimeString != null) { 468bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastPredictedTime = 469bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani Long.parseLong(lastPredictedTimeString); 470bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani } 47117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String currentBucketString = parser.getAttributeValue(null, 47217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ATTR_CURRENT_BUCKET); 47317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = currentBucketString == null 474afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani ? UsageStatsManager.STANDBY_BUCKET_ACTIVE 47517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani : Integer.parseInt(currentBucketString); 47617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = 47717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani parser.getAttributeValue(null, ATTR_BUCKETING_REASON); 47817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory.bucketingReason == null) { 479afbccb7d37647f6da61ebcc52a598c7a9f54bc3fAmith Yamasani appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; 48017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 481bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani appUsageHistory.lastInformedBucket = -1; 48217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani userHistory.put(packageName, appUsageHistory); 483a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 484a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 485a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 486a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException | XmlPullParserException e) { 487a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 488a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } finally { 489a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani IoUtils.closeQuietly(fis); 490a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 491a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 492a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 49361d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void writeAppIdleTimes(int userId) { 494a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileOutputStream fos = null; 495a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 496a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 497a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos = appIdleFile.startWrite(); 498a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final BufferedOutputStream bos = new BufferedOutputStream(fos); 499a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 500a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FastXmlSerializer xml = new FastXmlSerializer(); 501a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setOutput(bos, StandardCharsets.UTF_8.name()); 502a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startDocument(null, true); 503a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 504a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 505a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGES); 506a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 50717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId); 508a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int N = userHistory.size(); 509a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int i = 0; i < N; i++) { 510a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani String packageName = userHistory.keyAt(i); 51117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory history = userHistory.valueAt(i); 512a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGE); 513a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_NAME, packageName); 514a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_ELAPSED_IDLE, 515a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedElapsedTime)); 516a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_SCREEN_IDLE, 517a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedScreenTime)); 518bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani xml.attribute(null, ATTR_LAST_PREDICTED_TIME, 519bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani Long.toString(history.lastPredictedTime)); 52017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani xml.attribute(null, ATTR_CURRENT_BUCKET, 52117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani Integer.toString(history.currentBucket)); 52217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); 523a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGE); 524a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 525a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 526a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGES); 527a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endDocument(); 528a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.finishWrite(fos); 529a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (Exception e) { 530a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.failWrite(fos); 531a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Error writing app idle file for user " + userId); 532a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 5330a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 5340a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 535c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn public void dump(IndentingPrintWriter idpw, int userId, String pkg) { 536a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println("Package idle stats:"); 537a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.increaseIndent(); 53817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 539a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 54061d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani final long totalElapsedTime = getElapsedTime(elapsedRealtime); 54161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani final long screenOnTime = getScreenOnTime(elapsedRealtime); 542a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (userHistory == null) return; 543a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int P = userHistory.size(); 544a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int p = 0; p < P; p++) { 545a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = userHistory.keyAt(p); 54617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani final AppUsageHistory appUsageHistory = userHistory.valueAt(p); 547c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn if (pkg != null && !pkg.equals(packageName)) { 548c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn continue; 549c81983a0c3d7bfe8384dbf48909f4bcf300e36d2Dianne Hackborn } 550a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("package=" + packageName); 551a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedElapsed="); 55217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); 553a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedScreenOn="); 55417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); 555bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani idpw.print(" lastPredictedTime="); 556bd7b302f91f225f2dd2367cc37db9d2b75aec521Amith Yamasani TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); 55761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); 55817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani idpw.print(" bucket=" + appUsageHistory.currentBucket 55917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " reason=" + appUsageHistory.bucketingReason); 560a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 561a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 562a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 563a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalElapsedTime="); 56461d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani TimeUtils.formatDuration(getElapsedTime(elapsedRealtime), idpw); 565a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 566a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalScreenOnTime="); 56761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani TimeUtils.formatDuration(getScreenOnTime(elapsedRealtime), idpw); 568a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 569a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.decreaseIndent(); 570a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 571a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 572a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void dumpHistory(IndentingPrintWriter idpw, int userId) { 57317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 574a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 5750a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani if (userHistory == null) return; 5760a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final int P = userHistory.size(); 5770a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int p = 0; p < P; p++) { 5780a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final String packageName = userHistory.keyAt(p); 579a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] history = userHistory.valueAt(p).recent; 5800a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < HISTORY_SIZE; i++) { 5810a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(history[i] == 0 ? '.' : 'A'); 5820a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 58361d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); 5840a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(" " + packageName); 5850a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.println(); 5860a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 5870a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 588a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani} 589