From e6dad8005eaea65badea431a08cd4b284d51cb26 Mon Sep 17 00:00:00 2001 From: alexbianchi Date: Mon, 15 Aug 2022 15:57:02 +0000 Subject: [PATCH] Plug in DeviceConfig to Variables in Flexibility Controller Added the ability to adjust the percents that the number of constraints get dropped, fallback flexibility deadline, minimum time between alarms, hard cut off for flexible constraints, and if flexible constraints should be applied at all. Bug: 239925946 Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/job Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/job Test: atest CtsJobSchedulerTestCases Change-Id: I8b8c1dcf74f74f2ad171d2fc1c51a3571f5e8a8a --- .../controllers/FlexibilityController.java | 251 +++++++++++++----- .../FlexibilityControllerTest.java | 181 ++++++++++++- 2 files changed, 359 insertions(+), 73 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 e69cbedb955e6..485dc32ca5495 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,28 +69,16 @@ 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; - /** Hard cutoff to remove flexible constraints. */ - private static final long DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS; - - /** - * The default deadline that all flexible constraints should be dropped by if a job lacks - * a deadline. - */ - @VisibleForTesting - static final long DEFAULT_FLEXIBILITY_DEADLINE = 72 * HOUR_IN_MILLIS; - /** * Keeps track of what flexible constraints are satisfied at the moment. * Is updated by the other controllers. @@ -97,28 +86,44 @@ public final class FlexibilityController extends StateController { @VisibleForTesting @GuardedBy("mLock") int mSatisfiedFlexibleConstraints; + + /** Hard cutoff to remove flexible constraints. */ + 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") - private boolean mFlexibilityEnabled = FcConstants.DEFAULT_FLEXIBILITY_ENABLED; + @VisibleForTesting + boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED; + + private long mMinTimeBetweenFlexibilityAlarmsMs = + FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS; + + /** + * 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. + */ + private int[] mPercentToDropConstraints; @VisibleForTesting @GuardedBy("mLock") final FlexibilityTracker mFlexibilityTracker; - private final FcConstants mFcConstants; + @VisibleForTesting + @GuardedBy("mLock") + final FlexibilityAlarmQueue mFlexibilityAlarmQueue; + @VisibleForTesting + final FcConfig mFcConfig; @VisibleForTesting final PrefetchController mPrefetchController; - @GuardedBy("mLock") - private final FlexibilityAlarmQueue mFlexibilityAlarmQueue; - private static final long MIN_TIME_BETWEEN_ALARMS_MS = MINUTE_IN_MILLIS; - - /** - * 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}; - /** * Stores the beginning of prefetch jobs lifecycle per app as a maximum of * the last time the app was used and the last time the launch time was updated. @@ -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 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 { + @VisibleForTesting + class FlexibilityAlarmQueue extends AlarmQueue { 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; - private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false; - - public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED; - /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */ - private static final String FC_CONSTANT_PREFIX = "fc_"; + private static final String FC_CONFIG_PREFIX = "fc_"; - static final String KEY_FLEXIBILITY_ENABLED = FC_CONSTANT_PREFIX + "enable_flexibility"; + 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; - // TODO(b/239925946): properly handle DeviceConfig and changing variables @GuardedBy("mLock") public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { @@ -595,9 +665,70 @@ 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) { pw.println(); pw.print(FlexibilityController.class.getSimpleName()); @@ -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 35e6db947ac31..1b39add3b4210 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()); @@ -576,6 +674,65 @@ 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 jobs = new ArraySet(); @@ -583,13 +740,11 @@ public class FlexibilityControllerTest { 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()); }