diff options
2 files changed, 88 insertions, 3 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java index 428f2cbaefc2..0f385efae5cc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java @@ -17,6 +17,7 @@ package com.android.server.job.controllers; import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; @@ -90,6 +91,14 @@ public class PrefetchController extends StateController { @CurrentTimeMillisLong private long mLaunchTimeThresholdMs = PcConstants.DEFAULT_LAUNCH_TIME_THRESHOLD_MS; + /** + * The additional time we'll add to a launch time estimate before considering it obsolete and + * try to get a new estimate. This will help make prefetch jobs more viable in case an estimate + * is a few minutes early. + */ + @GuardedBy("mLock") + private long mLaunchTimeAllowanceMs = PcConstants.DEFAULT_LAUNCH_TIME_ALLOWANCE_MS; + @SuppressWarnings("FieldCanBeLocal") private final EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener = new EstimatedLaunchTimeChangedListener() { @@ -204,7 +213,8 @@ public class PrefetchController extends StateController { private long getNextEstimatedLaunchTimeLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { final Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); - if (nextEstimatedLaunchTime == null || nextEstimatedLaunchTime < now) { + if (nextEstimatedLaunchTime == null + || nextEstimatedLaunchTime < now - mLaunchTimeAllowanceMs) { // Don't query usage stats here because it may have to read from disk. mHandler.obtainMessage(MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME, userId, 0, pkgName) .sendToTarget(); @@ -335,7 +345,9 @@ public class PrefetchController extends StateController { } final long nextEstimatedLaunchTime = getNextEstimatedLaunchTimeLocked(userId, pkgName, now); - if (nextEstimatedLaunchTime - now > mLaunchTimeThresholdMs) { + // Avoid setting an alarm for the end of time. + if (nextEstimatedLaunchTime != Long.MAX_VALUE + && nextEstimatedLaunchTime - now > mLaunchTimeThresholdMs) { // Set alarm to be notified when this crosses the threshold. final long timeToCrossThresholdMs = nextEstimatedLaunchTime - (now + mLaunchTimeThresholdMs); @@ -354,7 +366,7 @@ public class PrefetchController extends StateController { private boolean willBeLaunchedSoonLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { return getNextEstimatedLaunchTimeLocked(userId, pkgName, now) - <= now + mLaunchTimeThresholdMs; + <= now + mLaunchTimeThresholdMs - mLaunchTimeAllowanceMs; } @Override @@ -494,16 +506,37 @@ public class PrefetchController extends StateController { @VisibleForTesting static final String KEY_LAUNCH_TIME_THRESHOLD_MS = PC_CONSTANT_PREFIX + "launch_time_threshold_ms"; + @VisibleForTesting + static final String KEY_LAUNCH_TIME_ALLOWANCE_MS = + PC_CONSTANT_PREFIX + "launch_time_allowance_ms"; private static final long DEFAULT_LAUNCH_TIME_THRESHOLD_MS = 7 * HOUR_IN_MILLIS; + private static final long DEFAULT_LAUNCH_TIME_ALLOWANCE_MS = 20 * MINUTE_IN_MILLIS; /** How much time each app will have to run jobs within their standby bucket window. */ public long LAUNCH_TIME_THRESHOLD_MS = DEFAULT_LAUNCH_TIME_THRESHOLD_MS; + /** + * How much additional time to add to an estimated launch time before considering it + * unusable. + */ + public long LAUNCH_TIME_ALLOWANCE_MS = DEFAULT_LAUNCH_TIME_ALLOWANCE_MS; + @GuardedBy("mLock") public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { switch (key) { + case KEY_LAUNCH_TIME_ALLOWANCE_MS: + LAUNCH_TIME_ALLOWANCE_MS = + properties.getLong(key, DEFAULT_LAUNCH_TIME_ALLOWANCE_MS); + // Limit the allowance to the range [0 minutes, 2 hours]. + long newLaunchTimeAllowanceMs = Math.min(2 * HOUR_IN_MILLIS, + Math.max(0, LAUNCH_TIME_ALLOWANCE_MS)); + if (mLaunchTimeAllowanceMs != newLaunchTimeAllowanceMs) { + mLaunchTimeAllowanceMs = newLaunchTimeAllowanceMs; + mShouldReevaluateConstraints = true; + } + break; case KEY_LAUNCH_TIME_THRESHOLD_MS: LAUNCH_TIME_THRESHOLD_MS = properties.getLong(key, DEFAULT_LAUNCH_TIME_THRESHOLD_MS); @@ -528,6 +561,7 @@ public class PrefetchController extends StateController { pw.increaseIndent(); pw.print(KEY_LAUNCH_TIME_THRESHOLD_MS, LAUNCH_TIME_THRESHOLD_MS).println(); + pw.print(KEY_LAUNCH_TIME_ALLOWANCE_MS, LAUNCH_TIME_ALLOWANCE_MS).println(); pw.decreaseIndent(); } @@ -536,6 +570,11 @@ public class PrefetchController extends StateController { //////////////////////// TESTING HELPERS ///////////////////////////// @VisibleForTesting + long getLaunchTimeAllowanceMs() { + return mLaunchTimeAllowanceMs; + } + + @VisibleForTesting long getLaunchTimeThresholdMs() { return mLaunchTimeThresholdMs; } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java index 8a954caad939..5f9f1b226958 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java @@ -233,27 +233,34 @@ public class PrefetchControllerTest { @Test public void testConstantsUpdating_ValidValues() { setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 5 * HOUR_IN_MILLIS); + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 5 * MINUTE_IN_MILLIS); assertEquals(5 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs()); + assertEquals(5 * MINUTE_IN_MILLIS, mPrefetchController.getLaunchTimeAllowanceMs()); } @Test public void testConstantsUpdating_InvalidValues() { // Test negatives/too low. setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 4 * MINUTE_IN_MILLIS); + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, -MINUTE_IN_MILLIS); assertEquals(HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs()); + assertEquals(0, mPrefetchController.getLaunchTimeAllowanceMs()); // Test larger than a day. Controller should cap at one day. setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 5 * HOUR_IN_MILLIS); assertEquals(24 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs()); + assertEquals(2 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeAllowanceMs()); } @Test public void testConstantsUpdating_ThresholdChangesAlarms() { final long launchDelayMs = 11 * HOUR_IN_MILLIS; setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); when(mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(sSystemClock.millis() + launchDelayMs); @@ -276,6 +283,7 @@ public class PrefetchControllerTest { @Test public void testConstraintNotSatisfiedWhenLaunchLate() { + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); final JobStatus job = createJobStatus("testConstraintNotSatisfiedWhenLaunchLate", 1); @@ -290,6 +298,8 @@ public class PrefetchControllerTest { @Test public void testConstraintSatisfiedWhenLaunchSoon() { + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); + final JobStatus job = createJobStatus("testConstraintSatisfiedWhenLaunchSoon", 2); when(mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) @@ -338,6 +348,8 @@ public class PrefetchControllerTest { @Test public void testConstraintSatisfiedWhenWidget() { + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); + final JobStatus jobNonWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 1); final JobStatus jobWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 2); @@ -365,6 +377,7 @@ public class PrefetchControllerTest { @Test public void testEstimatedLaunchTimeChangedToLate() { setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); when(mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(sSystemClock.millis() + HOUR_IN_MILLIS); @@ -393,6 +406,7 @@ public class PrefetchControllerTest { @Test public void testEstimatedLaunchTimeChangedToSoon() { setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); when(mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS); @@ -413,4 +427,36 @@ public class PrefetchControllerTest { verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any()); assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); } + + @Test + public void testEstimatedLaunchTimeAllowance() { + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); + setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 15 * MINUTE_IN_MILLIS); + when(mUsageStatsManagerInternal + .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) + .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS); + + InOrder inOrder = inOrder(mUsageStatsManagerInternal); + + JobStatus jobStatus = createJobStatus("testEstimatedLaunchTimeAllowance", 1); + trackJobs(jobStatus); + inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) + .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); + // The allowance shouldn't shift the alarm + verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1)) + .setWindow( + anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS), + anyLong(), eq(TAG_PREFETCH), any(), any()); + assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); + + mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID, + SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS); + + inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0)) + .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); + verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any()); + assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); + + sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS); + } } |