Merge "Adjust Flex Deadline For Rescheduled Jobs"
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.job.controllers;
|
||||
|
||||
import static android.text.format.DateUtils.DAY_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||
|
||||
@@ -98,8 +99,11 @@ public final class FlexibilityController extends StateController {
|
||||
private long mFallbackFlexibilityDeadlineMs =
|
||||
FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private long mRescheduledJobDeadline = FcConfig.DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
|
||||
private long mMaxRescheduledDeadline = FcConfig.DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
|
||||
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mLock")
|
||||
boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
|
||||
|
||||
private long mMinTimeBetweenFlexibilityAlarmsMs =
|
||||
@@ -321,6 +325,12 @@ public final class FlexibilityController extends StateController {
|
||||
// There is no deadline and no estimated launch time.
|
||||
return NO_LIFECYCLE_END;
|
||||
}
|
||||
if (js.getNumFailures() > 1) {
|
||||
// Number of failures will not equal one as per restriction in JobStatus constructor.
|
||||
return earliest + Math.min(
|
||||
(long) Math.scalb(mRescheduledJobDeadline, js.getNumFailures() - 2),
|
||||
mMaxRescheduledDeadline);
|
||||
}
|
||||
return js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
|
||||
? earliest + mFallbackFlexibilityDeadlineMs : js.getLatestRunTimeElapsed();
|
||||
}
|
||||
@@ -340,7 +350,6 @@ public final class FlexibilityController extends StateController {
|
||||
return percentInTime;
|
||||
}
|
||||
|
||||
/** The elapsed time that marks when the next constraint should be dropped. */
|
||||
@VisibleForTesting
|
||||
@ElapsedRealtimeLong
|
||||
@GuardedBy("mLock")
|
||||
@@ -351,7 +360,6 @@ public final class FlexibilityController extends StateController {
|
||||
}
|
||||
|
||||
/** The elapsed time that marks when the next constraint should be dropped. */
|
||||
@VisibleForTesting
|
||||
@ElapsedRealtimeLong
|
||||
@GuardedBy("mLock")
|
||||
long getNextConstraintDropTimeElapsedLocked(JobStatus js, long earliest, long latest) {
|
||||
@@ -531,6 +539,8 @@ public final class FlexibilityController extends StateController {
|
||||
js.printUniqueId(pw);
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, js.getSourceUid());
|
||||
pw.print(" Num Required Constraints: ");
|
||||
pw.print(js.getNumRequiredFlexibleConstraints());
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
@@ -551,20 +561,24 @@ public final class FlexibilityController extends StateController {
|
||||
}
|
||||
|
||||
public void scheduleDropNumConstraintsAlarm(JobStatus js, long nowElapsed) {
|
||||
long nextTimeElapsed;
|
||||
synchronized (mLock) {
|
||||
final long earliest = getLifeCycleBeginningElapsedLocked(js);
|
||||
final long latest = getLifeCycleEndElapsedLocked(js, earliest);
|
||||
nextTimeElapsed = getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
|
||||
final long nextTimeElapsed =
|
||||
getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
|
||||
|
||||
if (latest - nowElapsed < mDeadlineProximityLimitMs) {
|
||||
mFlexibilityTracker.adjustJobsRequiredConstraints(js,
|
||||
-js.getNumRequiredFlexibleConstraints(), nowElapsed);
|
||||
return;
|
||||
}
|
||||
if (nextTimeElapsed == NO_LIFECYCLE_END) {
|
||||
// There is no known or estimated next time to drop a constraint.
|
||||
removeAlarmForKey(js);
|
||||
return;
|
||||
}
|
||||
|
||||
if (latest - nextTimeElapsed < mDeadlineProximityLimitMs) {
|
||||
mFlexibilityTracker.adjustJobsRequiredConstraints(
|
||||
js, -js.getNumRequiredFlexibleConstraints(), nowElapsed);
|
||||
addAlarm(js, latest - mDeadlineProximityLimitMs);
|
||||
return;
|
||||
}
|
||||
addAlarm(js, nextTimeElapsed);
|
||||
@@ -579,20 +593,8 @@ 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);
|
||||
|
||||
final long earliest = getLifeCycleBeginningElapsedLocked(js);
|
||||
final long latest = getLifeCycleEndElapsedLocked(js, earliest);
|
||||
|
||||
if (latest - nowElapsed < mDeadlineProximityLimitMs) {
|
||||
mFlexibilityTracker.adjustJobsRequiredConstraints(js,
|
||||
-js.getNumRequiredFlexibleConstraints(), nowElapsed);
|
||||
} else {
|
||||
long nextTimeElapsed =
|
||||
getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
|
||||
if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)
|
||||
&& nextTimeElapsed != NO_LIFECYCLE_END) {
|
||||
mFlexibilityAlarmQueue.addAlarm(js, nextTimeElapsed);
|
||||
}
|
||||
if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)) {
|
||||
scheduleDropNumConstraintsAlarm(js, nowElapsed);
|
||||
}
|
||||
if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
|
||||
changedJobs.add(js);
|
||||
@@ -619,6 +621,10 @@ public final class FlexibilityController extends StateController {
|
||||
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";
|
||||
static final String KEY_MAX_RESCHEDULED_DEADLINE_MS =
|
||||
FC_CONFIG_PREFIX + "max_rescheduled_deadline_ms";
|
||||
static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
|
||||
FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";
|
||||
|
||||
private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
|
||||
@VisibleForTesting
|
||||
@@ -628,6 +634,8 @@ public final class FlexibilityController extends StateController {
|
||||
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};
|
||||
private static final long DEFAULT_RESCHEDULED_JOB_DEADLINE_MS = HOUR_IN_MILLIS;
|
||||
private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = 5 * DAY_IN_MILLIS;
|
||||
|
||||
/**
|
||||
* If false the controller will not track new jobs
|
||||
@@ -643,6 +651,10 @@ public final class FlexibilityController extends StateController {
|
||||
/** 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;
|
||||
/** Initial fallback flexible deadline for rescheduled jobs. */
|
||||
public long RESCHEDULED_JOB_DEADLINE_MS = DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
|
||||
/** The max deadline for rescheduled jobs. */
|
||||
public long MAX_RESCHEDULED_DEADLINE_MS = DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
|
||||
@@ -662,6 +674,22 @@ public final class FlexibilityController extends StateController {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KEY_RESCHEDULED_JOB_DEADLINE_MS:
|
||||
RESCHEDULED_JOB_DEADLINE_MS =
|
||||
properties.getLong(key, DEFAULT_RESCHEDULED_JOB_DEADLINE_MS);
|
||||
if (mRescheduledJobDeadline != RESCHEDULED_JOB_DEADLINE_MS) {
|
||||
mRescheduledJobDeadline = RESCHEDULED_JOB_DEADLINE_MS;
|
||||
mShouldReevaluateConstraints = true;
|
||||
}
|
||||
break;
|
||||
case KEY_MAX_RESCHEDULED_DEADLINE_MS:
|
||||
MAX_RESCHEDULED_DEADLINE_MS =
|
||||
properties.getLong(key, DEFAULT_MAX_RESCHEDULED_DEADLINE_MS);
|
||||
if (mMaxRescheduledDeadline != MAX_RESCHEDULED_DEADLINE_MS) {
|
||||
mMaxRescheduledDeadline = MAX_RESCHEDULED_DEADLINE_MS;
|
||||
mShouldReevaluateConstraints = true;
|
||||
}
|
||||
break;
|
||||
case KEY_DEADLINE_PROXIMITY_LIMIT:
|
||||
DEADLINE_PROXIMITY_LIMIT_MS =
|
||||
properties.getLong(key, DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS);
|
||||
@@ -733,6 +761,14 @@ public final class FlexibilityController extends StateController {
|
||||
pw.increaseIndent();
|
||||
|
||||
pw.print(KEY_FLEXIBILITY_ENABLED, FLEXIBILITY_ENABLED).println();
|
||||
pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println();
|
||||
pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println();
|
||||
pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS,
|
||||
MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS).println();
|
||||
pw.print(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS,
|
||||
PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS).println();
|
||||
pw.print(KEY_RESCHEDULED_JOB_DEADLINE_MS, RESCHEDULED_JOB_DEADLINE_MS).println();
|
||||
pw.print(KEY_MAX_RESCHEDULED_DEADLINE_MS, MAX_RESCHEDULED_DEADLINE_MS).println();
|
||||
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
@@ -748,9 +784,15 @@ public final class FlexibilityController extends StateController {
|
||||
@GuardedBy("mLock")
|
||||
public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
|
||||
pw.println("# Constraints Satisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints));
|
||||
pw.print("Satisfied Flexible Constraints: ");
|
||||
JobStatus.dumpConstraints(pw, mSatisfiedFlexibleConstraints);
|
||||
pw.println();
|
||||
pw.println();
|
||||
|
||||
mFlexibilityTracker.dump(pw, predicate);
|
||||
pw.println();
|
||||
mFlexibilityAlarmQueue.dump(pw);
|
||||
pw.println();
|
||||
mFcConfig.dump(pw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,8 +572,11 @@ public final class JobStatus {
|
||||
(latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis)
|
||||
>= MIN_WINDOW_FOR_FLEXIBILITY_MS;
|
||||
|
||||
// The first time a job is rescheduled it will not be subject to flexible constraints.
|
||||
// Otherwise, every consecutive reschedule increases a jobs' flexibility deadline.
|
||||
if (!isRequestedExpeditedJob()
|
||||
&& satisfiesMinWindowException
|
||||
&& numFailures != 1
|
||||
&& lacksSomeFlexibleConstraints) {
|
||||
mNumRequiredFlexibleConstraints =
|
||||
NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
|
||||
@@ -1928,35 +1931,38 @@ public final class JobStatus {
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
void dumpConstraints(PrintWriter pw, int constraints) {
|
||||
if ((constraints&CONSTRAINT_CHARGING) != 0) {
|
||||
static void dumpConstraints(PrintWriter pw, int constraints) {
|
||||
if ((constraints & CONSTRAINT_CHARGING) != 0) {
|
||||
pw.print(" CHARGING");
|
||||
}
|
||||
if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) {
|
||||
if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
|
||||
pw.print(" BATTERY_NOT_LOW");
|
||||
}
|
||||
if ((constraints& CONSTRAINT_STORAGE_NOT_LOW) != 0) {
|
||||
if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
|
||||
pw.print(" STORAGE_NOT_LOW");
|
||||
}
|
||||
if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) {
|
||||
if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
|
||||
pw.print(" TIMING_DELAY");
|
||||
}
|
||||
if ((constraints&CONSTRAINT_DEADLINE) != 0) {
|
||||
if ((constraints & CONSTRAINT_DEADLINE) != 0) {
|
||||
pw.print(" DEADLINE");
|
||||
}
|
||||
if ((constraints&CONSTRAINT_IDLE) != 0) {
|
||||
if ((constraints & CONSTRAINT_IDLE) != 0) {
|
||||
pw.print(" IDLE");
|
||||
}
|
||||
if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
|
||||
if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
|
||||
pw.print(" CONNECTIVITY");
|
||||
}
|
||||
if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
|
||||
if ((constraints & CONSTRAINT_FLEXIBLE) != 0) {
|
||||
pw.print(" FLEXIBILITY");
|
||||
}
|
||||
if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
|
||||
pw.print(" CONTENT_TRIGGER");
|
||||
}
|
||||
if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
|
||||
if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
|
||||
pw.print(" DEVICE_NOT_DOZING");
|
||||
}
|
||||
if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
|
||||
if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
|
||||
pw.print(" BACKGROUND_NOT_RESTRICTED");
|
||||
}
|
||||
if ((constraints & CONSTRAINT_PREFETCH) != 0) {
|
||||
@@ -2242,6 +2248,14 @@ public final class JobStatus {
|
||||
((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH)
|
||||
& ~satisfiedConstraints));
|
||||
pw.println();
|
||||
if (hasFlexibilityConstraint()) {
|
||||
pw.print("Num Required Flexible constraints: ");
|
||||
pw.print(getNumRequiredFlexibleConstraints());
|
||||
pw.println();
|
||||
pw.print("Num Dropped Flexible constraints: ");
|
||||
pw.print(getNumDroppedFlexibleConstraints());
|
||||
pw.println();
|
||||
}
|
||||
|
||||
pw.println("Constraint history:");
|
||||
pw.increaseIndent();
|
||||
|
||||
@@ -33,6 +33,7 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NO
|
||||
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 com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -390,7 +391,7 @@ public class FlexibilityControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLifeCycleBeginningElapsedLocked_prefetch() {
|
||||
public void testGetLifeCycleBeginningElapsedLocked_Prefetch() {
|
||||
// prefetch with lifecycle
|
||||
when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(700L);
|
||||
JobInfo.Builder jb = createJob(0).setPrefetch(true);
|
||||
@@ -417,7 +418,7 @@ public class FlexibilityControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLifeCycleBeginningElapsedLocked_nonPrefetch() {
|
||||
public void testGetLifeCycleBeginningElapsedLocked_NonPrefetch() {
|
||||
// delay
|
||||
long delay = 100;
|
||||
JobInfo.Builder jb = createJob(0).setMinimumLatency(delay);
|
||||
@@ -432,7 +433,7 @@ public class FlexibilityControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLifeCycleEndElapsedLocked_prefetch() {
|
||||
public void testGetLifeCycleEndElapsedLocked_Prefetch() {
|
||||
// prefetch no estimate
|
||||
JobInfo.Builder jb = createJob(0).setPrefetch(true);
|
||||
JobStatus js = createJobStatus("time", jb);
|
||||
@@ -444,8 +445,9 @@ public class FlexibilityControllerTest {
|
||||
when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(1000L);
|
||||
assertEquals(1000L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLifeCycleEndElapsedLocked_nonPrefetch() {
|
||||
public void testGetLifeCycleEndElapsedLocked_NonPrefetch() {
|
||||
// deadline
|
||||
JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L);
|
||||
JobStatus js = createJobStatus("time", jb);
|
||||
@@ -458,6 +460,28 @@ public class FlexibilityControllerTest {
|
||||
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 100L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLifeCycleEndElapsedLocked_Rescheduled() {
|
||||
JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L);
|
||||
JobStatus js = createJobStatus("time", jb);
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, FROZEN_TIME, FROZEN_TIME);
|
||||
|
||||
assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
|
||||
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
|
||||
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 3, FROZEN_TIME, FROZEN_TIME);
|
||||
|
||||
assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
|
||||
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
|
||||
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 10, FROZEN_TIME, FROZEN_TIME);
|
||||
assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS,
|
||||
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWontStopJobFromRunning() {
|
||||
JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101));
|
||||
@@ -577,6 +601,15 @@ public class FlexibilityControllerTest {
|
||||
assertFalse(js.hasFlexibilityConstraint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptions_RescheduledOnce() {
|
||||
JobInfo.Builder jb = createJob(0);
|
||||
JobStatus js = createJobStatus("time", jb);
|
||||
js = new JobStatus(
|
||||
js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, FROZEN_TIME, FROZEN_TIME);
|
||||
assertFalse(js.hasFlexibilityConstraint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptions_None() {
|
||||
JobInfo.Builder jb = createJob(0);
|
||||
|
||||
Reference in New Issue
Block a user