Loading apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +5 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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() Loading apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +52 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -328,6 +336,7 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = false; mStoppedReason = null; mStoppedTime = 0; job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob(); return true; } } Loading Loading @@ -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() : Loading Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -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(); } } Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +14 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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(); Loading apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +7 −11 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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()); } Loading Loading @@ -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 { Loading @@ -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 Loading
apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +5 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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() Loading
apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +52 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -328,6 +336,7 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = false; mStoppedReason = null; mStoppedTime = 0; job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob(); return true; } } Loading Loading @@ -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() : Loading Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -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(); } } Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +14 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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(); Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +7 −11 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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()); } Loading Loading @@ -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 { Loading @@ -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