diff options
3 files changed, 29 insertions, 148 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index 096238aeda7c..012ede274bc1 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -1737,17 +1737,6 @@ class JobConcurrencyManager { continue; } - if (!nextPending.isReady()) { - // This could happen when the job count reached its quota, the constrains - // for the job has been updated but hasn't been removed from the pending - // queue yet. - if (DEBUG) { - Slog.w(TAG, "Pending+not ready job: " + nextPending); - } - pendingJobQueue.remove(nextPending); - continue; - } - if (DEBUG && isSimilarJobRunningLocked(nextPending)) { Slog.w(TAG, "Already running similar job to: " + nextPending); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index cfbfa5dce399..3c9648b20003 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -512,7 +512,7 @@ public final class QuotaController extends StateController { /** An app has reached its quota. The message should contain a {@link UserPackage} object. */ @VisibleForTesting - static final int MSG_REACHED_TIME_QUOTA = 0; + static final int MSG_REACHED_QUOTA = 0; /** Drop any old timing sessions. */ private static final int MSG_CLEAN_UP_SESSIONS = 1; /** Check if a package is now within its quota. */ @@ -524,7 +524,7 @@ public final class QuotaController extends StateController { * object. */ @VisibleForTesting - static final int MSG_REACHED_EJ_TIME_QUOTA = 4; + static final int MSG_REACHED_EJ_QUOTA = 4; /** * Process a new {@link UsageEvents.Event}. The event will be the message's object and the * userId will the first arg. @@ -533,11 +533,6 @@ public final class QuotaController extends StateController { /** A UID's free quota grace period has ended. */ @VisibleForTesting static final int MSG_END_GRACE_PERIOD = 6; - /** - * An app has reached its job count quota. The message should contain a {@link UserPackage} - * object. - */ - static final int MSG_REACHED_COUNT_QUOTA = 7; public QuotaController(@NonNull JobSchedulerService service, @NonNull BackgroundJobsController backgroundJobsController, @@ -879,37 +874,17 @@ public final class QuotaController extends StateController { } @VisibleForTesting - @GuardedBy("mLock") boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { final int standbyBucket = jobStatus.getEffectiveStandbyBucket(); // A job is within quota if one of the following is true: // 1. it was started while the app was in the TOP state // 2. the app is currently in the foreground // 3. the app overall is within its quota - if (jobStatus.shouldTreatAsUserInitiatedJob() + return jobStatus.shouldTreatAsUserInitiatedJob() || isTopStartedJobLocked(jobStatus) - || isUidInForeground(jobStatus.getSourceUid())) { - return true; - } - - if (standbyBucket == NEVER_INDEX) return false; - - if (isQuotaFreeLocked(standbyBucket)) return true; - - final ExecutionStats stats = getExecutionStatsLocked(jobStatus.getSourceUserId(), - jobStatus.getSourcePackageName(), standbyBucket); - if (!(getRemainingExecutionTimeLocked(stats) > 0)) { - // Out of execution time quota. - return false; - } - - if (mService.isCurrentlyRunningLocked(jobStatus)) { - // if job is running, considered as in quota so it can keep running. - return true; - } - - // Check if the app is within job count quota. - return isUnderJobCountQuotaLocked(stats) && isUnderSessionCountQuotaLocked(stats); + || isUidInForeground(jobStatus.getSourceUid()) + || isWithinQuotaLocked( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket); } @GuardedBy("mLock") @@ -934,11 +909,12 @@ public final class QuotaController extends StateController { ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); // TODO: use a higher minimum remaining time for jobs with MINIMUM priority return getRemainingExecutionTimeLocked(stats) > 0 - && isUnderJobCountQuotaLocked(stats) - && isUnderSessionCountQuotaLocked(stats); + && isUnderJobCountQuotaLocked(stats, standbyBucket) + && isUnderSessionCountQuotaLocked(stats, standbyBucket); } - private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats) { + private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats, + final int standbyBucket) { final long now = sElapsedRealtimeClock.millis(); final boolean isUnderAllowedTimeQuota = (stats.jobRateLimitExpirationTimeElapsed <= now @@ -947,7 +923,8 @@ public final class QuotaController extends StateController { && stats.bgJobCountInWindow < stats.jobCountLimit; } - private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats) { + private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats, + final int standbyBucket) { final long now = sElapsedRealtimeClock.millis(); final boolean isUnderAllowedTimeQuota = (stats.sessionRateLimitExpirationTimeElapsed <= now || stats.sessionCountInRateLimitingWindow < mMaxSessionCountPerRateLimitingWindow); @@ -1472,7 +1449,6 @@ public final class QuotaController extends StateController { stats.jobCountInRateLimitingWindow = 0; } stats.jobCountInRateLimitingWindow += count; - stats.bgJobCountInWindow += count; } } @@ -1707,11 +1683,10 @@ public final class QuotaController extends StateController { changedJobs.add(js); } } else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX - && realStandbyBucket == js.getEffectiveStandbyBucket() - && !mService.isCurrentlyRunningLocked(js)) { + && realStandbyBucket == js.getEffectiveStandbyBucket()) { // An app in the ACTIVE bucket may be out of quota while the job could be in quota // for some reason. Therefore, avoid setting the real value here and check each job - // individually. Running job need to determine its own quota status as well. + // individually. if (setConstraintSatisfied(js, nowElapsed, realInQuota, isWithinEJQuota)) { changedJobs.add(js); } @@ -1830,8 +1805,9 @@ public final class QuotaController extends StateController { } ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); - final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats); - final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats); + final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket); + final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats, + standbyBucket); final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName); final boolean inRegularQuota = @@ -2150,11 +2126,6 @@ public final class QuotaController extends StateController { mBgJobCount++; if (mRegularJobTimer) { incrementJobCountLocked(mPkg.userId, mPkg.packageName, 1); - final ExecutionStats stats = getExecutionStatsLocked(mPkg.userId, - mPkg.packageName, jobStatus.getEffectiveStandbyBucket(), false); - if (stats.bgJobCountInWindow >= stats.jobCountLimit) { - mHandler.obtainMessage(MSG_REACHED_COUNT_QUOTA, mPkg).sendToTarget(); - } } if (mRunningBgJobs.size() == 1) { // Started tracking the first job. @@ -2286,6 +2257,7 @@ public final class QuotaController extends StateController { // repeatedly plugged in and unplugged, or an app changes foreground state // very frequently, the job count for a package may be artificially high. mBgJobCount = mRunningBgJobs.size(); + if (mRegularJobTimer) { incrementJobCountLocked(mPkg.userId, mPkg.packageName, mBgJobCount); // Starting the timer means that all cached execution stats are now @@ -2312,8 +2284,7 @@ public final class QuotaController extends StateController { return; } Message msg = mHandler.obtainMessage( - mRegularJobTimer ? MSG_REACHED_TIME_QUOTA : MSG_REACHED_EJ_TIME_QUOTA, - mPkg); + mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg); final long timeRemainingMs = mRegularJobTimer ? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName) : getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName); @@ -2330,7 +2301,7 @@ public final class QuotaController extends StateController { private void cancelCutoff() { mHandler.removeMessages( - mRegularJobTimer ? MSG_REACHED_TIME_QUOTA : MSG_REACHED_EJ_TIME_QUOTA, mPkg); + mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg); } public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { @@ -2586,7 +2557,7 @@ public final class QuotaController extends StateController { break; default: if (DEBUG) { - Slog.d(TAG, "Dropping usage event " + event.getEventType()); + Slog.d(TAG, "Dropping event " + event.getEventType()); } break; } @@ -2695,7 +2666,7 @@ public final class QuotaController extends StateController { public void handleMessage(Message msg) { synchronized (mLock) { switch (msg.what) { - case MSG_REACHED_TIME_QUOTA: { + case MSG_REACHED_QUOTA: { UserPackage pkg = (UserPackage) msg.obj; if (DEBUG) { Slog.d(TAG, "Checking if " + pkg + " has reached its quota."); @@ -2714,7 +2685,7 @@ public final class QuotaController extends StateController { // This could potentially happen if an old session phases out while a // job is currently running. // Reschedule message - Message rescheduleMsg = obtainMessage(MSG_REACHED_TIME_QUOTA, pkg); + Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg); timeRemainingMs = getTimeUntilQuotaConsumedLocked(pkg.userId, pkg.packageName); if (DEBUG) { @@ -2724,7 +2695,7 @@ public final class QuotaController extends StateController { } break; } - case MSG_REACHED_EJ_TIME_QUOTA: { + case MSG_REACHED_EJ_QUOTA: { UserPackage pkg = (UserPackage) msg.obj; if (DEBUG) { Slog.d(TAG, "Checking if " + pkg + " has reached its EJ quota."); @@ -2742,7 +2713,7 @@ public final class QuotaController extends StateController { // This could potentially happen if an old session phases out while a // job is currently running. // Reschedule message - Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_TIME_QUOTA, pkg); + Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_QUOTA, pkg); timeRemainingMs = getTimeUntilEJQuotaConsumedLocked( pkg.userId, pkg.packageName); if (DEBUG) { @@ -2752,18 +2723,6 @@ public final class QuotaController extends StateController { } break; } - case MSG_REACHED_COUNT_QUOTA: { - UserPackage pkg = (UserPackage) msg.obj; - if (DEBUG) { - Slog.d(TAG, pkg + " has reached its count quota."); - } - - mStateChangedListener.onControllerStateChanged( - maybeUpdateConstraintForPkgLocked( - sElapsedRealtimeClock.millis(), - pkg.userId, pkg.packageName)); - break; - } case MSG_CLEAN_UP_SESSIONS: if (DEBUG) { Slog.d(TAG, "Cleaning up timing sessions."); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 671472d619d7..6df4907af93c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -1978,7 +1978,7 @@ public class QuotaControllerTest { } @Test - public void testIsWithinQuotaLocked_UnderDuration_OverJobCountRateLimitWindow() { + public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() { setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; @@ -2021,7 +2021,7 @@ public class QuotaControllerTest { } @Test - public void testIsWithinQuotaLocked_OverDuration_OverJobCountRateLimitWindow() { + public void testIsWithinQuotaLocked_OverDuration_OverJobCount() { setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; @@ -2167,73 +2167,6 @@ public class QuotaControllerTest { } @Test - public void testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow() { - setDischarging(); - - JobStatus jobRunning = createJobStatus( - "testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow", 1); - JobStatus jobPending = createJobStatus( - "testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow", 2); - setStandbyBucket(WORKING_INDEX, jobRunning, jobPending); - - setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 9), false); - - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(10, stats.jobCountLimit); - assertEquals(9, stats.bgJobCountInWindow); - } - - when(mJobSchedulerService.isCurrentlyRunningLocked(jobRunning)).thenReturn(true); - when(mJobSchedulerService.isCurrentlyRunningLocked(jobPending)).thenReturn(false); - - InOrder inOrder = inOrder(mJobSchedulerService); - trackJobs(jobRunning, jobPending); - // UID in the background. - setProcessState(ActivityManager.PROCESS_STATE_SERVICE); - // Start the job. - synchronized (mQuotaController.mLock) { - mQuotaController.prepareForExecutionLocked(jobRunning); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - // Wait for some extra time to allow for job processing. - ArraySet<JobStatus> expected = new ArraySet<>(); - expected.add(jobPending); - inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) - .onControllerStateChanged(eq(expected)); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController.isWithinQuotaLocked(jobRunning)); - assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); - assertTrue(jobRunning.isReady()); - assertFalse(mQuotaController.isWithinQuotaLocked(jobPending)); - assertFalse(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); - assertFalse(jobPending.isReady()); - assertEquals(10, stats.bgJobCountInWindow); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(jobRunning, null); - } - - synchronized (mQuotaController.mLock) { - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(10, stats.bgJobCountInWindow); - } - } - - @Test public void testIsWithinQuotaLocked_TimingSession() { setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); @@ -4718,7 +4651,7 @@ public class QuotaControllerTest { // Handler is told to check when the quota will be consumed, not when the initial // remaining time is over. verify(handler, atLeast(1)).sendMessageDelayed( - argThat(msg -> msg.what == QuotaController.MSG_REACHED_TIME_QUOTA), + argThat(msg -> msg.what == QuotaController.MSG_REACHED_QUOTA), eq(10 * SECOND_IN_MILLIS)); verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); @@ -6685,7 +6618,7 @@ public class QuotaControllerTest { // Handler is told to check when the quota will be consumed, not when the initial // remaining time is over. verify(handler, atLeast(1)).sendMessageDelayed( - argThat(msg -> msg.what == QuotaController.MSG_REACHED_EJ_TIME_QUOTA), + argThat(msg -> msg.what == QuotaController.MSG_REACHED_EJ_QUOTA), eq(10 * SECOND_IN_MILLIS)); verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); } |