Add a new API in JobInfo.Builder for Data Transfer jobs.
Also add a new API in JobScheduler which allows apps to check if they have the RUN_LONG_JOBS permission. Bug: 255371817 Test: atest JobInfoTest Change-Id: I433977e8bbf041f53bcb9a63f0a992433d06fb40
This commit is contained in:
@@ -16,11 +16,13 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.job.IJobScheduler;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.job.JobSnapshot;
|
||||
import android.app.job.JobWorkItem;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.util.List;
|
||||
@@ -36,8 +38,10 @@ import java.util.List;
|
||||
*/
|
||||
public class JobSchedulerImpl extends JobScheduler {
|
||||
IJobScheduler mBinder;
|
||||
private final Context mContext;
|
||||
|
||||
public JobSchedulerImpl(IJobScheduler binder) {
|
||||
public JobSchedulerImpl(@NonNull Context context, IJobScheduler binder) {
|
||||
mContext = context;
|
||||
mBinder = binder;
|
||||
}
|
||||
|
||||
@@ -102,6 +106,15 @@ public class JobSchedulerImpl extends JobScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRunLongJobs() {
|
||||
try {
|
||||
return mBinder.canRunLongJobs(mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRunLongJobsPermission(String packageName, int userId) {
|
||||
try {
|
||||
|
||||
@@ -33,6 +33,7 @@ interface IJobScheduler {
|
||||
void cancelAll();
|
||||
ParceledListSlice getAllPendingJobs();
|
||||
JobInfo getPendingJob(int jobId);
|
||||
boolean canRunLongJobs(String packageName);
|
||||
boolean hasRunLongJobsPermission(String packageName, int userId);
|
||||
List<JobInfo> getStartedJobs();
|
||||
ParceledListSlice getAllJobSnapshots();
|
||||
|
||||
@@ -395,6 +395,13 @@ public class JobInfo implements Parcelable {
|
||||
*/
|
||||
public static final int FLAG_EXPEDITED = 1 << 4;
|
||||
|
||||
/**
|
||||
* Whether it's a data transfer job or not.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int FLAG_DATA_TRANSFER = 1 << 5;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -722,6 +729,14 @@ public class JobInfo implements Parcelable {
|
||||
return (flags & FLAG_EXPEDITED) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JobInfo.Builder#setDataTransfer(boolean)
|
||||
* @hide
|
||||
*/
|
||||
public boolean isDataTransfer() {
|
||||
return (flags & FLAG_DATA_TRANSFER) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JobInfo.Builder#setImportantWhileForeground(boolean)
|
||||
*/
|
||||
@@ -1815,6 +1830,52 @@ public class JobInfo implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this job will be used to transfer data to or from a remote server. The
|
||||
* system could attempt to run a data transfer job longer than a regular job if the data
|
||||
* being transferred is potentially very large and can take a long time to complete.
|
||||
*
|
||||
* <p>
|
||||
* The app must hold the {@link android.Manifest.permission#RUN_LONG_JOBS} permission to
|
||||
* use this API. JobScheduler will throw a {@link SecurityException} if an app without the
|
||||
* permission granted attempts to schedule a data transfer job.
|
||||
*
|
||||
* <p>
|
||||
* You must provide an estimate of the payload size via
|
||||
* {@link #setEstimatedNetworkBytes(long, long)} when scheduling the job or use
|
||||
* {@link JobService#updateEstimatedNetworkBytes(JobParameters, long, long)} or
|
||||
* {@link JobService#updateEstimatedNetworkBytes(JobParameters, JobWorkItem, long, long)}
|
||||
* shortly after the job starts.
|
||||
*
|
||||
* <p>
|
||||
* For user-initiated transfers that must be started immediately, call
|
||||
* {@link #setExpedited(boolean) setExpedited(true)}. Otherwise, the system may defer the
|
||||
* job to a more opportune time. Using {@link #setExpedited(boolean) setExpedited(true)}
|
||||
* with this API will only be allowed for foreground apps and when the user has clearly
|
||||
* interacted with the app. {@link #setExpedited(boolean) setExpedited(true)} will return
|
||||
* {@link JobScheduler#RESULT_FAILURE} for a data transfer job if the app is in the
|
||||
* background. Apps that successfully schedule data transfer jobs with
|
||||
* {@link #setExpedited(boolean) setExpedited(true)} will not have quotas applied to them,
|
||||
* though they may still be stopped for system health or constraint reasons. The system will
|
||||
* also give a user the ability to stop a data transfer job via the Task Manager.
|
||||
*
|
||||
* <p>
|
||||
* If you want to perform more than one data transfer job, consider enqueuing multiple
|
||||
* {@link JobWorkItem JobWorkItems} along with {@link #setDataTransfer(boolean)}.
|
||||
*
|
||||
* @see JobInfo#isDataTransfer()
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setDataTransfer(boolean dataTransfer) {
|
||||
if (dataTransfer) {
|
||||
mFlags |= FLAG_DATA_TRANSFER;
|
||||
} else {
|
||||
mFlags &= (~FLAG_DATA_TRANSFER);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting this to true indicates that this job is important while the scheduling app
|
||||
* is in the foreground or on the temporary whitelist for background restrictions.
|
||||
@@ -2062,8 +2123,9 @@ public class JobInfo implements Parcelable {
|
||||
"An expedited job must be high or max priority. Don't use expedited jobs"
|
||||
+ " for unimportant tasks.");
|
||||
}
|
||||
if ((constraintFlags & ~CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0
|
||||
|| (flags & ~(FLAG_EXPEDITED | FLAG_EXEMPT_FROM_APP_STANDBY)) != 0) {
|
||||
if (((constraintFlags & ~CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0
|
||||
|| (flags & ~(FLAG_EXPEDITED | FLAG_EXEMPT_FROM_APP_STANDBY
|
||||
| FLAG_DATA_TRANSFER)) != 0)) {
|
||||
throw new IllegalArgumentException(
|
||||
"An expedited job can only have network and storage-not-low constraints");
|
||||
}
|
||||
@@ -2072,6 +2134,24 @@ public class JobInfo implements Parcelable {
|
||||
"Can't call addTriggerContentUri() on an expedited job");
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & FLAG_DATA_TRANSFER) != 0) {
|
||||
if (backoffPolicy == BACKOFF_POLICY_LINEAR) {
|
||||
throw new IllegalArgumentException(
|
||||
"A data transfer job cannot have a linear backoff policy.");
|
||||
}
|
||||
if (hasLateConstraint) {
|
||||
throw new IllegalArgumentException("A data transfer job cannot have a deadline");
|
||||
}
|
||||
if ((flags & FLAG_PREFETCH) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"A data transfer job cannot also be a prefetch job");
|
||||
}
|
||||
if (networkRequest == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"A data transfer job must specify a valid network type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -249,6 +249,14 @@ public abstract class JobScheduler {
|
||||
*/
|
||||
public abstract @Nullable JobInfo getPendingJob(int jobId);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the calling app currently holds the
|
||||
* {@link android.Manifest.permission#RUN_LONG_JOBS} permission, allowing it to run long jobs.
|
||||
*/
|
||||
public boolean canRunLongJobs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the app currently holds the
|
||||
* {@link android.Manifest.permission#RUN_LONG_JOBS} permission, allowing it to run long jobs.
|
||||
|
||||
@@ -44,9 +44,9 @@ public class JobSchedulerFrameworkInitializer {
|
||||
* <p>If this is called from other places, it throws a {@link IllegalStateException).
|
||||
*/
|
||||
public static void registerServiceWrappers() {
|
||||
SystemServiceRegistry.registerStaticService(
|
||||
SystemServiceRegistry.registerContextAwareService(
|
||||
Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
|
||||
(b) -> new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)));
|
||||
(context, b) -> new JobSchedulerImpl(context, IJobScheduler.Stub.asInterface(b)));
|
||||
SystemServiceRegistry.registerContextAwareService(
|
||||
Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
|
||||
(context, b) -> new DeviceIdleManager(
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.IUidObserver;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.app.job.IJobScheduler;
|
||||
@@ -48,6 +47,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.PermissionChecker;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -305,7 +305,6 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
final JobHandler mHandler;
|
||||
final JobSchedulerStub mJobSchedulerStub;
|
||||
|
||||
private AppOpsManager mAppOps;
|
||||
PackageManagerInternal mLocalPM;
|
||||
ActivityManagerInternal mActivityManagerInternal;
|
||||
DeviceIdleInternal mLocalDeviceIdleController;
|
||||
@@ -1806,8 +1805,6 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
controller.onSystemServicesReady();
|
||||
}
|
||||
|
||||
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
|
||||
|
||||
mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
|
||||
LocalServices.getService(AppStateTracker.class));
|
||||
|
||||
@@ -3413,6 +3410,19 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRunLongJobs(@NonNull String packageName) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int userId = UserHandle.getUserId(callingUid);
|
||||
final int packageUid = mLocalPM.getPackageUid(packageName, 0, userId);
|
||||
if (callingUid != packageUid) {
|
||||
throw new SecurityException("Uid " + callingUid
|
||||
+ " cannot query canRunLongJobs for package " + packageName);
|
||||
}
|
||||
|
||||
return checkRunLongJobsPermission(packageUid, packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRunLongJobsPermission(@NonNull String packageName,
|
||||
@UserIdInt int userId) {
|
||||
@@ -3423,9 +3433,14 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
+ " cannot query canRunLongJobs for package " + packageName);
|
||||
}
|
||||
|
||||
final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_RUN_LONG_JOBS, uid,
|
||||
packageName);
|
||||
return mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_DEFAULT;
|
||||
return checkRunLongJobsPermission(uid, packageName);
|
||||
}
|
||||
|
||||
private boolean checkRunLongJobsPermission(int packageUid, String packageName) {
|
||||
// Returns true if both the appop and permission are granted.
|
||||
return PermissionChecker.checkPermissionForPreflight(getContext(),
|
||||
android.Manifest.permission.RUN_LONG_JOBS, PermissionChecker.PID_UNKNOWN,
|
||||
packageUid, packageName) == PermissionChecker.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8440,6 +8440,7 @@ package android.app.job {
|
||||
|
||||
public abstract class JobScheduler {
|
||||
ctor public JobScheduler();
|
||||
method public boolean canRunLongJobs();
|
||||
method public abstract void cancel(int);
|
||||
method public abstract void cancelAll();
|
||||
method public abstract int enqueue(@NonNull android.app.job.JobInfo, @NonNull android.app.job.JobWorkItem);
|
||||
|
||||
Reference in New Issue
Block a user