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
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user