Loading apex/jobscheduler/framework/java/android/app/job/JobInfo.java +167 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) */ Loading Loading @@ -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. Loading Loading @@ -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; } Loading Loading @@ -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); } Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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() */ Loading @@ -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 Loading Loading @@ -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"); Loading @@ -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"); } Loading apex/jobscheduler/service/java/com/android/server/job/JobStore.java +16 −0 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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)); } /** Loading apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +2 −3 Original line number Diff line number Diff line Loading @@ -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)); } Loading apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +4 −4 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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 Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +21 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -1142,6 +1141,10 @@ public final class JobStatus { mInternalFlags |= flags; } int getPreferredConstraintFlags() { return mPreferredConstraints; } public int getSatisfiedConstraintFlags() { return satisfiedConstraints; } Loading Loading @@ -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 Loading
apex/jobscheduler/framework/java/android/app/job/JobInfo.java +167 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) */ Loading Loading @@ -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. Loading Loading @@ -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; } Loading Loading @@ -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); } Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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() */ Loading @@ -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 Loading Loading @@ -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"); Loading @@ -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"); } Loading
apex/jobscheduler/service/java/com/android/server/job/JobStore.java +16 −0 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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)); } /** Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +2 −3 Original line number Diff line number Diff line Loading @@ -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)); } Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +4 −4 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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 Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +21 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -1142,6 +1141,10 @@ public final class JobStatus { mInternalFlags |= flags; } int getPreferredConstraintFlags() { return mPreferredConstraints; } public int getSatisfiedConstraintFlags() { return satisfiedConstraints; } Loading Loading @@ -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