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

Commit 985612bb authored by Kweku Adams's avatar Kweku Adams
Browse files

Rework expedited status.

Change the OK for a job to be treated as expedited from a "constraint"
to a boolean. This will make it a little nicer to add different ways of
approving a job to be treated as expedited.

Bug: 158300259
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/job
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/job
Test: atest CtsJobSchedulerTestCases
Change-Id: Id1c85b0bbdd7201ab325b0ef71f764c578869fd7
parent b448a897
Loading
Loading
Loading
Loading
+33 −35
Original line number Original line Diff line number Diff line
@@ -92,7 +92,6 @@ public final class JobStatus {
    static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
    static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
    static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
    static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
    static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
    static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
    static final int CONSTRAINT_WITHIN_EXPEDITED_QUOTA = 1 << 23;    // Implicit constraint
    static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
    static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint


    // The following set of dynamic constraints are for specific use cases (as explained in their
    // The following set of dynamic constraints are for specific use cases (as explained in their
@@ -148,8 +147,7 @@ public final class JobStatus {
            | CONSTRAINT_DEADLINE
            | CONSTRAINT_DEADLINE
            | CONSTRAINT_IDLE
            | CONSTRAINT_IDLE
            | CONSTRAINT_TIMING_DELAY
            | CONSTRAINT_TIMING_DELAY
            | CONSTRAINT_WITHIN_QUOTA
            | CONSTRAINT_WITHIN_QUOTA;
            | CONSTRAINT_WITHIN_EXPEDITED_QUOTA;


    // TODO(b/129954980)
    // TODO(b/129954980)
    private static final boolean STATS_LOG_ENABLED = false;
    private static final boolean STATS_LOG_ENABLED = false;
@@ -393,6 +391,11 @@ public final class JobStatus {
    private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
    private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
    private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
    private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;


    /**
     * Whether or not this job is approved to be treated as expedited per quota policy.
     */
    private boolean mExpeditedQuotaApproved;

    /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
    /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
    /////// states change.
    /////// states change.


@@ -418,9 +421,6 @@ public final class JobStatus {
    /** The job is within its quota based on its standby bucket. */
    /** The job is within its quota based on its standby bucket. */
    private boolean mReadyWithinQuota;
    private boolean mReadyWithinQuota;


    /** The job is an expedited job with sufficient quota to run as an expedited job. */
    private boolean mReadyWithinExpeditedQuota;

    /** The job's dynamic requirements have been satisfied. */
    /** The job's dynamic requirements have been satisfied. */
    private boolean mReadyDynamicSatisfied;
    private boolean mReadyDynamicSatisfied;


@@ -1132,7 +1132,7 @@ public final class JobStatus {
     * treated as an expedited job.
     * treated as an expedited job.
     */
     */
    public boolean shouldTreatAsExpeditedJob() {
    public boolean shouldTreatAsExpeditedJob() {
        return mReadyWithinExpeditedQuota && isRequestedExpeditedJob();
        return mExpeditedQuotaApproved && isRequestedExpeditedJob();
    }
    }


    /**
    /**
@@ -1230,19 +1230,27 @@ public final class JobStatus {
        return false;
        return false;
    }
    }


    /** @return true if the constraint was changed, false otherwise. */
    /**
    boolean setExpeditedJobQuotaConstraintSatisfied(final long nowElapsed, boolean state) {
     * Sets whether or not this job is approved to be treated as an expedited job based on quota
        if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, nowElapsed, state)) {
     * policy.
            // The constraint was changed. Update the ready flag.
     *
            mReadyWithinExpeditedQuota = state;
     * @return true if the approval bit was changed, false otherwise.
     */
    boolean setExpeditedJobQuotaApproved(final long nowElapsed, boolean state) {
        if (mExpeditedQuotaApproved == state) {
            return false;
        }
        mExpeditedQuotaApproved = state;
        updateExpeditedDependencies();
        return true;
    }

    private void updateExpeditedDependencies() {
        // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
        // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
        // Making it also track requested-expedited jobs would add unnecessary hops since the
        // Making it also track requested-expedited jobs would add unnecessary hops since the
        // controller would then defer to canRunInDoze. Avoid the hops and just update
        // controller would then defer to canRunInDoze. Avoid the hops and just update
        // mReadyNotDozing directly.
        // mReadyNotDozing directly.
        mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze();
        mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze();
            return true;
        }
        return false;
    }
    }


    /** @return true if the state was changed, false otherwise. */
    /** @return true if the state was changed, false otherwise. */
@@ -1346,7 +1354,6 @@ public final class JobStatus {
                return JobParameters.STOP_REASON_DEVICE_STATE;
                return JobParameters.STOP_REASON_DEVICE_STATE;


            case CONSTRAINT_WITHIN_QUOTA:
            case CONSTRAINT_WITHIN_QUOTA:
            case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
                return JobParameters.STOP_REASON_QUOTA;
                return JobParameters.STOP_REASON_QUOTA;


            // These should never be stop reasons since they can never go from true to false.
            // These should never be stop reasons since they can never go from true to false.
@@ -1363,6 +1370,10 @@ public final class JobStatus {
        return (satisfiedConstraints&constraint) != 0;
        return (satisfiedConstraints&constraint) != 0;
    }
    }


    boolean isExpeditedQuotaApproved() {
        return mExpeditedQuotaApproved;
    }

    boolean clearTrackingController(int which) {
    boolean clearTrackingController(int which) {
        if ((trackingControllers&which) != 0) {
        if ((trackingControllers&which) != 0) {
            trackingControllers &= ~which;
            trackingControllers &= ~which;
@@ -1465,10 +1476,6 @@ public final class JobStatus {
                oldValue = mReadyWithinQuota;
                oldValue = mReadyWithinQuota;
                mReadyWithinQuota = value;
                mReadyWithinQuota = value;
                break;
                break;
            case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
                oldValue = mReadyWithinExpeditedQuota;
                mReadyWithinExpeditedQuota = value;
                break;
            default:
            default:
                if (value) {
                if (value) {
                    satisfied |= constraint;
                    satisfied |= constraint;
@@ -1496,9 +1503,6 @@ public final class JobStatus {
            case CONSTRAINT_WITHIN_QUOTA:
            case CONSTRAINT_WITHIN_QUOTA:
                mReadyWithinQuota = oldValue;
                mReadyWithinQuota = oldValue;
                break;
                break;
            case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
                mReadyWithinExpeditedQuota = oldValue;
                break;
            default:
            default:
                mReadyDynamicSatisfied = mDynamicConstraints != 0
                mReadyDynamicSatisfied = mDynamicConstraints != 0
                        && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
                        && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
@@ -1726,9 +1730,6 @@ public final class JobStatus {
        if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
        if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
            pw.print(" WITHIN_QUOTA");
            pw.print(" WITHIN_QUOTA");
        }
        }
        if ((constraints & CONSTRAINT_WITHIN_EXPEDITED_QUOTA) != 0) {
            pw.print(" WITHIN_EXPEDITED_QUOTA");
        }
        if (constraints != 0) {
        if (constraints != 0) {
            pw.print(" [0x");
            pw.print(" [0x");
            pw.print(Integer.toHexString(constraints));
            pw.print(Integer.toHexString(constraints));
@@ -1801,9 +1802,6 @@ public final class JobStatus {
        if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
        if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
        }
        }
        if ((constraints & CONSTRAINT_WITHIN_EXPEDITED_QUOTA) != 0) {
            proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_EXPEDITED_JOB_QUOTA);
        }
    }
    }


    private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) {
    private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) {
@@ -2050,8 +2048,8 @@ public final class JobStatus {
        pw.print("readyComponentEnabled: ");
        pw.print("readyComponentEnabled: ");
        pw.println(serviceInfo != null);
        pw.println(serviceInfo != null);
        if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) {
        if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) {
            pw.print("readyWithinExpeditedQuota: ");
            pw.print("expeditedQuotaApproved: ");
            pw.print(mReadyWithinExpeditedQuota);
            pw.print(mExpeditedQuotaApproved);
            pw.print(" (started as EJ: ");
            pw.print(" (started as EJ: ");
            pw.print(startedAsExpeditedJob);
            pw.print(startedAsExpeditedJob);
            pw.println(")");
            pw.println(")");
+24 −23
Original line number Original line Diff line number Diff line
@@ -634,11 +634,12 @@ public final class QuotaController extends StateController {
        jobs.add(jobStatus);
        jobs.add(jobStatus);
        jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
        jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
        final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
        final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
        setConstraintSatisfied(jobStatus, nowElapsed, isWithinQuota);
        final boolean isWithinEJQuota =
                jobStatus.isRequestedExpeditedJob() && isWithinEJQuotaLocked(jobStatus);
        setConstraintSatisfied(jobStatus, nowElapsed, isWithinQuota || isWithinEJQuota);
        final boolean outOfEJQuota;
        final boolean outOfEJQuota;
        if (jobStatus.isRequestedExpeditedJob()) {
        if (jobStatus.isRequestedExpeditedJob()) {
            final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
            setExpeditedQuotaApproved(jobStatus, nowElapsed, isWithinEJQuota);
            setExpeditedConstraintSatisfied(jobStatus, nowElapsed, isWithinEJQuota);
            outOfEJQuota = !isWithinEJQuota;
            outOfEJQuota = !isWithinEJQuota;
        } else {
        } else {
            outOfEJQuota = false;
            outOfEJQuota = false;
@@ -1614,6 +1615,8 @@ public final class QuotaController extends StateController {
        boolean changed = false;
        boolean changed = false;
        for (int i = jobs.size() - 1; i >= 0; --i) {
        for (int i = jobs.size() - 1; i >= 0; --i) {
            final JobStatus js = jobs.valueAt(i);
            final JobStatus js = jobs.valueAt(i);
            final boolean isWithinEJQuota =
                    js.isRequestedExpeditedJob() && isWithinEJQuotaLocked(js);
            if (isTopStartedJobLocked(js)) {
            if (isTopStartedJobLocked(js)) {
                // Job was started while the app was in the TOP state so we should allow it to
                // Job was started while the app was in the TOP state so we should allow it to
                // finish.
                // finish.
@@ -1623,15 +1626,15 @@ public final class QuotaController extends StateController {
                // An app in the ACTIVE bucket may be out of quota while the job could be in quota
                // An app in the ACTIVE bucket may be out of quota while the job could be in quota
                // for some reason. Therefore, avoid setting the real value here and check each job
                // for some reason. Therefore, avoid setting the real value here and check each job
                // individually.
                // individually.
                changed |= setConstraintSatisfied(js, nowElapsed, realInQuota);
                changed |= setConstraintSatisfied(js, nowElapsed, isWithinEJQuota || realInQuota);
            } else {
            } else {
                // This job is somehow exempted. Need to determine its own quota status.
                // This job is somehow exempted. Need to determine its own quota status.
                changed |= setConstraintSatisfied(js, nowElapsed, isWithinQuotaLocked(js));
                changed |= setConstraintSatisfied(js, nowElapsed,
                        isWithinEJQuota || isWithinQuotaLocked(js));
            }
            }


            if (js.isRequestedExpeditedJob()) {
            if (js.isRequestedExpeditedJob()) {
                boolean isWithinEJQuota = isWithinEJQuotaLocked(js);
                changed |= setExpeditedQuotaApproved(js, nowElapsed, isWithinEJQuota);
                changed |= setExpeditedConstraintSatisfied(js, nowElapsed, isWithinEJQuota);
                outOfEJQuota |= !isWithinEJQuota;
                outOfEJQuota |= !isWithinEJQuota;
            }
            }
        }
        }
@@ -1659,26 +1662,24 @@ public final class QuotaController extends StateController {


        @Override
        @Override
        public void accept(JobStatus jobStatus) {
        public void accept(JobStatus jobStatus) {
            if (setConstraintSatisfied(
            final boolean isWithinEJQuota;
                    jobStatus, mUpdateTimeElapsed, isWithinQuotaLocked(jobStatus))) {
                changedJobs.add(jobStatus);
            }
            final boolean outOfEJQuota;
            if (jobStatus.isRequestedExpeditedJob()) {
            if (jobStatus.isRequestedExpeditedJob()) {
                final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
                isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
                if (setExpeditedConstraintSatisfied(
            } else {
                        jobStatus, mUpdateTimeElapsed, isWithinEJQuota)) {
                isWithinEJQuota = false;
            }
            if (setConstraintSatisfied(jobStatus, mUpdateTimeElapsed,
                    isWithinEJQuota || isWithinQuotaLocked(jobStatus))) {
                changedJobs.add(jobStatus);
                changedJobs.add(jobStatus);
            }
            }
                outOfEJQuota = !isWithinEJQuota;
            if (setExpeditedQuotaApproved(jobStatus, mUpdateTimeElapsed, isWithinEJQuota)) {
            } else {
                changedJobs.add(jobStatus);
                outOfEJQuota = false;
            }
            }


            final int userId = jobStatus.getSourceUserId();
            final int userId = jobStatus.getSourceUserId();
            final String packageName = jobStatus.getSourcePackageName();
            final String packageName = jobStatus.getSourcePackageName();
            final int realStandbyBucket = jobStatus.getStandbyBucket();
            final int realStandbyBucket = jobStatus.getStandbyBucket();
            if (isWithinQuotaLocked(userId, packageName, realStandbyBucket) && !outOfEJQuota) {
            if (isWithinQuotaLocked(userId, packageName, realStandbyBucket) && isWithinEJQuota) {
                // TODO(141645789): we probably shouldn't cancel the alarm until we've verified
                // TODO(141645789): we probably shouldn't cancel the alarm until we've verified
                // that all jobs for the userId-package are within quota.
                // that all jobs for the userId-package are within quota.
                mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
                mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
@@ -1827,9 +1828,9 @@ public final class QuotaController extends StateController {
     * If the satisfaction changes, this will tell connectivity & background jobs controller to
     * If the satisfaction changes, this will tell connectivity & background jobs controller to
     * also re-evaluate their state.
     * also re-evaluate their state.
     */
     */
    private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed,
    private boolean setExpeditedQuotaApproved(@NonNull JobStatus jobStatus, long nowElapsed,
            boolean isWithinQuota) {
            boolean isWithinQuota) {
        if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(nowElapsed, isWithinQuota)) {
        if (jobStatus.setExpeditedJobQuotaApproved(nowElapsed, isWithinQuota)) {
            mBackgroundJobsController.evaluateStateLocked(jobStatus);
            mBackgroundJobsController.evaluateStateLocked(jobStatus);
            mConnectivityController.evaluateStateLocked(jobStatus);
            mConnectivityController.evaluateStateLocked(jobStatus);
            if (isWithinQuota && jobStatus.isReady()) {
            if (isWithinQuota && jobStatus.isReady()) {
@@ -4330,7 +4331,7 @@ public final class QuotaController extends StateController {
                        js.isRequestedExpeditedJob());
                        js.isRequestedExpeditedJob());
                proto.write(
                proto.write(
                        StateControllerProto.QuotaController.TrackedJob.IS_WITHIN_FG_JOB_QUOTA,
                        StateControllerProto.QuotaController.TrackedJob.IS_WITHIN_FG_JOB_QUOTA,
                        js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
                        js.isExpeditedQuotaApproved());
                proto.end(jsToken);
                proto.end(jsToken);
            }
            }
        });
        });
+17 −19
Original line number Original line Diff line number Diff line
@@ -5417,10 +5417,9 @@ public class QuotaControllerTest {
                timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
                timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
                .onControllerStateChanged(any());
                .onControllerStateChanged(any());
        // Top should still be "in quota" since it started before the app ran on top out of quota.
        // Top should still be "in quota" since it started before the app ran on top out of quota.
        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertFalse(jobBg.isExpeditedQuotaApproved());
        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobTop.isExpeditedQuotaApproved());
        assertFalse(
        assertFalse(jobUnstarted.isExpeditedQuotaApproved());
                jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        synchronized (mQuotaController.mLock) {
        synchronized (mQuotaController.mLock) {
            assertTrue(
            assertTrue(
                    0 >= mQuotaController
                    0 >= mQuotaController
@@ -5445,18 +5444,18 @@ public class QuotaControllerTest {
        synchronized (mQuotaController.mLock) {
        synchronized (mQuotaController.mLock) {
            mQuotaController.prepareForExecutionLocked(jobTop2);
            mQuotaController.prepareForExecutionLocked(jobTop2);
        }
        }
        assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobTop2.isExpeditedQuotaApproved());
        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobFg.isExpeditedQuotaApproved());
        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobBg.isExpeditedQuotaApproved());
        assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobUnstarted.isExpeditedQuotaApproved());


        // App still in foreground so everything should be in quota.
        // App still in foreground so everything should be in quota.
        advanceElapsedClock(20 * SECOND_IN_MILLIS);
        advanceElapsedClock(20 * SECOND_IN_MILLIS);
        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
        assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobTop2.isExpeditedQuotaApproved());
        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobFg.isExpeditedQuotaApproved());
        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobBg.isExpeditedQuotaApproved());
        assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobUnstarted.isExpeditedQuotaApproved());


        advanceElapsedClock(20 * SECOND_IN_MILLIS);
        advanceElapsedClock(20 * SECOND_IN_MILLIS);
        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
@@ -5464,13 +5463,12 @@ public class QuotaControllerTest {
                .onControllerStateChanged(any());
                .onControllerStateChanged(any());
        // App is now in background and out of quota. Fg should now change to out of quota since it
        // App is now in background and out of quota. Fg should now change to out of quota since it
        // wasn't started. Top should remain in quota since it started when the app was in TOP.
        // wasn't started. Top should remain in quota since it started when the app was in TOP.
        assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobTop2.isExpeditedQuotaApproved());
        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertFalse(jobFg.isExpeditedQuotaApproved());
        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertFalse(jobBg.isExpeditedQuotaApproved());
        trackJobs(jobBg2);
        trackJobs(jobBg2);
        assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertFalse(jobBg2.isExpeditedQuotaApproved());
        assertFalse(
        assertFalse(jobUnstarted.isExpeditedQuotaApproved());
                jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        synchronized (mQuotaController.mLock) {
        synchronized (mQuotaController.mLock) {
            assertTrue(
            assertTrue(
                    0 >= mQuotaController
                    0 >= mQuotaController
@@ -5602,7 +5600,7 @@ public class QuotaControllerTest {
        verify(mJobSchedulerService,
        verify(mJobSchedulerService,
                timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
                timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
                .onControllerStateChanged(any());
                .onControllerStateChanged(any());
        assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
        assertTrue(jobStatus.isExpeditedQuotaApproved());
        // The job used up the remaining quota, but in that time, the same amount of time in the
        // The job used up the remaining quota, but in that time, the same amount of time in the
        // old TimingSession also fell out of the quota window, so it should still have the same
        // old TimingSession also fell out of the quota window, so it should still have the same
        // amount of remaining time left its quota.
        // amount of remaining time left its quota.