diff options
| author | 2023-11-29 20:56:29 +0000 | |
|---|---|---|
| committer | 2023-11-29 20:56:29 +0000 | |
| commit | 2d0db1b3ba957b6c0ffecee7ec4fdeac6edbf1c5 (patch) | |
| tree | 1c9e6c5d865b27e93d3d68439d6505c4de51536f | |
| parent | 418c3175124fabeb9479b1911a65391657aff3a2 (diff) | |
Avoid doubly penalizing rescheduled periodic jobs for flex.
Periodic jobs that were completed successfully and rescheduled sometimes
have adjusted start times to avoid running them back to back. When this
happens, make sure the flex logic takes the adjustment into account and
doesn't delay the constraint drops too significantly.
Bug: 236261941
Test: atest FrameworksMockingServicesTests:FlexibilityControllerTest
Change-Id: I039f86a409754089c7592719e0fd3355d6883cfb
2 files changed, 78 insertions, 3 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java index fed3c42ab87f..a900d162ab96 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java @@ -369,8 +369,23 @@ public final class FlexibilityController extends StateController { @VisibleForTesting @GuardedBy("mLock") long getLifeCycleBeginningElapsedLocked(JobStatus js) { + long earliestRuntime = js.getEarliestRunTime() == JobStatus.NO_EARLIEST_RUNTIME + ? js.enqueueTime : js.getEarliestRunTime(); + if (js.getJob().isPeriodic() && js.getNumPreviousAttempts() == 0) { + // Rescheduling periodic jobs (after a successful execution) may result in the job's + // start time being a little after the "true" periodic start time (to avoid jobs + // running back to back). See JobSchedulerService#getRescheduleJobForPeriodic for more + // details. Since rescheduled periodic jobs may already be delayed slightly by this + // policy, don't penalize them further by then enforcing the full set of applied + // flex constraints at the beginning of the newly determined start time. Let the flex + // constraint requirement start closer to the true periodic start time. + final long truePeriodicStartTimeElapsed = + js.getLatestRunTimeElapsed() - js.getJob().getFlexMillis(); + // For now, treat the lifecycle beginning as the midpoint between the true periodic + // start time and the adjusted start time. + earliestRuntime = (earliestRuntime + truePeriodicStartTimeElapsed) / 2; + } if (js.getJob().isPrefetch()) { - final long earliestRuntime = Math.max(js.enqueueTime, js.getEarliestRunTime()); final long estimatedLaunchTime = mPrefetchController.getNextEstimatedLaunchTimeLocked(js); long prefetchWindowStart = mPrefetchLifeCycleStart.getOrDefault( @@ -381,8 +396,7 @@ public final class FlexibilityController extends StateController { } return Math.max(prefetchWindowStart, earliestRuntime); } - return js.getEarliestRunTime() == JobStatus.NO_EARLIEST_RUNTIME - ? js.enqueueTime : js.getEarliestRunTime(); + return earliestRuntime; } @VisibleForTesting diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java index 0659f7e9a064..debc69604690 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java @@ -23,6 +23,7 @@ import static android.app.job.JobInfo.NETWORK_TYPE_CELLULAR; import static android.app.job.JobInfo.NETWORK_TYPE_NONE; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -422,6 +423,66 @@ public class FlexibilityControllerTest { } @Test + public void testGetLifeCycleBeginningElapsedLocked_Periodic() { + // Periodic with lifecycle + JobInfo.Builder jbBasic = createJob(0).setPeriodic(HOUR_IN_MILLIS); + JobInfo.Builder jbFlex = createJob(0) + .setPeriodic(HOUR_IN_MILLIS, 20 * MINUTE_IN_MILLIS); + JobStatus jsBasic = + createJobStatus("testGetLifeCycleBeginningElapsedLocked_Periodic", jbBasic); + JobStatus jsFlex = + createJobStatus("testGetLifeCycleBeginningElapsedLocked_Periodic", jbFlex); + + final long nowElapsed = JobSchedulerService.sElapsedRealtimeClock.millis(); + // Base case, no start adjustment + assertEquals(nowElapsed, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsBasic)); + assertEquals(nowElapsed + 40 * MINUTE_IN_MILLIS, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsFlex)); + + // Rescheduled with start adjustment + final long adjustmentMs = 4 * MINUTE_IN_MILLIS; + jsBasic = new JobStatus(jsBasic, + // "True" start is nowElapsed + HOUR_IN_MILLIS + nowElapsed + HOUR_IN_MILLIS + adjustmentMs, + nowElapsed + 2 * HOUR_IN_MILLIS, + 0 /* numFailures */, 0 /* numSystemStops */, + JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, + 0, 0); + jsFlex = new JobStatus(jsFlex, + // "True" start is nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + adjustmentMs, + nowElapsed + 2 * HOUR_IN_MILLIS, + 0 /* numFailures */, 0 /* numSystemStops */, + JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, + 0, 0); + + assertEquals(nowElapsed + HOUR_IN_MILLIS + adjustmentMs / 2, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsBasic)); + assertEquals(nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + adjustmentMs / 2, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsFlex)); + + // Rescheduled for failure + jsBasic = new JobStatus(jsBasic, + nowElapsed + 30 * MINUTE_IN_MILLIS, + NO_LATEST_RUNTIME, + 1 /* numFailures */, 1 /* numSystemStops */, + JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, + 0, 0); + jsFlex = new JobStatus(jsFlex, + nowElapsed + 30 * MINUTE_IN_MILLIS, + NO_LATEST_RUNTIME, + 1 /* numFailures */, 1 /* numSystemStops */, + JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, + 0, 0); + + assertEquals(nowElapsed + 30 * MINUTE_IN_MILLIS, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsBasic)); + assertEquals(nowElapsed + 30 * MINUTE_IN_MILLIS, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsFlex)); + } + + @Test public void testGetLifeCycleBeginningElapsedLocked_Prefetch() { // prefetch with lifecycle when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(700L); |