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

Commit 31a69549 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Make sure out-of-quota EJs stop in time." into sc-dev

parents 934f10e6 5871539d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -891,7 +891,7 @@ class JobConcurrencyManager {
        }

        // Only expedited jobs can replace expedited jobs.
        if (js.shouldTreatAsExpeditedJob()) {
        if (js.shouldTreatAsExpeditedJob() || js.startedAsExpeditedJob) {
            // Keep fg/bg user distinction.
            if (workType == WORK_TYPE_BGUSER_IMPORTANT || workType == WORK_TYPE_BGUSER) {
                // Let any important bg user job replace a bg user expedited job.
+5 −1
Original line number Diff line number Diff line
@@ -2187,6 +2187,10 @@ public class JobSchedulerService extends com.android.server.SystemService
     */
    @VisibleForTesting
    boolean isReadyToBeExecutedLocked(JobStatus job) {
        return isReadyToBeExecutedLocked(job, true);
    }

    boolean isReadyToBeExecutedLocked(JobStatus job, boolean rejectActive) {
        final boolean jobReady = job.isReady();

        if (DEBUG) {
@@ -2225,7 +2229,7 @@ public class JobSchedulerService extends com.android.server.SystemService
        }

        final boolean jobPending = mPendingJobs.contains(job);
        final boolean jobActive = isCurrentlyActiveLocked(job);
        final boolean jobActive = rejectActive && isCurrentlyActiveLocked(job);

        if (DEBUG) {
            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+52 −1
Original line number Diff line number Diff line
@@ -151,6 +151,14 @@ public final class JobServiceContext implements ServiceConnection {
    /** The absolute maximum amount of time the job can run */
    private long mMaxExecutionTimeMillis;

    /**
     * The stop reason for a pending cancel. If there's not pending cancel, then the value should be
     * {@link JobParameters#STOP_REASON_UNDEFINED}.
     */
    private int mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED;
    private int mPendingLegacyStopReason;
    private String mPendingDebugStopReason;

    // Debugging: reason this job was last stopped.
    public String mStoppedReason;

@@ -328,6 +336,7 @@ public final class JobServiceContext implements ServiceConnection {
            mAvailable = false;
            mStoppedReason = null;
            mStoppedTime = 0;
            job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob();
            return true;
        }
    }
@@ -625,6 +634,19 @@ public final class JobServiceContext implements ServiceConnection {
            }
            return;
        }
        if (mRunningJob.startedAsExpeditedJob
                && stopReasonCode == JobParameters.STOP_REASON_QUOTA) {
            // EJs should be able to run for at least the min upper limit regardless of quota.
            final long earliestStopTimeElapsed =
                    mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
            final long nowElapsed = sElapsedRealtimeClock.millis();
            if (nowElapsed < earliestStopTimeElapsed) {
                mPendingStopReason = stopReasonCode;
                mPendingLegacyStopReason = legacyStopReason;
                mPendingDebugStopReason = debugReason;
                return;
            }
        }
        mParams.setStopReason(stopReasonCode, legacyStopReason, debugReason);
        if (legacyStopReason == JobParameters.REASON_PREEMPT) {
            mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
@@ -777,6 +799,23 @@ public final class JobServiceContext implements ServiceConnection {
                closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
                break;
            case VERB_EXECUTING:
                if (mPendingStopReason != JobParameters.STOP_REASON_UNDEFINED) {
                    if (mService.isReadyToBeExecutedLocked(mRunningJob, false)) {
                        // Job became ready again while we were waiting to stop it (for example,
                        // the device was temporarily taken off the charger). Ignore the pending
                        // stop and see what the manager says.
                        mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED;
                        mPendingLegacyStopReason = 0;
                        mPendingDebugStopReason = null;
                    } else {
                        Slog.i(TAG, "JS was waiting to stop this job."
                                + " Sending onStop: " + getRunningJobNameLocked());
                        mParams.setStopReason(mPendingStopReason, mPendingLegacyStopReason,
                                mPendingDebugStopReason);
                        sendStopMessageLocked(mPendingDebugStopReason);
                        break;
                    }
                }
                final long latestStopTimeElapsed =
                        mExecutionStartTimeElapsed + mMaxExecutionTimeMillis;
                final long nowElapsed = sElapsedRealtimeClock.millis();
@@ -886,6 +925,9 @@ public final class JobServiceContext implements ServiceConnection {
        mCancelled = false;
        service = null;
        mAvailable = true;
        mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED;
        mPendingLegacyStopReason = 0;
        mPendingDebugStopReason = null;
        removeOpTimeOutLocked();
        mCompletedListener.onJobCompletedLocked(completedJob, legacyStopReason, reschedule);
        mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
@@ -972,7 +1014,16 @@ public final class JobServiceContext implements ServiceConnection {
            pw.print(", ");
            TimeUtils.formatDuration(
                    (mExecutionStartTimeElapsed + mMaxExecutionTimeMillis) - nowElapsed, pw);
            pw.println("]");
            pw.print("]");
            if (mPendingStopReason != JobParameters.STOP_REASON_UNDEFINED) {
                pw.print(" Pending stop because ");
                pw.print(mPendingStopReason);
                pw.print("/");
                pw.print(mPendingLegacyStopReason);
                pw.print("/");
                pw.print(mPendingDebugStopReason);
            }
            pw.println();
            pw.decreaseIndent();
        }
    }
+14 −4
Original line number Diff line number Diff line
@@ -328,6 +328,12 @@ public final class JobStatus {
    /** The evaluated priority of the job when it started running. */
    public int lastEvaluatedPriority;

    /**
     * Whether or not this particular JobStatus instance was treated as an EJ when it started
     * running. This isn't copied over when a job is rescheduled.
     */
    public boolean startedAsExpeditedJob = false;

    // If non-null, this is work that has been enqueued for the job.
    public ArrayList<JobWorkItem> pendingWork;

@@ -1122,18 +1128,19 @@ public final class JobStatus {
     */
    public boolean canRunInDoze() {
        return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
                || (shouldTreatAsExpeditedJob()
                || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
                && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
    }

    boolean canRunInBatterySaver() {
        return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
                || (shouldTreatAsExpeditedJob()
                || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
                && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
    }

    boolean shouldIgnoreNetworkBlocking() {
        return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsExpeditedJob();
        return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
                || (shouldTreatAsExpeditedJob() || startedAsExpeditedJob);
    }

    /** @return true if the constraint was changed, false otherwise. */
@@ -2032,7 +2039,10 @@ public final class JobStatus {
        pw.println(serviceInfo != null);
        if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) {
            pw.print("readyWithinExpeditedQuota: ");
            pw.println(mReadyWithinExpeditedQuota);
            pw.print(mReadyWithinExpeditedQuota);
            pw.print(" (started as EJ: ");
            pw.print(startedAsExpeditedJob);
            pw.println(")");
        }
        pw.decreaseIndent();

+7 −11
Original line number Diff line number Diff line
@@ -849,10 +849,9 @@ public final class QuotaController extends StateController {
            return true;
        }
        // A job is within quota if one of the following is true:
        //   1. it's already running (already executing expedited jobs should be allowed to finish)
        //   2. the app is currently in the foreground
        //   3. the app overall is within its quota
        //   4. It's on the temp allowlist (or within the grace period)
        //   1. the app is currently in the foreground
        //   2. the app overall is within its quota
        //   3. It's on the temp allowlist (or within the grace period)
        if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) {
            return true;
        }
@@ -873,13 +872,6 @@ public final class QuotaController extends StateController {
            return true;
        }

        Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
                jobStatus.getSourcePackageName());
        // Any already executing expedited jobs should be allowed to finish.
        if (ejTimer != null && ejTimer.isRunning(jobStatus)) {
            return true;
        }

        return 0 < getRemainingEJExecutionTimeLocked(
                jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
    }
@@ -4153,6 +4145,8 @@ public final class QuotaController extends StateController {
                pw.print(", ");
                if (js.shouldTreatAsExpeditedJob()) {
                    pw.print("within EJ quota");
                } else if (js.startedAsExpeditedJob) {
                    pw.print("out of EJ quota");
                } else if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
                    pw.print("within regular quota");
                } else {
@@ -4163,6 +4157,8 @@ public final class QuotaController extends StateController {
                    pw.print(getRemainingEJExecutionTimeLocked(
                            js.getSourceUserId(), js.getSourcePackageName()));
                    pw.print("ms remaining in EJ quota");
                } else if (js.startedAsExpeditedJob) {
                    pw.print("should be stopped after min execution time");
                } else {
                    pw.print(getRemainingExecutionTimeLocked(js));
                    pw.print("ms remaining in quota");
Loading