Merge "Partially undo "Try to avoid overlapping job execution."" into tm-dev am: 55e09c9efa

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18355274

Change-Id: I6740030a8439b785a07e5987f0ed026c728e13f7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Kweku Adams
2022-05-20 20:15:12 +00:00
committed by Automerger Merge Worker
5 changed files with 16 additions and 62 deletions

View File

@@ -1777,10 +1777,7 @@ public class JobInfo implements Parcelable {
* {@link Build.VERSION_CODES#S}, but starting from Android version * {@link Build.VERSION_CODES#S}, but starting from Android version
* {@link Build.VERSION_CODES#TIRAMISU}, expedited jobs for the foreground app are * {@link Build.VERSION_CODES#TIRAMISU}, expedited jobs for the foreground app are
* guaranteed to be started before {@link JobScheduler#schedule(JobInfo)} returns (assuming * guaranteed to be started before {@link JobScheduler#schedule(JobInfo)} returns (assuming
* all requested constraints are satisfied), similar to foreground services. However, this * all requested constraints are satisfied), similar to foreground services.
* 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.
* *
* @see JobInfo#isExpedited() * @see JobInfo#isExpedited()
*/ */

View File

@@ -107,9 +107,7 @@ public abstract class JobScheduler {
/** /**
* Schedule a job to be executed. Will replace any currently scheduled job with the same * 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 * 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 * running, it will be stopped.
* before the previously running job has been fully stopped. See {@link JobService} for
* additional details.
* *
* <p class="caution"><strong>Note:</strong> Scheduling a job can have a high cost, even if it's * <p class="caution"><strong>Note:</strong> 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 * rescheduling the same job and the job didn't execute, especially on platform versions before

View File

@@ -32,16 +32,11 @@ import android.os.IBinder;
* {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the * {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the
* scheduling requirements are no longer being met.</p> * scheduling requirements are no longer being met.</p>
* *
* As a subclass of {@link Service}, there will only be one active instance of any JobService * <p>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 * 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 * 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 * {@link #onStartJob(JobParameters)} and {@link #onStopJob(JobParameters)}, with each call being
* for the separate jobs. * for the separate jobs.</p>
*
* <p class="note">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.</p>
*/ */
public abstract class JobService extends Service { public abstract class JobService extends Service {
private static final String TAG = "JobService"; private static final String TAG = "JobService";

View File

@@ -717,17 +717,9 @@ class JobConcurrencyManager {
final boolean isTopEj = nextPending.shouldTreatAsExpeditedJob() final boolean isTopEj = nextPending.shouldTreatAsExpeditedJob()
&& nextPending.lastEvaluatedBias == JobInfo.BIAS_TOP_APP; && nextPending.lastEvaluatedBias == JobInfo.BIAS_TOP_APP;
// Avoid overlapping job execution as much as possible. if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
if (!isTopEj && isSimilarJobRunningLocked(nextPending)) { Slog.w(TAG, "Already running similar " + (isTopEj ? "TOP-EJ" : "job")
if (DEBUG) { + " to: " + nextPending);
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;
} }
// Find an available slot for nextPending. The context should be one of the following: // Find an available slot for nextPending. The context should be one of the following:
@@ -1206,13 +1198,8 @@ class JobConcurrencyManager {
continue; continue;
} }
// Avoid overlapping job execution as much as possible. if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
if (isSimilarJobRunningLocked(nextPending)) { Slog.w(TAG, "Already running similar job to: " + nextPending);
if (DEBUG) {
Slog.w(TAG, "Avoiding execution of job because of similarly running one: "
+ nextPending);
}
continue;
} }
if (worker.getPreferredUid() != nextPending.getUid()) { if (worker.getPreferredUid() != nextPending.getUid()) {
@@ -1298,13 +1285,8 @@ class JobConcurrencyManager {
continue; continue;
} }
// Avoid overlapping job execution as much as possible. if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
if (isSimilarJobRunningLocked(nextPending)) { Slog.w(TAG, "Already running similar job to: " + nextPending);
if (DEBUG) {
Slog.w(TAG, "Avoiding execution of job because of similarly running one: "
+ nextPending);
}
continue;
} }
if (isPkgConcurrencyLimitedLocked(nextPending)) { if (isPkgConcurrencyLimitedLocked(nextPending)) {

View File

@@ -1208,22 +1208,12 @@ public class JobSchedulerService extends com.android.server.SystemService
// This may throw a SecurityException. // This may throw a SecurityException.
jobStatus.prepareLocked(); jobStatus.prepareLocked();
final boolean canExecuteImmediately;
if (toCancel != null) { if (toCancel != null) {
// Implicitly replaces the existing job record with the new instance // Implicitly replaces the existing job record with the new instance
final boolean wasJobExecuting = cancelJobImplLocked(toCancel, jobStatus, cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
JobParameters.STOP_REASON_CANCELLED_BY_APP, JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled 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);
} else { } else {
startTrackingJobLocked(jobStatus, null); startTrackingJobLocked(jobStatus, null);
canExecuteImmediately = true;
} }
if (work != null) { if (work != null) {
@@ -1266,12 +1256,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// list and try to run it. // list and try to run it.
mJobPackageTracker.notePending(jobStatus); mJobPackageTracker.notePending(jobStatus);
mPendingJobQueue.add(jobStatus); mPendingJobQueue.add(jobStatus);
if (canExecuteImmediately) { maybeRunPendingJobsLocked();
// 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();
}
} else { } else {
evaluateControllerStatesLocked(jobStatus); 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 * 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 * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
* currently scheduled jobs. * 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) { @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
cancelled.unprepareLocked(); cancelled.unprepareLocked();
@@ -1406,7 +1389,7 @@ public class JobSchedulerService extends com.android.server.SystemService
} }
mChangedJobList.remove(cancelled); mChangedJobList.remove(cancelled);
// Cancel if running. // Cancel if running.
boolean wasRunning = mConcurrencyManager.stopJobOnServiceContextLocked( mConcurrencyManager.stopJobOnServiceContextLocked(
cancelled, reason, internalReasonCode, debugReason); cancelled, reason, internalReasonCode, debugReason);
// If this is a replacement, bring in the new version of the job // If this is a replacement, bring in the new version of the job
if (incomingJob != null) { if (incomingJob != null) {
@@ -1414,7 +1397,6 @@ public class JobSchedulerService extends com.android.server.SystemService
startTrackingJobLocked(incomingJob, cancelled); startTrackingJobLocked(incomingJob, cancelled);
} }
reportActiveLocked(); reportActiveLocked();
return wasRunning;
} }
void updateUidState(int uid, int procState) { void updateUidState(int uid, int procState) {