Merge "Rework backoff policy calculation."
This commit is contained in:
committed by
Android (Google) Code Review
commit
8c75791bb4
@@ -432,6 +432,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
break;
|
||||
case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS:
|
||||
case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS:
|
||||
case Constants.KEY_SYSTEM_STOP_TO_FAILURE_RATIO:
|
||||
mConstants.updateBackoffConstantsLocked();
|
||||
break;
|
||||
case Constants.KEY_CONN_CONGESTION_DELAY_FRAC:
|
||||
@@ -509,6 +510,8 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
|
||||
private static final String KEY_MIN_LINEAR_BACKOFF_TIME_MS = "min_linear_backoff_time_ms";
|
||||
private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms";
|
||||
private static final String KEY_SYSTEM_STOP_TO_FAILURE_RATIO =
|
||||
"system_stop_to_failure_ratio";
|
||||
private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
|
||||
private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
|
||||
private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH =
|
||||
@@ -540,6 +543,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
|
||||
private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
|
||||
private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
|
||||
private static final int DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO = 3;
|
||||
private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
|
||||
private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
|
||||
private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true;
|
||||
@@ -589,6 +593,11 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
* The minimum backoff time to allow for exponential backoff.
|
||||
*/
|
||||
long MIN_EXP_BACKOFF_TIME_MS = DEFAULT_MIN_EXP_BACKOFF_TIME_MS;
|
||||
/**
|
||||
* The ratio to use to convert number of times a job was stopped by JobScheduler to an
|
||||
* incremental failure in the backoff policy calculation.
|
||||
*/
|
||||
int SYSTEM_STOP_TO_FAILURE_RATIO = DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO;
|
||||
|
||||
/**
|
||||
* The fraction of a job's running window that must pass before we
|
||||
@@ -700,6 +709,9 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
MIN_EXP_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
|
||||
KEY_MIN_EXP_BACKOFF_TIME_MS,
|
||||
DEFAULT_MIN_EXP_BACKOFF_TIME_MS);
|
||||
SYSTEM_STOP_TO_FAILURE_RATIO = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
|
||||
KEY_SYSTEM_STOP_TO_FAILURE_RATIO,
|
||||
DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO);
|
||||
}
|
||||
|
||||
private void updateConnectivityConstantsLocked() {
|
||||
@@ -797,6 +809,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
|
||||
pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println();
|
||||
pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println();
|
||||
pw.print(KEY_SYSTEM_STOP_TO_FAILURE_RATIO, SYSTEM_STOP_TO_FAILURE_RATIO).println();
|
||||
pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
|
||||
pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
|
||||
pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println();
|
||||
@@ -1277,7 +1290,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
jobStatus.getJob().isPrefetch(),
|
||||
jobStatus.getJob().getPriority(),
|
||||
jobStatus.getEffectivePriority(),
|
||||
jobStatus.getNumFailures());
|
||||
jobStatus.getNumPreviousAttempts());
|
||||
|
||||
// If the job is immediately ready to run, then we can just immediately
|
||||
// put it in the pending list and try to schedule it. This is especially
|
||||
@@ -1476,7 +1489,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
cancelled.getJob().isPrefetch(),
|
||||
cancelled.getJob().getPriority(),
|
||||
cancelled.getEffectivePriority(),
|
||||
cancelled.getNumFailures());
|
||||
cancelled.getNumPreviousAttempts());
|
||||
}
|
||||
// If this is a replacement, bring in the new version of the job
|
||||
if (incomingJob != null) {
|
||||
@@ -1903,7 +1916,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
* Reschedules the given job based on the job's backoff policy. It doesn't make sense to
|
||||
* specify an override deadline on a failed job (the failed job will run even though it's not
|
||||
* ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
|
||||
* ready job with {@link JobStatus#getNumFailures()} > 0 will be executed.
|
||||
* ready job with {@link JobStatus#getNumPreviousAttempts()} > 0 will be executed.
|
||||
*
|
||||
* @param failureToReschedule Provided job status that we will reschedule.
|
||||
* @return A newly instantiated JobStatus with the same constraints as the last job except
|
||||
@@ -1911,12 +1924,24 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
* @see #maybeQueueReadyJobsForExecutionLocked
|
||||
*/
|
||||
@VisibleForTesting
|
||||
JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
|
||||
JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule,
|
||||
int internalStopReason) {
|
||||
final long elapsedNowMillis = sElapsedRealtimeClock.millis();
|
||||
final JobInfo job = failureToReschedule.getJob();
|
||||
|
||||
final long initialBackoffMillis = job.getInitialBackoffMillis();
|
||||
final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
|
||||
int numFailures = failureToReschedule.getNumFailures();
|
||||
int numSystemStops = failureToReschedule.getNumSystemStops();
|
||||
// We should back off slowly if JobScheduler keeps stopping the job,
|
||||
// but back off immediately if the issue appeared to be the app's fault.
|
||||
if (internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH
|
||||
|| internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT) {
|
||||
numFailures++;
|
||||
} else {
|
||||
numSystemStops++;
|
||||
}
|
||||
final int backoffAttempts = Math.max(1,
|
||||
numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO);
|
||||
long delayMillis;
|
||||
|
||||
switch (job.getBackoffPolicy()) {
|
||||
@@ -1943,7 +1968,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
|
||||
JobStatus newJob = new JobStatus(failureToReschedule,
|
||||
elapsedNowMillis + delayMillis,
|
||||
JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
|
||||
JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops,
|
||||
failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
|
||||
if (job.isPeriodic()) {
|
||||
newJob.setOriginalLatestRunTimeElapsed(
|
||||
@@ -2034,7 +2059,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
+ newLatestRuntimeElapsed);
|
||||
return new JobStatus(periodicToReschedule,
|
||||
elapsedNow + period - flex, elapsedNow + period,
|
||||
0 /* backoffAttempt */,
|
||||
0 /* numFailures */, 0 /* numSystemStops */,
|
||||
sSystemClock.millis() /* lastSuccessfulRunTime */,
|
||||
periodicToReschedule.getLastFailedRunTime());
|
||||
}
|
||||
@@ -2049,7 +2074,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
return new JobStatus(periodicToReschedule,
|
||||
newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
|
||||
0 /* backoffAttempt */,
|
||||
0 /* numFailures */, 0 /* numSystemStops */,
|
||||
sSystemClock.millis() /* lastSuccessfulRunTime */,
|
||||
periodicToReschedule.getLastFailedRunTime());
|
||||
}
|
||||
@@ -2093,7 +2118,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
// job so we can transfer any appropriate state over from the previous job when
|
||||
// we stop it.
|
||||
final JobStatus rescheduledJob = needsReschedule
|
||||
? getRescheduleJobForFailureLocked(jobStatus) : null;
|
||||
? getRescheduleJobForFailureLocked(jobStatus, debugStopReason) : null;
|
||||
if (rescheduledJob != null
|
||||
&& (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
|
||||
|| debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) {
|
||||
@@ -2427,7 +2452,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
shouldForceBatchJob =
|
||||
mPrefetchController.getNextEstimatedLaunchTimeLocked(job)
|
||||
> relativelySoonCutoffTime;
|
||||
} else if (job.getNumFailures() > 0) {
|
||||
} else if (job.getNumPreviousAttempts() > 0) {
|
||||
shouldForceBatchJob = false;
|
||||
} else {
|
||||
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||
|
||||
@@ -363,7 +363,7 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
job.getJob().isPrefetch(),
|
||||
job.getJob().getPriority(),
|
||||
job.getEffectivePriority(),
|
||||
job.getNumFailures());
|
||||
job.getNumPreviousAttempts());
|
||||
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
|
||||
// Use the context's ID to distinguish traces since there'll only be one job
|
||||
// running per context.
|
||||
@@ -1032,7 +1032,7 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
completedJob.getJob().isPrefetch(),
|
||||
completedJob.getJob().getPriority(),
|
||||
completedJob.getEffectivePriority(),
|
||||
completedJob.getNumFailures());
|
||||
completedJob.getNumPreviousAttempts());
|
||||
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
|
||||
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
|
||||
completedJob.getTag(), getId());
|
||||
|
||||
@@ -194,7 +194,7 @@ public final class JobStore {
|
||||
convertRtcBoundsToElapsed(utcTimes, elapsedNow);
|
||||
JobStatus newJob = new JobStatus(job,
|
||||
elapsedRuntimes.first, elapsedRuntimes.second,
|
||||
0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime());
|
||||
0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime());
|
||||
newJob.prepareLocked();
|
||||
toAdd.add(newJob);
|
||||
toRemove.add(job);
|
||||
|
||||
@@ -342,10 +342,10 @@ public final class FlexibilityController extends StateController {
|
||||
// There is no deadline and no estimated launch time.
|
||||
return NO_LIFECYCLE_END;
|
||||
}
|
||||
if (js.getNumFailures() > 1) {
|
||||
// Number of failures will not equal one as per restriction in JobStatus constructor.
|
||||
// Increase the flex deadline for jobs rescheduled more than once.
|
||||
if (js.getNumPreviousAttempts() > 1) {
|
||||
return earliest + Math.min(
|
||||
(long) Math.scalb(mRescheduledJobDeadline, js.getNumFailures() - 2),
|
||||
(long) Math.scalb(mRescheduledJobDeadline, js.getNumPreviousAttempts() - 2),
|
||||
mMaxRescheduledDeadline);
|
||||
}
|
||||
return js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
|
||||
|
||||
@@ -241,9 +241,21 @@ public final class JobStatus {
|
||||
*/
|
||||
private long mOriginalLatestRunTimeElapsedMillis;
|
||||
|
||||
/** How many times this job has failed, used to compute back-off. */
|
||||
/**
|
||||
* How many times this job has failed to complete on its own
|
||||
* (via {@link android.app.job.JobService#jobFinished(JobParameters, boolean)} or because of
|
||||
* a timeout).
|
||||
* This count doesn't include most times JobScheduler decided to stop the job
|
||||
* (via {@link android.app.job.JobService#onStopJob(JobParameters)}.
|
||||
*/
|
||||
private final int numFailures;
|
||||
|
||||
/**
|
||||
* The number of times JobScheduler has forced this job to stop due to reasons mostly outside
|
||||
* of the app's control.
|
||||
*/
|
||||
private final int mNumSystemStops;
|
||||
|
||||
/**
|
||||
* Which app standby bucket this job's app is in. Updated when the app is moved to a
|
||||
* different bucket.
|
||||
@@ -488,6 +500,8 @@ public final class JobStatus {
|
||||
* @param tag A string associated with the job for debugging/logging purposes.
|
||||
* @param numFailures Count of how many times this job has requested a reschedule because
|
||||
* its work was not yet finished.
|
||||
* @param numSystemStops Count of how many times JobScheduler has forced this job to stop due to
|
||||
* factors mostly out of the app's control.
|
||||
* @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job
|
||||
* is to be considered runnable
|
||||
* @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be
|
||||
@@ -497,7 +511,7 @@ public final class JobStatus {
|
||||
* @param internalFlags Non-API property flags about this job
|
||||
*/
|
||||
private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
|
||||
int sourceUserId, int standbyBucket, String tag, int numFailures,
|
||||
int sourceUserId, int standbyBucket, String tag, int numFailures, int numSystemStops,
|
||||
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
|
||||
long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags,
|
||||
int dynamicConstraints) {
|
||||
@@ -535,6 +549,7 @@ public final class JobStatus {
|
||||
this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
|
||||
this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
|
||||
this.numFailures = numFailures;
|
||||
mNumSystemStops = numSystemStops;
|
||||
|
||||
int requiredConstraints = job.getConstraintFlags();
|
||||
if (job.getRequiredNetwork() != null) {
|
||||
@@ -576,7 +591,7 @@ public final class JobStatus {
|
||||
// Otherwise, every consecutive reschedule increases a jobs' flexibility deadline.
|
||||
if (!isRequestedExpeditedJob()
|
||||
&& satisfiesMinWindowException
|
||||
&& numFailures != 1
|
||||
&& (numFailures + numSystemStops) != 1
|
||||
&& lacksSomeFlexibleConstraints) {
|
||||
mNumRequiredFlexibleConstraints =
|
||||
NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
|
||||
@@ -626,7 +641,7 @@ public final class JobStatus {
|
||||
this(jobStatus.getJob(), jobStatus.getUid(),
|
||||
jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
|
||||
jobStatus.getStandbyBucket(),
|
||||
jobStatus.getSourceTag(), jobStatus.getNumFailures(),
|
||||
jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(),
|
||||
jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
|
||||
jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
|
||||
jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints);
|
||||
@@ -654,7 +669,7 @@ public final class JobStatus {
|
||||
int innerFlags, int dynamicConstraints) {
|
||||
this(job, callingUid, sourcePkgName, sourceUserId,
|
||||
standbyBucket,
|
||||
sourceTag, 0,
|
||||
sourceTag, /* numFailures */ 0, /* numSystemStops */ 0,
|
||||
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
|
||||
lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints);
|
||||
|
||||
@@ -673,12 +688,13 @@ public final class JobStatus {
|
||||
/** Create a new job to be rescheduled with the provided parameters. */
|
||||
public JobStatus(JobStatus rescheduling,
|
||||
long newEarliestRuntimeElapsedMillis,
|
||||
long newLatestRuntimeElapsedMillis, int backoffAttempt,
|
||||
long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops,
|
||||
long lastSuccessfulRunTime, long lastFailedRunTime) {
|
||||
this(rescheduling.job, rescheduling.getUid(),
|
||||
rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
|
||||
rescheduling.getStandbyBucket(),
|
||||
rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
|
||||
rescheduling.getSourceTag(), numFailures, numSystemStops,
|
||||
newEarliestRuntimeElapsedMillis,
|
||||
newLatestRuntimeElapsedMillis,
|
||||
lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(),
|
||||
rescheduling.mDynamicConstraints);
|
||||
@@ -715,7 +731,7 @@ public final class JobStatus {
|
||||
int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
|
||||
sourceUserId, elapsedNow);
|
||||
return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
|
||||
standbyBucket, tag, 0,
|
||||
standbyBucket, tag, /* numFailures */ 0, /* numSystemStops */ 0,
|
||||
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
|
||||
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
|
||||
/*innerFlags=*/ 0, /* dynamicConstraints */ 0);
|
||||
@@ -868,10 +884,27 @@ public final class JobStatus {
|
||||
pw.print(job.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of times the job stopped previously for reasons that appeared to be within
|
||||
* the app's control.
|
||||
*/
|
||||
public int getNumFailures() {
|
||||
return numFailures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of times the system stopped a previous execution of this job for reasons
|
||||
* that were likely outside the app's control.
|
||||
*/
|
||||
public int getNumSystemStops() {
|
||||
return mNumSystemStops;
|
||||
}
|
||||
|
||||
/** Returns the total number of times we've attempted to run this job in the past. */
|
||||
public int getNumPreviousAttempts() {
|
||||
return numFailures + mNumSystemStops;
|
||||
}
|
||||
|
||||
public ComponentName getServiceComponent() {
|
||||
return job.getService();
|
||||
}
|
||||
@@ -1857,6 +1890,10 @@ public final class JobStatus {
|
||||
sb.append(" failures=");
|
||||
sb.append(numFailures);
|
||||
}
|
||||
if (mNumSystemStops != 0) {
|
||||
sb.append(" system stops=");
|
||||
sb.append(mNumSystemStops);
|
||||
}
|
||||
if (isReady()) {
|
||||
sb.append(" READY");
|
||||
} else {
|
||||
@@ -2382,6 +2419,9 @@ public final class JobStatus {
|
||||
if (numFailures != 0) {
|
||||
pw.print("Num failures: "); pw.println(numFailures);
|
||||
}
|
||||
if (mNumSystemStops != 0) {
|
||||
pw.print("Num system stops: "); pw.println(mNumSystemStops);
|
||||
}
|
||||
if (mLastSuccessfulRunTime != 0) {
|
||||
pw.print("Last successful run: ");
|
||||
pw.println(formatTime(mLastSuccessfulRunTime));
|
||||
@@ -2579,7 +2619,7 @@ public final class JobStatus {
|
||||
proto.write(JobStatusDumpProto.ORIGINAL_LATEST_RUNTIME_ELAPSED,
|
||||
mOriginalLatestRunTimeElapsedMillis);
|
||||
|
||||
proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures);
|
||||
proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures + mNumSystemStops);
|
||||
proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime);
|
||||
proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.app.ActivityManagerInternal;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.UiModeManager;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.ComponentName;
|
||||
@@ -235,6 +236,59 @@ public class JobSchedulerServiceTest {
|
||||
mService.getMinJobExecutionGuaranteeMs(jobDef));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Confirm that {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int)}
|
||||
* returns a job with the correct delay and deadline constraints.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRescheduleJobForFailure() {
|
||||
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||
final long initialBackoffMs = MINUTE_IN_MILLIS;
|
||||
mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3;
|
||||
|
||||
JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure",
|
||||
createJobInfo()
|
||||
.setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR));
|
||||
assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime());
|
||||
assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed());
|
||||
|
||||
// failure = 0, systemStop = 1
|
||||
JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob,
|
||||
JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
|
||||
assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
|
||||
|
||||
// failure = 0, systemStop = 2
|
||||
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
|
||||
JobParameters.INTERNAL_STOP_REASON_PREEMPT);
|
||||
// failure = 0, systemStop = 3
|
||||
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
|
||||
JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED);
|
||||
assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
|
||||
|
||||
// failure = 0, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
|
||||
for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) {
|
||||
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
|
||||
JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED);
|
||||
}
|
||||
assertEquals(nowElapsed + 2 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
|
||||
|
||||
// failure = 1, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
|
||||
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
|
||||
JobParameters.INTERNAL_STOP_REASON_TIMEOUT);
|
||||
assertEquals(nowElapsed + 3 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
|
||||
|
||||
// failure = 2, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO
|
||||
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
assertEquals(nowElapsed + 4 * initialBackoffMs, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
|
||||
* with the correct delay and deadline constraints if the periodic job is scheduled with the
|
||||
@@ -544,14 +598,16 @@ public class JobSchedulerServiceTest {
|
||||
final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
|
||||
JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob",
|
||||
createJobInfo().setPeriodic(HOUR_IN_MILLIS));
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
|
||||
JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
|
||||
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
|
||||
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
|
||||
|
||||
advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes
|
||||
failedJob = mService.getRescheduleJobForFailureLocked(job);
|
||||
failedJob = mService.getRescheduleJobForFailureLocked(job,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes
|
||||
|
||||
rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
|
||||
@@ -559,7 +615,8 @@ public class JobSchedulerServiceTest {
|
||||
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
|
||||
|
||||
advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes
|
||||
failedJob = mService.getRescheduleJobForFailureLocked(job);
|
||||
failedJob = mService.getRescheduleJobForFailureLocked(job,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes
|
||||
|
||||
rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
|
||||
@@ -569,7 +626,8 @@ public class JobSchedulerServiceTest {
|
||||
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
|
||||
|
||||
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes
|
||||
failedJob = mService.getRescheduleJobForFailureLocked(job);
|
||||
failedJob = mService.getRescheduleJobForFailureLocked(job,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes
|
||||
|
||||
rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
|
||||
@@ -665,7 +723,8 @@ public class JobSchedulerServiceTest {
|
||||
public void testGetRescheduleJobForPeriodic_outsideWindow_failedJob() {
|
||||
JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob",
|
||||
createJobInfo().setPeriodic(HOUR_IN_MILLIS));
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
long now = sElapsedRealtimeClock.millis();
|
||||
long nextWindowStartTime = now + HOUR_IN_MILLIS;
|
||||
long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
|
||||
@@ -701,7 +760,8 @@ public class JobSchedulerServiceTest {
|
||||
JobStatus job = createJobStatus(
|
||||
"testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob",
|
||||
createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
// First window starts 30 minutes from now.
|
||||
advanceElapsedClock(30 * MINUTE_IN_MILLIS);
|
||||
long now = sElapsedRealtimeClock.millis();
|
||||
@@ -742,7 +802,8 @@ public class JobSchedulerServiceTest {
|
||||
JobStatus job = createJobStatus(
|
||||
"testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod",
|
||||
createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS));
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
|
||||
JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job,
|
||||
JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
|
||||
// First window starts 6.625 days from now.
|
||||
advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS);
|
||||
long now = sElapsedRealtimeClock.millis();
|
||||
|
||||
@@ -489,19 +489,22 @@ public class FlexibilityControllerTest {
|
||||
JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L);
|
||||
JobStatus js = createJobStatus("time", jb);
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, FROZEN_TIME, FROZEN_TIME);
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 0,
|
||||
FROZEN_TIME, FROZEN_TIME);
|
||||
|
||||
assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
|
||||
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
|
||||
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 3, FROZEN_TIME, FROZEN_TIME);
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 1,
|
||||
FROZEN_TIME, FROZEN_TIME);
|
||||
|
||||
assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
|
||||
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
|
||||
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 10, FROZEN_TIME, FROZEN_TIME);
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 10,
|
||||
FROZEN_TIME, FROZEN_TIME);
|
||||
assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS,
|
||||
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
|
||||
}
|
||||
@@ -637,7 +640,12 @@ public class FlexibilityControllerTest {
|
||||
JobInfo.Builder jb = createJob(0);
|
||||
JobStatus js = createJobStatus("time", jb);
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, FROZEN_TIME, FROZEN_TIME);
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, /* numSystemStops */ 0,
|
||||
FROZEN_TIME, FROZEN_TIME);
|
||||
assertFalse(js.hasFlexibilityConstraint());
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 1,
|
||||
FROZEN_TIME, FROZEN_TIME);
|
||||
assertFalse(js.hasFlexibilityConstraint());
|
||||
}
|
||||
|
||||
|
||||
@@ -248,20 +248,32 @@ public class JobStatusTest {
|
||||
|
||||
// Less than 2 failures, priority shouldn't be affected.
|
||||
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
|
||||
int backoffAttempt = 1;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
int numFailures = 1;
|
||||
int numSystemStops = 0;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
|
||||
|
||||
// 2+ failures, priority should be lowered as much as possible.
|
||||
backoffAttempt = 2;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 2;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
|
||||
backoffAttempt = 5;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 5;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
|
||||
backoffAttempt = 8;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 8;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
|
||||
|
||||
// System stops shouldn't factor in the downgrade.
|
||||
numSystemStops = 10;
|
||||
numFailures = 0;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -274,33 +286,48 @@ public class JobStatusTest {
|
||||
|
||||
// Less than 2 failures, priority shouldn't be affected.
|
||||
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
|
||||
int backoffAttempt = 1;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
int numFailures = 1;
|
||||
int numSystemStops = 0;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
|
||||
|
||||
// Failures in [2,4), priority should be lowered slightly.
|
||||
backoffAttempt = 2;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 2;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
|
||||
backoffAttempt = 3;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 3;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
|
||||
|
||||
// Failures in [4,6), priority should be lowered more.
|
||||
backoffAttempt = 4;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 4;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
|
||||
backoffAttempt = 5;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 5;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
|
||||
|
||||
// 6+ failures, priority should be lowered as much as possible.
|
||||
backoffAttempt = 6;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 6;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
|
||||
backoffAttempt = 12;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 12;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
|
||||
|
||||
// System stops shouldn't factor in the downgrade.
|
||||
numSystemStops = 10;
|
||||
numFailures = 0;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,23 +344,36 @@ public class JobStatusTest {
|
||||
|
||||
// Less than 6 failures, priority shouldn't be affected.
|
||||
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
|
||||
int backoffAttempt = 1;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
int numFailures = 1;
|
||||
int numSystemStops = 0;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
|
||||
backoffAttempt = 4;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 4;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
|
||||
backoffAttempt = 5;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 5;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
|
||||
|
||||
// 6+ failures, priority should be lowered as much as possible.
|
||||
backoffAttempt = 6;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 6;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
|
||||
backoffAttempt = 12;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
|
||||
numFailures = 12;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
|
||||
|
||||
// System stops shouldn't factor in the downgrade.
|
||||
numSystemStops = 10;
|
||||
numFailures = 0;
|
||||
job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures,
|
||||
numSystemStops, 0, 0);
|
||||
assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user