Merge "Adding checks for bad periodic job scheduling." into qt-dev am: d6d8091d51
am: 624208db6e
Change-Id: If37a8d0a861c7dde5f451f4e2f5330e056fedf25
This commit is contained in:
@@ -1635,6 +1635,9 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
*/
|
||||
private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
|
||||
|
||||
/** The maximum period a periodic job can have. Anything higher will be clamped down to this. */
|
||||
public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -1652,11 +1655,21 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
|
||||
final long elapsedNow = sElapsedRealtimeClock.millis();
|
||||
final long newLatestRuntimeElapsed;
|
||||
final long period = periodicToReschedule.getJob().getIntervalMillis();
|
||||
final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed();
|
||||
final long flex = periodicToReschedule.getJob().getFlexMillis();
|
||||
// Make sure period is in the interval [min_possible_period, max_possible_period].
|
||||
final long period = Math.max(JobInfo.getMinPeriodMillis(),
|
||||
Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis()));
|
||||
// Make sure flex is in the interval [min_possible_flex, period].
|
||||
final long flex = Math.max(JobInfo.getMinFlexMillis(),
|
||||
Math.min(period, periodicToReschedule.getJob().getFlexMillis()));
|
||||
long rescheduleBuffer = 0;
|
||||
|
||||
long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed();
|
||||
if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) {
|
||||
Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte);
|
||||
olrte = elapsedNow;
|
||||
}
|
||||
final long latestRunTimeElapsed = olrte;
|
||||
|
||||
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
|
||||
@@ -1664,7 +1677,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Periodic job ran after its intended window.");
|
||||
}
|
||||
int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window
|
||||
long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window
|
||||
if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
|
||||
(period - flex) / 2)) {
|
||||
if (DEBUG) {
|
||||
@@ -1684,6 +1697,16 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
}
|
||||
|
||||
if (newLatestRuntimeElapsed < elapsedNow) {
|
||||
Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: "
|
||||
+ newLatestRuntimeElapsed);
|
||||
return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
|
||||
elapsedNow + period - flex, elapsedNow + period,
|
||||
0 /* backoffAttempt */,
|
||||
sSystemClock.millis() /* lastSuccessfulRunTime */,
|
||||
periodicToReschedule.getLastFailedRunTime());
|
||||
}
|
||||
|
||||
final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
|
||||
- Math.min(flex, period - rescheduleBuffer);
|
||||
|
||||
|
||||
@@ -511,8 +511,13 @@ public final class JobStatus {
|
||||
final long elapsedNow = sElapsedRealtimeClock.millis();
|
||||
final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
|
||||
if (job.isPeriodic()) {
|
||||
latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
|
||||
earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
|
||||
// Make sure period is in the interval [min_possible_period, max_possible_period].
|
||||
final long period = Math.max(JobInfo.getMinPeriodMillis(),
|
||||
Math.min(JobSchedulerService.MAX_ALLOWED_PERIOD_MS, job.getIntervalMillis()));
|
||||
latestRunTimeElapsedMillis = elapsedNow + period;
|
||||
earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis
|
||||
// Make sure flex is in the interval [min_possible_flex, period].
|
||||
- Math.max(JobInfo.getMinFlexMillis(), Math.min(period, job.getFlexMillis()));
|
||||
} else {
|
||||
earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
|
||||
elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
|
||||
@@ -1631,6 +1636,9 @@ public final class JobStatus {
|
||||
formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis);
|
||||
pw.print(", latest=");
|
||||
formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis);
|
||||
pw.print(", original latest=");
|
||||
formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis,
|
||||
NO_LATEST_RUNTIME, elapsedRealtimeMillis);
|
||||
pw.println();
|
||||
if (numFailures != 0) {
|
||||
pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.job;
|
||||
|
||||
import static android.text.format.DateUtils.DAY_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||
|
||||
@@ -160,6 +161,56 @@ public class JobSchedulerServiceTest {
|
||||
jobInfoBuilder.build(), 1234, "com.android.test", 0, testTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
|
||||
* with the correct delay and deadline constraints if the periodic job is scheduled with the
|
||||
* minimum possible period.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRescheduleJobForPeriodic_minPeriod() {
|
||||
final long now = sElapsedRealtimeClock.millis();
|
||||
JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow",
|
||||
createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS));
|
||||
final long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS;
|
||||
final long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS;
|
||||
|
||||
for (int i = 0; i < 25; i++) {
|
||||
JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
|
||||
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
|
||||
advanceElapsedClock(30_000); // 30 seconds
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
// Window buffering in last 1/6 of window.
|
||||
JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
|
||||
assertEquals(nextWindowStartTime + i * 30_000, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
|
||||
advanceElapsedClock(30_000); // 30 seconds
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
|
||||
* with the correct delay and deadline constraints if the periodic job is scheduled with a
|
||||
* period that's too large.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRescheduleJobForPeriodic_largePeriod() {
|
||||
final long now = sElapsedRealtimeClock.millis();
|
||||
JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow",
|
||||
createJobInfo().setPeriodic(2 * 365 * DAY_IN_MILLIS));
|
||||
assertEquals(now, job.getEarliestRunTime());
|
||||
// Periods are capped at 365 days (1 year).
|
||||
assertEquals(now + 365 * DAY_IN_MILLIS, job.getLatestRunTimeElapsed());
|
||||
final long nextWindowStartTime = now + 365 * DAY_IN_MILLIS;
|
||||
final long nextWindowEndTime = nextWindowStartTime + 365 * DAY_IN_MILLIS;
|
||||
|
||||
JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
|
||||
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
|
||||
* with the correct delay and deadline constraints if the periodic job is completed and
|
||||
|
||||
Reference in New Issue
Block a user