diff options
8 files changed, 242 insertions, 68 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index d28ebde9cb23..e8ce5b041aaf 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -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(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index d6456f0edea5..9e3f19de56f1 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -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()); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index 22b09683187f..df8f729b7ec8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -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); 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 547f94badd69..0eb93367e5e2 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 @@ -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 diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index de602a8f7f25..f9fb0d0df002 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -241,10 +241,22 @@ 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); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index 1753fc7a61e4..fc737d06059d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -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(); 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 674e500d2e41..3bee6871b4b9 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 @@ -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()); } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 149ae0b81740..7f522b0a1af5 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -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()); } /** |