AppIdleHistory.java revision 17fffee4908f11038ba9cc5a672d15cb25be3dfe
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 1917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasaniimport static android.app.usage.AppStandby.*; 2017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 2117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasaniimport android.app.usage.AppStandby; 22a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.Environment; 23a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.SystemClock; 240a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.ArrayMap; 25a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.AtomicFile; 26a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Slog; 270a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.SparseArray; 28a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.TimeUtils; 29a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Xml; 300a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 31a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.annotations.VisibleForTesting; 32a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.util.FastXmlSerializer; 330a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport com.android.internal.util.IndentingPrintWriter; 340a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 35a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport libcore.io.IoUtils; 36a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 37a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParser; 38a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParserException; 39a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 40a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedOutputStream; 41a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedReader; 42a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.File; 43a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileInputStream; 44a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileOutputStream; 45a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileReader; 46a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.IOException; 47a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.nio.charset.StandardCharsets; 48a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 490a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani/** 500a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Keeps track of recent active state changes in apps. 510a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Access should be guarded by a lock by the caller. 520a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani */ 530a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasanipublic class AppIdleHistory { 540a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 55a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG = "AppIdleHistory"; 56a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 5717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final boolean DEBUG = AppStandbyController.DEBUG; 5817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 59a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // History for all users and all packages 6017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>(); 61a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mLastPeriod = 0; 620a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final long ONE_MINUTE = 60 * 1000; 630a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int HISTORY_SIZE = 100; 640a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_LAST_STATE = 2; 650a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_PARTIAL_ACTIVE = 1; 666776849dc5ff851a225745393f082b702754e278Amith Yamasani private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE 670a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani : 60 * ONE_MINUTE; 680a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 69a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 70a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; 71a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGES = "packages"; 72a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGE = "package"; 73a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_NAME = "name"; 74a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Screen on timebase time when app was last used 75a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; 76a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Elapsed timebase time when app was last used 77a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; 7817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; 7917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final String ATTR_BUCKETING_REASON = "bucketReason"; 8017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 8117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // State that was last informed to listeners, since boot 8217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final int STATE_UNINFORMED = 0; 8317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final int STATE_ACTIVE = 1; 8417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static final int STATE_IDLE = 2; 85a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 86a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) 87a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration 88a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedDuration; // Total device on duration since device was "born" 89a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 90a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot) 91a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration 92a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnDuration; // Total screen on duration since device was "born" 93a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 94a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedTimeThreshold; 95a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnTimeThreshold; 96a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private final File mStorageDir; 97a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 98a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private boolean mScreenOn; 99a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 10017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private static class AppUsageHistory { 101a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] recent = new byte[HISTORY_SIZE]; 102a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedElapsedTime; 103a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedScreenTime; 10417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani @StandbyBuckets int currentBucket; 10517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String bucketingReason; 10617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int lastInformedState; 107a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 108a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 109a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AppIdleHistory(File storageDir, long elapsedRealtime) { 110a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 111a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnSnapshot = elapsedRealtime; 112a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mStorageDir = storageDir; 11361d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani readScreenOnTime(); 114a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 115a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 116a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) { 117a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedTimeThreshold = elapsedTimeThreshold; 118a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnTimeThreshold = screenOnTimeThreshold; 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 18961d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void 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; 20017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_ACTIVE; 20117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = AppStandby.REASON_USAGE; 20217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) { 20317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 20417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 20517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 206a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 207a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 208a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void setIdle(String packageName, int userId, long elapsedRealtime) { 20917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 21017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 21117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 212a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 213a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani shiftHistoryToNow(userHistory, elapsedRealtime); 2146776849dc5ff851a225745393f082b702754e278Amith Yamasani 21517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; 216a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 217a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 21817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private void shiftHistoryToNow(ArrayMap<String, AppUsageHistory> userHistory, 219a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long elapsedRealtime) { 220a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long thisPeriod = elapsedRealtime / PERIOD_DURATION; 2210a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Has the period switched over? Slide all users' package histories 222a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mLastPeriod != 0 && mLastPeriod < thisPeriod 223a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) { 224a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int diff = (int) (thisPeriod - mLastPeriod); 2256776849dc5ff851a225745393f082b702754e278Amith Yamasani final int NUSERS = mIdleHistory.size(); 2260a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int u = 0; u < NUSERS; u++) { 2276776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = mIdleHistory.valueAt(u); 22817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani for (AppUsageHistory idleState : userHistory.values()) { 2290a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Shift left 230a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani System.arraycopy(idleState.recent, diff, idleState.recent, 0, 231a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani HISTORY_SIZE - diff); 2320a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Replicate last state across the diff 2330a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < diff; i++) { 234a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idleState.recent[HISTORY_SIZE - i - 1] = 235a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); 2360a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2370a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2380a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2390a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 240a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mLastPeriod = thisPeriod; 2410a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2420a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 24317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { 24417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 2456776849dc5ff851a225745393f082b702754e278Amith Yamasani if (userHistory == null) { 2466776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = new ArrayMap<>(); 2476776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.put(userId, userHistory); 24861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani readAppIdleTimes(userId, userHistory); 2496776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2506776849dc5ff851a225745393f082b702754e278Amith Yamasani return userHistory; 2516776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2526776849dc5ff851a225745393f082b702754e278Amith Yamasani 25317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory, 25417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String packageName, long elapsedRealtime, boolean create) { 25517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = userHistory.get(packageName); 25617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null && create) { 25717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory = new AppUsageHistory(); 25817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime); 25917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); 26017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_NEVER; 26117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = REASON_DEFAULT; 26217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani userHistory.put(packageName, appUsageHistory); 2636776849dc5ff851a225745393f082b702754e278Amith Yamasani } 26417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory; 2656776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2666776849dc5ff851a225745393f082b702754e278Amith Yamasani 267a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void onUserRemoved(int userId) { 2686776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.remove(userId); 2696776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2706776849dc5ff851a225745393f082b702754e278Amith Yamasani 27161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public boolean isIdle(String packageName, int userId, long elapsedRealtime) { 27217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 27317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 27417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 27517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null) { 276a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return false; // Default to not idle 277a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 27817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory.currentBucket >= AppStandby.STANDBY_BUCKET_RARE; 27917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // Whether or not it's passed will now be externally calculated and the 28017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // bucket will be pushed to the history using setAppStandbyBucket() 28117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani //return hasPassedThresholds(appUsageHistory, elapsedRealtime); 28217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 28317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 28417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 28517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime, 28617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int bucket, String reason) { 28717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 28817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 28917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 29017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = bucket; 29117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = reason; 29217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) { 29317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket 29417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", reason=" + appUsageHistory.bucketingReason); 295a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 296a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 297a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 29817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) { 29917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 30017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 30117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, true); 30217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory.currentBucket; 30317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 30417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 30517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) { 30617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 30717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = 30817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani getPackageHistory(userHistory, packageName, elapsedRealtime, false); 30917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return appUsageHistory != null ? appUsageHistory.bucketingReason : null; 31017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 31117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 31261d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani private long getElapsedTime(long elapsedRealtime) { 313a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); 314a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 315a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 31661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) { 31717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 31817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 31917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 32017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (idle) { 32117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = STANDBY_BUCKET_RARE; 32217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = REASON_FORCED; 32317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } else { 32417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE; 32517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // This is to pretend that the app was just used, don't freeze the state anymore. 32617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = REASON_USAGE; 32717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 328a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 329a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 33061d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void clearUsage(String packageName, int userId) { 33117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 332bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani userHistory.remove(packageName); 333bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani } 334bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani 33517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani boolean shouldInformListeners(String packageName, int userId, 33617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long elapsedRealtime, boolean isIdle) { 33717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 33817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 33917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, true); 34017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int targetState = isIdle? STATE_IDLE : STATE_ACTIVE; 34117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory.lastInformedState != (isIdle ? STATE_IDLE : STATE_ACTIVE)) { 34217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastInformedState = targetState; 34317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return true; 34417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 34517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return false; 34617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 34717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 34817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani /** 34917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds 35017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * that corresponds to how long since the app was used. 35117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param packageName 35217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param userId 35317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param elapsedRealtime current time 35417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0 35517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0 35617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani * @return The index whose values the app's used time exceeds (in both arrays) 35717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani */ 35817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani int getThresholdIndex(String packageName, int userId, long elapsedRealtime, 35917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long[] screenTimeThresholds, long[] elapsedTimeThresholds) { 36017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); 36117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, 36217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani elapsedRealtime, false); 36317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani // If we don't have any state for the app, assume never used 36417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory == null) return screenTimeThresholds.length - 1; 36517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 36617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime; 36717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime; 36817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani 36917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, packageName 37017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime 37117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " lastUsedElapsed=" + appUsageHistory.lastUsedElapsedTime); 37217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta 37317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + ", elapsed=" + elapsedDelta); 37417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani for (int i = screenTimeThresholds.length - 1; i >= 0; i--) { 37517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (screenOnDelta >= screenTimeThresholds[i] 37617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani && elapsedDelta >= elapsedTimeThresholds[i]) { 37717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return i; 37817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 37917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 38017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani return 0; 381a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 382a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 38317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani @VisibleForTesting 38417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani File getUserFile(int userId) { 385a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return new File(new File(new File(mStorageDir, "users"), 386a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Integer.toString(userId)), APP_IDLE_FILENAME); 387a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 388a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 38917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani private void readAppIdleTimes(int userId, ArrayMap<String, AppUsageHistory> userHistory) { 390a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileInputStream fis = null; 391a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 392a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 393a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fis = appIdleFile.openRead(); 394a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani XmlPullParser parser = Xml.newPullParser(); 395a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani parser.setInput(fis, StandardCharsets.UTF_8.name()); 396a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 397a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int type; 398a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.START_TAG 399a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && type != XmlPullParser.END_DOCUMENT) { 400a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Skip 401a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 402a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 403a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type != XmlPullParser.START_TAG) { 404a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 405a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 406a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 407a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (!parser.getName().equals(TAG_PACKAGES)) { 408a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 409a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 410a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 411a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type == XmlPullParser.START_TAG) { 412a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String name = parser.getName(); 413a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (name.equals(TAG_PACKAGE)) { 414a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = parser.getAttributeValue(null, ATTR_NAME); 41517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory appUsageHistory = new AppUsageHistory(); 41617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedElapsedTime = 417a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); 41817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.lastUsedScreenTime = 419a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); 42017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani String currentBucketString = parser.getAttributeValue(null, 42117fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ATTR_CURRENT_BUCKET); 42217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.currentBucket = currentBucketString == null 42317fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ? AppStandby.STANDBY_BUCKET_ACTIVE 42417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani : Integer.parseInt(currentBucketString); 42517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = 42617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani parser.getAttributeValue(null, ATTR_BUCKETING_REASON); 42717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani if (appUsageHistory.bucketingReason == null) { 42817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani appUsageHistory.bucketingReason = REASON_DEFAULT; 42917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani } 43017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani userHistory.put(packageName, appUsageHistory); 431a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 432a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 433a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 434a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException | XmlPullParserException e) { 435a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 436a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } finally { 437a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani IoUtils.closeQuietly(fis); 438a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 439a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 440a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 44161d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani public void writeAppIdleTimes(int userId) { 442a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileOutputStream fos = null; 443a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 444a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 445a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos = appIdleFile.startWrite(); 446a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final BufferedOutputStream bos = new BufferedOutputStream(fos); 447a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 448a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FastXmlSerializer xml = new FastXmlSerializer(); 449a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setOutput(bos, StandardCharsets.UTF_8.name()); 450a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startDocument(null, true); 451a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 452a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 453a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGES); 454a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 45517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId); 456a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int N = userHistory.size(); 457a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int i = 0; i < N; i++) { 458a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani String packageName = userHistory.keyAt(i); 45917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani AppUsageHistory history = userHistory.valueAt(i); 460a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGE); 461a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_NAME, packageName); 462a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_ELAPSED_IDLE, 463a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedElapsedTime)); 464a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_SCREEN_IDLE, 465a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedScreenTime)); 46617fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani xml.attribute(null, ATTR_CURRENT_BUCKET, 46717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani Integer.toString(history.currentBucket)); 46817fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); 469a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGE); 470a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 471a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 472a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGES); 473a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endDocument(); 474a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.finishWrite(fos); 475a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (Exception e) { 476a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.failWrite(fos); 477a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Error writing app idle file for user " + userId); 478a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 4790a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 4800a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 4810a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani public void dump(IndentingPrintWriter idpw, int userId) { 482a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println("Package idle stats:"); 483a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.increaseIndent(); 48417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 485a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 48661d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani final long totalElapsedTime = getElapsedTime(elapsedRealtime); 48761d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani final long screenOnTime = getScreenOnTime(elapsedRealtime); 488a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (userHistory == null) return; 489a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int P = userHistory.size(); 490a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int p = 0; p < P; p++) { 491a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = userHistory.keyAt(p); 49217fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani final AppUsageHistory appUsageHistory = userHistory.valueAt(p); 493a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("package=" + packageName); 494a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedElapsed="); 49517fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); 496a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedScreenOn="); 49717fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); 49861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); 49917fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani idpw.print(" bucket=" + appUsageHistory.currentBucket 50017fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani + " reason=" + appUsageHistory.bucketingReason); 501a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 502a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 503a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 504a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalElapsedTime="); 50561d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani TimeUtils.formatDuration(getElapsedTime(elapsedRealtime), idpw); 506a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 507a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalScreenOnTime="); 50861d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani TimeUtils.formatDuration(getScreenOnTime(elapsedRealtime), idpw); 509a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 510a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.decreaseIndent(); 511a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 512a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 513a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void dumpHistory(IndentingPrintWriter idpw, int userId) { 51417fffee4908f11038ba9cc5a672d15cb25be3dfeAmith Yamasani ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); 515a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 5160a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani if (userHistory == null) return; 5170a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final int P = userHistory.size(); 5180a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int p = 0; p < P; p++) { 5190a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final String packageName = userHistory.keyAt(p); 520a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] history = userHistory.valueAt(p).recent; 5210a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < HISTORY_SIZE; i++) { 5220a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(history[i] == 0 ? '.' : 'A'); 5230a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 52461d5fd7fee3250bdf4b6ddfbccbd6bceae9436c6Amith Yamasani idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); 5250a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(" " + packageName); 5260a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.println(); 5270a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 5280a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 529a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani} 530