Prevent prefetch jobs from having deadlines.
Now that we're trying to run prefetch jobs close to app launch, there's no good reason for a prefetch job to have a deadline. Periodic jobs don't run until the constraints are satisfied. Just wanting to run a prefetch job by some time just for the sake of running it is not something we want to allow. Gated by target SDK, updated apps will be prevented from scheduling a prefetch job with a deadline. Bug: 194532703 Test: adb shell am compat disable 194532703 android.jobscheduler.cts; atest android.jobscheduler.cts.JobInfoTest#testPrefetch Test: adb shell am compat reset 194532703 android.jobscheduler.cts; atest android.jobscheduler.cts.JobInfoTest#testPrefetch Change-Id: I92d91c89d01765f76064dd00725137f166ca483f
This commit is contained in:
@@ -29,6 +29,9 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.compat.Compatibility;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ClipData;
|
||||
import android.content.ComponentName;
|
||||
@@ -63,6 +66,25 @@ import java.util.Objects;
|
||||
public class JobInfo implements Parcelable {
|
||||
private static String TAG = "JobInfo";
|
||||
|
||||
/**
|
||||
* Disallow setting a deadline (via {@link Builder#setOverrideDeadline(long)}) for prefetch
|
||||
* jobs ({@link Builder#setPrefetch(boolean)}. Prefetch jobs are meant to run close to the next
|
||||
* app launch, so there's no good reason to allow them to have deadlines.
|
||||
*
|
||||
* We don't drop or cancel any previously scheduled prefetch jobs with a deadline.
|
||||
* There's no way for an app to keep a perpetually scheduled prefetch job with a deadline.
|
||||
* Prefetch jobs with a deadline will run and apps under this restriction won't be able to
|
||||
* schedule new prefetch jobs with a deadline. If a job is rescheduled (by providing
|
||||
* {@code true} via {@link JobService#jobFinished(JobParameters, boolean)} or
|
||||
* {@link JobService#onStopJob(JobParameters)}'s return value),the deadline is dropped.
|
||||
* Periodic jobs require all constraints to be met, so there's no issue with their deadlines.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@ChangeId
|
||||
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
|
||||
public static final long DISALLOW_DEADLINES_FOR_PREFETCH_JOBS = 194532703L;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "NETWORK_TYPE_" }, value = {
|
||||
NETWORK_TYPE_NONE,
|
||||
@@ -1445,7 +1467,9 @@ public class JobInfo implements Parcelable {
|
||||
/**
|
||||
* Specify that this job should recur with the provided interval, not more than once per
|
||||
* period. You have no control over when within this interval this job will be executed,
|
||||
* only the guarantee that it will be executed at most once within this interval.
|
||||
* only the guarantee that it will be executed at most once within this interval, as long
|
||||
* as the constraints are satisfied. If the constraints are not satisfied within this
|
||||
* interval, the job will wait until the constraints are satisfied.
|
||||
* Setting this function on the builder with {@link #setMinimumLatency(long)} or
|
||||
* {@link #setOverrideDeadline(long)} will result in an error.
|
||||
* @param intervalMillis Millisecond interval for which this job will repeat.
|
||||
@@ -1641,6 +1665,9 @@ public class JobInfo implements Parcelable {
|
||||
* the specific user of this device. For example, fetching top headlines
|
||||
* of interest to the current user.
|
||||
* <p>
|
||||
* Starting with Android version {@link Build.VERSION_CODES#TIRAMISU}, prefetch jobs are
|
||||
* not allowed to have deadlines (set via {@link #setOverrideDeadline(long)}.
|
||||
* <p>
|
||||
* The system may use this signal to relax the network constraints you
|
||||
* originally requested, such as allowing a
|
||||
* {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered
|
||||
@@ -1675,6 +1702,11 @@ public class JobInfo implements Parcelable {
|
||||
* @return The job object to hand to the JobScheduler. This object is immutable.
|
||||
*/
|
||||
public JobInfo build() {
|
||||
return build(Compatibility.isChangeEnabled(DISALLOW_DEADLINES_FOR_PREFETCH_JOBS));
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public JobInfo build(boolean disallowPrefetchDeadlines) {
|
||||
// This check doesn't need to be inside enforceValidity. It's an unnecessary legacy
|
||||
// check that would ideally be phased out instead.
|
||||
if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
|
||||
@@ -1683,7 +1715,7 @@ public class JobInfo implements Parcelable {
|
||||
" setRequiresDeviceIdle is an error.");
|
||||
}
|
||||
JobInfo jobInfo = new JobInfo(this);
|
||||
jobInfo.enforceValidity();
|
||||
jobInfo.enforceValidity(disallowPrefetchDeadlines);
|
||||
return jobInfo;
|
||||
}
|
||||
|
||||
@@ -1701,7 +1733,7 @@ public class JobInfo implements Parcelable {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final void enforceValidity() {
|
||||
public final void enforceValidity(boolean disallowPrefetchDeadlines) {
|
||||
// Check that network estimates require network type and are reasonable values.
|
||||
if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0)
|
||||
&& networkRequest == null) {
|
||||
@@ -1725,9 +1757,10 @@ public class JobInfo implements Parcelable {
|
||||
throw new IllegalArgumentException("Minimum chunk size must be positive");
|
||||
}
|
||||
|
||||
final boolean hasDeadline = maxExecutionDelayMillis != 0L;
|
||||
// Check that a deadline was not set on a periodic job.
|
||||
if (isPeriodic) {
|
||||
if (maxExecutionDelayMillis != 0L) {
|
||||
if (hasDeadline) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't call setOverrideDeadline() on a periodic job.");
|
||||
}
|
||||
@@ -1741,6 +1774,12 @@ public class JobInfo implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
// Prefetch jobs should not have deadlines
|
||||
if (disallowPrefetchDeadlines && hasDeadline && (flags & FLAG_PREFETCH) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't call setOverrideDeadline() on a prefetch job.");
|
||||
}
|
||||
|
||||
if (isPersisted) {
|
||||
// We can't serialize network specifiers
|
||||
if (networkRequest != null
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.IUidObserver;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.app.job.IJobScheduler;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobParameters;
|
||||
@@ -2932,7 +2933,9 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
|
||||
private void validateJobFlags(JobInfo job, int callingUid) {
|
||||
job.enforceValidity();
|
||||
job.enforceValidity(
|
||||
CompatChanges.isChangeEnabled(
|
||||
JobInfo.DISALLOW_DEADLINES_FOR_PREFETCH_JOBS, callingUid));
|
||||
if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
|
||||
|
||||
@@ -49,7 +49,6 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.BitUtils;
|
||||
import com.android.server.IoThread;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.job.JobSchedulerInternal.JobStorePersistStats;
|
||||
import com.android.server.job.controllers.JobStatus;
|
||||
|
||||
@@ -978,10 +977,17 @@ public final class JobStore {
|
||||
|
||||
final JobInfo builtJob;
|
||||
try {
|
||||
builtJob = jobBuilder.build();
|
||||
// Don't perform prefetch-deadline check here. Apps targeting S- shouldn't have
|
||||
// any prefetch-with-deadline jobs accidentally dropped. It's not worth doing
|
||||
// target SDK version checks here for apps targeting T+. There's no way for an
|
||||
// app to keep a perpetually scheduled prefetch job with a deadline. Prefetch jobs
|
||||
// with a deadline would run and then any newly scheduled prefetch jobs wouldn't
|
||||
// have a deadline. If a job is rescheduled (via jobFinished(true) or onStopJob()'s
|
||||
// return value), the deadline is dropped. Periodic jobs require all constraints
|
||||
// to be met, so there's no issue with their deadlines.
|
||||
builtJob = jobBuilder.build(false);
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "Unable to build job from XML, ignoring: "
|
||||
+ jobBuilder.summarize());
|
||||
Slog.w(TAG, "Unable to build job from XML, ignoring: " + jobBuilder.summarize(), e);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -997,11 +1003,10 @@ public final class JobStore {
|
||||
}
|
||||
|
||||
// And now we're done
|
||||
JobSchedulerInternal service = LocalServices.getService(JobSchedulerInternal.class);
|
||||
final int appBucket = JobSchedulerService.standbyBucketForPackage(sourcePackageName,
|
||||
sourceUserId, elapsedNow);
|
||||
JobStatus js = new JobStatus(
|
||||
jobBuilder.build(), uid, sourcePackageName, sourceUserId,
|
||||
builtJob, uid, sourcePackageName, sourceUserId,
|
||||
appBucket, sourceTag,
|
||||
elapsedRuntimes.first, elapsedRuntimes.second,
|
||||
lastSuccessfulRunTime, lastFailedRunTime,
|
||||
|
||||
@@ -555,7 +555,9 @@ public final class JobStatus {
|
||||
requestBuilder.setUids(
|
||||
Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid)));
|
||||
builder.setRequiredNetwork(requestBuilder.build());
|
||||
job = builder.build();
|
||||
// Don't perform prefetch-deadline check at this point. We've already passed the
|
||||
// initial validation check.
|
||||
job = builder.build(false);
|
||||
}
|
||||
|
||||
final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
|
||||
|
||||
Reference in New Issue
Block a user