diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 4710322db283e..f49cdbf403f09 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -1777,10 +1777,7 @@ public class JobInfo implements Parcelable { * {@link Build.VERSION_CODES#S}, but starting from Android version * {@link Build.VERSION_CODES#TIRAMISU}, expedited jobs for the foreground app are * guaranteed to be started before {@link JobScheduler#schedule(JobInfo)} returns (assuming - * all requested constraints are satisfied), similar to foreground services. However, this - * start guarantee means there is a higher chance of overlapping executions, as noted in - * {@link JobService}, so be sure to handle that properly if you intend to reschedule the - * job while it's actively running. + * all requested constraints are satisfied), similar to foreground services. * * @see JobInfo#isExpedited() */ diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java index 388bbf1b26a02..632ecb2c03813 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java @@ -107,9 +107,7 @@ public abstract class JobScheduler { /** * Schedule a job to be executed. Will replace any currently scheduled job with the same * ID with the new information in the {@link JobInfo}. If a job with the given ID is currently - * running, it will be stopped. Note that in some cases, the newly scheduled job may be started - * before the previously running job has been fully stopped. See {@link JobService} for - * additional details. + * running, it will be stopped. * *

Note: Scheduling a job can have a high cost, even if it's * rescheduling the same job and the job didn't execute, especially on platform versions before diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java index 7ed4b62ae7e40..d184d44239edb 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobService.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java @@ -32,16 +32,11 @@ import android.os.IBinder; * {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the * scheduling requirements are no longer being met.

* - * As a subclass of {@link Service}, there will only be one active instance of any JobService + *

As a subclass of {@link Service}, there will only be one active instance of any JobService * subclasses, regardless of job ID. This means that if you schedule multiple jobs with different * job IDs but using the same JobService class, that JobService may receive multiple calls to * {@link #onStartJob(JobParameters)} and {@link #onStopJob(JobParameters)}, with each call being - * for the separate jobs. - * - *

Note that if you cancel and reschedule an already executing job, - * there may be a small period of time where {@link #onStartJob(JobParameters)} has been called for - * the newly scheduled job instance before {@link #onStopJob(JobParameters)} has been called or - * fully processed for the old job.

+ * for the separate jobs.

*/ public abstract class JobService extends Service { private static final String TAG = "JobService"; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index d5a7f2851d031..28116a8df1809 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -717,17 +717,9 @@ class JobConcurrencyManager { final boolean isTopEj = nextPending.shouldTreatAsExpeditedJob() && nextPending.lastEvaluatedBias == JobInfo.BIAS_TOP_APP; - // Avoid overlapping job execution as much as possible. - if (!isTopEj && isSimilarJobRunningLocked(nextPending)) { - if (DEBUG) { - Slog.w(TAG, "Delaying execution of job because of similarly running one: " - + nextPending); - } - // It would be nice to let the JobService running the other similar job know about - // this new job so that it doesn't unbind from the JobService and we can call - // onStartJob as soon as the older job finishes. - // TODO: optimize the job reschedule flow to reduce service binding churn - continue; + if (DEBUG && isSimilarJobRunningLocked(nextPending)) { + Slog.w(TAG, "Already running similar " + (isTopEj ? "TOP-EJ" : "job") + + " to: " + nextPending); } // Find an available slot for nextPending. The context should be one of the following: @@ -1206,13 +1198,8 @@ class JobConcurrencyManager { continue; } - // Avoid overlapping job execution as much as possible. - if (isSimilarJobRunningLocked(nextPending)) { - if (DEBUG) { - Slog.w(TAG, "Avoiding execution of job because of similarly running one: " - + nextPending); - } - continue; + if (DEBUG && isSimilarJobRunningLocked(nextPending)) { + Slog.w(TAG, "Already running similar job to: " + nextPending); } if (worker.getPreferredUid() != nextPending.getUid()) { @@ -1298,13 +1285,8 @@ class JobConcurrencyManager { continue; } - // Avoid overlapping job execution as much as possible. - if (isSimilarJobRunningLocked(nextPending)) { - if (DEBUG) { - Slog.w(TAG, "Avoiding execution of job because of similarly running one: " - + nextPending); - } - continue; + if (DEBUG && isSimilarJobRunningLocked(nextPending)) { + Slog.w(TAG, "Already running similar job to: " + nextPending); } if (isPkgConcurrencyLimitedLocked(nextPending)) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index cd70e88b18aa6..60afdc76d249a 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1208,22 +1208,12 @@ public class JobSchedulerService extends com.android.server.SystemService // This may throw a SecurityException. jobStatus.prepareLocked(); - final boolean canExecuteImmediately; if (toCancel != null) { // Implicitly replaces the existing job record with the new instance - final boolean wasJobExecuting = cancelJobImplLocked(toCancel, jobStatus, - JobParameters.STOP_REASON_CANCELLED_BY_APP, - JobParameters.INTERNAL_STOP_REASON_CANCELED, - "job rescheduled by app"); - // Avoid overlapping job executions. Don't push for immediate execution if an old - // job with the same ID was running, but let TOP EJs start immediately. - canExecuteImmediately = !wasJobExecuting - || (jobStatus.isRequestedExpeditedJob() - && mUidBiasOverride.get(jobStatus.getSourceUid(), JobInfo.BIAS_DEFAULT) - == JobInfo.BIAS_TOP_APP); + cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP, + JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled by app"); } else { startTrackingJobLocked(jobStatus, null); - canExecuteImmediately = true; } if (work != null) { @@ -1266,12 +1256,7 @@ public class JobSchedulerService extends com.android.server.SystemService // list and try to run it. mJobPackageTracker.notePending(jobStatus); mPendingJobQueue.add(jobStatus); - if (canExecuteImmediately) { - // Don't ask the JobConcurrencyManager to try to run the job immediately. The - // JobServiceContext will ask the JobConcurrencyManager for another job once - // it finishes cleaning up the old job. - maybeRunPendingJobsLocked(); - } + maybeRunPendingJobsLocked(); } else { evaluateControllerStatesLocked(jobStatus); } @@ -1392,10 +1377,8 @@ public class JobSchedulerService extends com.android.server.SystemService * is null, the cancelled job is removed outright from the system. If * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of * currently scheduled jobs. - * - * @return true if the cancelled job was running */ - private boolean cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, + private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); cancelled.unprepareLocked(); @@ -1406,7 +1389,7 @@ public class JobSchedulerService extends com.android.server.SystemService } mChangedJobList.remove(cancelled); // Cancel if running. - boolean wasRunning = mConcurrencyManager.stopJobOnServiceContextLocked( + mConcurrencyManager.stopJobOnServiceContextLocked( cancelled, reason, internalReasonCode, debugReason); // If this is a replacement, bring in the new version of the job if (incomingJob != null) { @@ -1414,7 +1397,6 @@ public class JobSchedulerService extends com.android.server.SystemService startTrackingJobLocked(incomingJob, cancelled); } reportActiveLocked(); - return wasRunning; } void updateUidState(int uid, int procState) {