summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java91
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java151
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java159
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java192
4 files changed, 507 insertions, 86 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 0cf0cc5dcd22..e06006f25d3f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -640,22 +640,27 @@ public final class ConnectivityController extends RestrictingController implemen
if (mCcConfig.mShouldReprocessNetworkCapabilities
|| (mFlexibilityController.isEnabled() != mCcConfig.mFlexIsEnabled)) {
AppSchedulingModuleThread.getHandler().post(() -> {
- boolean shouldUpdateJobs = false;
- for (int i = 0; i < mAvailableNetworks.size(); ++i) {
- CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
- if (metadata == null || metadata.networkCapabilities == null) {
- continue;
+ boolean flexAffinitiesChanged = false;
+ boolean flexAffinitiesSatisfied = false;
+ synchronized (mLock) {
+ for (int i = 0; i < mAvailableNetworks.size(); ++i) {
+ CachedNetworkMetadata metadata = mAvailableNetworks.valueAt(i);
+ if (metadata == null) {
+ continue;
+ }
+ if (updateTransportAffinitySatisfaction(metadata)) {
+ // Something changed. Update jobs.
+ flexAffinitiesChanged = true;
+ }
+ flexAffinitiesSatisfied |= metadata.satisfiesTransportAffinities;
}
- boolean satisfies = satisfiesTransportAffinities(metadata.networkCapabilities);
- if (metadata.satisfiesTransportAffinities != satisfies) {
- metadata.satisfiesTransportAffinities = satisfies;
- // Something changed. Update jobs.
- shouldUpdateJobs = true;
+ if (flexAffinitiesChanged) {
+ mFlexibilityController.setConstraintSatisfied(
+ JobStatus.CONSTRAINT_CONNECTIVITY,
+ flexAffinitiesSatisfied, sElapsedRealtimeClock.millis());
+ updateAllTrackedJobsLocked(false);
}
}
- if (shouldUpdateJobs) {
- updateAllTrackedJobsLocked(false);
- }
});
}
}
@@ -1059,6 +1064,22 @@ public final class ConnectivityController extends RestrictingController implemen
return false;
}
+ /**
+ * Updates {@link CachedNetworkMetadata#satisfiesTransportAffinities} in the given
+ * {@link CachedNetworkMetadata} object.
+ * @return true if the satisfaction changed
+ */
+ private boolean updateTransportAffinitySatisfaction(
+ @NonNull CachedNetworkMetadata cachedNetworkMetadata) {
+ final boolean satisfiesAffinities =
+ satisfiesTransportAffinities(cachedNetworkMetadata.networkCapabilities);
+ if (cachedNetworkMetadata.satisfiesTransportAffinities != satisfiesAffinities) {
+ cachedNetworkMetadata.satisfiesTransportAffinities = satisfiesAffinities;
+ return true;
+ }
+ return false;
+ }
+
private boolean satisfiesTransportAffinities(@Nullable NetworkCapabilities capabilities) {
if (!mFlexibilityController.isEnabled()) {
return true;
@@ -1552,7 +1573,9 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
cnm.networkCapabilities = capabilities;
- cnm.satisfiesTransportAffinities = satisfiesTransportAffinities(capabilities);
+ if (updateTransportAffinitySatisfaction(cnm)) {
+ maybeUpdateFlexConstraintLocked(cnm);
+ }
maybeRegisterSignalStrengthCallbackLocked(capabilities);
updateTrackedJobsLocked(-1, network);
postAdjustCallbacks();
@@ -1566,8 +1589,13 @@ public final class ConnectivityController extends RestrictingController implemen
}
synchronized (mLock) {
final CachedNetworkMetadata cnm = mAvailableNetworks.remove(network);
- if (cnm != null && cnm.networkCapabilities != null) {
- maybeUnregisterSignalStrengthCallbackLocked(cnm.networkCapabilities);
+ if (cnm != null) {
+ if (cnm.networkCapabilities != null) {
+ maybeUnregisterSignalStrengthCallbackLocked(cnm.networkCapabilities);
+ }
+ if (cnm.satisfiesTransportAffinities) {
+ maybeUpdateFlexConstraintLocked(null);
+ }
}
for (int u = 0; u < mCurrentDefaultNetworkCallbacks.size(); ++u) {
UidDefaultNetworkCallback callback = mCurrentDefaultNetworkCallbacks.valueAt(u);
@@ -1639,6 +1667,37 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
}
+
+ /**
+ * Maybe call {@link FlexibilityController#setConstraintSatisfied(int, boolean, long)}
+ * if the network affinity state has changed.
+ */
+ @GuardedBy("mLock")
+ private void maybeUpdateFlexConstraintLocked(
+ @Nullable CachedNetworkMetadata cachedNetworkMetadata) {
+ if (cachedNetworkMetadata != null
+ && cachedNetworkMetadata.satisfiesTransportAffinities) {
+ mFlexibilityController.setConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY,
+ true, sElapsedRealtimeClock.millis());
+ } else {
+ // This network doesn't satisfy transport affinities. Check if any other
+ // available networks do satisfy the affinities before saying that the
+ // transport affinity is no longer satisfied for flex.
+ boolean isTransportAffinitySatisfied = false;
+ for (int i = mAvailableNetworks.size() - 1; i >= 0; --i) {
+ final CachedNetworkMetadata cnm = mAvailableNetworks.valueAt(i);
+ if (cnm != null && cnm.satisfiesTransportAffinities) {
+ isTransportAffinitySatisfied = true;
+ break;
+ }
+ }
+ if (!isTransportAffinitySatisfied) {
+ mFlexibilityController.setConstraintSatisfied(
+ JobStatus.CONSTRAINT_CONNECTIVITY, false,
+ sElapsedRealtimeClock.millis());
+ }
+ }
+ }
};
private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 70f9a52f3299..fed3c42ab87f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -43,6 +43,8 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
+import android.util.SparseLongArray;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -68,11 +70,6 @@ public final class FlexibilityController extends StateController {
| CONSTRAINT_CHARGING
| CONSTRAINT_IDLE;
- /** List of flexible constraints a job can opt into. */
- static final int OPTIONAL_FLEXIBLE_CONSTRAINTS = CONSTRAINT_BATTERY_NOT_LOW
- | CONSTRAINT_CHARGING
- | CONSTRAINT_IDLE;
-
/** List of all job flexible constraints whose satisfaction is job specific. */
private static final int JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = CONSTRAINT_CONNECTIVITY;
@@ -83,9 +80,6 @@ public final class FlexibilityController extends StateController {
private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);
- static final int NUM_OPTIONAL_FLEXIBLE_CONSTRAINTS =
- Integer.bitCount(OPTIONAL_FLEXIBLE_CONSTRAINTS);
-
static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
@@ -103,6 +97,9 @@ public final class FlexibilityController extends StateController {
private long mRescheduledJobDeadline = FcConfig.DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
private long mMaxRescheduledDeadline = FcConfig.DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
+ private long mUnseenConstraintGracePeriodMs =
+ FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
+
@VisibleForTesting
@GuardedBy("mLock")
boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
@@ -132,6 +129,9 @@ public final class FlexibilityController extends StateController {
@GuardedBy("mLock")
int mSatisfiedFlexibleConstraints;
+ @GuardedBy("mLock")
+ private final SparseLongArray mLastSeenConstraintTimesElapsed = new SparseLongArray();
+
@VisibleForTesting
@GuardedBy("mLock")
final FlexibilityTracker mFlexibilityTracker;
@@ -258,25 +258,68 @@ public final class FlexibilityController extends StateController {
boolean isFlexibilitySatisfiedLocked(JobStatus js) {
return !mFlexibilityEnabled
|| mService.getUidBias(js.getSourceUid()) == JobInfo.BIAS_TOP_APP
- || getNumSatisfiedRequiredConstraintsLocked(js)
- >= js.getNumRequiredFlexibleConstraints()
+ || hasEnoughSatisfiedConstraintsLocked(js)
|| mService.isCurrentlyRunningLocked(js);
}
+ /**
+ * Returns whether there are enough constraints satisfied to allow running the job from flex's
+ * perspective. This takes into account unseen constraint combinations and expectations around
+ * whether additional constraints can ever be satisfied.
+ */
@VisibleForTesting
@GuardedBy("mLock")
- int getNumSatisfiedRequiredConstraintsLocked(JobStatus js) {
- return Integer.bitCount(mSatisfiedFlexibleConstraints)
- // Connectivity is job-specific, so must be handled separately.
- + (js.canApplyTransportAffinities()
- && js.areTransportAffinitiesSatisfied() ? 1 : 0);
+ boolean hasEnoughSatisfiedConstraintsLocked(@NonNull JobStatus js) {
+ final int satisfiedConstraints = mSatisfiedFlexibleConstraints
+ & (SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ | (js.areTransportAffinitiesSatisfied() ? CONSTRAINT_CONNECTIVITY : 0));
+ final int numSatisfied = Integer.bitCount(satisfiedConstraints);
+ if (numSatisfied >= js.getNumRequiredFlexibleConstraints()) {
+ return true;
+ }
+ // We don't yet have the full number of required flex constraints. See if we should expect
+ // to be able to reach it. If not, then there's no point waiting anymore.
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (nowElapsed < mUnseenConstraintGracePeriodMs) {
+ // Too soon after boot. Not enough time to start predicting. Wait longer.
+ return false;
+ }
+
+ // The intention is to not force jobs to wait for constraint combinations that have never
+ // been seen together in a while. The job may still be allowed to wait for other constraint
+ // combinations. Thus, the logic is:
+ // If all the constraint combinations that have a count higher than the current satisfied
+ // count have not been seen recently enough, then assume they won't be seen anytime soon,
+ // so don't force the job to wait longer. If any combinations with a higher count have been
+ // seen recently, then the job can potentially wait for those combinations.
+ final int irrelevantConstraints = ~(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0));
+ for (int i = mLastSeenConstraintTimesElapsed.size() - 1; i >= 0; --i) {
+ final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i);
+ if ((constraints & irrelevantConstraints) != 0) {
+ // Ignore combinations that couldn't satisfy this job's needs.
+ continue;
+ }
+ final long lastSeenElapsed = mLastSeenConstraintTimesElapsed.valueAt(i);
+ final boolean seenRecently =
+ nowElapsed - lastSeenElapsed <= mUnseenConstraintGracePeriodMs;
+ if (Integer.bitCount(constraints) > numSatisfied && seenRecently) {
+ // We've seen a set of constraints with a higher count than what is currently
+ // satisfied recently enough, which means we can expect to see it again at some
+ // point. Keep waiting for now.
+ return false;
+ }
+ }
+
+ // We haven't seen any constraint set with more satisfied than the current satisfied count.
+ // There's no reason to expect additional constraints to be satisfied. Let the job run.
+ return true;
}
/**
* Sets the controller's constraint to a given state.
* Changes flexibility constraint satisfaction for affected jobs.
*/
- @VisibleForTesting
void setConstraintSatisfied(int constraint, boolean state, long nowElapsed) {
synchronized (mLock) {
final boolean old = (mSatisfiedFlexibleConstraints & constraint) != 0;
@@ -286,14 +329,34 @@ public final class FlexibilityController extends StateController {
if (DEBUG) {
Slog.d(TAG, "setConstraintSatisfied: "
- + " constraint: " + constraint + " state: " + state);
+ + " constraint: " + constraint + " state: " + state);
+ }
+
+ // Mark now as the last time we saw this set of constraints.
+ mLastSeenConstraintTimesElapsed.put(mSatisfiedFlexibleConstraints, nowElapsed);
+ if (!state) {
+ // Mark now as the last time we saw this particular constraint.
+ // (Good for logging/dump purposes).
+ mLastSeenConstraintTimesElapsed.put(constraint, nowElapsed);
}
mSatisfiedFlexibleConstraints =
(mSatisfiedFlexibleConstraints & ~constraint) | (state ? constraint : 0);
- // Push the job update to the handler to avoid blocking other controllers and
- // potentially batch back-to-back controller state updates together.
- mHandler.obtainMessage(MSG_UPDATE_JOBS).sendToTarget();
+
+ if ((JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS & constraint) != 0) {
+ // Job-specific constraint --> don't need to proceed with logic below that
+ // works with system-wide constraints.
+ return;
+ }
+
+ if (mFlexibilityEnabled) {
+ // Only attempt to update jobs if the flex logic is enabled. Otherwise, the status
+ // of the jobs won't change, so all the work will be a waste.
+
+ // Push the job update to the handler to avoid blocking other controllers and
+ // potentially batch back-to-back controller state updates together.
+ mHandler.obtainMessage(MSG_UPDATE_JOBS).sendToTarget();
+ }
}
}
@@ -543,7 +606,6 @@ public final class FlexibilityController extends StateController {
if (!predicate.test(js)) {
continue;
}
- pw.print("#");
js.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, js.getSourceUid());
@@ -645,7 +707,7 @@ public final class FlexibilityController extends StateController {
final long nowElapsed = sElapsedRealtimeClock.millis();
final ArraySet<JobStatus> changedJobs = new ArraySet<>();
- for (int o = 0; o <= NUM_OPTIONAL_FLEXIBLE_CONSTRAINTS; ++o) {
+ for (int o = 0; o <= NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; ++o) {
final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
.getJobsByNumRequiredConstraints(o);
@@ -687,6 +749,8 @@ public final class FlexibilityController extends StateController {
FC_CONFIG_PREFIX + "max_rescheduled_deadline_ms";
static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";
+ static final String KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS =
+ FC_CONFIG_PREFIX + "unseen_constraint_grace_period_ms";
static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
@VisibleForTesting
@@ -698,6 +762,8 @@ public final class FlexibilityController extends StateController {
final int[] DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS = {50, 60, 70, 80};
private static final long DEFAULT_RESCHEDULED_JOB_DEADLINE_MS = HOUR_IN_MILLIS;
private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = 5 * DAY_IN_MILLIS;
+ @VisibleForTesting
+ static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS;
/**
* If false the controller will not track new jobs
@@ -717,6 +783,11 @@ public final class FlexibilityController extends StateController {
public long RESCHEDULED_JOB_DEADLINE_MS = DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
/** The max deadline for rescheduled jobs. */
public long MAX_RESCHEDULED_DEADLINE_MS = DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
+ /**
+ * How long to wait after last seeing a constraint combination before no longer waiting for
+ * it in order to run jobs.
+ */
+ public long UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
@GuardedBy("mLock")
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@@ -780,6 +851,14 @@ public final class FlexibilityController extends StateController {
mShouldReevaluateConstraints = true;
}
break;
+ case KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS:
+ UNSEEN_CONSTRAINT_GRACE_PERIOD_MS =
+ properties.getLong(key, DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS);
+ if (mUnseenConstraintGracePeriodMs != UNSEEN_CONSTRAINT_GRACE_PERIOD_MS) {
+ mUnseenConstraintGracePeriodMs = UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
case KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS:
String dropPercentString = properties.getString(key, "");
PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
@@ -834,6 +913,8 @@ public final class FlexibilityController extends StateController {
PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS).println();
pw.print(KEY_RESCHEDULED_JOB_DEADLINE_MS, RESCHEDULED_JOB_DEADLINE_MS).println();
pw.print(KEY_MAX_RESCHEDULED_DEADLINE_MS, MAX_RESCHEDULED_DEADLINE_MS).println();
+ pw.print(KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS, UNSEEN_CONSTRAINT_GRACE_PERIOD_MS)
+ .println();
pw.decreaseIndent();
}
@@ -854,12 +935,34 @@ public final class FlexibilityController extends StateController {
@Override
@GuardedBy("mLock")
public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
- pw.println("# Constraints Satisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints));
- pw.print("Satisfied Flexible Constraints: ");
+ pw.print("Satisfied Flexible Constraints:");
JobStatus.dumpConstraints(pw, mSatisfiedFlexibleConstraints);
pw.println();
pw.println();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ pw.println("Time since constraint combos last seen:");
+ pw.increaseIndent();
+ for (int i = 0; i < mLastSeenConstraintTimesElapsed.size(); ++i) {
+ final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i);
+ if (constraints == mSatisfiedFlexibleConstraints) {
+ pw.print("0ms");
+ } else {
+ TimeUtils.formatDuration(
+ mLastSeenConstraintTimesElapsed.valueAt(i), nowElapsed, pw);
+ }
+ pw.print(":");
+ if (constraints != 0) {
+ // dumpConstraints prepends with a space, so no need to add a space after the :
+ JobStatus.dumpConstraints(pw, constraints);
+ } else {
+ pw.print(" none");
+ }
+ pw.println();
+ }
+ pw.decreaseIndent();
+
+ pw.println();
mFlexibilityTracker.dump(pw, predicate);
pw.println();
mFlexibilityAlarmQueue.dump(pw);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 10f8510c7c70..4958f1c1c214 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -44,6 +44,7 @@ import static com.android.server.job.controllers.ConnectivityController.CcConfig
import static com.android.server.job.controllers.ConnectivityController.TRANSPORT_AFFINITY_AVOID;
import static com.android.server.job.controllers.ConnectivityController.TRANSPORT_AFFINITY_PREFER;
import static com.android.server.job.controllers.ConnectivityController.TRANSPORT_AFFINITY_UNDEFINED;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -51,6 +52,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
@@ -989,6 +991,7 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = new ConnectivityController(mService,
mFlexibilityController);
+ InOrder flexControllerInOrder = inOrder(mFlexibilityController);
final Network meteredNet = mock(Network.class);
final NetworkCapabilities meteredCaps = createCapabilitiesBuilder().build();
@@ -1042,10 +1045,13 @@ public class ConnectivityControllerTest {
answerNetwork(generalCallback, redCallback, null, null, null);
answerNetwork(generalCallback, blueCallback, null, null, null);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
assertFalse(red.areTransportAffinitiesSatisfied());
assertFalse(blue.areTransportAffinitiesSatisfied());
assertFalse(red2.areTransportAffinitiesSatisfied());
@@ -1059,13 +1065,15 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(meteredNet, meteredCaps);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// No transport is specified. Accept the network for transport affinity.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
assertFalse(red.areTransportAffinitiesSatisfied());
assertTrue(blue.areTransportAffinitiesSatisfied());
assertFalse(red2.areTransportAffinitiesSatisfied());
@@ -1073,6 +1081,8 @@ public class ConnectivityControllerTest {
// No transport is specified. Avoid the network for transport affinity.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
assertFalse(red.areTransportAffinitiesSatisfied());
assertFalse(blue.areTransportAffinitiesSatisfied());
assertFalse(red2.areTransportAffinitiesSatisfied());
@@ -1086,12 +1096,17 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// No transport is specified. Accept the network for transport affinity.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
assertFalse(red.areTransportAffinitiesSatisfied());
assertTrue(blue.areTransportAffinitiesSatisfied());
assertFalse(red2.areTransportAffinitiesSatisfied());
@@ -1099,6 +1114,8 @@ public class ConnectivityControllerTest {
// No transport is specified. Avoid the network for transport affinity.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
assertFalse(red.areTransportAffinitiesSatisfied());
assertFalse(blue.areTransportAffinitiesSatisfied());
assertFalse(red2.areTransportAffinitiesSatisfied());
@@ -1112,8 +1129,13 @@ public class ConnectivityControllerTest {
generalCallback.onLost(meteredNet);
- assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ // Only the metered network is lost. The unmetered network still satisfies the
+ // affinities.
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+
+ assertTrue(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
}
// Specific UID was blocked
@@ -1123,8 +1145,12 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ // No change
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
}
// Metered wifi
@@ -1134,10 +1160,13 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(meteredNet, meteredWifiCaps);
- assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// Wifi is preferred.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
@@ -1164,10 +1193,14 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCelullarCaps);
- assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(red2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ // Metered network still has wifi transport
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+
+ assertTrue(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// Cellular is avoided.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
@@ -1185,6 +1218,14 @@ public class ConnectivityControllerTest {
assertFalse(blue2.areTransportAffinitiesSatisfied());
}
+ // Remove wifi transport
+ {
+ generalCallback.onCapabilitiesChanged(meteredNet, meteredCaps);
+
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+ }
+
// Undefined affinity
final NetworkCapabilities unmeteredTestCaps = createCapabilitiesBuilder()
.addCapability(NET_CAPABILITY_NOT_METERED)
@@ -1198,14 +1239,16 @@ public class ConnectivityControllerTest {
generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredTestCaps);
- assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(red2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(blue2.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertTrue(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(red2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue2.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// Undefined is preferred.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
assertTrue(red.areTransportAffinitiesSatisfied());
assertTrue(blue.areTransportAffinitiesSatisfied());
assertFalse(red2.areTransportAffinitiesSatisfied());
@@ -1213,6 +1256,46 @@ public class ConnectivityControllerTest {
// Undefined is avoided.
setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ }
+
+ // Lost all networks
+ {
+ // Set network as accepted to help confirm onLost notifies flex controller
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(true), anyLong());
+
+ answerNetwork(generalCallback, redCallback, unmeteredNet, null, null);
+ answerNetwork(generalCallback, blueCallback, unmeteredNet, null, null);
+
+ generalCallback.onLost(meteredNet);
+ generalCallback.onLost(unmeteredNet);
+
+ flexControllerInOrder.verify(mFlexibilityController)
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), eq(false), anyLong());
+
+ assertFalse(red.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(blue.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, false);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
+ assertFalse(red.areTransportAffinitiesSatisfied());
+ assertFalse(blue.areTransportAffinitiesSatisfied());
+ assertFalse(red2.areTransportAffinitiesSatisfied());
+ assertFalse(blue2.areTransportAffinitiesSatisfied());
+ // Undefined is avoided.
+ setDeviceConfigBoolean(controller, KEY_AVOID_UNDEFINED_TRANSPORT_AFFINITY, true);
+ controller.onConstantsUpdatedLocked();
+ flexControllerInOrder.verify(mFlexibilityController, never())
+ .setConstraintSatisfied(eq(CONSTRAINT_CONNECTIVITY), anyBoolean(), anyLong());
assertFalse(red.areTransportAffinitiesSatisfied());
assertFalse(blue.areTransportAffinitiesSatisfied());
assertFalse(red2.areTransportAffinitiesSatisfied());
@@ -1275,7 +1358,7 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = spy(
new ConnectivityController(mService, mFlexibilityController));
doReturn(true).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
doReturn(true).when(controller).isNetworkAvailable(any());
final JobStatus red = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
@@ -1318,7 +1401,7 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = spy(
new ConnectivityController(mService, mFlexibilityController));
doReturn(false).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
final JobStatus red = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
@@ -1388,7 +1471,7 @@ public class ConnectivityControllerTest {
// Both jobs would still be ready. Exception should not be revoked.
doReturn(true).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
doReturn(true).when(controller).isNetworkAvailable(any());
controller.reevaluateStateLocked(UID_RED);
inOrder.verify(mNetPolicyManagerInternal, never())
@@ -1396,9 +1479,9 @@ public class ConnectivityControllerTest {
// One job is still ready. Exception should not be revoked.
doReturn(true).when(controller).wouldBeReadyWithConstraintLocked(
- eq(redOne), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ eq(redOne), eq(CONSTRAINT_CONNECTIVITY));
doReturn(false).when(controller).wouldBeReadyWithConstraintLocked(
- eq(redTwo), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ eq(redTwo), eq(CONSTRAINT_CONNECTIVITY));
controller.reevaluateStateLocked(UID_RED);
inOrder.verify(mNetPolicyManagerInternal, never())
.setAppIdleWhitelist(eq(UID_RED), anyBoolean());
@@ -1406,7 +1489,7 @@ public class ConnectivityControllerTest {
// Both jobs are not ready. Exception should be revoked.
doReturn(false).when(controller)
- .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY));
+ .wouldBeReadyWithConstraintLocked(any(), eq(CONSTRAINT_CONNECTIVITY));
controller.reevaluateStateLocked(UID_RED);
inOrder.verify(mNetPolicyManagerInternal, times(1))
.setAppIdleWhitelist(eq(UID_RED), eq(false));
@@ -1473,26 +1556,26 @@ public class ConnectivityControllerTest {
controller.maybeStartTrackingJobLocked(unnetworked, null);
answerNetwork(callback.getValue(), redCallback.getValue(), null, cellularNet, cellularCaps);
- assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(networked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
+ assertFalse(unnetworked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
networked.setStandbyBucket(RESTRICTED_INDEX);
unnetworked.setStandbyBucket(RESTRICTED_INDEX);
controller.startTrackingRestrictedJobLocked(networked);
controller.startTrackingRestrictedJobLocked(unnetworked);
- assertFalse(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(networked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
// connectivity constraint.
- assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(unnetworked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
networked.setStandbyBucket(RARE_INDEX);
unnetworked.setStandbyBucket(RARE_INDEX);
controller.stopTrackingRestrictedJobLocked(networked);
controller.stopTrackingRestrictedJobLocked(unnetworked);
- assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertTrue(networked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
// Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
// connectivity constraint.
- assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(unnetworked.isConstraintSatisfied(CONSTRAINT_CONNECTIVITY));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index ee68b6d0e546..0659f7e9a064 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -19,19 +19,25 @@ package com.android.server.job.controllers;
import static android.app.job.JobInfo.BIAS_FOREGROUND_SERVICE;
import static android.app.job.JobInfo.BIAS_TOP_APP;
import static android.app.job.JobInfo.NETWORK_TYPE_ANY;
+import static android.app.job.JobInfo.NETWORK_TYPE_CELLULAR;
+import static android.app.job.JobInfo.NETWORK_TYPE_NONE;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_DEADLINE_PROXIMITY_LIMIT;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FLEXIBILITY_ENABLED;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS;
+import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import static com.android.server.job.controllers.JobStatus.MIN_WINDOW_FOR_FLEXIBILITY_MS;
@@ -54,6 +60,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.net.NetworkRequest;
import android.os.Looper;
import android.provider.DeviceConfig;
import android.util.ArraySet;
@@ -693,15 +700,59 @@ public class FlexibilityControllerTest {
@Test
public void testTransportAffinity() {
- JobInfo.Builder jb = createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY);
- JobStatus js = createJobStatus("testTopAppBypass", jb);
+ JobStatus jsAny = createJobStatus("testTransportAffinity",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+ JobStatus jsCell = createJobStatus("testTransportAffinity",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_CELLULAR));
+ JobStatus jsWifi = createJobStatus("testTransportAffinity",
+ createJob(0).setRequiredNetwork(
+ new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .build()));
+ // Disable the unseen constraint logic.
+ mFlexibilityController.setConstraintSatisfied(
+ SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, true, FROZEN_TIME);
+ mFlexibilityController.setConstraintSatisfied(
+ SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, false, FROZEN_TIME);
+ // Require only a single constraint
+ jsAny.adjustNumRequiredFlexibleConstraints(-3);
+ jsCell.adjustNumRequiredFlexibleConstraints(-2);
+ jsWifi.adjustNumRequiredFlexibleConstraints(-2);
synchronized (mFlexibilityController.mLock) {
- js.setTransportAffinitiesSatisfied(false);
- assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
- js.setTransportAffinitiesSatisfied(true);
- assertEquals(1, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
- js.setTransportAffinitiesSatisfied(false);
- assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
+ jsAny.setTransportAffinitiesSatisfied(false);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(false);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, false, FROZEN_TIME);
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
+
+ // A good network exists, but the network hasn't been assigned to any of the jobs
+ jsAny.setTransportAffinitiesSatisfied(false);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(false);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true, FROZEN_TIME);
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
+
+ // The good network has been assigned to the relevant jobs
+ jsAny.setTransportAffinitiesSatisfied(true);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(true);
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
+
+ // One job loses access to the network.
+ jsAny.setTransportAffinitiesSatisfied(true);
+ jsCell.setTransportAffinitiesSatisfied(false);
+ jsWifi.setTransportAffinitiesSatisfied(false);
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi));
}
}
@@ -768,6 +819,131 @@ public class FlexibilityControllerTest {
}
@Test
+ public void testHasEnoughSatisfiedConstraints_unseenConstraints_soonAfterBoot() {
+ // Add connectivity to require 4 constraints
+ JobStatus js = createJobStatus("testHasEnoughSatisfiedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+
+ // Too soon after boot
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(100 - 1), ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js));
+ }
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS - 1),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js));
+ }
+
+ // Long after boot
+
+ // No constraints ever seen. Don't bother waiting
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js));
+ }
+ }
+
+ @Test
+ public void testHasEnoughSatisfiedConstraints_unseenConstraints_longAfterBoot() {
+ // Add connectivity to require 4 constraints
+ JobStatus connJs = createJobStatus("testHasEnoughSatisfiedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY));
+ JobStatus nonConnJs = createJobStatus("testHasEnoughSatisfiedConstraints",
+ createJob(0).setRequiredNetworkType(NETWORK_TYPE_NONE));
+
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_BATTERY_NOT_LOW, true,
+ 2 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CHARGING, true,
+ 3 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_IDLE, true,
+ 4 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true,
+ 5 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+
+ // Long after boot
+ // All constraints satisfied right now
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // Go down to 2 satisfied
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, false,
+ 6 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_IDLE, false,
+ 7 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ // 3 & 4 constraints were seen recently enough, so the job should wait
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // 4 constraints still in the grace period. Wait.
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(16 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // 3 constraints still in the grace period. Wait.
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(17 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // 3 constraints haven't been seen recently. Don't wait.
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(
+ 17 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10 + 1),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+
+ // Add then remove connectivity. Resets expectation of 3 constraints for connectivity jobs.
+ // Connectivity job should wait while the non-connectivity job can run.
+ // of getting back to 4 constraints.
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, true,
+ 18 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_CONNECTIVITY, false,
+ 19 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(
+ Instant.ofEpochMilli(
+ 19 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10 + 1),
+ ZoneOffset.UTC);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs));
+ assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs));
+ }
+ }
+
+ @Test
public void testResetJobNumDroppedConstraints() {
JobInfo.Builder jb = createJob(22);
JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb);