diff options
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()); } |