From 4297b5da1cf23c71bd902ed106b8398066beb914 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Thu, 6 Feb 2020 15:56:12 -0800 Subject: [PATCH] Let apps timeout into the RESTRICTED bucket. Let apps that haven't been used in 4 days time out into the RESTRICTED bucket (after being in the RARE bucket). Apps that timed out into the RESTRICTED bucket can be pulled out of the bucket via prediction. Bug: 145551233 Test: atest FrameworksServicesTests:AppStandbyControllerTests Change-Id: I646b5c4e3d3b45bd0ba8404b5a0c3d26186737c0 --- .../server/usage/AppStandbyController.java | 82 +++++++++++++++---- .../usage/AppStandbyControllerTests.java | 35 ++++++-- 2 files changed, 93 insertions(+), 24 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index b1b8fba78ab9d..6e05bc0cf4d96 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -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. */ @@ -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; @@ -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; } @@ -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( @@ -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, @@ -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 @@ -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; diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 03dc21370e24d..290683ddb7a13 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -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; @@ -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 @@ -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 @@ -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 @@ -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); } @@ -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); } @@ -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);