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

Commit 93d5b793 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add methods to mark some constraints as optional." into udc-dev

parents 2d55867d eb72459a
Loading
Loading
Loading
Loading
+167 −7
Original line number Diff line number Diff line
@@ -432,6 +432,7 @@ public class JobInfo implements Parcelable {
    @UnsupportedAppUsage
    private final ComponentName service;
    private final int constraintFlags;
    private final int mPreferredConstraintFlags;
    private final TriggerContentUri[] triggerContentUris;
    private final long triggerContentUpdateDelay;
    private final long triggerContentMaxDelay;
@@ -521,6 +522,30 @@ public class JobInfo implements Parcelable {
        return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic();
    }

    /**
     * @hide
     * @see JobInfo.Builder#setPrefersBatteryNotLow(boolean)
     */
    public boolean isPreferBatteryNotLow() {
        return (mPreferredConstraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
    }

    /**
     * @hide
     * @see JobInfo.Builder#setPrefersCharging(boolean)
     */
    public boolean isPreferCharging() {
        return (mPreferredConstraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
    }

    /**
     * @hide
     * @see JobInfo.Builder#setPrefersDeviceIdle(boolean)
     */
    public boolean isPreferDeviceIdle() {
        return (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
    }

    /**
     * @see JobInfo.Builder#setRequiresCharging(boolean)
     */
@@ -556,6 +581,13 @@ public class JobInfo implements Parcelable {
        return constraintFlags;
    }

    /**
     * @hide
     */
    public int getPreferredConstraintFlags() {
        return mPreferredConstraintFlags;
    }

    /**
     * Which content: URIs must change for the job to be scheduled.  Returns null
     * if there are none required.
@@ -800,6 +832,9 @@ public class JobInfo implements Parcelable {
        if (constraintFlags != j.constraintFlags) {
            return false;
        }
        if (mPreferredConstraintFlags != j.mPreferredConstraintFlags) {
            return false;
        }
        if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) {
            return false;
        }
@@ -880,6 +915,7 @@ public class JobInfo implements Parcelable {
            hashCode = 31 * hashCode + service.hashCode();
        }
        hashCode = 31 * hashCode + constraintFlags;
        hashCode = 31 * hashCode + mPreferredConstraintFlags;
        if (triggerContentUris != null) {
            hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris);
        }
@@ -922,6 +958,7 @@ public class JobInfo implements Parcelable {
        }
        service = in.readParcelable(null);
        constraintFlags = in.readInt();
        mPreferredConstraintFlags = in.readInt();
        triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
        triggerContentUpdateDelay = in.readLong();
        triggerContentMaxDelay = in.readLong();
@@ -956,6 +993,7 @@ public class JobInfo implements Parcelable {
        clipGrantFlags = b.mClipGrantFlags;
        service = b.mJobService;
        constraintFlags = b.mConstraintFlags;
        mPreferredConstraintFlags = b.mPreferredConstraintFlags;
        triggerContentUris = b.mTriggerContentUris != null
                ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()])
                : null;
@@ -999,6 +1037,7 @@ public class JobInfo implements Parcelable {
        }
        out.writeParcelable(service, flags);
        out.writeInt(constraintFlags);
        out.writeInt(mPreferredConstraintFlags);
        out.writeTypedArray(triggerContentUris, flags);
        out.writeLong(triggerContentUpdateDelay);
        out.writeLong(triggerContentMaxDelay);
@@ -1146,6 +1185,7 @@ public class JobInfo implements Parcelable {
        private int mFlags;
        // Requirements.
        private int mConstraintFlags;
        private int mPreferredConstraintFlags;
        private NetworkRequest mNetworkRequest;
        private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
        private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
@@ -1199,6 +1239,7 @@ public class JobInfo implements Parcelable {
            mBias = job.getBias();
            mFlags = job.getFlags();
            mConstraintFlags = job.getConstraintFlags();
            mPreferredConstraintFlags = job.getPreferredConstraintFlags();
            mNetworkRequest = job.getRequiredNetwork();
            mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
            mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
@@ -1341,9 +1382,6 @@ public class JobInfo implements Parcelable {
         * Calling this method will override any requirements previously defined
         * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only
         * want to call one of these methods.
         * <p> Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
         * {@link JobScheduler} may try to shift the execution of jobs requiring
         * {@link #NETWORK_TYPE_ANY} to when there is access to an un-metered network.
         * <p class="note">
         * When your job executes in
         * {@link JobService#onStartJob(JobParameters)}, be sure to use the
@@ -1505,10 +1543,105 @@ public class JobInfo implements Parcelable {
        }

        /**
         * Specify that to run this job, the device must be charging (or be a
         * Specify that this job would prefer to be run when the device's battery is not low.
         * This defaults to {@code false}.
         *
         * <p>The system may attempt to delay this job until the device's battery is not low,
         * but may choose to run it even if the device's battery is low. JobScheduler will not stop
         * this job if this constraint is no longer satisfied after the job has started running.
         * If this job must only run when the device's battery is not low,
         * use {@link #setRequiresBatteryNotLow(boolean)} instead.
         *
         * <p>
         * Because it doesn't make sense for a constraint to be both preferred and required,
         * calling both this and {@link #setRequiresBatteryNotLow(boolean)} with {@code true}
         * will result in an {@link java.lang.IllegalArgumentException} when
         * {@link android.app.job.JobInfo.Builder#build()} is called.
         *
         * @param prefersBatteryNotLow Pass {@code true} to prefer that the device's battery level
         *                             not be low in order to run the job.
         * @return This object for method chaining
         * @see JobInfo#isPreferBatteryNotLow()
         * @hide
         */
        @NonNull
        public Builder setPrefersBatteryNotLow(boolean prefersBatteryNotLow) {
            mPreferredConstraintFlags =
                    (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
                            | (prefersBatteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0);
            return this;
        }

        /**
         * Specify that this job would prefer to be run when the device is charging (or be a
         * non-battery-powered device connected to permanent power, such as Android TV
         * devices). This defaults to {@code false}.
         *
         * <p>
         * The system may attempt to delay this job until the device is charging, but may
         * choose to run it even if the device is not charging. JobScheduler will not stop
         * this job if this constraint is no longer satisfied after the job has started running.
         * If this job must only run when the device is charging,
         * use {@link #setRequiresCharging(boolean)} instead.
         *
         * <p>
         * Because it doesn't make sense for a constraint to be both preferred and required,
         * calling both this and {@link #setRequiresCharging(boolean)} with {@code true}
         * will result in an {@link java.lang.IllegalArgumentException} when
         * {@link android.app.job.JobInfo.Builder#build()} is called.
         *
         * @param prefersCharging Pass {@code true} to prefer that the device be
         *                        charging in order to run the job.
         * @return This object for method chaining
         * @see JobInfo#isPreferCharging()
         * @hide
         */
        @NonNull
        public Builder setPrefersCharging(boolean prefersCharging) {
            mPreferredConstraintFlags = (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_CHARGING)
                    | (prefersCharging ? CONSTRAINT_FLAG_CHARGING : 0);
            return this;
        }

        /**
         * Specify that this job would prefer to be run when the device is not in active use.
         * This defaults to {@code false}.
         *
         * <p>The system may attempt to delay this job until the device is not in active use,
         * but may choose to run it even if the device is not idle. JobScheduler will not stop
         * this job if this constraint is no longer satisfied after the job has started running.
         * If this job must only run when the device is not in active use,
         * use {@link #setRequiresDeviceIdle(boolean)} instead.
         *
         * <p>
         * Because it doesn't make sense for a constraint to be both preferred and required,
         * calling both this and {@link #setRequiresDeviceIdle(boolean)} with {@code true}
         * will result in an {@link java.lang.IllegalArgumentException} when
         * {@link android.app.job.JobInfo.Builder#build()} is called.
         *
         * <p class="note">Despite the similar naming, this job constraint is <em>not</em>
         * related to the system's "device idle" or "doze" states.  This constraint only
         * determines whether a job is allowed to run while the device is directly in use.
         *
         * @param prefersDeviceIdle Pass {@code true} to prefer that the device not be in active
         *                          use when running this job.
         * @return This object for method chaining
         * @see JobInfo#isRequireDeviceIdle()
         * @hide
         */
        @NonNull
        public Builder setPrefersDeviceIdle(boolean prefersDeviceIdle) {
            mPreferredConstraintFlags = (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_DEVICE_IDLE)
                    | (prefersDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0);
            return this;
        }

        /**
         * Specify that to run this job, the device must be charging (or be a
         * non-battery-powered device connected to permanent power, such as Android TV
         * devices). This defaults to {@code false}. Setting this to {@code false} <b>DOES NOT</b>
         * mean the job will only run when the device is not charging.
         *
         * <p class="note">For purposes of running jobs, a battery-powered device
         * "charging" is not quite the same as simply being connected to power.  If the
         * device is so busy that the battery is draining despite a power connection, jobs
@@ -1530,7 +1663,9 @@ public class JobInfo implements Parcelable {
         * Specify that to run this job, the device's battery level must not be low.
         * This defaults to false.  If true, the job will only run when the battery level
         * is not low, which is generally the point where the user is given a "low battery"
         * warning.
         * warning. Setting this to {@code false} <b>DOES NOT</b> mean the job will only run
         * when the battery is low.
         *
         * @param batteryNotLow Whether or not the device's battery level must not be low.
         * @see JobInfo#isRequireBatteryNotLow()
         */
@@ -1543,7 +1678,8 @@ public class JobInfo implements Parcelable {
        /**
         * When set {@code true}, ensure that this job will not run if the device is in active use.
         * The default state is {@code false}: that is, the for the job to be runnable even when
         * someone is interacting with the device.
         * someone is interacting with the device. Setting this to {@code false} <b>DOES NOT</b>
         * mean the job will only run when the device is not idle.
         *
         * <p>This state is a loose definition provided by the system. In general, it means that
         * the device is not currently being used interactively, and has not been in use for some
@@ -2156,6 +2292,29 @@ public class JobInfo implements Parcelable {
            }
        }

        if ((constraintFlags & mPreferredConstraintFlags) != 0) {
            // Something is marked as both preferred and required. Try to give a clear exception
            // reason.
            if ((constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0
                    && (mPreferredConstraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0) {
                throw new IllegalArgumentException(
                        "battery-not-low constraint cannot be both preferred and required");
            }
            if ((constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0
                    && (mPreferredConstraintFlags & CONSTRAINT_FLAG_CHARGING) != 0) {
                throw new IllegalArgumentException(
                        "charging constraint cannot be both preferred and required");
            }
            if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0
                    && (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
                throw new IllegalArgumentException(
                        "device idle constraint cannot be both preferred and required");
            }
            // Couldn't figure out what the overlap was. Just use a generic message.
            throw new IllegalArgumentException(
                    "constraints cannot be both preferred and required");
        }

        if (isUserInitiated) {
            if (hasEarlyConstraint) {
                throw new IllegalArgumentException("A user-initiated job cannot have a time delay");
@@ -2173,7 +2332,8 @@ public class JobInfo implements Parcelable {
            if (mPriority != PRIORITY_MAX) {
                throw new IllegalArgumentException("A user-initiated job must be max priority.");
            }
            if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
            if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0
                    || (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
                throw new IllegalArgumentException(
                        "A user-initiated job cannot have a device-idle constraint");
            }
+16 −0
Original line number Diff line number Diff line
@@ -883,6 +883,15 @@ public final class JobStore {
            if (job.isRequireStorageNotLow()) {
                out.attribute(null, "storage-not-low", Boolean.toString(true));
            }
            if (job.isPreferBatteryNotLow()) {
                out.attributeBoolean(null, "prefer-battery-not-low", true);
            }
            if (job.isPreferCharging()) {
                out.attributeBoolean(null, "prefer-charging", true);
            }
            if (job.isPreferDeviceIdle()) {
                out.attributeBoolean(null, "prefer-idle", true);
            }
            out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS);
        }

@@ -1538,6 +1547,13 @@ public final class JobStore {
            if (val != null) {
                jobBuilder.setRequiresStorageNotLow(true);
            }

            jobBuilder.setPrefersBatteryNotLow(
                    parser.getAttributeBoolean(null, "prefer-battery-not-low", false));
            jobBuilder.setPrefersCharging(
                    parser.getAttributeBoolean(null, "prefer-charging", false));
            jobBuilder.setPrefersDeviceIdle(
                    parser.getAttributeBoolean(null, "prefer-idle", false));
        }

        /**
+2 −3
Original line number Diff line number Diff line
@@ -1147,10 +1147,9 @@ public final class ConnectivityController extends RestrictingController implemen

        final boolean changed = jobStatus.setConnectivityConstraintSatisfied(nowElapsed, satisfied);

        if (jobStatus.getPreferUnmetered()) {
        jobStatus.setHasAccessToUnmetered(satisfied && capabilities != null
                && capabilities.hasCapability(NET_CAPABILITY_NOT_METERED));

        if (jobStatus.getPreferUnmetered()) {
            jobStatus.setFlexibilityConstraintSatisfied(nowElapsed,
                    mFlexibilityController.isFlexibilitySatisfiedLocked(jobStatus));
        }
+4 −4
Original line number Diff line number Diff line
@@ -239,14 +239,14 @@ public final class FlexibilityController extends StateController {
        return !mFlexibilityEnabled
                || mService.getUidBias(js.getSourceUid()) == JobInfo.BIAS_TOP_APP
                || mService.isCurrentlyRunningLocked(js)
                || getNumSatisfiedRequiredConstraintsLocked(js)
                || getNumSatisfiedFlexibleConstraintsLocked(js)
                >= js.getNumRequiredFlexibleConstraints();
    }

    @VisibleForTesting
    @GuardedBy("mLock")
    int getNumSatisfiedRequiredConstraintsLocked(JobStatus js) {
        return Integer.bitCount(mSatisfiedFlexibleConstraints)
    int getNumSatisfiedFlexibleConstraintsLocked(JobStatus js) {
        return Integer.bitCount(mSatisfiedFlexibleConstraints & js.getPreferredConstraintFlags())
                + (js.getHasAccessToUnmetered() ? 1 : 0);
    }

@@ -651,7 +651,7 @@ public final class FlexibilityController extends StateController {
        static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
                FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";

        private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
        private static final boolean DEFAULT_FLEXIBILITY_ENABLED = true;
        @VisibleForTesting
        static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
        @VisibleForTesting
+21 −15
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.server.job.controllers;

import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;

import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
@@ -25,8 +24,6 @@ import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.controllers.FlexibilityController.NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
@@ -115,12 +112,11 @@ public final class JobStatus {
    static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
    static final int CONSTRAINT_PREFETCH = 1 << 23;
    static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
    static final int CONSTRAINT_FLEXIBLE = 1 << 21; // Implicit constraint
    static final int CONSTRAINT_FLEXIBLE = 1 << 21;

    private static final int IMPLICIT_CONSTRAINTS = 0
            | CONSTRAINT_BACKGROUND_NOT_RESTRICTED
            | CONSTRAINT_DEVICE_NOT_DOZING
            | CONSTRAINT_FLEXIBLE
            | CONSTRAINT_TARE_WEALTH
            | CONSTRAINT_WITHIN_QUOTA;

@@ -298,6 +294,7 @@ public final class JobStatus {

    // Constraints.
    final int requiredConstraints;
    private final int mPreferredConstraints;
    private final int mRequiredConstraintsOfInterest;
    int satisfiedConstraints = 0;
    private int mSatisfiedConstraintsOfInterest = 0;
@@ -618,24 +615,26 @@ public final class JobStatus {
        }
        mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly;

        mPreferUnmetered = job.getRequiredNetwork() != null
                && !job.getRequiredNetwork().hasCapability(NET_CAPABILITY_NOT_METERED);
        mPreferredConstraints = job.getPreferredConstraintFlags();

        // Exposing a preferredNetworkRequest API requires that we make sure that the preferred
        // NetworkRequest is a subset of the required NetworkRequest. We currently don't have the
        // code to ensure that, so disable this part for now.
        // TODO(236261941): look into enabling flexible network constraint requests
        mPreferUnmetered = false;
                // && job.getRequiredNetwork() != null
                // && !job.getRequiredNetwork().hasCapability(NET_CAPABILITY_NOT_METERED);

        final boolean lacksSomeFlexibleConstraints =
                ((~requiredConstraints) & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS) != 0
                        || mPreferUnmetered;
        final boolean satisfiesMinWindowException =
                (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis)
                >= MIN_WINDOW_FOR_FLEXIBILITY_MS;

        // The first time a job is rescheduled it will not be subject to flexible constraints.
        // Otherwise, every consecutive reschedule increases a jobs' flexibility deadline.
        if (!isRequestedExpeditedJob() && !job.isUserInitiated()
        if (mPreferredConstraints != 0 && !isRequestedExpeditedJob() && !job.isUserInitiated()
                && satisfiesMinWindowException
                && (numFailures + numSystemStops) != 1
                && lacksSomeFlexibleConstraints) {
            mNumRequiredFlexibleConstraints =
                    NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
                && (numFailures + numSystemStops) != 1) {
            mNumRequiredFlexibleConstraints = Integer.bitCount(mPreferredConstraints);
            requiredConstraints |= CONSTRAINT_FLEXIBLE;
        } else {
            mNumRequiredFlexibleConstraints = 0;
@@ -1142,6 +1141,10 @@ public final class JobStatus {
        mInternalFlags |= flags;
    }

    int getPreferredConstraintFlags() {
        return mPreferredConstraints;
    }

    public int getSatisfiedConstraintFlags() {
        return satisfiedConstraints;
    }
@@ -2501,6 +2504,9 @@ public final class JobStatus {
        pw.print("Required constraints:");
        dumpConstraints(pw, requiredConstraints);
        pw.println();
        pw.print("Preferred constraints:");
        dumpConstraints(pw, mPreferredConstraints);
        pw.println();
        pw.print("Dynamic constraints:");
        dumpConstraints(pw, mDynamicConstraints);
        pw.println();
Loading