Loading apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +66 −16 Original line number Diff line number Diff line Loading @@ -136,25 +136,59 @@ public class AppStandbyController implements AppStandbyInternal { private static final long ONE_HOUR = ONE_MINUTE * 60; private static final long ONE_DAY = ONE_HOUR * 24; static final long[] SCREEN_TIME_THRESHOLDS = { /** * The minimum amount of time the screen must have been on before an app can time out from its * current bucket to the next bucket. */ private static final long[] SCREEN_TIME_THRESHOLDS = { 0, 0, COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR, COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR, COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR, COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR }; static final long[] ELAPSED_TIME_THRESHOLDS = { /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */ private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME ? new long[SCREEN_TIME_THRESHOLDS.length] : new long[]{ 0, 0, 0, 30 * ONE_MINUTE, ONE_HOUR }; /** * The minimum amount of elapsed time that must have passed before an app can time out from its * current bucket to the next bucket. */ private static final long[] ELAPSED_TIME_THRESHOLDS = { 0, COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR, COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR, COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR, // TODO(149050681): increase timeout to 30+ days COMPRESS_TIME ? 32 * ONE_MINUTE : 4 * ONE_DAY }; static final int[] THRESHOLD_BUCKETS = { /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */ private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME ? new long[ELAPSED_TIME_THRESHOLDS.length] : new long[]{ 0, ONE_HOUR, ONE_HOUR, 2 * ONE_HOUR, 4 * ONE_DAY }; private static final int[] THRESHOLD_BUCKETS = { STANDBY_BUCKET_ACTIVE, STANDBY_BUCKET_WORKING_SET, STANDBY_BUCKET_FREQUENT, STANDBY_BUCKET_RARE STANDBY_BUCKET_RARE, STANDBY_BUCKET_RESTRICTED }; /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */ Loading Loading @@ -204,7 +238,15 @@ public class AppStandbyController implements AppStandbyInternal { static final int MSG_REPORT_EXEMPTED_SYNC_START = 13; long mCheckIdleIntervalMillis; /** * The minimum amount of time the screen must have been on before an app can time out from its * current bucket to the next bucket. */ long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS; /** * The minimum amount of elapsed time that must have passed before an app can time out from its * current bucket to the next bucket. */ long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS; /** Minimum time a strong usage event should keep the bucket elevated. */ long mStrongUsageTimeoutMillis; Loading Loading @@ -1147,9 +1189,11 @@ public class AppStandbyController implements AppStandbyInternal { final boolean isForcedByUser = (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER; // If the current bucket is RESTRICTED, only user force or usage should bring it out. // If the current bucket is RESTRICTED, only user force or usage should bring it out, // unless the app was put into the bucket due to timing out. if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason) && !isForcedByUser) { && !isForcedByUser && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) { return; } Loading Loading @@ -1829,12 +1873,12 @@ public class AppStandbyController implements AppStandbyInternal { String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null); mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue, SCREEN_TIME_THRESHOLDS); SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS); String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null); mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue, ELAPSED_TIME_THRESHOLDS); ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS); mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4, COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours mStrongUsageTimeoutMillis = mParser.getDurationMillis( Loading Loading @@ -1870,8 +1914,8 @@ public class AppStandbyController implements AppStandbyInternal { mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis( KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO COMPRESS_TIME ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); mSystemInteractionTimeoutMillis = mParser.getDurationMillis( KEY_SYSTEM_INTERACTION_HOLD_DURATION, Loading @@ -1888,7 +1932,7 @@ public class AppStandbyController implements AppStandbyInternal { setAppIdleEnabled(mInjector.isAppIdleEnabled()); } long[] parseLongArray(String values, long[] defaults) { long[] parseLongArray(String values, long[] defaults, long[] minValues) { if (values == null) return defaults; if (values.isEmpty()) { // Reset to defaults Loading @@ -1896,13 +1940,19 @@ public class AppStandbyController implements AppStandbyInternal { } else { String[] thresholds = values.split("/"); if (thresholds.length == THRESHOLD_BUCKETS.length) { if (minValues.length != THRESHOLD_BUCKETS.length) { Slog.wtf(TAG, "minValues array is the wrong size"); // Use zeroes as the minimums. minValues = new long[THRESHOLD_BUCKETS.length]; } long[] array = new long[THRESHOLD_BUCKETS.length]; for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) { try { if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) { array[i] = Duration.parse(thresholds[i]).toMillis(); array[i] = Math.max(minValues[i], Duration.parse(thresholds[i]).toMillis()); } else { array[i] = Long.parseLong(thresholds[i]); array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i])); } } catch (NumberFormatException|DateTimeParseException e) { return defaults; Loading services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +27 −8 Original line number Diff line number Diff line Loading @@ -115,6 +115,7 @@ public class AppStandbyControllerTests { private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS; private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS; private static final long RARE_THRESHOLD = 48 * HOUR_MS; private static final long RESTRICTED_THRESHOLD = 96 * HOUR_MS; /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */ private static boolean isPackageInstalled = true; Loading Loading @@ -232,9 +233,8 @@ public class AppStandbyControllerTests { @Override String getAppIdleSettings() { return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/" + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD; + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD + "/" + RESTRICTED_THRESHOLD; } @Override Loading Loading @@ -372,12 +372,15 @@ public class AppStandbyControllerTests { // RARE bucket assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE); // RESTRICTED bucket assertTimeout(mController, RESTRICTED_THRESHOLD + 1, STANDBY_BUCKET_RESTRICTED); reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1); assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE); // RARE bucket assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); // RESTRICTED bucket assertTimeout(mController, RESTRICTED_THRESHOLD * 2 + 2, STANDBY_BUCKET_RESTRICTED); } @Test Loading Loading @@ -437,7 +440,7 @@ public class AppStandbyControllerTests { assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); mInjector.setDisplayOn(true); assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); assertTimeout(mController, RARE_THRESHOLD + 2 * HOUR_MS + 1, STANDBY_BUCKET_RARE); } @Test Loading Loading @@ -642,7 +645,7 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_FREQUENT); // Way past prediction timeout, use system thresholds mInjector.mElapsedRealtime = RARE_THRESHOLD * 4; mInjector.mElapsedRealtime = RARE_THRESHOLD; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RARE); } Loading @@ -669,7 +672,7 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); // Way past all timeouts. Make sure timeout processing doesn't raise bucket. mInjector.mElapsedRealtime += RARE_THRESHOLD * 4; mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RESTRICTED); } Loading @@ -696,6 +699,22 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); } @Test public void testPredictionRaiseFromRestrictedTimeout() { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); // Way past all timeouts. App times out into RESTRICTED bucket. mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RESTRICTED); // Since the app timed out into RESTRICTED, prediction should be able to remove from the // bucket. mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, REASON_MAIN_PREDICTED); } @Test public void testCascadingTimeouts() throws Exception { reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); Loading Loading
apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +66 −16 Original line number Diff line number Diff line Loading @@ -136,25 +136,59 @@ public class AppStandbyController implements AppStandbyInternal { private static final long ONE_HOUR = ONE_MINUTE * 60; private static final long ONE_DAY = ONE_HOUR * 24; static final long[] SCREEN_TIME_THRESHOLDS = { /** * The minimum amount of time the screen must have been on before an app can time out from its * current bucket to the next bucket. */ private static final long[] SCREEN_TIME_THRESHOLDS = { 0, 0, COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR, COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR, COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR, COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR }; static final long[] ELAPSED_TIME_THRESHOLDS = { /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */ private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME ? new long[SCREEN_TIME_THRESHOLDS.length] : new long[]{ 0, 0, 0, 30 * ONE_MINUTE, ONE_HOUR }; /** * The minimum amount of elapsed time that must have passed before an app can time out from its * current bucket to the next bucket. */ private static final long[] ELAPSED_TIME_THRESHOLDS = { 0, COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR, COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR, COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR, // TODO(149050681): increase timeout to 30+ days COMPRESS_TIME ? 32 * ONE_MINUTE : 4 * ONE_DAY }; static final int[] THRESHOLD_BUCKETS = { /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */ private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME ? new long[ELAPSED_TIME_THRESHOLDS.length] : new long[]{ 0, ONE_HOUR, ONE_HOUR, 2 * ONE_HOUR, 4 * ONE_DAY }; private static final int[] THRESHOLD_BUCKETS = { STANDBY_BUCKET_ACTIVE, STANDBY_BUCKET_WORKING_SET, STANDBY_BUCKET_FREQUENT, STANDBY_BUCKET_RARE STANDBY_BUCKET_RARE, STANDBY_BUCKET_RESTRICTED }; /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */ Loading Loading @@ -204,7 +238,15 @@ public class AppStandbyController implements AppStandbyInternal { static final int MSG_REPORT_EXEMPTED_SYNC_START = 13; long mCheckIdleIntervalMillis; /** * The minimum amount of time the screen must have been on before an app can time out from its * current bucket to the next bucket. */ long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS; /** * The minimum amount of elapsed time that must have passed before an app can time out from its * current bucket to the next bucket. */ long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS; /** Minimum time a strong usage event should keep the bucket elevated. */ long mStrongUsageTimeoutMillis; Loading Loading @@ -1147,9 +1189,11 @@ public class AppStandbyController implements AppStandbyInternal { final boolean isForcedByUser = (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER; // If the current bucket is RESTRICTED, only user force or usage should bring it out. // If the current bucket is RESTRICTED, only user force or usage should bring it out, // unless the app was put into the bucket due to timing out. if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason) && !isForcedByUser) { && !isForcedByUser && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) { return; } Loading Loading @@ -1829,12 +1873,12 @@ public class AppStandbyController implements AppStandbyInternal { String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null); mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue, SCREEN_TIME_THRESHOLDS); SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS); String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null); mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue, ELAPSED_TIME_THRESHOLDS); ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS); mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4, COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours mStrongUsageTimeoutMillis = mParser.getDurationMillis( Loading Loading @@ -1870,8 +1914,8 @@ public class AppStandbyController implements AppStandbyInternal { mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis( KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO COMPRESS_TIME ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); mSystemInteractionTimeoutMillis = mParser.getDurationMillis( KEY_SYSTEM_INTERACTION_HOLD_DURATION, Loading @@ -1888,7 +1932,7 @@ public class AppStandbyController implements AppStandbyInternal { setAppIdleEnabled(mInjector.isAppIdleEnabled()); } long[] parseLongArray(String values, long[] defaults) { long[] parseLongArray(String values, long[] defaults, long[] minValues) { if (values == null) return defaults; if (values.isEmpty()) { // Reset to defaults Loading @@ -1896,13 +1940,19 @@ public class AppStandbyController implements AppStandbyInternal { } else { String[] thresholds = values.split("/"); if (thresholds.length == THRESHOLD_BUCKETS.length) { if (minValues.length != THRESHOLD_BUCKETS.length) { Slog.wtf(TAG, "minValues array is the wrong size"); // Use zeroes as the minimums. minValues = new long[THRESHOLD_BUCKETS.length]; } long[] array = new long[THRESHOLD_BUCKETS.length]; for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) { try { if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) { array[i] = Duration.parse(thresholds[i]).toMillis(); array[i] = Math.max(minValues[i], Duration.parse(thresholds[i]).toMillis()); } else { array[i] = Long.parseLong(thresholds[i]); array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i])); } } catch (NumberFormatException|DateTimeParseException e) { return defaults; Loading
services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +27 −8 Original line number Diff line number Diff line Loading @@ -115,6 +115,7 @@ public class AppStandbyControllerTests { private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS; private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS; private static final long RARE_THRESHOLD = 48 * HOUR_MS; private static final long RESTRICTED_THRESHOLD = 96 * HOUR_MS; /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */ private static boolean isPackageInstalled = true; Loading Loading @@ -232,9 +233,8 @@ public class AppStandbyControllerTests { @Override String getAppIdleSettings() { return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/" + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD; + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD + "/" + RESTRICTED_THRESHOLD; } @Override Loading Loading @@ -372,12 +372,15 @@ public class AppStandbyControllerTests { // RARE bucket assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE); // RESTRICTED bucket assertTimeout(mController, RESTRICTED_THRESHOLD + 1, STANDBY_BUCKET_RESTRICTED); reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1); assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE); // RARE bucket assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); // RESTRICTED bucket assertTimeout(mController, RESTRICTED_THRESHOLD * 2 + 2, STANDBY_BUCKET_RESTRICTED); } @Test Loading Loading @@ -437,7 +440,7 @@ public class AppStandbyControllerTests { assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); mInjector.setDisplayOn(true); assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); assertTimeout(mController, RARE_THRESHOLD + 2 * HOUR_MS + 1, STANDBY_BUCKET_RARE); } @Test Loading Loading @@ -642,7 +645,7 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_FREQUENT); // Way past prediction timeout, use system thresholds mInjector.mElapsedRealtime = RARE_THRESHOLD * 4; mInjector.mElapsedRealtime = RARE_THRESHOLD; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RARE); } Loading @@ -669,7 +672,7 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); // Way past all timeouts. Make sure timeout processing doesn't raise bucket. mInjector.mElapsedRealtime += RARE_THRESHOLD * 4; mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RESTRICTED); } Loading @@ -696,6 +699,22 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); } @Test public void testPredictionRaiseFromRestrictedTimeout() { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); // Way past all timeouts. App times out into RESTRICTED bucket. mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RESTRICTED); // Since the app timed out into RESTRICTED, prediction should be able to remove from the // bucket. mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, REASON_MAIN_PREDICTED); } @Test public void testCascadingTimeouts() throws Exception { reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); Loading