Add permission check to setBias.
The job bias is meant to be an indication of the job type and the state
the app was in when the job was scheduled. This can only be determined
by the system and certain privileged apps. The job bias can affect some
things such as which system health factors are taken into consideration
when deciding when to run a job. Given all this, the bias should only be
set by the system or certain privileged apps. Note the required permission
on the hidden API and enforce that the calling app has the permission
when the job is scheduled.
Bug: 300477393
Test: atest CtsJobSchedulerTestCases:JobInfoTest
Test: atest CtsSyncManagerTestCases
Test: atest DownloadManagerApi28Test
Test: atest FrameworksServicesTests:BiasSchedulingTest
Change-Id: I1cb37eafc42f961dee9d2e7bc7d496a31dff31f4
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 5dc994e..a92a01f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1265,6 +1265,7 @@
/** @hide */
@NonNull
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public Builder setBias(int bias) {
mBias = bias;
return this;
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 bbe1485..30d680d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -202,6 +202,15 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
static final long REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS = 271850009L;
+ /**
+ * Throw an exception when biases are set by an unsupported client.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final long THROW_ON_UNSUPPORTED_BIAS_USAGE = 300477393L;
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static Clock sSystemClock = Clock.systemUTC();
@@ -4331,6 +4340,24 @@
}
}
+ private JobInfo enforceBuilderApiPermissions(int uid, int pid, JobInfo job) {
+ if (job.getBias() != JobInfo.BIAS_DEFAULT
+ && !hasPermission(uid, pid, Manifest.permission.UPDATE_DEVICE_STATS)) {
+ if (CompatChanges.isChangeEnabled(THROW_ON_UNSUPPORTED_BIAS_USAGE, uid)) {
+ throw new SecurityException("Apps may not call setBias()");
+ } else {
+ // We can't throw the exception. Log the issue and modify the job to remove
+ // the invalid value.
+ Slog.w(TAG, "Uid " + uid + " set bias on its job");
+ return new JobInfo.Builder(job)
+ .setBias(JobInfo.BIAS_DEFAULT)
+ .build(false, false);
+ }
+ }
+
+ return job;
+ }
+
private boolean canPersistJobs(int pid, int uid) {
// Persisting jobs is tantamount to running at boot, so we permit
// it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
@@ -4512,6 +4539,8 @@
namespace = validateNamespace(namespace);
+ job = enforceBuilderApiPermissions(uid, pid, job);
+
final long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
@@ -4543,6 +4572,8 @@
namespace = validateNamespace(namespace);
+ job = enforceBuilderApiPermissions(uid, pid, job);
+
final long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
@@ -4582,6 +4613,8 @@
namespace = validateNamespace(namespace);
+ job = enforceBuilderApiPermissions(callerUid, callerPid, job);
+
final long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,