Loading Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -95,7 +95,7 @@ filegroup { ":platform-compat-native-aidl", // AIDL sources from external directories ":android.hardware.biometrics.common-V3-java-source", ":android.hardware.biometrics.common-V4-java-source", ":android.hardware.biometrics.fingerprint-V3-java-source", ":android.hardware.gnss-V2-java-source", ":android.hardware.graphics.common-V3-java-source", Loading apex/jobscheduler/framework/aconfig/job.aconfig +7 −0 Original line number Diff line number Diff line 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" Loading apex/jobscheduler/framework/java/android/app/job/JobInfo.java +85 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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"); Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +4 −2 Original line number Diff line number Diff line Loading @@ -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); } } Loading @@ -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); Loading apex/jobscheduler/service/java/com/android/server/job/JobStore.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading
Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -95,7 +95,7 @@ filegroup { ":platform-compat-native-aidl", // AIDL sources from external directories ":android.hardware.biometrics.common-V3-java-source", ":android.hardware.biometrics.common-V4-java-source", ":android.hardware.biometrics.fingerprint-V3-java-source", ":android.hardware.gnss-V2-java-source", ":android.hardware.graphics.common-V3-java-source", Loading
apex/jobscheduler/framework/aconfig/job.aconfig +7 −0 Original line number Diff line number Diff line 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" Loading
apex/jobscheduler/framework/java/android/app/job/JobInfo.java +85 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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"); Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +4 −2 Original line number Diff line number Diff line Loading @@ -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); } } Loading @@ -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); Loading
apex/jobscheduler/service/java/com/android/server/job/JobStore.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading