Loading services/core/java/com/android/server/job/JobSchedulerService.java +20 −3 Original line number Diff line number Diff line Loading @@ -1625,6 +1625,15 @@ public class JobSchedulerService extends com.android.server.SystemService return newJob; } /** * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This * does not cause a job's period to be larger than requested (eg: if the requested period is * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene * and try to optimize scheduling if the current job finished less than this amount of time to * the start of the next period */ private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS; /** * Called after a periodic has executed so we can reschedule it. We take the last execution * time of the job to be the time of completion (i.e. the time at which this function is Loading @@ -1645,16 +1654,18 @@ public class JobSchedulerService extends com.android.server.SystemService final long period = periodicToReschedule.getJob().getIntervalMillis(); final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed(); final long flex = periodicToReschedule.getJob().getFlexMillis(); long rescheduleBuffer = 0; final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed); if (elapsedNow > latestRunTimeElapsed) { // 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."); } final long diffMs = (elapsedNow - latestRunTimeElapsed); int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window if (period != flex && diffMs > Math.min(30 * MINUTE_IN_MILLIS, (period - flex) / 2)) { if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER, (period - flex) / 2)) { if (DEBUG) { Slog.d(TAG, "Custom flex job ran too close to next window."); } Loading @@ -1665,9 +1676,15 @@ public class JobSchedulerService extends com.android.server.SystemService newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows); } else { newLatestRuntimeElapsed = latestRunTimeElapsed + period; if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) { // Add a little buffer to the start of the next window so the job doesn't run // too soon after this completed one. rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs); } } final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - Math.min(flex, period - rescheduleBuffer); if (DEBUG) { Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + Loading services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +180 −3 Original line number Diff line number Diff line Loading @@ -183,15 +183,188 @@ public class JobSchedulerServiceTest { assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(45 * MINUTE_IN_MILLIS); // now + 55 minutes advanceElapsedClock(20 * MINUTE_IN_MILLIS); // now + 30 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(25 * MINUTE_IN_MILLIS); // now + 55 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(job); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(job); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } /** * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job * with an extra delay and correct deadline constraint if the periodic job is completed near the * end of its expected running window. */ @Test public void testGetRescheduleJobForPeriodic_closeToEndOfWindow() { JobStatus frequentJob = createJobStatus( "testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS)); long now = sElapsedRealtimeClock.millis(); long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS; long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS; // At the beginning of the window. Next window should be unaffected. JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock((long) (7.5 * MINUTE_IN_MILLIS)); rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); assertEquals(nextWindowStartTime + MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); JobStatus mediumJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(HOUR_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + HOUR_IN_MILLIS; nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(30 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(20 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime + (6 * MINUTE_IN_MILLIS), rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); JobStatus longJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(6 * HOUR_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + 6 * HOUR_IN_MILLIS; nextWindowEndTime = now + 12 * HOUR_IN_MILLIS; // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(3 * HOUR_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(2 * HOUR_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window should be unaffected since we're over the shift cap. advanceElapsedClock(15 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(30 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime + (30 * MINUTE_IN_MILLIS), rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Flex duration close to period duration. JobStatus gameyFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(HOUR_IN_MILLIS, 59 * MINUTE_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS; nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; advanceElapsedClock(MINUTE_IN_MILLIS); // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(29 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(20 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime + (5 * MINUTE_IN_MILLIS), rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Very short flex duration compared to period duration. JobStatus superFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(HOUR_IN_MILLIS, 10 * MINUTE_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + HOUR_IN_MILLIS + 50 * MINUTE_IN_MILLIS; nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; advanceElapsedClock(MINUTE_IN_MILLIS); // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(29 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(20 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window should be unaffected since the flex duration pushes // the next window start time far enough away. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } Loading Loading @@ -265,7 +438,9 @@ public class JobSchedulerServiceTest { advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes Loading @@ -273,7 +448,9 @@ public class JobSchedulerServiceTest { advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } Loading Loading
services/core/java/com/android/server/job/JobSchedulerService.java +20 −3 Original line number Diff line number Diff line Loading @@ -1625,6 +1625,15 @@ public class JobSchedulerService extends com.android.server.SystemService return newJob; } /** * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This * does not cause a job's period to be larger than requested (eg: if the requested period is * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene * and try to optimize scheduling if the current job finished less than this amount of time to * the start of the next period */ private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS; /** * Called after a periodic has executed so we can reschedule it. We take the last execution * time of the job to be the time of completion (i.e. the time at which this function is Loading @@ -1645,16 +1654,18 @@ public class JobSchedulerService extends com.android.server.SystemService final long period = periodicToReschedule.getJob().getIntervalMillis(); final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed(); final long flex = periodicToReschedule.getJob().getFlexMillis(); long rescheduleBuffer = 0; final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed); if (elapsedNow > latestRunTimeElapsed) { // 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."); } final long diffMs = (elapsedNow - latestRunTimeElapsed); int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window if (period != flex && diffMs > Math.min(30 * MINUTE_IN_MILLIS, (period - flex) / 2)) { if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER, (period - flex) / 2)) { if (DEBUG) { Slog.d(TAG, "Custom flex job ran too close to next window."); } Loading @@ -1665,9 +1676,15 @@ public class JobSchedulerService extends com.android.server.SystemService newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows); } else { newLatestRuntimeElapsed = latestRunTimeElapsed + period; if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) { // Add a little buffer to the start of the next window so the job doesn't run // too soon after this completed one. rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs); } } final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - Math.min(flex, period - rescheduleBuffer); if (DEBUG) { Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + Loading
services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +180 −3 Original line number Diff line number Diff line Loading @@ -183,15 +183,188 @@ public class JobSchedulerServiceTest { assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(45 * MINUTE_IN_MILLIS); // now + 55 minutes advanceElapsedClock(20 * MINUTE_IN_MILLIS); // now + 30 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(job); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(25 * MINUTE_IN_MILLIS); // now + 55 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(job); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(job); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } /** * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job * with an extra delay and correct deadline constraint if the periodic job is completed near the * end of its expected running window. */ @Test public void testGetRescheduleJobForPeriodic_closeToEndOfWindow() { JobStatus frequentJob = createJobStatus( "testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS)); long now = sElapsedRealtimeClock.millis(); long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS; long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS; // At the beginning of the window. Next window should be unaffected. JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock((long) (7.5 * MINUTE_IN_MILLIS)); rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); assertEquals(nextWindowStartTime + MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); JobStatus mediumJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(HOUR_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + HOUR_IN_MILLIS; nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(30 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(20 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); assertEquals(nextWindowStartTime + (6 * MINUTE_IN_MILLIS), rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); JobStatus longJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(6 * HOUR_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + 6 * HOUR_IN_MILLIS; nextWindowEndTime = now + 12 * HOUR_IN_MILLIS; // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(3 * HOUR_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(2 * HOUR_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window should be unaffected since we're over the shift cap. advanceElapsedClock(15 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(30 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); assertEquals(nextWindowStartTime + (30 * MINUTE_IN_MILLIS), rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Flex duration close to period duration. JobStatus gameyFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(HOUR_IN_MILLIS, 59 * MINUTE_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS; nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; advanceElapsedClock(MINUTE_IN_MILLIS); // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(29 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(20 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window start time should be shifted slightly. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); assertEquals(nextWindowStartTime + (5 * MINUTE_IN_MILLIS), rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Very short flex duration compared to period duration. JobStatus superFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", createJobInfo().setPeriodic(HOUR_IN_MILLIS, 10 * MINUTE_IN_MILLIS)); now = sElapsedRealtimeClock.millis(); nextWindowStartTime = now + HOUR_IN_MILLIS + 50 * MINUTE_IN_MILLIS; nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; advanceElapsedClock(MINUTE_IN_MILLIS); // At the beginning of the window. Next window should be unaffected. rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // Halfway through window. Next window should be unaffected. advanceElapsedClock(29 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // At the edge 1/6 of window. Next window should be unaffected. advanceElapsedClock(20 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); // In last 1/6 of window. Next window should be unaffected since the flex duration pushes // the next window start time far enough away. advanceElapsedClock(6 * MINUTE_IN_MILLIS); rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } Loading Loading @@ -265,7 +438,9 @@ public class JobSchedulerServiceTest { advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes Loading @@ -273,7 +448,9 @@ public class JobSchedulerServiceTest { advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); // Shifted because it's close to the end of the window. assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, rescheduledJob.getEarliestRunTime()); assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); } Loading