diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index ab0ac5aa88564..4c849fe831de3 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -32,6 +32,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.compat.Compatibility; import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; @@ -97,6 +98,15 @@ public class JobInfo implements Parcelable { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long THROW_ON_INVALID_PRIORITY_VALUE = 140852299L; + /** + * Require that estimated network bytes are nonnegative. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long REJECT_NEGATIVE_NETWORK_ESTIMATES = 253665015L; + /** @hide */ @IntDef(prefix = { "NETWORK_TYPE_" }, value = { NETWORK_TYPE_NONE, @@ -1890,11 +1900,13 @@ 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)); + return build(Compatibility.isChangeEnabled(DISALLOW_DEADLINES_FOR_PREFETCH_JOBS), + Compatibility.isChangeEnabled(REJECT_NEGATIVE_NETWORK_ESTIMATES)); } /** @hide */ - public JobInfo build(boolean disallowPrefetchDeadlines) { + public JobInfo build(boolean disallowPrefetchDeadlines, + boolean rejectNegativeNetworkEstimates) { // 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) { @@ -1903,7 +1915,7 @@ public class JobInfo implements Parcelable { " setRequiresDeviceIdle is an error."); } JobInfo jobInfo = new JobInfo(this); - jobInfo.enforceValidity(disallowPrefetchDeadlines); + jobInfo.enforceValidity(disallowPrefetchDeadlines, rejectNegativeNetworkEstimates); return jobInfo; } @@ -1921,13 +1933,24 @@ public class JobInfo implements Parcelable { /** * @hide */ - public final void enforceValidity(boolean disallowPrefetchDeadlines) { + public final void enforceValidity(boolean disallowPrefetchDeadlines, + boolean rejectNegativeNetworkEstimates) { // Check that network estimates require network type and are reasonable values. if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0) && networkRequest == null) { throw new IllegalArgumentException( "Can't provide estimated network usage without requiring a network"); } + if (networkRequest != null && rejectNegativeNetworkEstimates) { + if (networkUploadBytes != NETWORK_BYTES_UNKNOWN && networkUploadBytes < 0) { + throw new IllegalArgumentException( + "Invalid network upload bytes: " + networkUploadBytes); + } + if (networkDownloadBytes != NETWORK_BYTES_UNKNOWN && networkDownloadBytes < 0) { + throw new IllegalArgumentException( + "Invalid network download bytes: " + networkDownloadBytes); + } + } final long estimatedTransfer; if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) { estimatedTransfer = networkDownloadBytes; diff --git a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java index 372f9faacd5a2..32945e00c8e3d 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java @@ -20,6 +20,7 @@ import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN; import android.annotation.BytesLong; import android.annotation.Nullable; +import android.compat.Compatibility; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.os.Build; @@ -88,25 +89,11 @@ final public class JobWorkItem implements Parcelable { */ public JobWorkItem(@Nullable Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes, @BytesLong long minimumChunkBytes) { - if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && minimumChunkBytes <= 0) { - throw new IllegalArgumentException("Minimum chunk size must be positive"); - } - final long estimatedTransfer; - if (uploadBytes == NETWORK_BYTES_UNKNOWN) { - estimatedTransfer = downloadBytes; - } else { - estimatedTransfer = uploadBytes - + (downloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : downloadBytes); - } - if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && estimatedTransfer != NETWORK_BYTES_UNKNOWN - && minimumChunkBytes > estimatedTransfer) { - throw new IllegalArgumentException( - "Minimum chunk size can't be greater than estimated network usage"); - } mIntent = intent; mNetworkDownloadBytes = downloadBytes; mNetworkUploadBytes = uploadBytes; mMinimumChunkBytes = minimumChunkBytes; + enforceValidity(Compatibility.isChangeEnabled(JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES)); } /** @@ -222,7 +209,17 @@ final public class JobWorkItem implements Parcelable { /** * @hide */ - public void enforceValidity() { + public void enforceValidity(boolean rejectNegativeNetworkEstimates) { + if (rejectNegativeNetworkEstimates) { + if (mNetworkUploadBytes != NETWORK_BYTES_UNKNOWN && mNetworkUploadBytes < 0) { + throw new IllegalArgumentException( + "Invalid network upload bytes: " + mNetworkUploadBytes); + } + if (mNetworkDownloadBytes != NETWORK_BYTES_UNKNOWN && mNetworkDownloadBytes < 0) { + throw new IllegalArgumentException( + "Invalid network download bytes: " + mNetworkDownloadBytes); + } + } final long estimatedTransfer; if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) { estimatedTransfer = mNetworkDownloadBytes; 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 048f4a48ab52c..d28ebde9cb238 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -3163,9 +3163,12 @@ public class JobSchedulerService extends com.android.server.SystemService } private void validateJob(JobInfo job, int callingUid, @Nullable JobWorkItem jobWorkItem) { + final boolean rejectNegativeNetworkEstimates = CompatChanges.isChangeEnabled( + JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES, callingUid); job.enforceValidity( CompatChanges.isChangeEnabled( - JobInfo.DISALLOW_DEADLINES_FOR_PREFETCH_JOBS, callingUid)); + JobInfo.DISALLOW_DEADLINES_FOR_PREFETCH_JOBS, callingUid), + rejectNegativeNetworkEstimates); if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); @@ -3180,7 +3183,7 @@ public class JobSchedulerService extends com.android.server.SystemService } } if (jobWorkItem != null) { - jobWorkItem.enforceValidity(); + jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates); if (jobWorkItem.getEstimatedNetworkDownloadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN || jobWorkItem.getEstimatedNetworkUploadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index f731b8d1d0d7e..ff9ff343d4b5d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -1024,7 +1024,8 @@ public final class JobStore { // 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); + // The same logic applies for other target SDK-based validation checks. + builtJob = jobBuilder.build(false, false); } catch (Exception e) { Slog.w(TAG, "Unable to build job from XML, ignoring: " + jobBuilder.summarize(), e); return null; 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 669234b4a6703..de602a8f7f259 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 @@ -612,9 +612,9 @@ public final class JobStatus { requestBuilder.setUids( Collections.singleton(new Range(this.sourceUid, this.sourceUid))); builder.setRequiredNetwork(requestBuilder.build()); - // Don't perform prefetch-deadline check at this point. We've already passed the + // Don't perform validation checks at this point since we've already passed the // initial validation check. - job = builder.build(false); + job = builder.build(false, false); } updateMediaBackupExemptionStatus();