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

Commit abb975e9 authored by Kweku Adams's avatar Kweku Adams
Browse files

Update minimum time window policy.

The new policy rejects jobs with short time windows and functional
constraints (eg. charging, connectivity, etc.). Jobs without functional
constraints and short time windows are allowed, but discouraged in a log
message. The minimum accepted window is also brought down to be in line
with the minimum job period.

The setOverrideDeadline documentation is also updated to make it clear
that the deadline just invalidates other requested (ie. functional)
constraints, but the job may be delayed by factors such as system health
signals.

Bug: 311402873
Test: atest CtsJobSchedulerTestCases:JobInfoTest
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/job
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/job
Change-Id: I05def8a34e102c2e9b843d03f303f377fe4d4356
parent c80b0a81
Loading
Loading
Loading
Loading
+47 −53
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ 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;
@@ -50,9 +49,7 @@ 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;

@@ -206,6 +203,8 @@ public class JobInfo implements Parcelable {
    /* Minimum flex for a periodic job, in milliseconds. */
    private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes

    private static final long MIN_ALLOWED_TIME_WINDOW_MILLIS = MIN_PERIOD_MILLIS;

    /**
     * Minimum backoff interval for a job, in milliseconds
     * @hide
@@ -1881,11 +1880,12 @@ 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.
         * Set a deadline after which all other functional requested constraints will be ignored.
         * After the deadline has passed, the job can run 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.
         * deadline has passed. The job's execution may be delayed beyond the set deadline by
         * other factors such as Doze mode and system health signals.
         *
         * <p>
         * Because it doesn't make sense setting this property on a periodic job, doing so will
@@ -1894,30 +1894,23 @@ public class JobInfo implements Parcelable {
         *
         * <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
         * constraints, setting a deadline of 0 (or a {@link #setMinimumLatency(long) delay} equal
         * to the deadline) with other constraints makes those constraints
         * meaningless when it comes to execution decisions. Since doing so is indicative of an
         * error in the logic, starting in Android version
         * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, jobs with extremely short
         * time windows will fail to build. 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.
         *
         * <p>
         * This API aimed to guarantee execution of the job by the deadline only on Android version
         * {@link android.os.Build.VERSION_CODES#LOLLIPOP}. That aim and guarantee has not existed
         * since {@link android.os.Build.VERSION_CODES#M}.
         *
         * @see JobInfo#getMaxExecutionDelayMillis()
         */
        public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
@@ -2347,35 +2340,36 @@ 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 boolean hasFunctionalConstraint = networkRequest != null
                || constraintFlags != 0
                || (triggerContentUris != null && triggerContentUris.length > 0);
        if (hasLateConstraint && !isPeriodic) {
            if (!hasFunctionalConstraint) {
                Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'"
                        + " has a deadline with no functional constraints."
                        + " The deadline won't improve job execution latency."
                        + " Consider removing the deadline.");
            } else {
                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
                if (maxExecutionDelayMillis - windowStart < MIN_ALLOWED_TIME_WINDOW_MILLIS) {
                    if (enforceMinimumTimeWindows
                            && Flags.enforceMinimumTimeWindows()) {
                        throw new IllegalArgumentException("Jobs with a deadline and"
                                + " functional constraints cannot have a time window less than "
                                + MIN_ALLOWED_TIME_WINDOW_MILLIS + " ms."
                                + " Job '" + service.flattenToShortString() + "#" + jobId + "'"
                                + " has 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);
                        Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'"
                                + " has a deadline with functional constraints and an extremely"
                                + " short time window of "
                                + (maxExecutionDelayMillis - windowStart) + " ms"
                                + " (delay=" + windowStart
                                + ", deadline=" + maxExecutionDelayMillis + ")."
                                + " The functional constraints are not likely to be satisfied when"
                                + " the job runs.");
                    }
                }
            }
        }