Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d5d56f8 authored by Kweku Adams's avatar Kweku Adams
Browse files

Prevent apps from setting negative estimated bytes.

Bug: 253665015
Test: atest CtsJobSchedulerTestCases:JobInfoTest
Test: atest CtsJobSchedulerTestCases:JobWorkItemTest
Change-Id: I4f8185b4f6abbe872d8944448de629ed0d903634
parent 9bdbc578
Loading
Loading
Loading
Loading
+27 −4
Original line number Diff line number Diff line
@@ -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;
+13 −16
Original line number Diff line number Diff line
@@ -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;
+5 −2
Original line number Diff line number Diff line
@@ -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
+2 −1
Original line number Diff line number Diff line
@@ -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;
+2 −2
Original line number Diff line number Diff line
@@ -612,9 +612,9 @@ public final class JobStatus {
            requestBuilder.setUids(
                    Collections.singleton(new Range<Integer>(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();