Merge "Add methods to mark some constraints as optional." into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
93d5b7930d
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1147,10 +1147,9 @@ public final class ConnectivityController extends RestrictingController implemen
|
||||
|
||||
final boolean changed = jobStatus.setConnectivityConstraintSatisfied(nowElapsed, satisfied);
|
||||
|
||||
jobStatus.setHasAccessToUnmetered(satisfied && capabilities != null
|
||||
&& capabilities.hasCapability(NET_CAPABILITY_NOT_METERED));
|
||||
if (jobStatus.getPreferUnmetered()) {
|
||||
jobStatus.setHasAccessToUnmetered(satisfied && capabilities != null
|
||||
&& capabilities.hasCapability(NET_CAPABILITY_NOT_METERED));
|
||||
|
||||
jobStatus.setFlexibilityConstraintSatisfied(nowElapsed,
|
||||
mFlexibilityController.isFlexibilitySatisfiedLocked(jobStatus));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -840,8 +840,9 @@ public class ConnectivityControllerTest {
|
||||
final JobStatus blue = createJobStatus(createJob()
|
||||
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
|
||||
// Unmetered preference is disabled for now.
|
||||
assertFalse(red.getPreferUnmetered());
|
||||
assertTrue(blue.getPreferUnmetered());
|
||||
assertFalse(blue.getPreferUnmetered());
|
||||
|
||||
controller.maybeStartTrackingJobLocked(red, null);
|
||||
controller.maybeStartTrackingJobLocked(blue, null);
|
||||
@@ -895,7 +896,7 @@ public class ConnectivityControllerTest {
|
||||
generalCallback.onLost(meteredNet);
|
||||
|
||||
assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
|
||||
assertFalse(red.getHasAccessToUnmetered());
|
||||
assertTrue(red.getHasAccessToUnmetered());
|
||||
|
||||
assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
|
||||
assertTrue(blue.getHasAccessToUnmetered());
|
||||
|
||||
@@ -189,7 +189,10 @@ public class FlexibilityControllerTest {
|
||||
}
|
||||
|
||||
private static JobInfo.Builder createJob(int id) {
|
||||
return new JobInfo.Builder(id, new ComponentName("foo", "bar"));
|
||||
return new JobInfo.Builder(id, new ComponentName("foo", "bar"))
|
||||
.setPrefersBatteryNotLow(true)
|
||||
.setPrefersCharging(true)
|
||||
.setPrefersDeviceIdle(true);
|
||||
}
|
||||
|
||||
private JobStatus createJobStatus(String testTag, JobInfo.Builder job) {
|
||||
@@ -533,12 +536,15 @@ public class FlexibilityControllerTest {
|
||||
jb = createJob(i);
|
||||
if (i > 0) {
|
||||
jb.setRequiresDeviceIdle(true);
|
||||
jb.setPrefersDeviceIdle(false);
|
||||
}
|
||||
if (i > 1) {
|
||||
jb.setRequiresBatteryNotLow(true);
|
||||
jb.setPrefersBatteryNotLow(false);
|
||||
}
|
||||
if (i > 2) {
|
||||
jb.setRequiresCharging(true);
|
||||
jb.setPrefersCharging(false);
|
||||
}
|
||||
jobs[i] = createJobStatus("", jb);
|
||||
flexTracker.add(jobs[i]);
|
||||
@@ -547,53 +553,55 @@ public class FlexibilityControllerTest {
|
||||
synchronized (mFlexibilityController.mLock) {
|
||||
ArrayList<ArraySet<JobStatus>> trackedJobs = flexTracker.getArrayList();
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(0, trackedJobs.get(1).size());
|
||||
assertEquals(0, trackedJobs.get(2).size());
|
||||
assertEquals(3, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(0, trackedJobs.get(1).size());
|
||||
assertEquals(1, trackedJobs.get(1).size());
|
||||
assertEquals(1, trackedJobs.get(2).size());
|
||||
assertEquals(2, trackedJobs.get(3).size());
|
||||
assertEquals(1, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(1, trackedJobs.get(1).size());
|
||||
assertEquals(0, trackedJobs.get(2).size());
|
||||
assertEquals(2, trackedJobs.get(3).size());
|
||||
assertEquals(2, trackedJobs.get(2).size());
|
||||
assertEquals(0, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(2, trackedJobs.get(1).size());
|
||||
assertEquals(1, trackedJobs.get(2).size());
|
||||
assertEquals(0, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
|
||||
assertEquals(2, trackedJobs.get(0).size());
|
||||
assertEquals(0, trackedJobs.get(1).size());
|
||||
assertEquals(0, trackedJobs.get(2).size());
|
||||
assertEquals(2, trackedJobs.get(3).size());
|
||||
assertEquals(1, trackedJobs.get(1).size());
|
||||
assertEquals(1, trackedJobs.get(2).size());
|
||||
assertEquals(0, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.remove(jobs[1]);
|
||||
assertEquals(2, trackedJobs.get(0).size());
|
||||
assertEquals(0, trackedJobs.get(1).size());
|
||||
assertEquals(1, trackedJobs.get(1).size());
|
||||
assertEquals(0, trackedJobs.get(2).size());
|
||||
assertEquals(1, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.resetJobNumDroppedConstraints(jobs[0], FROZEN_TIME);
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(0, trackedJobs.get(1).size());
|
||||
assertEquals(0, trackedJobs.get(2).size());
|
||||
assertEquals(2, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(1, trackedJobs.get(1).size());
|
||||
assertEquals(0, trackedJobs.get(2).size());
|
||||
assertEquals(1, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(2, trackedJobs.get(1).size());
|
||||
assertEquals(0, trackedJobs.get(2).size());
|
||||
assertEquals(0, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
|
||||
// Over halfway through the flex window. The job that prefers all flex constraints
|
||||
// should have its first flex constraint dropped.
|
||||
final long nowElapsed = ((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2)
|
||||
+ HOUR_IN_MILLIS);
|
||||
JobSchedulerService.sElapsedRealtimeClock =
|
||||
@@ -601,9 +609,9 @@ public class FlexibilityControllerTest {
|
||||
|
||||
flexTracker.resetJobNumDroppedConstraints(jobs[0], nowElapsed);
|
||||
assertEquals(1, trackedJobs.get(0).size());
|
||||
assertEquals(0, trackedJobs.get(1).size());
|
||||
assertEquals(1, trackedJobs.get(1).size());
|
||||
assertEquals(1, trackedJobs.get(2).size());
|
||||
assertEquals(1, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(3).size());
|
||||
assertEquals(0, trackedJobs.get(4).size());
|
||||
}
|
||||
}
|
||||
@@ -618,8 +626,13 @@ public class FlexibilityControllerTest {
|
||||
|
||||
@Test
|
||||
public void testExceptions_UserInitiated() {
|
||||
JobInfo.Builder jb = createJob(0);
|
||||
jb.setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
|
||||
JobInfo.Builder jb = createJob(0)
|
||||
.setUserInitiated(true)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||
// Attempt to add flex constraints to the job. For now, we will ignore them.
|
||||
.setPrefersBatteryNotLow(true)
|
||||
.setPrefersCharging(true)
|
||||
.setPrefersDeviceIdle(false);
|
||||
JobStatus js = createJobStatus("testExceptions_UserInitiated", jb);
|
||||
assertFalse(js.hasFlexibilityConstraint());
|
||||
}
|
||||
@@ -635,10 +648,10 @@ public class FlexibilityControllerTest {
|
||||
|
||||
@Test
|
||||
public void testExceptions_NoFlexibleConstraints() {
|
||||
JobInfo.Builder jb = createJob(0);
|
||||
jb.setRequiresDeviceIdle(true);
|
||||
jb.setRequiresCharging(true);
|
||||
jb.setRequiresBatteryNotLow(true);
|
||||
JobInfo.Builder jb = createJob(0)
|
||||
.setPrefersBatteryNotLow(false)
|
||||
.setPrefersCharging(false)
|
||||
.setPrefersDeviceIdle(false);
|
||||
JobStatus js = createJobStatus("testExceptions_NoFlexibleConstraints", jb);
|
||||
assertFalse(js.hasFlexibilityConstraint());
|
||||
}
|
||||
@@ -697,14 +710,49 @@ public class FlexibilityControllerTest {
|
||||
JobStatus js = createJobStatus("testTopAppBypass", jb);
|
||||
synchronized (mFlexibilityController.mLock) {
|
||||
js.setHasAccessToUnmetered(false);
|
||||
assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
|
||||
assertEquals(0, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
|
||||
js.setHasAccessToUnmetered(true);
|
||||
assertEquals(1, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
|
||||
assertEquals(1, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
|
||||
js.setHasAccessToUnmetered(false);
|
||||
assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
|
||||
assertEquals(0, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNumSatisfiedFlexibleConstraints() {
|
||||
long nowElapsed = FROZEN_TIME;
|
||||
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, true, nowElapsed);
|
||||
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING, true, nowElapsed);
|
||||
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, true, nowElapsed);
|
||||
JobInfo.Builder jb = createJob(0)
|
||||
.setPrefersBatteryNotLow(false)
|
||||
.setPrefersCharging(false)
|
||||
.setPrefersDeviceIdle(false);
|
||||
JobStatus js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
|
||||
assertEquals(0, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
|
||||
|
||||
jb = createJob(0)
|
||||
.setPrefersBatteryNotLow(true)
|
||||
.setPrefersCharging(false)
|
||||
.setPrefersDeviceIdle(false);
|
||||
js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
|
||||
assertEquals(1, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
|
||||
|
||||
jb = createJob(0)
|
||||
.setPrefersBatteryNotLow(true)
|
||||
.setPrefersCharging(false)
|
||||
.setPrefersDeviceIdle(true);
|
||||
js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
|
||||
assertEquals(2, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
|
||||
|
||||
jb = createJob(0)
|
||||
.setPrefersBatteryNotLow(true)
|
||||
.setPrefersCharging(true)
|
||||
.setPrefersDeviceIdle(true);
|
||||
js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
|
||||
assertEquals(3, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetConstraintSatisfied_Constraints() {
|
||||
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME);
|
||||
@@ -736,8 +784,11 @@ public class FlexibilityControllerTest {
|
||||
jb = createJob(i);
|
||||
constraints = constraintCombinations[i];
|
||||
jb.setRequiresDeviceIdle((constraints & CONSTRAINT_IDLE) != 0);
|
||||
jb.setPrefersDeviceIdle((constraints & CONSTRAINT_IDLE) == 0);
|
||||
jb.setRequiresBatteryNotLow((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0);
|
||||
jb.setPrefersBatteryNotLow((constraints & CONSTRAINT_BATTERY_NOT_LOW) == 0);
|
||||
jb.setRequiresCharging((constraints & CONSTRAINT_CHARGING) != 0);
|
||||
jb.setPrefersCharging((constraints & CONSTRAINT_CHARGING) == 0);
|
||||
synchronized (mFlexibilityController.mLock) {
|
||||
mFlexibilityController.maybeStartTrackingJobLocked(
|
||||
createJobStatus(String.valueOf(i), jb), null);
|
||||
|
||||
@@ -1039,7 +1039,9 @@ public class JobStatusTest {
|
||||
@Test
|
||||
public void testReadinessStatusWithConstraint_FlexibilityConstraint() {
|
||||
final JobStatus job = createJobStatus(
|
||||
new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
|
||||
new JobInfo.Builder(101, new ComponentName("foo", "bar"))
|
||||
.setPrefersCharging(true)
|
||||
.build());
|
||||
job.setConstraintSatisfied(CONSTRAINT_FLEXIBLE, sElapsedRealtimeClock.millis(), false);
|
||||
markImplicitConstraintsSatisfied(job, true);
|
||||
assertTrue(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true));
|
||||
|
||||
@@ -727,6 +727,66 @@ public class JobStoreTest {
|
||||
taskStatus.getJob().isRequireBatteryNotLow());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistedPreferredBatteryNotLowConstraint() throws Exception {
|
||||
JobInfo.Builder b = new Builder(8, mComponent)
|
||||
.setPrefersBatteryNotLow(true)
|
||||
.setPersisted(true);
|
||||
JobStatus taskStatus =
|
||||
JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null, null);
|
||||
|
||||
mTaskStoreUnderTest.add(taskStatus);
|
||||
waitForPendingIo();
|
||||
|
||||
final JobSet jobStatusSet = new JobSet();
|
||||
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
|
||||
assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
|
||||
JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
|
||||
assertEquals("Battery-not-low constraint not persisted correctly.",
|
||||
taskStatus.getJob().isPreferBatteryNotLow(),
|
||||
loaded.getJob().isPreferBatteryNotLow());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistedPreferredChargingConstraint() throws Exception {
|
||||
JobInfo.Builder b = new Builder(8, mComponent)
|
||||
.setPrefersCharging(true)
|
||||
.setPersisted(true);
|
||||
JobStatus taskStatus =
|
||||
JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null, null);
|
||||
|
||||
mTaskStoreUnderTest.add(taskStatus);
|
||||
waitForPendingIo();
|
||||
|
||||
final JobSet jobStatusSet = new JobSet();
|
||||
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
|
||||
assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
|
||||
JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
|
||||
assertEquals("Charging constraint not persisted correctly.",
|
||||
taskStatus.getJob().isPreferCharging(),
|
||||
loaded.getJob().isPreferCharging());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistedPreferredDeviceIdleConstraint() throws Exception {
|
||||
JobInfo.Builder b = new Builder(8, mComponent)
|
||||
.setPrefersDeviceIdle(true)
|
||||
.setPersisted(true);
|
||||
JobStatus taskStatus =
|
||||
JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null, null);
|
||||
|
||||
mTaskStoreUnderTest.add(taskStatus);
|
||||
waitForPendingIo();
|
||||
|
||||
final JobSet jobStatusSet = new JobSet();
|
||||
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
|
||||
assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
|
||||
JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
|
||||
assertEquals("Idle constraint not persisted correctly.",
|
||||
taskStatus.getJob().isPreferDeviceIdle(),
|
||||
loaded.getJob().isPreferDeviceIdle());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobWorkItems() throws Exception {
|
||||
JobWorkItem item1 = new JobWorkItem.Builder().build();
|
||||
|
||||
Reference in New Issue
Block a user