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); +    }  }  |