summaryrefslogtreecommitdiff
path: root/apex
diff options
context:
space:
mode:
authorKweku Adams <kwekua@google.com>2023-11-17 19:06:59 +0000
committerKweku Adams <kwekua@google.com>2023-12-01 21:16:49 +0000
commitda434496c9455e51d4343050b238664e9c27e034 (patch)
treea04bdb37f12381d3b36707b17a0cef8f4146f869 /apex
parent0c7bac3f44a5699e79740231edf0e2940e3a5f87 (diff)
downloadbase-da434496c9455e51d4343050b238664e9c27e034.tar.gz
Enforce minimum job time windows.
Set a minimum difference between a job's latency and deadline to give the system time to optimize execution decisions. Bug: 311402873 Bug: 297106511 Bug: 299329948 Bug: 236261941 Test: atest CtsJobSchedulerSharedUidTestCases Test: atest CtsJobSchedulerTestCases Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/job Test: atset frameworks/base/services/tests/servicestests/src/com/android/server/job Change-Id: Ia6a7f1ddfdcef3fb7298543170b03ba679e0779c
Diffstat (limited to 'apex')
-rw-r--r--apex/jobscheduler/framework/aconfig/job.aconfig7
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java89
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java2
5 files changed, 98 insertions, 8 deletions
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index f5e33a80211b..e73b434042af 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -1,6 +1,13 @@
package: "android.app.job"
flag {
+ name: "enforce_minimum_time_windows"
+ namespace: "backstage_power"
+ description: "Enforce a minimum time window for job latencies & deadlines"
+ bug: "311402873"
+}
+
+flag {
name: "job_debug_info_apis"
namespace: "backstage_power"
description: "Add APIs to let apps attach debug information to jobs"
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 742ed5f2eeb7..4bc73130db29 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -23,6 +23,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.util.TimeUtils.formatDuration;
import android.annotation.BytesLong;
@@ -36,6 +37,7 @@ import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ComponentName;
@@ -48,7 +50,9 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.Trace;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
@@ -113,6 +117,16 @@ public class JobInfo implements Parcelable {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long REJECT_NEGATIVE_NETWORK_ESTIMATES = 253665015L;
+ /**
+ * Enforce a minimum time window between job latencies and deadlines.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Overridable // Aid in testing
+ public static final long ENFORCE_MINIMUM_TIME_WINDOWS = 311402873L;
+
/** @hide */
@IntDef(prefix = { "NETWORK_TYPE_" }, value = {
NETWORK_TYPE_NONE,
@@ -1866,10 +1880,40 @@ public class JobInfo implements Parcelable {
* Set deadline which is the maximum scheduling latency. The job will be run by this
* deadline even if other requirements (including a delay set through
* {@link #setMinimumLatency(long)}) are not met.
+ * {@link JobParameters#isOverrideDeadlineExpired()} will return {@code true} if the job's
+ * deadline has passed.
+ *
* <p>
* Because it doesn't make sense setting this property on a periodic job, doing so will
* throw an {@link java.lang.IllegalArgumentException} when
* {@link android.app.job.JobInfo.Builder#build()} is called.
+ *
+ * <p class="note">
+ * Since a job will run once the deadline has passed regardless of the status of other
+ * constraints, setting a deadline of 0 with other constraints makes those constraints
+ * meaningless when it comes to execution decisions. Avoid doing this.
+ * </p>
+ *
+ * <p>
+ * Short deadlines hinder the system's ability to optimize scheduling behavior and may
+ * result in running jobs at inopportune times. Therefore, starting in Android version
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, minimum time windows will be
+ * enforced to help make it easier to better optimize job execution. Time windows are
+ * defined as the time between a job's {@link #setMinimumLatency(long) minimum latency}
+ * and its deadline. If the minimum latency is not set, it is assumed to be 0.
+ * The following minimums will be enforced:
+ * <ul>
+ * <li>
+ * Jobs with {@link #PRIORITY_DEFAULT} or higher priorities have a minimum time
+ * window of one hour.
+ * </li>
+ * <li>Jobs with {@link #PRIORITY_LOW} have a minimum time window of 6 hours.</li>
+ * <li>Jobs with {@link #PRIORITY_MIN} have a minimum time window of 12 hours.</li>
+ * </ul>
+ *
+ * Work that must happen immediately should use {@link #setExpedited(boolean)} or
+ * {@link #setUserInitiated(boolean)} in the appropriate manner.
+ *
* @see JobInfo#getMaxExecutionDelayMillis()
*/
public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
@@ -2143,12 +2187,14 @@ public class JobInfo implements Parcelable {
*/
public JobInfo build() {
return build(Compatibility.isChangeEnabled(DISALLOW_DEADLINES_FOR_PREFETCH_JOBS),
- Compatibility.isChangeEnabled(REJECT_NEGATIVE_NETWORK_ESTIMATES));
+ Compatibility.isChangeEnabled(REJECT_NEGATIVE_NETWORK_ESTIMATES),
+ Compatibility.isChangeEnabled(ENFORCE_MINIMUM_TIME_WINDOWS));
}
/** @hide */
public JobInfo build(boolean disallowPrefetchDeadlines,
- boolean rejectNegativeNetworkEstimates) {
+ boolean rejectNegativeNetworkEstimates,
+ boolean enforceMinimumTimeWindows) {
// This check doesn't need to be inside enforceValidity. It's an unnecessary legacy
// check that would ideally be phased out instead.
if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
@@ -2157,7 +2203,8 @@ public class JobInfo implements Parcelable {
" setRequiresDeviceIdle is an error.");
}
JobInfo jobInfo = new JobInfo(this);
- jobInfo.enforceValidity(disallowPrefetchDeadlines, rejectNegativeNetworkEstimates);
+ jobInfo.enforceValidity(disallowPrefetchDeadlines, rejectNegativeNetworkEstimates,
+ enforceMinimumTimeWindows);
return jobInfo;
}
@@ -2176,7 +2223,8 @@ public class JobInfo implements Parcelable {
* @hide
*/
public final void enforceValidity(boolean disallowPrefetchDeadlines,
- boolean rejectNegativeNetworkEstimates) {
+ boolean rejectNegativeNetworkEstimates,
+ boolean enforceMinimumTimeWindows) {
// Check that network estimates require network type and are reasonable values.
if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0)
&& networkRequest == null) {
@@ -2291,6 +2339,39 @@ public class JobInfo implements Parcelable {
throw new IllegalArgumentException("Invalid priority level provided: " + mPriority);
}
+ if (enforceMinimumTimeWindows
+ && Flags.enforceMinimumTimeWindows()
+ // TODO(312197030): remove exemption for the system
+ && !UserHandle.isCore(Process.myUid())
+ && hasLateConstraint && !isPeriodic) {
+ final long windowStart = hasEarlyConstraint ? minLatencyMillis : 0;
+ if (mPriority >= PRIORITY_DEFAULT) {
+ if (maxExecutionDelayMillis - windowStart < HOUR_IN_MILLIS) {
+ throw new IllegalArgumentException(
+ getPriorityString(mPriority)
+ + " cannot have a time window less than 1 hour."
+ + " Delay=" + windowStart
+ + ", deadline=" + maxExecutionDelayMillis);
+ }
+ } else if (mPriority >= PRIORITY_LOW) {
+ if (maxExecutionDelayMillis - windowStart < 6 * HOUR_IN_MILLIS) {
+ throw new IllegalArgumentException(
+ getPriorityString(mPriority)
+ + " cannot have a time window less than 6 hours."
+ + " Delay=" + windowStart
+ + ", deadline=" + maxExecutionDelayMillis);
+ }
+ } else {
+ if (maxExecutionDelayMillis - windowStart < 12 * HOUR_IN_MILLIS) {
+ throw new IllegalArgumentException(
+ getPriorityString(mPriority)
+ + " cannot have a time window less than 12 hours."
+ + " Delay=" + windowStart
+ + ", deadline=" + maxExecutionDelayMillis);
+ }
+ }
+ }
+
if (isExpedited) {
if (hasEarlyConstraint) {
throw new IllegalArgumentException("An expedited job cannot have a time delay");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index fecd2fd60b1d..f97100bf2e9b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -4388,7 +4388,7 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.w(TAG, "Uid " + uid + " set bias on its job");
return new JobInfo.Builder(job)
.setBias(JobInfo.BIAS_DEFAULT)
- .build(false, false);
+ .build(false, false, false);
}
}
@@ -4410,7 +4410,9 @@ public class JobSchedulerService extends com.android.server.SystemService
job.enforceValidity(
CompatChanges.isChangeEnabled(
JobInfo.DISALLOW_DEADLINES_FOR_PREFETCH_JOBS, callingUid),
- rejectNegativeNetworkEstimates);
+ rejectNegativeNetworkEstimates,
+ CompatChanges.isChangeEnabled(
+ JobInfo.ENFORCE_MINIMUM_TIME_WINDOWS, callingUid));
if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index afcbddad611e..53b14d616ecc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -1495,7 +1495,7 @@ public final class JobStore {
// return value), the deadline is dropped. Periodic jobs require all constraints
// to be met, so there's no issue with their deadlines.
// The same logic applies for other target SDK-based validation checks.
- builtJob = jobBuilder.build(false, false);
+ builtJob = jobBuilder.build(false, false, false);
} catch (Exception e) {
Slog.w(TAG, "Unable to build job from XML, ignoring: " + jobBuilder.summarize(), e);
return null;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 13bea6bd1dd1..b74806494a60 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -636,7 +636,7 @@ public final class JobStatus {
.build());
// Don't perform validation checks at this point since we've already passed the
// initial validation check.
- job = builder.build(false, false);
+ job = builder.build(false, false, false);
}
this.job = job;