Merge "Add an API to query pending job reason."

This commit is contained in:
TreeHugger Robot
2022-12-06 16:08:06 +00:00
committed by Android (Google) Code Review
9 changed files with 485 additions and 11 deletions

View File

@@ -36,7 +36,7 @@ import java.util.List;
* Note android.app.job is the better package to put this class, but we can't move it there
* because that'd break robolectric. Grr.
*
* @hide
* @hide
*/
public class JobSchedulerImpl extends JobScheduler {
IJobScheduler mBinder;
@@ -108,6 +108,15 @@ public class JobSchedulerImpl extends JobScheduler {
}
}
@Override
public int getPendingJobReason(int jobId) {
try {
return mBinder.getPendingJobReason(jobId);
} catch (RemoteException e) {
return PENDING_JOB_REASON_UNDEFINED;
}
}
@Override
public boolean canRunLongJobs() {
try {

View File

@@ -33,6 +33,7 @@ interface IJobScheduler {
void cancelAll();
ParceledListSlice getAllPendingJobs();
JobInfo getPendingJob(int jobId);
int getPendingJobReason(int jobId);
boolean canRunLongJobs(String packageName);
boolean hasRunLongJobsPermission(String packageName, int userId);
List<JobInfo> getStartedJobs();

View File

@@ -23,10 +23,14 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.usage.UsageStatsManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.ClipData;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.NetworkRequest;
import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -133,6 +137,132 @@ public abstract class JobScheduler {
*/
public static final int RESULT_SUCCESS = 1;
/** The job doesn't exist. */
public static final int PENDING_JOB_REASON_INVALID_JOB_ID = -2;
/** The job is currently running and is therefore not pending. */
public static final int PENDING_JOB_REASON_EXECUTING = -1;
/**
* There is no known reason why the job is pending.
* If additional reasons are added on newer Android versions, the system may return this reason
* to apps whose target SDK is not high enough to expect that reason.
*/
public static final int PENDING_JOB_REASON_UNDEFINED = 0;
/**
* The app is in a state that prevents the job from running
* (eg. the {@link JobService} component is disabled).
*/
public static final int PENDING_JOB_REASON_APP = 1;
/**
* The current standby bucket prevents the job from running.
*
* @see UsageStatsManager#STANDBY_BUCKET_RESTRICTED
*/
public static final int PENDING_JOB_REASON_APP_STANDBY = 2;
/**
* The app is restricted from running in the background.
*
* @see ActivityManager#isBackgroundRestricted()
* @see PackageManager#isInstantApp()
*/
public static final int PENDING_JOB_REASON_BACKGROUND_RESTRICTION = 3;
/**
* The requested battery-not-low constraint is not satisfied.
*
* @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW = 4;
/**
* The requested charging constraint is not satisfied.
*
* @see JobInfo.Builder#setRequiresCharging(boolean)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_CHARGING = 5;
/**
* The requested connectivity constraint is not satisfied.
*
* @see JobInfo.Builder#setRequiredNetwork(NetworkRequest)
* @see JobInfo.Builder#setRequiredNetworkType(int)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY = 6;
/**
* The requested content trigger constraint is not satisfied.
*
* @see JobInfo.Builder#addTriggerContentUri(JobInfo.TriggerContentUri)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER = 7;
/**
* The requested idle constraint is not satisfied.
*
* @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE = 8;
/**
* The minimum latency has not transpired.
*
* @see JobInfo.Builder#setMinimumLatency(long)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY = 9;
/**
* The system's estimate of when the app will be launched is far away enough to warrant delaying
* this job.
*
* @see JobInfo#isPrefetch()
* @see JobInfo.Builder#setPrefetch(boolean)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_PREFETCH = 10;
/**
* The requested storage-not-low constraint is not satisfied.
*
* @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
*/
public static final int PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW = 11;
/**
* The job is being deferred due to the device state (eg. Doze, battery saver, memory usage,
* thermal status, etc.).
*/
public static final int PENDING_JOB_REASON_DEVICE_STATE = 12;
/**
* JobScheduler thinks it can defer this job to a more optimal running time.
*/
public static final int PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION = 13;
/**
* The app has consumed all of its current quota.
*
* @see UsageStatsManager#getAppStandbyBucket()
* @see JobParameters#STOP_REASON_QUOTA
*/
public static final int PENDING_JOB_REASON_QUOTA = 14;
/**
* JobScheduler is respecting one of the user's actions (eg. force stop or adb shell commands)
* to defer this job.
*/
public static final int PENDING_JOB_REASON_USER = 15;
/** @hide */
@IntDef(prefix = {"PENDING_JOB_REASON_"}, value = {
PENDING_JOB_REASON_UNDEFINED,
PENDING_JOB_REASON_APP,
PENDING_JOB_REASON_APP_STANDBY,
PENDING_JOB_REASON_BACKGROUND_RESTRICTION,
PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW,
PENDING_JOB_REASON_CONSTRAINT_CHARGING,
PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY,
PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER,
PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE,
PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY,
PENDING_JOB_REASON_CONSTRAINT_PREFETCH,
PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW,
PENDING_JOB_REASON_DEVICE_STATE,
PENDING_JOB_REASON_EXECUTING,
PENDING_JOB_REASON_INVALID_JOB_ID,
PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION,
PENDING_JOB_REASON_QUOTA,
PENDING_JOB_REASON_USER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PendingJobReason {
}
/**
* Schedule a job to be executed. Will replace any currently scheduled job with the same
* ID with the new information in the {@link JobInfo}. If a job with the given ID is currently
@@ -249,6 +379,15 @@ public abstract class JobScheduler {
*/
public abstract @Nullable JobInfo getPendingJob(int jobId);
/**
* Returns a reason why the job is pending and not currently executing. If there are multiple
* reasons why a job may be pending, this will only return one of them.
*/
@PendingJobReason
public int getPendingJobReason(int jobId) {
return PENDING_JOB_REASON_UNDEFINED;
}
/**
* Returns {@code true} if the calling app currently holds the
* {@link android.Manifest.permission#RUN_LONG_JOBS} permission, allowing it to run long jobs.

View File

@@ -1175,7 +1175,7 @@ class JobConcurrencyManager {
if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()
&& restriction.isJobRestricted(jobStatus)) {
jsc.cancelExecutingJobLocked(restriction.getReason(),
jsc.cancelExecutingJobLocked(restriction.getStopReason(),
restriction.getInternalReason(),
JobParameters.getInternalReasonCodeDescription(
restriction.getInternalReason()));
@@ -1208,7 +1208,7 @@ class JobConcurrencyManager {
final JobRestriction restriction = mService.checkIfRestricted(running);
if (restriction != null) {
final int internalReasonCode = restriction.getInternalReason();
serviceContext.cancelExecutingJobLocked(restriction.getReason(),
serviceContext.cancelExecutingJobLocked(restriction.getStopReason(),
internalReasonCode,
"restricted due to "
+ JobParameters.getInternalReasonCodeDescription(
@@ -1324,6 +1324,7 @@ class JobConcurrencyManager {
mActivePkgStats.add(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
packageStats);
mService.resetPendingJobReasonCache(jobStatus);
}
if (mService.getPendingJobQueue().remove(jobStatus)) {
mService.mJobPackageTracker.noteNonpending(jobStatus);

View File

@@ -363,6 +363,9 @@ public class JobSchedulerService extends com.android.server.SystemService
@GuardedBy("mLock")
private final ArraySet<JobStatus> mChangedJobList = new ArraySet<>();
@GuardedBy("mPendingJobReasonCache") // Use its own lock to avoid blocking JS processing
private final SparseArray<SparseIntArray> mPendingJobReasonCache = new SparseArray<>();
/**
* Named indices into standby bucket arrays, for clarity in referring to
* specific buckets' bookkeeping.
@@ -1357,6 +1360,134 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
@JobScheduler.PendingJobReason
private int getPendingJobReason(int uid, int jobId) {
int reason;
// Some apps may attempt to query this frequently, so cache the reason under a separate lock
// so that the rest of JS processing isn't negatively impacted.
synchronized (mPendingJobReasonCache) {
SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid);
if (jobIdToReason != null) {
reason = jobIdToReason.get(jobId, JobScheduler.PENDING_JOB_REASON_UNDEFINED);
if (reason != JobScheduler.PENDING_JOB_REASON_UNDEFINED) {
return reason;
}
}
}
synchronized (mLock) {
reason = getPendingJobReasonLocked(uid, jobId);
if (DEBUG) {
Slog.v(TAG, "getPendingJobReason(" + uid + "," + jobId + ")=" + reason);
}
}
synchronized (mPendingJobReasonCache) {
SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid);
if (jobIdToReason == null) {
jobIdToReason = new SparseIntArray();
mPendingJobReasonCache.put(uid, jobIdToReason);
}
jobIdToReason.put(jobId, reason);
}
return reason;
}
@JobScheduler.PendingJobReason
@GuardedBy("mLock")
private int getPendingJobReasonLocked(int uid, int jobId) {
// Very similar code to isReadyToBeExecutedLocked.
JobStatus job = mJobs.getJobByUidAndJobId(uid, jobId);
if (job == null) {
// Job doesn't exist.
return JobScheduler.PENDING_JOB_REASON_INVALID_JOB_ID;
}
if (isCurrentlyRunningLocked(job)) {
return JobScheduler.PENDING_JOB_REASON_EXECUTING;
}
final boolean jobReady = job.isReady();
if (DEBUG) {
Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
+ " ready=" + jobReady);
}
if (!jobReady) {
return job.getPendingJobReason();
}
final boolean userStarted = areUsersStartedLocked(job);
if (DEBUG) {
Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
+ " userStarted=" + userStarted);
}
if (!userStarted) {
return JobScheduler.PENDING_JOB_REASON_USER;
}
final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
if (DEBUG) {
Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
+ " backingUp=" + backingUp);
}
if (backingUp) {
// TODO: Should we make a special reason for this?
return JobScheduler.PENDING_JOB_REASON_APP;
}
JobRestriction restriction = checkIfRestricted(job);
if (DEBUG) {
Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
+ " restriction=" + restriction);
}
if (restriction != null) {
return restriction.getPendingReason();
}
// The following can be a little more expensive (especially jobActive, since we need to
// go through the array of all potentially active jobs), so we are doing them
// later... but still before checking with the package manager!
final boolean jobPending = mPendingJobQueue.contains(job);
if (DEBUG) {
Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
+ " pending=" + jobPending);
}
if (jobPending) {
// We haven't started the job for some reason. Presumably, there are too many jobs
// running.
return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
}
final boolean jobActive = mConcurrencyManager.isJobRunningLocked(job);
if (DEBUG) {
Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
+ " active=" + jobActive);
}
if (jobActive) {
return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
}
// Validate that the defined package+service is still present & viable.
final boolean componentUsable = isComponentUsable(job);
if (DEBUG) {
Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
+ " componentUsable=" + componentUsable);
}
if (!componentUsable) {
return JobScheduler.PENDING_JOB_REASON_APP;
}
return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
}
public JobInfo getPendingJob(int uid, int jobId) {
synchronized (mLock) {
ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid);
@@ -1389,6 +1520,9 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
mJobs.removeJobsOfUnlistedUsers(umi.getUserIds());
}
synchronized (mPendingJobReasonCache) {
mPendingJobReasonCache.clear();
}
}
private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
@@ -1874,6 +2008,8 @@ public class JobSchedulerService extends com.android.server.SystemService
jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
final boolean update = lastJob != null;
mJobs.add(jobStatus);
// Clear potentially cached INVALID_JOB_ID reason.
resetPendingJobReasonCache(jobStatus);
if (mReadyToRock) {
for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
@@ -1895,6 +2031,13 @@ public class JobSchedulerService extends com.android.server.SystemService
// Deal with any remaining work items in the old job.
jobStatus.stopTrackingJobLocked(incomingJob);
synchronized (mPendingJobReasonCache) {
SparseIntArray reasonCache = mPendingJobReasonCache.get(jobStatus.getUid());
if (reasonCache != null) {
reasonCache.delete(jobStatus.getJobId());
}
}
// Remove from store as well as controllers.
final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
if (!removed) {
@@ -1917,6 +2060,16 @@ public class JobSchedulerService extends com.android.server.SystemService
return removed;
}
/** Remove the pending job reason for this job from the cache. */
void resetPendingJobReasonCache(@NonNull JobStatus jobStatus) {
synchronized (mPendingJobReasonCache) {
final SparseIntArray reasons = mPendingJobReasonCache.get(jobStatus.getUid());
if (reasons != null) {
reasons.delete(jobStatus.getJobId());
}
}
}
/** Return {@code true} if the specified job is currently executing. */
@GuardedBy("mLock")
public boolean isCurrentlyRunningLocked(JobStatus job) {
@@ -2210,11 +2363,20 @@ public class JobSchedulerService extends com.android.server.SystemService
public void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs) {
if (changedJobs == null) {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
synchronized (mPendingJobReasonCache) {
mPendingJobReasonCache.clear();
}
} else if (changedJobs.size() > 0) {
synchronized (mLock) {
mChangedJobList.addAll(changedJobs);
}
mHandler.obtainMessage(MSG_CHECK_CHANGED_JOB_LIST).sendToTarget();
synchronized (mPendingJobReasonCache) {
for (int i = changedJobs.size() - 1; i >= 0; --i) {
final JobStatus job = changedJobs.valueAt(i);
resetPendingJobReasonCache(job);
}
}
}
}
@@ -2568,6 +2730,23 @@ public class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
}
final int numRunnableJobs = runnableJobs.size();
if (numRunnableJobs > 0) {
synchronized (mPendingJobReasonCache) {
for (int i = 0; i < numRunnableJobs; ++i) {
final JobStatus job = runnableJobs.get(i);
SparseIntArray reasons = mPendingJobReasonCache.get(job.getUid());
if (reasons == null) {
reasons = new SparseIntArray();
mPendingJobReasonCache.put(job.getUid(), reasons);
}
// We're force batching these jobs, so consider it an optimization
// policy reason.
reasons.put(job.getJobId(),
JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION);
}
}
}
}
// Be ready for next time
@@ -3369,6 +3548,18 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
@Override
public int getPendingJobReason(int jobId) throws RemoteException {
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.getPendingJobReason(uid, jobId);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public JobInfo getPendingJob(int jobId) throws RemoteException {
final int uid = Binder.getCallingUid();

View File

@@ -32,6 +32,7 @@ import android.annotation.ElapsedRealtimeLong;
import android.app.AppGlobals;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobWorkItem;
import android.content.ClipData;
import android.content.ComponentName;
@@ -106,6 +107,13 @@ public final class JobStatus {
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
static final int CONSTRAINT_FLEXIBLE = 1 << 21; // Implicit constraint
private static final int IMPLICIT_CONSTRAINTS = 0
| CONSTRAINT_BACKGROUND_NOT_RESTRICTED
| CONSTRAINT_DEVICE_NOT_DOZING
| CONSTRAINT_FLEXIBLE
| CONSTRAINT_TARE_WEALTH
| CONSTRAINT_WITHIN_QUOTA;
// The following set of dynamic constraints are for specific use cases (as explained in their
// relative naming and comments). Right now, they apply different constraints, which is fine,
// but if in the future, we have overlapping dynamic constraint sets, removing one constraint
@@ -1613,6 +1621,101 @@ public final class JobStatus {
}
}
/**
* If {@link #isReady()} returns false, this will return a single reason why the job isn't
* ready. If {@link #isReady()} returns true, this will return
* {@link JobScheduler#PENDING_JOB_REASON_UNDEFINED}.
*/
@JobScheduler.PendingJobReason
public int getPendingJobReason() {
final int unsatisfiedConstraints = ~satisfiedConstraints
& (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS);
if ((CONSTRAINT_BACKGROUND_NOT_RESTRICTED & unsatisfiedConstraints) != 0) {
// The BACKGROUND_NOT_RESTRICTED constraint could be unsatisfied either because
// the app is background restricted, or because we're restricting background work
// in battery saver. Assume that background restriction is the reason apps that
// jobs are not ready, and battery saver otherwise.
// This has the benefit of being consistent for background restricted apps
// (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of
// battery saver state.
if (mIsUserBgRestricted) {
return JobScheduler.PENDING_JOB_REASON_BACKGROUND_RESTRICTION;
}
return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
}
if ((CONSTRAINT_BATTERY_NOT_LOW & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_BATTERY_NOT_LOW & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW;
}
// Hard-coding right now since the current dynamic constraint sets don't overlap
// TODO: return based on active dynamic constraint sets when they start overlapping
return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
}
if ((CONSTRAINT_CHARGING & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_CHARGING & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CHARGING;
}
// Hard-coding right now since the current dynamic constraint sets don't overlap
// TODO: return based on active dynamic constraint sets when they start overlapping
return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
}
if ((CONSTRAINT_CONNECTIVITY & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY;
}
if ((CONSTRAINT_CONTENT_TRIGGER & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER;
}
if ((CONSTRAINT_DEVICE_NOT_DOZING & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
}
if ((CONSTRAINT_FLEXIBLE & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION;
}
if ((CONSTRAINT_IDLE & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_IDLE & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE;
}
// Hard-coding right now since the current dynamic constraint sets don't overlap
// TODO: return based on active dynamic constraint sets when they start overlapping
return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
}
if ((CONSTRAINT_PREFETCH & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_PREFETCH;
}
if ((CONSTRAINT_STORAGE_NOT_LOW & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW;
}
if ((CONSTRAINT_TARE_WEALTH & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_QUOTA;
}
if ((CONSTRAINT_TIMING_DELAY & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY;
}
if ((CONSTRAINT_WITHIN_QUOTA & unsatisfiedConstraints) != 0) {
return JobScheduler.PENDING_JOB_REASON_QUOTA;
}
if (getEffectiveStandbyBucket() == NEVER_INDEX) {
Slog.wtf(TAG, "App in NEVER bucket querying pending job reason");
// The user hasn't officially launched this app.
return JobScheduler.PENDING_JOB_REASON_USER;
}
if (serviceProcessName != null) {
return JobScheduler.PENDING_JOB_REASON_APP;
}
if (!isReady()) {
Slog.wtf(TAG, "Unknown reason job isn't ready");
}
return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
}
/** @return whether or not the @param constraint is satisfied */
public boolean isConstraintSatisfied(int constraint) {
return (satisfiedConstraints&constraint) != 0;

View File

@@ -18,6 +18,7 @@ package com.android.server.job.restrictions;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
@@ -28,20 +29,23 @@ import com.android.server.job.controllers.JobStatus;
* Used by {@link JobSchedulerService} to impose additional restrictions regarding whether jobs
* should be scheduled or not based on the state of the system/device.
* Every restriction is associated with exactly one stop reason, which could be retrieved using
* {@link #getReason()} (and the internal reason via {@link #getInternalReason()}).
* {@link #getStopReason()}, one pending reason (retrievable via {@link #getPendingReason()},
* (and the internal reason via {@link #getInternalReason()}).
* Note, that this is not taken into account for the jobs that have
* {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias or higher.
*/
public abstract class JobRestriction {
final JobSchedulerService mService;
private final int mReason;
private final int mStopReason;
private final int mPendingReason;
private final int mInternalReason;
JobRestriction(JobSchedulerService service, @JobParameters.StopReason int reason,
int internalReason) {
protected JobRestriction(JobSchedulerService service, @JobParameters.StopReason int stopReason,
@JobScheduler.PendingJobReason int pendingReason, int internalReason) {
mService = service;
mReason = reason;
mPendingReason = pendingReason;
mStopReason = stopReason;
mInternalReason = internalReason;
}
@@ -70,10 +74,15 @@ public abstract class JobRestriction {
public void dumpConstants(ProtoOutputStream proto) {
}
/** @return reason code for the Restriction. */
@JobScheduler.PendingJobReason
public final int getPendingReason() {
return mPendingReason;
}
/** @return stop reason code for the Restriction. */
@JobParameters.StopReason
public final int getReason() {
return mReason;
public final int getStopReason() {
return mStopReason;
}
public final int getInternalReason() {

View File

@@ -18,6 +18,7 @@ package com.android.server.job.restrictions;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.os.PowerManager;
import android.os.PowerManager.OnThermalStatusChangedListener;
import android.util.IndentingPrintWriter;
@@ -42,6 +43,7 @@ public class ThermalStatusRestriction extends JobRestriction {
public ThermalStatusRestriction(JobSchedulerService service) {
super(service, JobParameters.STOP_REASON_DEVICE_STATE,
JobScheduler.PENDING_JOB_REASON_DEVICE_STATE,
JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
}

View File

@@ -8485,7 +8485,26 @@ package android.app.job {
method public abstract int enqueue(@NonNull android.app.job.JobInfo, @NonNull android.app.job.JobWorkItem);
method @NonNull public abstract java.util.List<android.app.job.JobInfo> getAllPendingJobs();
method @Nullable public abstract android.app.job.JobInfo getPendingJob(int);
method public int getPendingJobReason(int);
method public abstract int schedule(@NonNull android.app.job.JobInfo);
field public static final int PENDING_JOB_REASON_APP = 1; // 0x1
field public static final int PENDING_JOB_REASON_APP_STANDBY = 2; // 0x2
field public static final int PENDING_JOB_REASON_BACKGROUND_RESTRICTION = 3; // 0x3
field public static final int PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW = 4; // 0x4
field public static final int PENDING_JOB_REASON_CONSTRAINT_CHARGING = 5; // 0x5
field public static final int PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY = 6; // 0x6
field public static final int PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER = 7; // 0x7
field public static final int PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE = 8; // 0x8
field public static final int PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY = 9; // 0x9
field public static final int PENDING_JOB_REASON_CONSTRAINT_PREFETCH = 10; // 0xa
field public static final int PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW = 11; // 0xb
field public static final int PENDING_JOB_REASON_DEVICE_STATE = 12; // 0xc
field public static final int PENDING_JOB_REASON_EXECUTING = -1; // 0xffffffff
field public static final int PENDING_JOB_REASON_INVALID_JOB_ID = -2; // 0xfffffffe
field public static final int PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION = 13; // 0xd
field public static final int PENDING_JOB_REASON_QUOTA = 14; // 0xe
field public static final int PENDING_JOB_REASON_UNDEFINED = 0; // 0x0
field public static final int PENDING_JOB_REASON_USER = 15; // 0xf
field public static final int RESULT_FAILURE = 0; // 0x0
field public static final int RESULT_SUCCESS = 1; // 0x1
}