Loading apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +17 −3 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -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 Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +61 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -421,6 +422,66 @@ public class FlexibilityControllerTest { assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); } @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 Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +17 −3 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -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 Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +61 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -421,6 +422,66 @@ public class FlexibilityControllerTest { assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); } @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 Loading