Merge "Plug in DeviceConfig to Variables in Flexibility Controller"
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user