Loading apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java +10 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +139 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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. Loading apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +3 −2 Original line number Diff line number Diff line Loading @@ -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())); Loading Loading @@ -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( Loading Loading @@ -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); Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +191 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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); } } } } Loading Loading @@ -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 Loading Loading @@ -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(); Loading Loading
apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java +10 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading
apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading
apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +139 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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. Loading
apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +3 −2 Original line number Diff line number Diff line Loading @@ -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())); Loading Loading @@ -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( Loading Loading @@ -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); Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +191 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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); } } } } Loading Loading @@ -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 Loading Loading @@ -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(); Loading