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

Commit bb5e3b5f authored by Kweku Adams's avatar Kweku Adams
Browse files

Reducing overhead of job constraint evaluation.

1. TimeController was checking its expired deadline and delay jobs even
when the job in question already had its constraint satisfied.
2. In the worst case, TimeController.evaluateStateLocked would check all
N jobs even though the job in question is the only one that matters in
that case. Adding the wouldBeReadyWithConstraint check in
evaluateStateLocked reduces worst case runtime by a factor of N.
3. With the introduction of job alarm skipping, TimeController would
sometimes schedule an alarm with AlarmManager for the exact same time it
had the alarm set for. Adding a check removes that unnecessary call.

Bug: 123756120
Bug: 123879683
Test: atest com.android.server.job.controllers.JobStatusTest
Test: atest com.android.server.job.controllers.TimeControllerTest
Test: atest FrameworksMockingServicesTests
Test: atest JobThrottlingTest
Test: atest TimingConstraintsTest
Change-Id: Iedf7873398a6938f6dd4fed5e92ee2e8d2c211bb
parent fce528d4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -509,7 +509,7 @@ public class JobSchedulerService extends com.android.server.SystemService
        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_USE_HEARTBEATS = false;
        private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
        private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
        private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
                10 * 60 * 1000L; // 10 minutes
        private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
+1 −0
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ public abstract class StateController {
        final boolean jobWouldBeReady = jobStatus.wouldBeReadyWithConstraint(constraint);
        if (DEBUG) {
            Slog.v(TAG, "wouldBeReadyWithConstraintLocked: " + jobStatus.toShortString()
                    + " constraint=" + constraint
                    + " readyWithConstraint=" + jobWouldBeReady);
        }
        if (!jobWouldBeReady) {
+47 −12
Original line number Diff line number Diff line
@@ -149,8 +149,8 @@ public final class TimeController extends StateController {

    @Override
    public void onConstantsUpdatedLocked() {
        checkExpiredDelaysAndResetAlarm();
        checkExpiredDeadlinesAndResetAlarm();
        checkExpiredDelaysAndResetAlarm();
    }

    @Override
@@ -159,20 +159,47 @@ public final class TimeController extends StateController {
            return;
        }

        final long nowElapsedMillis = sElapsedRealtimeClock.millis();

        // Check deadline constraint first because if it's satisfied, we avoid a little bit of
        // unnecessary processing of the timing delay.
        if (job.hasDeadlineConstraint()
                && !job.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE)
                && job.getLatestRunTimeElapsed() <= mNextJobExpiredElapsedMillis) {
            if (evaluateDeadlineConstraint(job, nowElapsedMillis)) {
                checkExpiredDeadlinesAndResetAlarm();
                checkExpiredDelaysAndResetAlarm();
            } else {
                final boolean isAlarmForJob =
                        job.getLatestRunTimeElapsed() == mNextJobExpiredElapsedMillis;
                final boolean wouldBeReady = wouldBeReadyWithConstraintLocked(
                        job, JobStatus.CONSTRAINT_DEADLINE);
                if ((isAlarmForJob && !wouldBeReady) || (!isAlarmForJob && wouldBeReady)) {
                    checkExpiredDeadlinesAndResetAlarm();
                }
            }
        }
        if (job.hasTimingDelayConstraint()
                && !job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)
                && job.getEarliestRunTime() <= mNextDelayExpiredElapsedMillis) {
            if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) {
                checkExpiredDelaysAndResetAlarm();
            } else {
                final boolean isAlarmForJob =
                        job.getEarliestRunTime() == mNextDelayExpiredElapsedMillis;
                final boolean wouldBeReady = wouldBeReadyWithConstraintLocked(
                        job, JobStatus.CONSTRAINT_TIMING_DELAY);
                if ((isAlarmForJob && !wouldBeReady) || (!isAlarmForJob && wouldBeReady)) {
                    checkExpiredDelaysAndResetAlarm();
                }
        if (job.hasDeadlineConstraint()
                && job.getLatestRunTimeElapsed() <= mNextJobExpiredElapsedMillis) {
            checkExpiredDeadlinesAndResetAlarm();
            }
        }
    }

    @Override
    public void reevaluateStateLocked(int uid) {
        checkExpiredDelaysAndResetAlarm();
        checkExpiredDeadlinesAndResetAlarm();
        checkExpiredDelaysAndResetAlarm();
    }

    /**
@@ -182,10 +209,10 @@ public final class TimeController extends StateController {
     * back and forth.
     */
    private boolean canStopTrackingJobLocked(JobStatus job) {
        return (!job.hasTimingDelayConstraint() ||
                (job.satisfiedConstraints&JobStatus.CONSTRAINT_TIMING_DELAY) != 0) &&
                (!job.hasDeadlineConstraint() ||
                        (job.satisfiedConstraints&JobStatus.CONSTRAINT_DEADLINE) != 0);
        return (!job.hasTimingDelayConstraint()
                        || job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY))
                && (!job.hasDeadlineConstraint()
                        || job.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE));
    }

    private void ensureAlarmServiceLocked() {
@@ -241,6 +268,7 @@ public final class TimeController extends StateController {
        }
    }

    /** @return true if the job's deadline constraint is satisfied */
    private boolean evaluateDeadlineConstraint(JobStatus job, long nowElapsedMillis) {
        final long jobDeadline = job.getLatestRunTimeElapsed();

@@ -279,7 +307,7 @@ public final class TimeController extends StateController {
                    if (job.isReady()) {
                        ready = true;
                    }
                } else if (!job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)) {
                } else {
                    if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS
                            && !wouldBeReadyWithConstraintLocked(
                            job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
@@ -319,6 +347,7 @@ public final class TimeController extends StateController {
        }
    }

    /** @return true if the job's delay constraint is satisfied */
    private boolean evaluateTimingDelayConstraint(JobStatus job, long nowElapsedMillis) {
        final long jobDelayTime = job.getEarliestRunTime();
        if (jobDelayTime <= nowElapsedMillis) {
@@ -347,6 +376,9 @@ public final class TimeController extends StateController {
     */
    private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
        alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
        if (mNextDelayExpiredElapsedMillis == alarmTimeElapsedMillis) {
            return;
        }
        mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
        updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
                mNextDelayExpiredElapsedMillis, ws);
@@ -359,6 +391,9 @@ public final class TimeController extends StateController {
     */
    private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
        alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
        if (mNextJobExpiredElapsedMillis == alarmTimeElapsedMillis) {
            return;
        }
        mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
        updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
                mNextJobExpiredElapsedMillis, ws);