diff options
| -rw-r--r-- | apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java | 74 | ||||
| -rw-r--r-- | apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java | 2 |
2 files changed, 67 insertions, 9 deletions
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 d940e380fa7e..b0f378d1752d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -159,6 +159,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -513,6 +514,10 @@ public class JobSchedulerService extends com.android.server.SystemService if (name == null) { continue; } + if (DEBUG) { + Slog.d(TAG, "DeviceConfig " + name + + " changed to " + properties.getString(name, null)); + } switch (name) { case Constants.KEY_ENABLE_API_QUOTAS: case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: @@ -3507,7 +3512,10 @@ public class JobSchedulerService extends com.android.server.SystemService } final boolean shouldForceBatchJob; - if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { + if (job.overrideState > JobStatus.OVERRIDE_NONE) { + // The job should run for some test. Don't force batch it. + shouldForceBatchJob = false; + } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { // Never batch expedited or user-initiated jobs, even for RESTRICTED apps. shouldForceBatchJob = false; } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { @@ -4960,6 +4968,8 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId + " " + jobId + " s=" + satisfied + " f=" + force); + final CountDownLatch delayLatch = new CountDownLatch(1); + final JobStatus js; try { final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); @@ -4968,7 +4978,7 @@ public class JobSchedulerService extends com.android.server.SystemService } synchronized (mLock) { - final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); + js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); if (js == null) { return JobSchedulerShellCommand.CMD_ERR_NO_JOB; } @@ -4979,23 +4989,71 @@ public class JobSchedulerService extends com.android.server.SystemService // Re-evaluate constraints after the override is set in case one of the overridden // constraints was preventing another constraint from thinking it needed to update. for (int c = mControllers.size() - 1; c >= 0; --c) { - mControllers.get(c).reevaluateStateLocked(uid); + mControllers.get(c).evaluateStateLocked(js); } if (!js.isConstraintsSatisfied()) { - js.overrideState = JobStatus.OVERRIDE_NONE; - return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + if (js.hasConnectivityConstraint() + && !js.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY) + && js.wouldBeReadyWithConstraint(JobStatus.CONSTRAINT_CONNECTIVITY)) { + // Because of how asynchronous the connectivity signals are, JobScheduler + // may not get the connectivity satisfaction signal immediately. In this + // case, wait a few seconds to see if it comes in before saying the + // connectivity constraint isn't satisfied. + mHandler.postDelayed( + checkConstraintRunnableForTesting( + mHandler, js, delayLatch, 5, 1000), + 1000); + } else { + // There's no asynchronous signal to wait for. We can immediately say the + // job's constraints aren't satisfied and return. + js.overrideState = JobStatus.OVERRIDE_NONE; + return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + } + } else { + delayLatch.countDown(); } - - queueReadyJobsForExecutionLocked(); - maybeRunPendingJobsLocked(); } } catch (RemoteException e) { // can't happen + return 0; + } + + // Choose to block the return until we're sure about the state of the connectivity job + // so that tests can expect a reliable state after calling the run command. + try { + delayLatch.await(7L, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Slog.e(TAG, "Couldn't wait for asynchronous constraint change", e); + } + + synchronized (mLock) { + if (!js.isConstraintsSatisfied()) { + js.overrideState = JobStatus.OVERRIDE_NONE; + return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + } + + queueReadyJobsForExecutionLocked(); + maybeRunPendingJobsLocked(); } return 0; } + private static Runnable checkConstraintRunnableForTesting(@NonNull final Handler handler, + @NonNull final JobStatus js, @NonNull final CountDownLatch latch, + final int remainingAttempts, final long delayMs) { + return () -> { + if (remainingAttempts <= 0 || js.isConstraintsSatisfied()) { + latch.countDown(); + return; + } + handler.postDelayed( + checkConstraintRunnableForTesting( + handler, js, latch, remainingAttempts - 1, delayMs), + delayMs); + }; + } + // Shell command infrastructure: immediately timeout currently executing jobs int executeStopCommand(PrintWriter pw, String pkgName, int userId, @Nullable String namespace, boolean hasJobId, int jobId, diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index bdc2246475d6..d39863c85f33 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -2192,7 +2192,7 @@ public final class JobStatus { * @return Whether or not this job would be ready to run if it had the specified constraint * granted, based on its requirements. */ - boolean wouldBeReadyWithConstraint(int constraint) { + public boolean wouldBeReadyWithConstraint(int constraint) { return readinessStatusWithConstraint(constraint, true); } |