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
This commit is contained in:
alexbianchi
2022-08-15 15:57:02 +00:00
parent e2d779319a
commit e6dad8005e
2 changed files with 359 additions and 73 deletions

View File

@@ -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<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;
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);
}
}

View File

@@ -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<JobStatus> jobs = new ArraySet<JobStatus>();
@@ -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());
}