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 19a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.Environment; 20a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.SystemClock; 21a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.os.UserHandle; 220a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.ArrayMap; 23a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.AtomicFile; 24a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Slog; 250a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport android.util.SparseArray; 26a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.TimeUtils; 27a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport android.util.Xml; 280a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 29a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.annotations.VisibleForTesting; 30a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport com.android.internal.util.FastXmlSerializer; 310a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasaniimport com.android.internal.util.IndentingPrintWriter; 320a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 33a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport libcore.io.IoUtils; 34a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 35a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParser; 36a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport org.xmlpull.v1.XmlPullParserException; 37a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 38a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedOutputStream; 39a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.BufferedReader; 40a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.File; 41a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileInputStream; 42a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileOutputStream; 43a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.FileReader; 44a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.io.IOException; 45a93542f9d341897f3206f775fd5720663b17504fAmith Yamasaniimport java.nio.charset.StandardCharsets; 46a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 470a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani/** 480a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Keeps track of recent active state changes in apps. 490a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani * Access should be guarded by a lock by the caller. 500a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani */ 510a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasanipublic class AppIdleHistory { 520a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 53a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG = "AppIdleHistory"; 54a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 55a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // History for all users and all packages 56a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private SparseArray<ArrayMap<String,PackageHistory>> mIdleHistory = new SparseArray<>(); 57a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mLastPeriod = 0; 580a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final long ONE_MINUTE = 60 * 1000; 590a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int HISTORY_SIZE = 100; 600a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_LAST_STATE = 2; 610a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani private static final int FLAG_PARTIAL_ACTIVE = 1; 626776849dc5ff851a225745393f082b702754e278Amith Yamasani private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE 630a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani : 60 * ONE_MINUTE; 640a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 65a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 66a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; 67a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGES = "packages"; 68a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String TAG_PACKAGE = "package"; 69a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_NAME = "name"; 70a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Screen on timebase time when app was last used 71a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; 72a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Elapsed timebase time when app was last used 73a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; 74a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 75a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) 76a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration 77a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedDuration; // Total device on duration since device was "born" 78a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 79a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot) 80a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration 81a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnDuration; // Total screen on duration since device was "born" 82a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 83a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mElapsedTimeThreshold; 84a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long mScreenOnTimeThreshold; 85a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private final File mStorageDir; 86a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 87a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private boolean mScreenOn; 88a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 89a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private static class PackageHistory { 90a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] recent = new byte[HISTORY_SIZE]; 91a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedElapsedTime; 92a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long lastUsedScreenTime; 93a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 94a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 95a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AppIdleHistory(long elapsedRealtime) { 96a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani this(Environment.getDataSystemDirectory(), elapsedRealtime); 97a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 98a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 99a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 100a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AppIdleHistory(File storageDir, long elapsedRealtime) { 101a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 102a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnSnapshot = elapsedRealtime; 103a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mStorageDir = storageDir; 104a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani readScreenOnTimeLocked(); 105a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 106a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 107a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) { 108a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedTimeThreshold = elapsedTimeThreshold; 109a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnTimeThreshold = screenOnTimeThreshold; 110a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 111a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 112a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void updateDisplayLocked(boolean screenOn, long elapsedRealtime) { 113a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (screenOn == mScreenOn) return; 114a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 115a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOn = screenOn; 116a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mScreenOn) { 117a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnSnapshot = elapsedRealtime; 118a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 119a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot; 120a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration += elapsedRealtime - mElapsedSnapshot; 121a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani writeScreenOnTimeLocked(); 122a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 123a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 124a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 125a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 126a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public long getScreenOnTimeLocked(long elapsedRealtime) { 127a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long screenOnTime = mScreenOnDuration; 128a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mScreenOn) { 129a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTime += elapsedRealtime - mScreenOnSnapshot; 130a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 131a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return screenOnTime; 132a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 133a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 134a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani @VisibleForTesting 135a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani File getScreenOnTimeFile() { 136a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return new File(mStorageDir, "screen_on_time"); 137a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 138a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 139a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private void readScreenOnTimeLocked() { 140a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani File screenOnTimeFile = getScreenOnTimeFile(); 141a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (screenOnTimeFile.exists()) { 142a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 143a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile)); 144a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mScreenOnDuration = Long.parseLong(reader.readLine()); 145a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration = Long.parseLong(reader.readLine()); 146a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani reader.close(); 147a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException | NumberFormatException e) { 148a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 149a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 150a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani writeScreenOnTimeLocked(); 151a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 152a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 153a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 154a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private void writeScreenOnTimeLocked() { 155a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile()); 156a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileOutputStream fos = null; 157a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 158a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos = screenOnTimeFile.startWrite(); 159a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos.write((Long.toString(mScreenOnDuration) + "\n" 160a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani + Long.toString(mElapsedDuration) + "\n").getBytes()); 161a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTimeFile.finishWrite(fos); 162a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException ioe) { 163a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani screenOnTimeFile.failWrite(fos); 164a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 165a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 166a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 167a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani /** 168a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani * To be called periodically to keep track of elapsed time when app idle times are written 169a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani */ 170a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void writeElapsedTimeLocked() { 171a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 172a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Only bump up and snapshot the elapsed time. Don't change screen on duration. 173a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedDuration += elapsedRealtime - mElapsedSnapshot; 174a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mElapsedSnapshot = elapsedRealtime; 175a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani writeScreenOnTimeLocked(); 176a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 177a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 178a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void reportUsageLocked(String packageName, int userId, long elapsedRealtime) { 179a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); 180a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName, 181a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani elapsedRealtime); 182a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 183a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani shiftHistoryToNow(userHistory, elapsedRealtime); 184a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 185a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedElapsedTime = mElapsedDuration 186a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani + (elapsedRealtime - mElapsedSnapshot); 187a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime); 188a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; 189a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 190a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 191a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void setIdle(String packageName, int userId, long elapsedRealtime) { 192a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); 193a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName, 194a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani elapsedRealtime); 195a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 196a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani shiftHistoryToNow(userHistory, elapsedRealtime); 1976776849dc5ff851a225745393f082b702754e278Amith Yamasani 198a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; 199a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 200a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 201a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private void shiftHistoryToNow(ArrayMap<String, PackageHistory> userHistory, 202a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long elapsedRealtime) { 203a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani long thisPeriod = elapsedRealtime / PERIOD_DURATION; 2040a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Has the period switched over? Slide all users' package histories 205a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (mLastPeriod != 0 && mLastPeriod < thisPeriod 206a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) { 207a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int diff = (int) (thisPeriod - mLastPeriod); 2086776849dc5ff851a225745393f082b702754e278Amith Yamasani final int NUSERS = mIdleHistory.size(); 2090a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int u = 0; u < NUSERS; u++) { 2106776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = mIdleHistory.valueAt(u); 211a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (PackageHistory idleState : userHistory.values()) { 2120a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Shift left 213a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani System.arraycopy(idleState.recent, diff, idleState.recent, 0, 214a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani HISTORY_SIZE - diff); 2150a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani // Replicate last state across the diff 2160a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < diff; i++) { 217a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idleState.recent[HISTORY_SIZE - i - 1] = 218a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); 2190a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2200a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2210a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2220a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 223a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani mLastPeriod = thisPeriod; 2240a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 2250a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 226a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private ArrayMap<String, PackageHistory> getUserHistoryLocked(int userId) { 227a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId); 2286776849dc5ff851a225745393f082b702754e278Amith Yamasani if (userHistory == null) { 2296776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory = new ArrayMap<>(); 2306776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.put(userId, userHistory); 231a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani readAppIdleTimesLocked(userId, userHistory); 2326776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2336776849dc5ff851a225745393f082b702754e278Amith Yamasani return userHistory; 2346776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2356776849dc5ff851a225745393f082b702754e278Amith Yamasani 236a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private PackageHistory getPackageHistoryLocked(ArrayMap<String, PackageHistory> userHistory, 237a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani String packageName, long elapsedRealtime) { 238a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani PackageHistory packageHistory = userHistory.get(packageName); 2396776849dc5ff851a225745393f082b702754e278Amith Yamasani if (packageHistory == null) { 240a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory = new PackageHistory(); 241a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime); 242a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime); 2436776849dc5ff851a225745393f082b702754e278Amith Yamasani userHistory.put(packageName, packageHistory); 2446776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2456776849dc5ff851a225745393f082b702754e278Amith Yamasani return packageHistory; 2466776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2476776849dc5ff851a225745393f082b702754e278Amith Yamasani 248a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void onUserRemoved(int userId) { 2496776849dc5ff851a225745393f082b702754e278Amith Yamasani mIdleHistory.remove(userId); 2506776849dc5ff851a225745393f082b702754e278Amith Yamasani } 2516776849dc5ff851a225745393f082b702754e278Amith Yamasani 252a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) { 253a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); 254a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani PackageHistory packageHistory = 255a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani getPackageHistoryLocked(userHistory, packageName, elapsedRealtime); 256a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (packageHistory == null) { 257a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return false; // Default to not idle 258a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } else { 259a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return hasPassedThresholdsLocked(packageHistory, elapsedRealtime); 260a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 261a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 262a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 263a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private long getElapsedTimeLocked(long elapsedRealtime) { 264a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); 265a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 266a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 267a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void setIdleLocked(String packageName, int userId, boolean idle, long elapsedRealtime) { 268a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); 269a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName, 270a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani elapsedRealtime); 271a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime) 272a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani - mElapsedTimeThreshold; 273a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime) 274a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani - (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */; 275a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 276a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 277bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani public void clearUsageLocked(String packageName, int userId) { 278bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId); 279bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani userHistory.remove(packageName); 280bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani } 281bdda1e076eb30bf88749a1a349f9bb6f52434ecdAmith Yamasani 282a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) { 283a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return (packageHistory.lastUsedScreenTime 284a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani <= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold) 285a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && (packageHistory.lastUsedElapsedTime 286a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani <= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold); 287a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 288a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 289a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private File getUserFile(int userId) { 290a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return new File(new File(new File(mStorageDir, "users"), 291a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Integer.toString(userId)), APP_IDLE_FILENAME); 292a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 293a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 294a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani private void readAppIdleTimesLocked(int userId, ArrayMap<String, PackageHistory> userHistory) { 295a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileInputStream fis = null; 296a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 297a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 298a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fis = appIdleFile.openRead(); 299a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani XmlPullParser parser = Xml.newPullParser(); 300a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani parser.setInput(fis, StandardCharsets.UTF_8.name()); 301a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 302a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani int type; 303a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.START_TAG 304a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani && type != XmlPullParser.END_DOCUMENT) { 305a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani // Skip 306a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 307a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 308a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type != XmlPullParser.START_TAG) { 309a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 310a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 311a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 312a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (!parser.getName().equals(TAG_PACKAGES)) { 313a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani return; 314a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 315a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 316a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (type == XmlPullParser.START_TAG) { 317a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String name = parser.getName(); 318a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (name.equals(TAG_PACKAGE)) { 319a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = parser.getAttributeValue(null, ATTR_NAME); 320a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani PackageHistory packageHistory = new PackageHistory(); 321a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedElapsedTime = 322a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); 323a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani packageHistory.lastUsedScreenTime = 324a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); 325a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani userHistory.put(packageName, packageHistory); 326a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 327a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 328a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 329a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (IOException | XmlPullParserException e) { 330a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Unable to read app idle file for user " + userId); 331a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } finally { 332a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani IoUtils.closeQuietly(fis); 333a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 334a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 335a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 336a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void writeAppIdleTimesLocked(int userId) { 337a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FileOutputStream fos = null; 338a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); 339a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani try { 340a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani fos = appIdleFile.startWrite(); 341a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final BufferedOutputStream bos = new BufferedOutputStream(fos); 342a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 343a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani FastXmlSerializer xml = new FastXmlSerializer(); 344a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setOutput(bos, StandardCharsets.UTF_8.name()); 345a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startDocument(null, true); 346a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 347a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 348a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGES); 349a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 350a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String,PackageHistory> userHistory = getUserHistoryLocked(userId); 351a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int N = userHistory.size(); 352a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int i = 0; i < N; i++) { 353a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani String packageName = userHistory.keyAt(i); 354a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani PackageHistory history = userHistory.valueAt(i); 355a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.startTag(null, TAG_PACKAGE); 356a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_NAME, packageName); 357a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_ELAPSED_IDLE, 358a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedElapsedTime)); 359a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.attribute(null, ATTR_SCREEN_IDLE, 360a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Long.toString(history.lastUsedScreenTime)); 361a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGE); 362a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 363a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 364a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endTag(null, TAG_PACKAGES); 365a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani xml.endDocument(); 366a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.finishWrite(fos); 367a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } catch (Exception e) { 368a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani appIdleFile.failWrite(fos); 369a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani Slog.e(TAG, "Error writing app idle file for user " + userId); 370a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 3710a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 3720a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani 3730a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani public void dump(IndentingPrintWriter idpw, int userId) { 374a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println("Package idle stats:"); 375a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.increaseIndent(); 376a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId); 377a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 378a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long totalElapsedTime = getElapsedTimeLocked(elapsedRealtime); 379a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long screenOnTime = getScreenOnTimeLocked(elapsedRealtime); 380a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani if (userHistory == null) return; 381a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final int P = userHistory.size(); 382a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani for (int p = 0; p < P; p++) { 383a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final String packageName = userHistory.keyAt(p); 384a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final PackageHistory packageHistory = userHistory.valueAt(p); 385a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("package=" + packageName); 386a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedElapsed="); 387a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw); 388a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" lastUsedScreenOn="); 389a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw); 390a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n")); 391a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 392a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 393a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 394a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalElapsedTime="); 395a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani TimeUtils.formatDuration(getElapsedTimeLocked(elapsedRealtime), idpw); 396a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 397a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print("totalScreenOnTime="); 398a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani TimeUtils.formatDuration(getScreenOnTimeLocked(elapsedRealtime), idpw); 399a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.println(); 400a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.decreaseIndent(); 401a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani } 402a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani 403a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani public void dumpHistory(IndentingPrintWriter idpw, int userId) { 404a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId); 405a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final long elapsedRealtime = SystemClock.elapsedRealtime(); 4060a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani if (userHistory == null) return; 4070a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final int P = userHistory.size(); 4080a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int p = 0; p < P; p++) { 4090a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani final String packageName = userHistory.keyAt(p); 410a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani final byte[] history = userHistory.valueAt(p).recent; 4110a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani for (int i = 0; i < HISTORY_SIZE; i++) { 4120a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(history[i] == 0 ? '.' : 'A'); 4130a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 414a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n")); 4150a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.print(" " + packageName); 4160a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani idpw.println(); 4170a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 4180a11e69428d4c00dfcb368c1eb4e60ad8e0dc918Amith Yamasani } 419a93542f9d341897f3206f775fd5720663b17504fAmith Yamasani} 420