Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c75791b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Rework backoff policy calculation."

parents c91eb23d 976d0a98
Loading
Loading
Loading
Loading
+35 −10
Original line number Diff line number Diff line
@@ -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();
+2 −2
Original line number Diff line number Diff line
@@ -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());
+1 −1
Original line number Diff line number Diff line
@@ -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);
+3 −3
Original line number Diff line number Diff line
@@ -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
+49 −9
Original line number Diff line number Diff line
@@ -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);

Loading