summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java245
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java181
2 files changed, 356 insertions, 70 deletions
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 e69cbedb955e..485dc32ca549 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
@@ -46,6 +46,7 @@ import com.android.server.job.JobSchedulerService;
import com.android.server.utils.AlarmQueue;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@@ -68,56 +69,60 @@ public final class FlexibilityController extends StateController {
private static final int FLEXIBLE_CONSTRAINTS =
JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS | SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
- @VisibleForTesting
- static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
+ private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);
static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
- @VisibleForTesting
static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS);
private static final long NO_LIFECYCLE_END = Long.MAX_VALUE;
+ /**
+ * Keeps track of what flexible constraints are satisfied at the moment.
+ * Is updated by the other controllers.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ int mSatisfiedFlexibleConstraints;
+
/** Hard cutoff to remove flexible constraints. */
- private static final long DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
+ private long mDeadlineProximityLimitMs =
+ FcConfig.DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
/**
* The default deadline that all flexible constraints should be dropped by if a job lacks
* a deadline.
*/
+ private long mFallbackFlexibilityDeadlineMs =
+ FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
+
+ @GuardedBy("mLock")
@VisibleForTesting
- static final long DEFAULT_FLEXIBILITY_DEADLINE = 72 * HOUR_IN_MILLIS;
+ boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
+
+ private long mMinTimeBetweenFlexibilityAlarmsMs =
+ FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
/**
- * Keeps track of what flexible constraints are satisfied at the moment.
- * Is updated by the other controllers.
+ * The percent of a job's lifecycle to drop number of required constraints.
+ * mPercentToDropConstraints[i] denotes that at x% of a Jobs lifecycle,
+ * the controller should have i+1 constraints dropped.
*/
- @VisibleForTesting
- @GuardedBy("mLock")
- int mSatisfiedFlexibleConstraints;
- @GuardedBy("mLock")
- private boolean mFlexibilityEnabled = FcConstants.DEFAULT_FLEXIBILITY_ENABLED;
+ private int[] mPercentToDropConstraints;
@VisibleForTesting
@GuardedBy("mLock")
final FlexibilityTracker mFlexibilityTracker;
- private final FcConstants mFcConstants;
-
@VisibleForTesting
- final PrefetchController mPrefetchController;
-
@GuardedBy("mLock")
- private final FlexibilityAlarmQueue mFlexibilityAlarmQueue;
- private static final long MIN_TIME_BETWEEN_ALARMS_MS = MINUTE_IN_MILLIS;
+ final FlexibilityAlarmQueue mFlexibilityAlarmQueue;
+ @VisibleForTesting
+ final FcConfig mFcConfig;
- /**
- * The percent of a Jobs lifecycle to drop number of required constraints.
- * PERCENT_TO_DROP_CONSTRAINTS[i] denotes that at x% of a Jobs lifecycle,
- * the controller should have i+1 constraints dropped.
- */
- private static final int[] PERCENT_TO_DROP_CONSTRAINTS = {50, 60, 70, 80};
+ @VisibleForTesting
+ final PrefetchController mPrefetchController;
/**
* Stores the beginning of prefetch jobs lifecycle per app as a maximum of
@@ -164,9 +169,11 @@ public final class FlexibilityController extends StateController {
JobSchedulerService service, PrefetchController prefetchController) {
super(service);
mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
- mFcConstants = new FcConstants();
+ mFcConfig = new FcConfig();
mFlexibilityAlarmQueue = new FlexibilityAlarmQueue(
mContext, JobSchedulerBackgroundThread.get().getLooper());
+ mPercentToDropConstraints =
+ mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
mPrefetchController = prefetchController;
if (mFlexibilityEnabled) {
mPrefetchController.registerPrefetchChangedListener(mPrefetchChangedListener);
@@ -317,8 +324,7 @@ public final class FlexibilityController extends StateController {
return NO_LIFECYCLE_END;
}
return js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
- ? earliest + DEFAULT_FLEXIBILITY_DEADLINE
- : js.getLatestRunTimeElapsed();
+ ? earliest + mFallbackFlexibilityDeadlineMs : js.getLatestRunTimeElapsed();
}
@VisibleForTesting
@@ -327,7 +333,7 @@ public final class FlexibilityController extends StateController {
final long earliest = getLifeCycleBeginningElapsedLocked(js);
final long latest = getLifeCycleEndElapsedLocked(js, earliest);
final long nowElapsed = sElapsedRealtimeClock.millis();
- if (latest == NO_LIFECYCLE_END || earliest > nowElapsed) {
+ if (latest == NO_LIFECYCLE_END || earliest >= nowElapsed) {
return 0;
}
if (nowElapsed > latest || latest == earliest) {
@@ -344,11 +350,19 @@ public final class FlexibilityController extends StateController {
long getNextConstraintDropTimeElapsedLocked(JobStatus js) {
final long earliest = getLifeCycleBeginningElapsedLocked(js);
final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ return getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
+ }
+
+ /** The elapsed time that marks when the next constraint should be dropped. */
+ @VisibleForTesting
+ @ElapsedRealtimeLong
+ @GuardedBy("mLock")
+ long getNextConstraintDropTimeElapsedLocked(JobStatus js, long earliest, long latest) {
if (latest == NO_LIFECYCLE_END
- || js.getNumDroppedFlexibleConstraints() == PERCENT_TO_DROP_CONSTRAINTS.length) {
+ || js.getNumDroppedFlexibleConstraints() == mPercentToDropConstraints.length) {
return NO_LIFECYCLE_END;
}
- final int percent = PERCENT_TO_DROP_CONSTRAINTS[js.getNumDroppedFlexibleConstraints()];
+ final int percent = mPercentToDropConstraints[js.getNumDroppedFlexibleConstraints()];
final long percentInTime = ((latest - earliest) * percent) / 100;
return earliest + percentInTime;
}
@@ -390,8 +404,7 @@ public final class FlexibilityController extends StateController {
@Override
@GuardedBy("mLock")
public void onConstantsUpdatedLocked() {
- if (mFcConstants.mShouldReevaluateConstraints) {
- // Update job bookkeeping out of band.
+ if (mFcConfig.mShouldReevaluateConstraints) {
JobSchedulerBackgroundThread.getHandler().post(() -> {
final ArraySet<JobStatus> changedJobs = new ArraySet<>();
synchronized (mLock) {
@@ -401,6 +414,8 @@ public final class FlexibilityController extends StateController {
.getJobsByNumRequiredConstraints(j);
for (int i = 0; i < jobs.size(); i++) {
JobStatus js = jobs.valueAt(i);
+ mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+ mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
if (js.setFlexibilityConstraintSatisfied(
nowElapsed, isFlexibilitySatisfiedLocked(js))) {
changedJobs.add(js);
@@ -418,7 +433,13 @@ public final class FlexibilityController extends StateController {
@Override
@GuardedBy("mLock")
public void prepareForUpdatedConstantsLocked() {
- mFcConstants.mShouldReevaluateConstraints = false;
+ mFcConfig.mShouldReevaluateConstraints = false;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void processConstantLocked(DeviceConfig.Properties properties, String key) {
+ mFcConfig.processConstantLocked(properties, key);
}
@VisibleForTesting
@@ -461,8 +482,10 @@ public final class FlexibilityController extends StateController {
public void resetJobNumDroppedConstraints(JobStatus js) {
final int curPercent = getCurPercentOfLifecycleLocked(js);
int toDrop = 0;
- for (int i = 0; i < PERCENT_TO_DROP_CONSTRAINTS.length; i++) {
- if (curPercent >= PERCENT_TO_DROP_CONSTRAINTS[i]) {
+ final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ + (js.getPreferUnmetered() ? 1 : 0);
+ for (int i = 0; i < jsMaxFlexibleConstraints; i++) {
+ if (curPercent >= mPercentToDropConstraints[i]) {
toDrop++;
}
}
@@ -480,6 +503,9 @@ public final class FlexibilityController extends StateController {
* Jobs with 0 required flexible constraints are removed from the tracker.
*/
public boolean adjustJobsRequiredConstraints(JobStatus js, int n) {
+ if (n == 0) {
+ return false;
+ }
remove(js);
js.adjustNumRequiredFlexibleConstraints(n);
final long nowElapsed = sElapsedRealtimeClock.millis();
@@ -514,11 +540,12 @@ public final class FlexibilityController extends StateController {
}
}
- private class FlexibilityAlarmQueue extends AlarmQueue<JobStatus> {
+ @VisibleForTesting
+ class FlexibilityAlarmQueue extends AlarmQueue<JobStatus> {
private FlexibilityAlarmQueue(Context context, Looper looper) {
super(context, looper, "*job.flexibility_check*",
"Flexible Constraint Check", false,
- MIN_TIME_BETWEEN_ALARMS_MS);
+ mMinTimeBetweenFlexibilityAlarmsMs);
}
@Override
@@ -526,16 +553,24 @@ public final class FlexibilityController extends StateController {
return js.getSourceUserId() == userId;
}
- protected void scheduleDropNumConstraintsAlarm(JobStatus js) {
+ public void scheduleDropNumConstraintsAlarm(JobStatus js) {
long nextTimeElapsed;
synchronized (mLock) {
- nextTimeElapsed = getNextConstraintDropTimeElapsedLocked(js);
+ final long earliest = getLifeCycleBeginningElapsedLocked(js);
+ final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ nextTimeElapsed = getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
if (nextTimeElapsed == NO_LIFECYCLE_END) {
// There is no known or estimated next time to drop a constraint.
removeAlarmForKey(js);
return;
}
- mFlexibilityAlarmQueue.addAlarm(js, nextTimeElapsed);
+
+ if (latest - nextTimeElapsed < mDeadlineProximityLimitMs) {
+ mFlexibilityTracker.adjustJobsRequiredConstraints(
+ js, -js.getNumRequiredFlexibleConstraints());
+ return;
+ }
+ addAlarm(js, nextTimeElapsed);
}
}
@@ -546,13 +581,21 @@ public final class FlexibilityController extends StateController {
for (int i = 0; i < expired.size(); i++) {
JobStatus js = expired.valueAt(i);
boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);
- long time = getNextConstraintDropTimeElapsedLocked(js);
- int toDecrease =
- js.getLatestRunTimeElapsed() - time < DEADLINE_PROXIMITY_LIMIT_MS
- ? -js.getNumRequiredFlexibleConstraints() : -1;
- if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, toDecrease)
- && time != NO_LIFECYCLE_END) {
- mFlexibilityAlarmQueue.addAlarm(js, time);
+
+ final long earliest = getLifeCycleBeginningElapsedLocked(js);
+ final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+
+ if (latest - nowElapsed < mDeadlineProximityLimitMs) {
+ mFlexibilityTracker.adjustJobsRequiredConstraints(js,
+ -js.getNumRequiredFlexibleConstraints());
+ } else {
+ long nextTimeElapsed =
+ getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
+ if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1)
+ && nextTimeElapsed != NO_LIFECYCLE_END) {
+ mFlexibilityAlarmQueue.addAlarm(js, nextTimeElapsed);
+ }
}
if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
changedJobs.add(js);
@@ -564,19 +607,46 @@ public final class FlexibilityController extends StateController {
}
@VisibleForTesting
- class FcConstants {
+ class FcConfig {
private boolean mShouldReevaluateConstraints = false;
+ /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
+ private static final String FC_CONFIG_PREFIX = "fc_";
+
+ static final String KEY_FLEXIBILITY_ENABLED = FC_CONFIG_PREFIX + "enable_flexibility";
+ static final String KEY_DEADLINE_PROXIMITY_LIMIT =
+ FC_CONFIG_PREFIX + "flexibility_deadline_proximity_limit_ms";
+ static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE =
+ FC_CONFIG_PREFIX + "fallback_flexibility_deadline_ms";
+ static final String KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS =
+ FC_CONFIG_PREFIX + "min_alarm_time_flexibility_ms";
+ static final String KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
+ FC_CONFIG_PREFIX + "percents_to_drop_num_flexible_constraints";
+
private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
+ @VisibleForTesting
+ static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ static final long DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS = 72 * HOUR_IN_MILLIS;
+ private static final long DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ final int[] DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS = {50, 60, 70, 80};
+ /**
+ * If false the controller will not track new jobs
+ * and the flexibility constraint will always be satisfied.
+ */
public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED;
+ /** How close to a jobs' deadline all flexible constraints will be dropped. */
+ public long DEADLINE_PROXIMITY_LIMIT_MS = DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
+ /** For jobs that lack a deadline, the time that will be used to drop all constraints by. */
+ public long FALLBACK_FLEXIBILITY_DEADLINE_MS = DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
+ public long MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS =
+ DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
+ /** The percentages of a jobs' lifecycle to drop the number of required constraints. */
+ public int[] PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
+ DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
- /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
- private static final String FC_CONSTANT_PREFIX = "fc_";
-
- static final String KEY_FLEXIBILITY_ENABLED = FC_CONSTANT_PREFIX + "enable_flexibility";
-
- // TODO(b/239925946): properly handle DeviceConfig and changing variables
@GuardedBy("mLock")
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
@@ -595,7 +665,68 @@ public final class FlexibilityController extends StateController {
}
}
break;
+ case KEY_DEADLINE_PROXIMITY_LIMIT:
+ DEADLINE_PROXIMITY_LIMIT_MS =
+ properties.getLong(key, DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS);
+ if (mDeadlineProximityLimitMs != DEADLINE_PROXIMITY_LIMIT_MS) {
+ mDeadlineProximityLimitMs = DEADLINE_PROXIMITY_LIMIT_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_FALLBACK_FLEXIBILITY_DEADLINE:
+ FALLBACK_FLEXIBILITY_DEADLINE_MS =
+ properties.getLong(key, DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS);
+ if (mFallbackFlexibilityDeadlineMs != FALLBACK_FLEXIBILITY_DEADLINE_MS) {
+ mFallbackFlexibilityDeadlineMs = FALLBACK_FLEXIBILITY_DEADLINE_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS:
+ MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS =
+ properties.getLong(key, DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS);
+ if (mMinTimeBetweenFlexibilityAlarmsMs
+ != MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS) {
+ mMinTimeBetweenFlexibilityAlarmsMs = MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS:
+ String dropPercentString = properties.getString(key, "");
+ PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
+ parsePercentToDropString(dropPercentString);
+ if (PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS != null
+ && !Arrays.equals(mPercentToDropConstraints,
+ PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS)) {
+ mPercentToDropConstraints = PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ }
+ }
+
+ private int[] parsePercentToDropString(String s) {
+ String[] dropPercentString = s.split(",");
+ int[] dropPercentInt = new int[NUM_FLEXIBLE_CONSTRAINTS];
+ if (dropPercentInt.length != dropPercentString.length) {
+ return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
}
+ int prevPercent = 0;
+ for (int i = 0; i < dropPercentString.length; i++) {
+ try {
+ dropPercentInt[i] =
+ Integer.parseInt(dropPercentString[i]);
+ } catch (NumberFormatException ex) {
+ Slog.e(TAG, "Provided string was improperly formatted.", ex);
+ return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
+ }
+ if (dropPercentInt[i] < prevPercent) {
+ Slog.wtf(TAG, "Percents to drop constraints were not in increasing order.");
+ return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
+ }
+ prevPercent = dropPercentInt[i];
+ }
+
+ return dropPercentInt;
}
private void dump(IndentingPrintWriter pw) {
@@ -612,8 +743,8 @@ public final class FlexibilityController extends StateController {
@VisibleForTesting
@NonNull
- FcConstants getFcConstants() {
- return mFcConstants;
+ FcConfig getFcConfig() {
+ return mFcConfig;
}
@Override
@@ -623,6 +754,6 @@ public final class FlexibilityController extends StateController {
pw.println();
mFlexibilityTracker.dump(pw, predicate);
- mFcConstants.dump(pw);
+ mFcConfig.dump(pw);
}
}
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 35e6db947ac3..1b39add3b421 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
@@ -24,13 +24,17 @@ 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.DEFAULT_FLEXIBILITY_DEADLINE;
-import static com.android.server.job.controllers.FlexibilityController.FcConstants.KEY_FLEXIBILITY_ENABLED;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_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.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -76,7 +80,7 @@ public class FlexibilityControllerTest {
private FlexibilityController mFlexibilityController;
private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private JobStore mJobStore;
- private FlexibilityController.FcConstants mFcConstants;
+ private FlexibilityController.FcConfig mFcConfig;
@Mock
private AlarmManager mAlarmManager;
@@ -129,8 +133,9 @@ public class FlexibilityControllerTest {
// Initialize real objects.
mFlexibilityController = new FlexibilityController(mJobSchedulerService,
mPrefetchController);
- mFcConstants = mFlexibilityController.getFcConstants();
+ mFcConfig = mFlexibilityController.getFcConfig();
+ setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, 0L);
setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
}
@@ -145,7 +150,25 @@ public class FlexibilityControllerTest {
mDeviceConfigPropertiesBuilder.setBoolean(key, val);
synchronized (mFlexibilityController.mLock) {
mFlexibilityController.prepareForUpdatedConstantsLocked();
- mFcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mFlexibilityController.onConstantsUpdatedLocked();
+ }
+ }
+
+ private void setDeviceConfigLong(String key, Long val) {
+ mDeviceConfigPropertiesBuilder.setLong(key, val);
+ synchronized (mFlexibilityController.mLock) {
+ mFlexibilityController.prepareForUpdatedConstantsLocked();
+ mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mFlexibilityController.onConstantsUpdatedLocked();
+ }
+ }
+
+ private void setDeviceConfigString(String key, String val) {
+ mDeviceConfigPropertiesBuilder.setString(key, val);
+ synchronized (mFlexibilityController.mLock) {
+ mFlexibilityController.prepareForUpdatedConstantsLocked();
+ mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
mFlexibilityController.onConstantsUpdatedLocked();
}
}
@@ -162,6 +185,81 @@ public class FlexibilityControllerTest {
return js;
}
+ /**
+ * Tests that the there are equally many percents to drop constraints as there are constraints
+ */
+ @Test
+ public void testDefaultVariableValues() {
+ assertEquals(FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS,
+ mFlexibilityController.mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS.length
+ );
+ }
+
+ @Test
+ public void testOnConstantsUpdated_DefaultFlexibility() {
+ JobStatus js = createJobStatus("testDefaultFlexibilityConfig", createJob(0));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
+ setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, false);
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
+ setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
+ }
+
+ @Test
+ public void testOnConstantsUpdated_DeadlineProximity() {
+ JobStatus js = createJobStatus("testDeadlineProximityConfig", createJob(0));
+ setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, Long.MAX_VALUE);
+ mFlexibilityController.mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
+ assertEquals(0, js.getNumRequiredFlexibleConstraints());
+ }
+
+ @Test
+ public void testOnConstantsUpdated_FallbackDeadline() {
+ JobStatus js = createJobStatus("testFallbackDeadlineConfig", createJob(0));
+ assertEquals(DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0L));
+ setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 100L);
+ assertEquals(100L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0L));
+ }
+
+ @Test
+ public void testOnConstantsUpdated_PercentsToDropConstraints() {
+ JobInfo.Builder jb = createJob(0).setOverrideDeadline(100L);
+ JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb);
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,20,30,40");
+ assertArrayEquals(
+ mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS,
+ new int[] {10, 20, 30, 40});
+ assertEquals(110L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ js.adjustNumRequiredFlexibleConstraints(-1);
+ assertEquals(120L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ js.adjustNumRequiredFlexibleConstraints(-1);
+ assertEquals(130L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ }
+
+ @Test
+ public void testOnConstantsUpdated_PercentsToDropConstraintsInvalidValues() {
+ JobInfo.Builder jb = createJob(0).setOverrideDeadline(100L);
+ JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb);
+ js.enqueueTime = 100L;
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,20a,030,40");
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,40");
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "50,40,10,40");
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ }
+
@Test
public void testGetNextConstraintDropTimeElapsedLocked() {
long nextTimeToDropNumConstraints;
@@ -347,7 +445,7 @@ public class FlexibilityControllerTest {
// no deadline
jb = createJob(0);
js = createJobStatus("time", jb);
- assertEquals(100L + DEFAULT_FLEXIBILITY_DEADLINE,
+ assertEquals(100L + DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 100L));
}
@@ -431,8 +529,8 @@ public class FlexibilityControllerTest {
assertEquals(0, trackedJobs.get(3).size());
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(
- (DEFAULT_FLEXIBILITY_DEADLINE / 2) + HOUR_IN_MILLIS), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2)
+ + HOUR_IN_MILLIS), ZoneOffset.UTC);
flexTracker.resetJobNumDroppedConstraints(jobs[0]);
assertEquals(0, trackedJobs.get(0).size());
@@ -577,19 +675,76 @@ public class FlexibilityControllerTest {
}
@Test
+ public void testResetJobNumDroppedConstraints() {
+ JobInfo.Builder jb = createJob(22).setOverrideDeadline(100L);
+ JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb);
+ js.adjustNumRequiredFlexibleConstraints(3);
+
+ mFlexibilityController.mFlexibilityTracker.add(js);
+
+ assertEquals(3, js.getNumRequiredFlexibleConstraints());
+ assertEquals(0, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(155L), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1);
+
+ assertEquals(2, js.getNumRequiredFlexibleConstraints());
+ assertEquals(1, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(2, js.getNumRequiredFlexibleConstraints());
+ assertEquals(1, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(140L), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(3, js.getNumRequiredFlexibleConstraints());
+ assertEquals(0, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(175), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(0, js.getNumRequiredFlexibleConstraints());
+ assertEquals(3, js.getNumDroppedFlexibleConstraints());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(165L), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(1, js.getNumRequiredFlexibleConstraints());
+ assertEquals(2, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(1).size());
+ }
+
+ @Test
public void testOnPrefetchCacheUpdated() {
ArraySet<JobStatus> jobs = new ArraySet<JobStatus>();
JobInfo.Builder jb = createJob(22).setPrefetch(true);
JobStatus js = createJobStatus("onPrefetchCacheUpdated", jb);
jobs.add(js);
when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(7 * HOUR_IN_MILLIS);
-
- mFlexibilityController.maybeStartTrackingJobLocked(js, null);
- mFlexibilityController.mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1);
-
when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(
1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS);
+ mFlexibilityController.maybeStartTrackingJobLocked(js, null);
+
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(150L), ZoneOffset.UTC);
@@ -604,9 +759,9 @@ public class FlexibilityControllerTest {
assertEquals(1150L,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 150L));
assertEquals(0, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
- assertEquals(0, js.getNumDroppedFlexibleConstraints());
assertEquals(650L, mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js));
+ assertEquals(3, js.getNumRequiredFlexibleConstraints());
assertEquals(1, mFlexibilityController
.mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
}