Flex time for periodic jobs in JobScheduler

A periodic job will start only within time
satisfying period - flex < time % period < period.

Bug: 26254850
Change-Id: I97a840446e5592e5151d784800855d909f1790b8
This commit is contained in:
Shreyas Basarge
2015-12-17 15:16:36 +00:00
parent db02dc190f
commit 89ee618280
7 changed files with 60 additions and 9 deletions

View File

@@ -6166,6 +6166,7 @@ package android.app.job {
method public int describeContents();
method public int getBackoffPolicy();
method public android.os.PersistableBundle getExtras();
method public long getFlexMillis();
method public int getId();
method public long getInitialBackoffMillis();
method public long getIntervalMillis();
@@ -6183,6 +6184,8 @@ package android.app.job {
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6196,6 +6199,7 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method public android.app.job.JobInfo.Builder setPersisted(boolean);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);

View File

@@ -6381,6 +6381,7 @@ package android.app.job {
method public int describeContents();
method public int getBackoffPolicy();
method public android.os.PersistableBundle getExtras();
method public long getFlexMillis();
method public int getId();
method public long getInitialBackoffMillis();
method public long getIntervalMillis();
@@ -6398,6 +6399,8 @@ package android.app.job {
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6411,6 +6414,7 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method public android.app.job.JobInfo.Builder setPersisted(boolean);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);

View File

@@ -6168,6 +6168,7 @@ package android.app.job {
method public int describeContents();
method public int getBackoffPolicy();
method public android.os.PersistableBundle getExtras();
method public long getFlexMillis();
method public int getId();
method public long getInitialBackoffMillis();
method public long getIntervalMillis();
@@ -6185,6 +6186,8 @@ package android.app.job {
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6198,6 +6201,7 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method public android.app.job.JobInfo.Builder setPersisted(boolean);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);

View File

@@ -64,6 +64,11 @@ public class JobInfo implements Parcelable {
*/
public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
/* Minimum interval for a periodic job, in milliseconds. */
public static final long MIN_PERIOD_MILLIS = 60 * 60 * 1000L; // 60 minutes
/* Minimum flex for a periodic job, in milliseconds. */
public static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
/**
* Default type of backoff.
* @hide
@@ -83,6 +88,7 @@ public class JobInfo implements Parcelable {
private final boolean isPeriodic;
private final boolean isPersisted;
private final long intervalMillis;
private final long flexMillis;
private final long initialBackoffMillis;
private final int backoffPolicy;
@@ -165,7 +171,17 @@ public class JobInfo implements Parcelable {
* job does not recur periodically.
*/
public long getIntervalMillis() {
return intervalMillis;
return intervalMillis >= MIN_PERIOD_MILLIS ? intervalMillis : MIN_PERIOD_MILLIS;
}
/**
* Flex time for this job. Only valid if this is a periodic job.
*/
public long getFlexMillis() {
long interval = getIntervalMillis();
long percentClamp = 5 * interval / 100;
long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, MIN_FLEX_MILLIS));
return clampedFlex <= interval ? clampedFlex : interval;
}
/**
@@ -216,6 +232,7 @@ public class JobInfo implements Parcelable {
isPeriodic = in.readInt() == 1;
isPersisted = in.readInt() == 1;
intervalMillis = in.readLong();
flexMillis = in.readLong();
initialBackoffMillis = in.readLong();
backoffPolicy = in.readInt();
hasEarlyConstraint = in.readInt() == 1;
@@ -234,6 +251,7 @@ public class JobInfo implements Parcelable {
isPeriodic = b.mIsPeriodic;
isPersisted = b.mIsPersisted;
intervalMillis = b.mIntervalMillis;
flexMillis = b.mFlexMillis;
initialBackoffMillis = b.mInitialBackoffMillis;
backoffPolicy = b.mBackoffPolicy;
hasEarlyConstraint = b.mHasEarlyConstraint;
@@ -258,6 +276,7 @@ public class JobInfo implements Parcelable {
out.writeInt(isPeriodic ? 1 : 0);
out.writeInt(isPersisted ? 1 : 0);
out.writeLong(intervalMillis);
out.writeLong(flexMillis);
out.writeLong(initialBackoffMillis);
out.writeInt(backoffPolicy);
out.writeInt(hasEarlyConstraint ? 1 : 0);
@@ -299,6 +318,7 @@ public class JobInfo implements Parcelable {
private boolean mHasEarlyConstraint;
private boolean mHasLateConstraint;
private long mIntervalMillis;
private long mFlexMillis;
// Back-off parameters.
private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
@@ -373,8 +393,21 @@ public class JobInfo implements Parcelable {
* @param intervalMillis Millisecond interval for which this job will repeat.
*/
public Builder setPeriodic(long intervalMillis) {
return setPeriodic(intervalMillis, intervalMillis);
}
/**
* Specify that this job should recur with the provided interval and flex. The job can
* execute at any time in a window of flex length at the end of the period.
* @param intervalMillis Millisecond interval for which this job will repeat.
* @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
* {@link #MIN_FLEX_MILLIS} or 5 percent of the period, whichever is
* higher.
*/
public Builder setPeriodic(long intervalMillis, long flexMillis) {
mIsPeriodic = true;
mIntervalMillis = intervalMillis;
mFlexMillis = flexMillis;
mHasEarlyConstraint = mHasLateConstraint = true;
return this;
}

View File

@@ -613,9 +613,10 @@ public class JobSchedulerService extends com.android.server.SystemService
if (periodicToReschedule.hasDeadlineConstraint()) {
runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
}
long newEarliestRunTimeElapsed = elapsedNow + runEarly;
long flex = periodicToReschedule.getJob().getFlexMillis();
long period = periodicToReschedule.getJob().getIntervalMillis();
long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
if (DEBUG) {
Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +

View File

@@ -397,6 +397,7 @@ public class JobStore {
if (jobStatus.getJob().isPeriodic()) {
out.startTag(null, XML_TAG_PERIODIC);
out.attribute(null, "period", Long.toString(job.getIntervalMillis()));
out.attribute(null, "flex", Long.toString(job.getFlexMillis()));
} else {
out.startTag(null, XML_TAG_ONEOFF);
}
@@ -594,13 +595,17 @@ public class JobStore {
String val = parser.getAttributeValue(null, "period");
final long periodMillis = Long.valueOf(val);
jobBuilder.setPeriodic(periodMillis);
// As a sanity check, cap the recreated run time to be no later than 2 periods
val = parser.getAttributeValue(null, "flex");
final long flexMillis = (val != null) ? Long.valueOf(val) : periodMillis;
// As a sanity check, cap the recreated run time to be no later than flex+period
// from now. This is the latest the periodic could be pushed out. This could
// happen if the periodic ran early (at the start of its period), and then the
// happen if the periodic ran early (at flex time before period), and then the
// device rebooted.
if (elapsedRuntimes.second > elapsedNow + 2 * periodMillis) {
final long clampedEarlyRuntimeElapsed = elapsedNow + periodMillis;
final long clampedLateRuntimeElapsed = elapsedNow + 2 * periodMillis;
if (elapsedRuntimes.second > elapsedNow + periodMillis + flexMillis) {
final long clampedLateRuntimeElapsed = elapsedNow + flexMillis
+ periodMillis;
final long clampedEarlyRuntimeElapsed = clampedLateRuntimeElapsed
- flexMillis;
Slog.w(TAG,
String.format("Periodic job for uid='%d' persisted run-time is" +
" too big [%s, %s]. Clamping to [%s,%s]",

View File

@@ -96,8 +96,8 @@ public class JobStatus {
final long elapsedNow = SystemClock.elapsedRealtime();
if (job.isPeriodic()) {
earliestRunTimeElapsedMillis = elapsedNow;
latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
} else {
earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;