summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kweku Adams <kwekua@google.com> 2023-11-29 20:56:29 +0000
committer Kweku Adams <kwekua@google.com> 2023-11-29 20:56:29 +0000
commit2d0db1b3ba957b6c0ffecee7ec4fdeac6edbf1c5 (patch)
tree1c9e6c5d865b27e93d3d68439d6505c4de51536f
parent418c3175124fabeb9479b1911a65391657aff3a2 (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
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java20
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java61
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);