Loading services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +26 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; import static android.app.usage.UsageEvents.Event.USER_INTERACTION; import static android.app.usage.UsageStatsManager.REASON_PREDICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; Loading Loading @@ -364,4 +365,29 @@ public class AppStandbyControllerTests { reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); } @Test public void testPredictionTimedout() throws Exception { AppStandbyController controller = setupController(); setChargingState(controller, false); controller.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, REASON_PREDICTED + "CTS", 1 * HOUR_MS); // Fast forward 12 hours mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD; controller.checkIdleStates(USER_ID); // Should still be in predicted bucket, since prediction timeout is 1 day since prediction assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller)); // Fast forward two more hours mInjector.mElapsedRealtime += 2 * HOUR_MS; controller.checkIdleStates(USER_ID); // Should have now applied prediction timeout assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); // Fast forward RARE bucket mInjector.mElapsedRealtime += RARE_THRESHOLD; controller.checkIdleStates(USER_ID); // Should continue to apply prediction timeout assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller)); } } services/usage/java/com/android/server/usage/AppIdleHistory.java +37 −7 Original line number Diff line number Diff line Loading @@ -72,14 +72,13 @@ public class AppIdleHistory { private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; // Elapsed timebase time when app was last used private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; // Elapsed timebase time when the app bucket was last predicted externally private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime"; // The standby bucket for the app private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; // The reason the app was put in the above bucket private static final String ATTR_BUCKETING_REASON = "bucketReason"; // State that was last informed to listeners, since boot private static final int STATE_UNINFORMED = 0; private static final int STATE_ACTIVE = 1; private static final int STATE_IDLE = 2; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration private long mElapsedDuration; // Total device on duration since device was "born" Loading @@ -92,13 +91,21 @@ public class AppIdleHistory { private boolean mScreenOn; private static class AppUsageHistory { static class AppUsageHistory { // Debug final byte[] recent = new byte[HISTORY_SIZE]; // Last used time using elapsed timebase long lastUsedElapsedTime; // Last used time using screen_on timebase long lastUsedScreenTime; // Last predicted time using elapsed timebase long lastPredictedTime; // Standby bucket @UsageStatsManager.StandbyBuckets int currentBucket; // Reason for setting the standby bucket. TODO: Switch to int. String bucketingReason; // In-memory only, last bucket for which the listeners were informed int lastInformedBucket; } Loading Loading @@ -269,6 +276,7 @@ public class AppIdleHistory { appUsageHistory = new AppUsageHistory(); appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); appUsageHistory.lastPredictedTime = getElapsedTime(0); appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER; appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; appUsageHistory.lastInformedBucket = -1; Loading @@ -295,6 +303,14 @@ public class AppIdleHistory { } } public AppUsageHistory getAppUsageHistory(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); return appUsageHistory; } public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime, int bucket, String reason) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); Loading @@ -302,6 +318,9 @@ public class AppIdleHistory { getPackageHistory(userHistory, packageName, elapsedRealtime, true); appUsageHistory.currentBucket = bucket; appUsageHistory.bucketingReason = reason; if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) { appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime); } if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket + ", reason=" + appUsageHistory.bucketingReason); Loading @@ -322,7 +341,7 @@ public class AppIdleHistory { return appUsageHistory != null ? appUsageHistory.bucketingReason : null; } private long getElapsedTime(long elapsedRealtime) { public long getElapsedTime(long elapsedRealtime) { return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); } Loading Loading @@ -431,6 +450,12 @@ public class AppIdleHistory { Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); appUsageHistory.lastUsedScreenTime = Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); String lastPredictedTimeString = parser.getAttributeValue(null, ATTR_LAST_PREDICTED_TIME); if (lastPredictedTimeString != null) { appUsageHistory.lastPredictedTime = Long.parseLong(lastPredictedTimeString); } String currentBucketString = parser.getAttributeValue(null, ATTR_CURRENT_BUCKET); appUsageHistory.currentBucket = currentBucketString == null Loading @@ -441,6 +466,7 @@ public class AppIdleHistory { if (appUsageHistory.bucketingReason == null) { appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; } appUsageHistory.lastInformedBucket = -1; userHistory.put(packageName, appUsageHistory); } } Loading Loading @@ -477,6 +503,8 @@ public class AppIdleHistory { Long.toString(history.lastUsedElapsedTime)); xml.attribute(null, ATTR_SCREEN_IDLE, Long.toString(history.lastUsedScreenTime)); xml.attribute(null, ATTR_LAST_PREDICTED_TIME, Long.toString(history.lastPredictedTime)); xml.attribute(null, ATTR_CURRENT_BUCKET, Integer.toString(history.currentBucket)); xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); Loading Loading @@ -512,6 +540,8 @@ public class AppIdleHistory { TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); idpw.print(" lastUsedScreenOn="); TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); idpw.print(" lastPredictedTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" bucket=" + appUsageHistory.currentBucket + " reason=" + appUsageHistory.bucketingReason); Loading services/usage/java/com/android/server/usage/AppStandbyController.java +22 −8 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageStatsManager.REASON_DEFAULT; import static android.app.usage.UsageStatsManager.REASON_FORCED; import static android.app.usage.UsageStatsManager.REASON_PREDICTED; import static android.app.usage.UsageStatsManager.REASON_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_USAGE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; Loading Loading @@ -118,6 +119,9 @@ public class AppStandbyController { STANDBY_BUCKET_RARE }; // Expiration time for predicted bucket private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR; // To name the lock for stack traces static class Lock {} Loading Loading @@ -388,25 +392,27 @@ public class AppStandbyController { STANDBY_BUCKET_EXEMPTED); } else { synchronized (mAppIdleLock) { String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName, AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime); // If the bucket was forced by the developer, leave it alone if (REASON_FORCED.equals(bucketingReason)) { if (REASON_FORCED.equals(app.bucketingReason)) { continue; } boolean predictionLate = false; // If the bucket was moved up due to usage, let the timeouts apply. if (REASON_DEFAULT.equals(bucketingReason) || REASON_USAGE.equals(bucketingReason) || REASON_TIMEOUT.equals(bucketingReason)) { int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime); if (REASON_DEFAULT.equals(app.bucketingReason) || REASON_USAGE.equals(app.bucketingReason) || REASON_TIMEOUT.equals(app.bucketingReason) || (predictionLate = predictionTimedOut(app, elapsedRealtime))) { int oldBucket = app.currentBucket; int newBucket = getBucketForLocked(packageName, userId, elapsedRealtime); if (DEBUG) { Slog.d(TAG, " Old bucket=" + oldBucket + ", newBucket=" + newBucket); } if (oldBucket < newBucket) { if (oldBucket < newBucket || predictionLate) { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, REASON_TIMEOUT); maybeInformListeners(packageName, userId, elapsedRealtime, Loading @@ -424,6 +430,14 @@ public class AppStandbyController { return true; } private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) { return app.bucketingReason != null && app.bucketingReason.startsWith(REASON_PREDICTED) && app.lastPredictedTime > 0 && mAppIdleHistory.getElapsedTime(elapsedRealtime) - app.lastPredictedTime > PREDICTION_TIMEOUT; } private void maybeInformListeners(String packageName, int userId, long elapsedRealtime, int bucket) { synchronized (mAppIdleLock) { Loading Loading
services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +26 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; import static android.app.usage.UsageEvents.Event.USER_INTERACTION; import static android.app.usage.UsageStatsManager.REASON_PREDICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; Loading Loading @@ -364,4 +365,29 @@ public class AppStandbyControllerTests { reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); } @Test public void testPredictionTimedout() throws Exception { AppStandbyController controller = setupController(); setChargingState(controller, false); controller.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, REASON_PREDICTED + "CTS", 1 * HOUR_MS); // Fast forward 12 hours mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD; controller.checkIdleStates(USER_ID); // Should still be in predicted bucket, since prediction timeout is 1 day since prediction assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller)); // Fast forward two more hours mInjector.mElapsedRealtime += 2 * HOUR_MS; controller.checkIdleStates(USER_ID); // Should have now applied prediction timeout assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); // Fast forward RARE bucket mInjector.mElapsedRealtime += RARE_THRESHOLD; controller.checkIdleStates(USER_ID); // Should continue to apply prediction timeout assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller)); } }
services/usage/java/com/android/server/usage/AppIdleHistory.java +37 −7 Original line number Diff line number Diff line Loading @@ -72,14 +72,13 @@ public class AppIdleHistory { private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; // Elapsed timebase time when app was last used private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; // Elapsed timebase time when the app bucket was last predicted externally private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime"; // The standby bucket for the app private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; // The reason the app was put in the above bucket private static final String ATTR_BUCKETING_REASON = "bucketReason"; // State that was last informed to listeners, since boot private static final int STATE_UNINFORMED = 0; private static final int STATE_ACTIVE = 1; private static final int STATE_IDLE = 2; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration private long mElapsedDuration; // Total device on duration since device was "born" Loading @@ -92,13 +91,21 @@ public class AppIdleHistory { private boolean mScreenOn; private static class AppUsageHistory { static class AppUsageHistory { // Debug final byte[] recent = new byte[HISTORY_SIZE]; // Last used time using elapsed timebase long lastUsedElapsedTime; // Last used time using screen_on timebase long lastUsedScreenTime; // Last predicted time using elapsed timebase long lastPredictedTime; // Standby bucket @UsageStatsManager.StandbyBuckets int currentBucket; // Reason for setting the standby bucket. TODO: Switch to int. String bucketingReason; // In-memory only, last bucket for which the listeners were informed int lastInformedBucket; } Loading Loading @@ -269,6 +276,7 @@ public class AppIdleHistory { appUsageHistory = new AppUsageHistory(); appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); appUsageHistory.lastPredictedTime = getElapsedTime(0); appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER; appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; appUsageHistory.lastInformedBucket = -1; Loading @@ -295,6 +303,14 @@ public class AppIdleHistory { } } public AppUsageHistory getAppUsageHistory(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); return appUsageHistory; } public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime, int bucket, String reason) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); Loading @@ -302,6 +318,9 @@ public class AppIdleHistory { getPackageHistory(userHistory, packageName, elapsedRealtime, true); appUsageHistory.currentBucket = bucket; appUsageHistory.bucketingReason = reason; if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) { appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime); } if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket + ", reason=" + appUsageHistory.bucketingReason); Loading @@ -322,7 +341,7 @@ public class AppIdleHistory { return appUsageHistory != null ? appUsageHistory.bucketingReason : null; } private long getElapsedTime(long elapsedRealtime) { public long getElapsedTime(long elapsedRealtime) { return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); } Loading Loading @@ -431,6 +450,12 @@ public class AppIdleHistory { Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); appUsageHistory.lastUsedScreenTime = Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); String lastPredictedTimeString = parser.getAttributeValue(null, ATTR_LAST_PREDICTED_TIME); if (lastPredictedTimeString != null) { appUsageHistory.lastPredictedTime = Long.parseLong(lastPredictedTimeString); } String currentBucketString = parser.getAttributeValue(null, ATTR_CURRENT_BUCKET); appUsageHistory.currentBucket = currentBucketString == null Loading @@ -441,6 +466,7 @@ public class AppIdleHistory { if (appUsageHistory.bucketingReason == null) { appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; } appUsageHistory.lastInformedBucket = -1; userHistory.put(packageName, appUsageHistory); } } Loading Loading @@ -477,6 +503,8 @@ public class AppIdleHistory { Long.toString(history.lastUsedElapsedTime)); xml.attribute(null, ATTR_SCREEN_IDLE, Long.toString(history.lastUsedScreenTime)); xml.attribute(null, ATTR_LAST_PREDICTED_TIME, Long.toString(history.lastPredictedTime)); xml.attribute(null, ATTR_CURRENT_BUCKET, Integer.toString(history.currentBucket)); xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); Loading Loading @@ -512,6 +540,8 @@ public class AppIdleHistory { TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); idpw.print(" lastUsedScreenOn="); TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); idpw.print(" lastPredictedTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" bucket=" + appUsageHistory.currentBucket + " reason=" + appUsageHistory.bucketingReason); Loading
services/usage/java/com/android/server/usage/AppStandbyController.java +22 −8 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageStatsManager.REASON_DEFAULT; import static android.app.usage.UsageStatsManager.REASON_FORCED; import static android.app.usage.UsageStatsManager.REASON_PREDICTED; import static android.app.usage.UsageStatsManager.REASON_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_USAGE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; Loading Loading @@ -118,6 +119,9 @@ public class AppStandbyController { STANDBY_BUCKET_RARE }; // Expiration time for predicted bucket private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR; // To name the lock for stack traces static class Lock {} Loading Loading @@ -388,25 +392,27 @@ public class AppStandbyController { STANDBY_BUCKET_EXEMPTED); } else { synchronized (mAppIdleLock) { String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName, AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime); // If the bucket was forced by the developer, leave it alone if (REASON_FORCED.equals(bucketingReason)) { if (REASON_FORCED.equals(app.bucketingReason)) { continue; } boolean predictionLate = false; // If the bucket was moved up due to usage, let the timeouts apply. if (REASON_DEFAULT.equals(bucketingReason) || REASON_USAGE.equals(bucketingReason) || REASON_TIMEOUT.equals(bucketingReason)) { int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime); if (REASON_DEFAULT.equals(app.bucketingReason) || REASON_USAGE.equals(app.bucketingReason) || REASON_TIMEOUT.equals(app.bucketingReason) || (predictionLate = predictionTimedOut(app, elapsedRealtime))) { int oldBucket = app.currentBucket; int newBucket = getBucketForLocked(packageName, userId, elapsedRealtime); if (DEBUG) { Slog.d(TAG, " Old bucket=" + oldBucket + ", newBucket=" + newBucket); } if (oldBucket < newBucket) { if (oldBucket < newBucket || predictionLate) { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, REASON_TIMEOUT); maybeInformListeners(packageName, userId, elapsedRealtime, Loading @@ -424,6 +430,14 @@ public class AppStandbyController { return true; } private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) { return app.bucketingReason != null && app.bucketingReason.startsWith(REASON_PREDICTED) && app.lastPredictedTime > 0 && mAppIdleHistory.getElapsedTime(elapsedRealtime) - app.lastPredictedTime > PREDICTION_TIMEOUT; } private void maybeInformListeners(String packageName, int userId, long elapsedRealtime, int bucket) { synchronized (mAppIdleLock) { Loading