Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +6 −3 Original line number Diff line number Diff line Loading @@ -1895,11 +1895,14 @@ public class JobSchedulerService extends com.android.server.SystemService // The job ran past its expected run window. Have it count towards the current window // and schedule a new job for the next window. if (DEBUG) { Slog.i(TAG, "Periodic job ran after its intended window."); Slog.i(TAG, "Periodic job ran after its intended window by " + diffMs + " ms"); } long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER, (period - flex) / 2)) { // Determine how far into a single period the job ran, and determine if it's too close // to the start of the next period. If the difference between the start of the execution // window and the previous execution time inside of the period is less than the // threshold, then we say that the job ran too close to the next period. if (period != flex && (period - flex - (diffMs % period)) <= flex / 6) { if (DEBUG) { Slog.d(TAG, "Custom flex job ran too close to next window."); } Loading services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +95 −0 Original line number Diff line number Diff line Loading @@ -737,6 +737,101 @@ public class JobSchedulerServiceTest { assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } @Test public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod() { JobStatus job = createJobStatus( "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod", createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS)); JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job); // First window starts 6.625 days from now. advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS); long now = sElapsedRealtimeClock.millis(); long nextWindowStartTime = now + 7 * DAY_IN_MILLIS; long nextWindowEndTime = nextWindowStartTime + 9 * HOUR_IN_MILLIS; advanceElapsedClock(6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS); // Say the job ran at the very end of its previous window. The intended JSS behavior is to // have consistent windows, so the new window should start as soon as the previous window // ended and end PERIOD time after the previous window ended. JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(DAY_IN_MILLIS); // Say the job ran a day late. Since the period is massive compared to the flex, JSS should // put the rescheduled job in the original window. rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 1 day before the start of the next window. Given the large period, respect the original // next window. advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 1 hour before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. long oneHourBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; long fiveMinsBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; advanceElapsedClock(oneHourBeforeNextWindow); nextWindowStartTime += 7 * DAY_IN_MILLIS; nextWindowEndTime += 7 * DAY_IN_MILLIS; rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 5 minutes before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. advanceElapsedClock(fiveMinsBeforeNextWindow); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(14 * DAY_IN_MILLIS); // Say that the job ran at this point, probably because the phone was off the entire time. // The next window should be consistent (start and end at the time it would have had the job // run normally in previous windows). nextWindowStartTime += 14 * DAY_IN_MILLIS; nextWindowEndTime += 14 * DAY_IN_MILLIS; rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Test original job again but with a huge delay from the original execution window // 1 day before the start of the next window. Given the large period, respect the original // next window. advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 1 hour before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. oneHourBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; fiveMinsBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; advanceElapsedClock(oneHourBeforeNextWindow); nextWindowStartTime += 7 * DAY_IN_MILLIS; nextWindowEndTime += 7 * DAY_IN_MILLIS; rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 5 minutes before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. advanceElapsedClock(fiveMinsBeforeNextWindow); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } /** Tests that rare job batching works as expected. */ @Test public void testRareJobBatching() { Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +6 −3 Original line number Diff line number Diff line Loading @@ -1895,11 +1895,14 @@ public class JobSchedulerService extends com.android.server.SystemService // The job ran past its expected run window. Have it count towards the current window // and schedule a new job for the next window. if (DEBUG) { Slog.i(TAG, "Periodic job ran after its intended window."); Slog.i(TAG, "Periodic job ran after its intended window by " + diffMs + " ms"); } long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER, (period - flex) / 2)) { // Determine how far into a single period the job ran, and determine if it's too close // to the start of the next period. If the difference between the start of the execution // window and the previous execution time inside of the period is less than the // threshold, then we say that the job ran too close to the next period. if (period != flex && (period - flex - (diffMs % period)) <= flex / 6) { if (DEBUG) { Slog.d(TAG, "Custom flex job ran too close to next window."); } Loading
services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +95 −0 Original line number Diff line number Diff line Loading @@ -737,6 +737,101 @@ public class JobSchedulerServiceTest { assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } @Test public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod() { JobStatus job = createJobStatus( "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod", createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS)); JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job); // First window starts 6.625 days from now. advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS); long now = sElapsedRealtimeClock.millis(); long nextWindowStartTime = now + 7 * DAY_IN_MILLIS; long nextWindowEndTime = nextWindowStartTime + 9 * HOUR_IN_MILLIS; advanceElapsedClock(6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS); // Say the job ran at the very end of its previous window. The intended JSS behavior is to // have consistent windows, so the new window should start as soon as the previous window // ended and end PERIOD time after the previous window ended. JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(DAY_IN_MILLIS); // Say the job ran a day late. Since the period is massive compared to the flex, JSS should // put the rescheduled job in the original window. rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 1 day before the start of the next window. Given the large period, respect the original // next window. advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 1 hour before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. long oneHourBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; long fiveMinsBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; advanceElapsedClock(oneHourBeforeNextWindow); nextWindowStartTime += 7 * DAY_IN_MILLIS; nextWindowEndTime += 7 * DAY_IN_MILLIS; rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 5 minutes before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. advanceElapsedClock(fiveMinsBeforeNextWindow); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(14 * DAY_IN_MILLIS); // Say that the job ran at this point, probably because the phone was off the entire time. // The next window should be consistent (start and end at the time it would have had the job // run normally in previous windows). nextWindowStartTime += 14 * DAY_IN_MILLIS; nextWindowEndTime += 14 * DAY_IN_MILLIS; rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Test original job again but with a huge delay from the original execution window // 1 day before the start of the next window. Given the large period, respect the original // next window. advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 1 hour before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. oneHourBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; fiveMinsBeforeNextWindow = nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; advanceElapsedClock(oneHourBeforeNextWindow); nextWindowStartTime += 7 * DAY_IN_MILLIS; nextWindowEndTime += 7 * DAY_IN_MILLIS; rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // 5 minutes before the start of the next window. It's too close to the next window, so the // returned job should be for the window after. advanceElapsedClock(fiveMinsBeforeNextWindow); rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } /** Tests that rare job batching works as expected. */ @Test public void testRareJobBatching() { Loading