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

Commit 56a14578 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Enforce minimum job time windows." into main

parents 74cf57f8 da434496
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
package: "android.app.job"

flag {
    name: "enforce_minimum_time_windows"
    namespace: "backstage_power"
    description: "Enforce a minimum time window for job latencies & deadlines"
    bug: "311402873"
}

flag {
    name: "job_debug_info_apis"
    namespace: "backstage_power"
+85 −4
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.util.TimeUtils.formatDuration;

import android.annotation.BytesLong;
@@ -36,6 +37,7 @@ import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ComponentName;
@@ -48,7 +50,9 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;

@@ -113,6 +117,16 @@ public class JobInfo implements Parcelable {
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
    public static final long REJECT_NEGATIVE_NETWORK_ESTIMATES = 253665015L;

    /**
     * Enforce a minimum time window between job latencies and deadlines.
     *
     * @hide
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    @Overridable // Aid in testing
    public static final long ENFORCE_MINIMUM_TIME_WINDOWS = 311402873L;

    /** @hide */
    @IntDef(prefix = { "NETWORK_TYPE_" }, value = {
            NETWORK_TYPE_NONE,
@@ -1866,10 +1880,40 @@ public class JobInfo implements Parcelable {
         * Set deadline which is the maximum scheduling latency. The job will be run by this
         * deadline even if other requirements (including a delay set through
         * {@link #setMinimumLatency(long)}) are not met.
         * {@link JobParameters#isOverrideDeadlineExpired()} will return {@code true} if the job's
         * deadline has passed.
         *
         * <p>
         * Because it doesn't make sense setting this property on a periodic job, doing so will
         * throw an {@link java.lang.IllegalArgumentException} when
         * {@link android.app.job.JobInfo.Builder#build()} is called.
         *
         * <p class="note">
         * Since a job will run once the deadline has passed regardless of the status of other
         * constraints, setting a deadline of 0 with other constraints makes those constraints
         * meaningless when it comes to execution decisions. Avoid doing this.
         * </p>
         *
         * <p>
         * Short deadlines hinder the system's ability to optimize scheduling behavior and may
         * result in running jobs at inopportune times. Therefore, starting in Android version
         * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, minimum time windows will be
         * enforced to help make it easier to better optimize job execution. Time windows are
         * defined as the time between a job's {@link #setMinimumLatency(long) minimum latency}
         * and its deadline. If the minimum latency is not set, it is assumed to be 0.
         * The following minimums will be enforced:
         * <ul>
         *     <li>
         *         Jobs with {@link #PRIORITY_DEFAULT} or higher priorities have a minimum time
         *         window of one hour.
         *     </li>
         *     <li>Jobs with {@link #PRIORITY_LOW} have a minimum time window of 6 hours.</li>
         *     <li>Jobs with {@link #PRIORITY_MIN} have a minimum time window of 12 hours.</li>
         * </ul>
         *
         * Work that must happen immediately should use {@link #setExpedited(boolean)} or
         * {@link #setUserInitiated(boolean)} in the appropriate manner.
         *
         * @see JobInfo#getMaxExecutionDelayMillis()
         */
        public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
@@ -2143,12 +2187,14 @@ public class JobInfo implements Parcelable {
         */
        public JobInfo build() {
            return build(Compatibility.isChangeEnabled(DISALLOW_DEADLINES_FOR_PREFETCH_JOBS),
                    Compatibility.isChangeEnabled(REJECT_NEGATIVE_NETWORK_ESTIMATES));
                    Compatibility.isChangeEnabled(REJECT_NEGATIVE_NETWORK_ESTIMATES),
                    Compatibility.isChangeEnabled(ENFORCE_MINIMUM_TIME_WINDOWS));
        }

        /** @hide */
        public JobInfo build(boolean disallowPrefetchDeadlines,
                boolean rejectNegativeNetworkEstimates) {
                boolean rejectNegativeNetworkEstimates,
                boolean enforceMinimumTimeWindows) {
            // 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) {
@@ -2157,7 +2203,8 @@ public class JobInfo implements Parcelable {
                        " setRequiresDeviceIdle is an error.");
            }
            JobInfo jobInfo = new JobInfo(this);
            jobInfo.enforceValidity(disallowPrefetchDeadlines, rejectNegativeNetworkEstimates);
            jobInfo.enforceValidity(disallowPrefetchDeadlines, rejectNegativeNetworkEstimates,
                    enforceMinimumTimeWindows);
            return jobInfo;
        }

@@ -2176,7 +2223,8 @@ public class JobInfo implements Parcelable {
     * @hide
     */
    public final void enforceValidity(boolean disallowPrefetchDeadlines,
            boolean rejectNegativeNetworkEstimates) {
            boolean rejectNegativeNetworkEstimates,
            boolean enforceMinimumTimeWindows) {
        // Check that network estimates require network type and are reasonable values.
        if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0)
                && networkRequest == null) {
@@ -2291,6 +2339,39 @@ public class JobInfo implements Parcelable {
                throw new IllegalArgumentException("Invalid priority level provided: " + mPriority);
        }

        if (enforceMinimumTimeWindows
                && Flags.enforceMinimumTimeWindows()
                // TODO(312197030): remove exemption for the system
                && !UserHandle.isCore(Process.myUid())
                && hasLateConstraint && !isPeriodic) {
            final long windowStart = hasEarlyConstraint ? minLatencyMillis : 0;
            if (mPriority >= PRIORITY_DEFAULT) {
                if (maxExecutionDelayMillis - windowStart < HOUR_IN_MILLIS) {
                    throw new IllegalArgumentException(
                            getPriorityString(mPriority)
                                    + " cannot have a time window less than 1 hour."
                                    + " Delay=" + windowStart
                                    + ", deadline=" + maxExecutionDelayMillis);
                }
            } else if (mPriority >= PRIORITY_LOW) {
                if (maxExecutionDelayMillis - windowStart < 6 * HOUR_IN_MILLIS) {
                    throw new IllegalArgumentException(
                            getPriorityString(mPriority)
                                    + " cannot have a time window less than 6 hours."
                                    + " Delay=" + windowStart
                                    + ", deadline=" + maxExecutionDelayMillis);
                }
            } else {
                if (maxExecutionDelayMillis - windowStart < 12 * HOUR_IN_MILLIS) {
                    throw new IllegalArgumentException(
                            getPriorityString(mPriority)
                                    + " cannot have a time window less than 12 hours."
                                    + " Delay=" + windowStart
                                    + ", deadline=" + maxExecutionDelayMillis);
                }
            }
        }

        if (isExpedited) {
            if (hasEarlyConstraint) {
                throw new IllegalArgumentException("An expedited job cannot have a time delay");
+4 −2
Original line number Diff line number Diff line
@@ -4388,7 +4388,7 @@ public class JobSchedulerService extends com.android.server.SystemService
                    Slog.w(TAG, "Uid " + uid + " set bias on its job");
                    return new JobInfo.Builder(job)
                            .setBias(JobInfo.BIAS_DEFAULT)
                            .build(false, false);
                            .build(false, false, false);
                }
            }

@@ -4410,7 +4410,9 @@ public class JobSchedulerService extends com.android.server.SystemService
            job.enforceValidity(
                    CompatChanges.isChangeEnabled(
                            JobInfo.DISALLOW_DEADLINES_FOR_PREFETCH_JOBS, callingUid),
                    rejectNegativeNetworkEstimates);
                    rejectNegativeNetworkEstimates,
                    CompatChanges.isChangeEnabled(
                            JobInfo.ENFORCE_MINIMUM_TIME_WINDOWS, callingUid));
            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
                getContext().enforceCallingOrSelfPermission(
                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
+1 −1
Original line number Diff line number Diff line
@@ -1495,7 +1495,7 @@ public final class JobStore {
                // return value), the deadline is dropped. Periodic jobs require all constraints
                // to be met, so there's no issue with their deadlines.
                // The same logic applies for other target SDK-based validation checks.
                builtJob = jobBuilder.build(false, false);
                builtJob = jobBuilder.build(false, false, false);
            } catch (Exception e) {
                Slog.w(TAG, "Unable to build job from XML, ignoring: " + jobBuilder.summarize(), e);
                return null;
+1 −1
Original line number Diff line number Diff line
@@ -636,7 +636,7 @@ public final class JobStatus {
                    .build());
            // Don't perform validation checks at this point since we've already passed the
            // initial validation check.
            job = builder.build(false, false);
            job = builder.build(false, false, false);
        }

        this.job = job;
Loading