Loading apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +12 −1 Original line number Diff line number Diff line Loading @@ -641,6 +641,9 @@ public final class QuotaController extends StateController { mTopStartedJobs.add(jobStatus); // Top jobs won't count towards quota so there's no need to involve the Timer. return; } else if (jobStatus.shouldTreatAsUserInitiated()) { // User-initiated jobs won't count towards quota. return; } final int userId = jobStatus.getSourceUserId(); Loading Loading @@ -892,7 +895,8 @@ public final class QuotaController extends StateController { // 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 return isTopStartedJobLocked(jobStatus) return jobStatus.shouldTreatAsUserInitiated() || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid()) || isWithinQuotaLocked( jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket); Loading Loading @@ -2116,6 +2120,13 @@ public final class QuotaController extends StateController { } void startTrackingJobLocked(@NonNull JobStatus jobStatus) { if (jobStatus.shouldTreatAsUserInitiated()) { if (DEBUG) { Slog.v(TAG, "Timer ignoring " + jobStatus.toShortString() + " because it's user-initiated"); } return; } if (isTopStartedJobLocked(jobStatus)) { // We intentionally don't pay attention to fg state changes after a TOP job has // started. Loading apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java +20 −0 Original line number Diff line number Diff line Loading @@ -313,6 +313,11 @@ public class TareController extends StateController { @GuardedBy("mLock") public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { final long nowElapsed = sElapsedRealtimeClock.millis(); if (jobStatus.shouldTreatAsUserInitiated()) { // User-initiated jobs should always be allowed to run. jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true); return; } jobStatus.setTareWealthConstraintSatisfied(nowElapsed, hasEnoughWealthLocked(jobStatus)); setExpeditedTareApproved(jobStatus, nowElapsed, jobStatus.isRequestedExpeditedJob() && canAffordExpeditedBillLocked(jobStatus)); Loading @@ -326,6 +331,11 @@ public class TareController extends StateController { @Override @GuardedBy("mLock") public void prepareForExecutionLocked(JobStatus jobStatus) { if (jobStatus.shouldTreatAsUserInitiated()) { // TODO(202954395): consider noting execution with the EconomyManager even though it // won't affect this job return; } final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = Loading Loading @@ -355,6 +365,9 @@ public class TareController extends StateController { @Override @GuardedBy("mLock") public void unprepareFromExecutionLocked(JobStatus jobStatus) { if (jobStatus.shouldTreatAsUserInitiated()) { return; } final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); // If this method is called, then jobStatus.madeActive was never updated, so don't use it Loading Loading @@ -384,6 +397,9 @@ public class TareController extends StateController { @Override @GuardedBy("mLock") public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) { if (jobStatus.shouldTreatAsUserInitiated()) { return; } final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); if (!mTopStartedJobs.remove(jobStatus) && jobStatus.madeActive > 0) { Loading Loading @@ -637,6 +653,10 @@ public class TareController extends StateController { if (!mIsEnabled) { return true; } if (jobStatus.shouldTreatAsUserInitiated()) { // Always allow user-initiated jobs. return true; } if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP || isTopStartedJobLocked(jobStatus)) { // Jobs for the top app should always be allowed to run, and any jobs started while Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +25 −0 Original line number Diff line number Diff line Loading @@ -2162,6 +2162,31 @@ public class QuotaControllerTest { } } @Test public void testIsWithinQuotaLocked_UserInitiated() { // Put app in a state where regular jobs are out of quota. setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount), false); JobStatus job = createJobStatus("testIsWithinQuotaLocked_UserInitiated", 1); spyOn(job); synchronized (mQuotaController.mLock) { mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, jobCount); assertFalse(mQuotaController .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); doReturn(false).when(job).shouldTreatAsUserInitiated(); assertFalse(mQuotaController.isWithinQuotaLocked(job)); // User-initiated job should still be allowed. doReturn(true).when(job).shouldTreatAsUserInitiated(); assertTrue(mQuotaController.isWithinQuotaLocked(job)); } } @Test public void testIsWithinQuotaLocked_WithQuotaBump_Duration() { setDischarging(); Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +12 −1 Original line number Diff line number Diff line Loading @@ -641,6 +641,9 @@ public final class QuotaController extends StateController { mTopStartedJobs.add(jobStatus); // Top jobs won't count towards quota so there's no need to involve the Timer. return; } else if (jobStatus.shouldTreatAsUserInitiated()) { // User-initiated jobs won't count towards quota. return; } final int userId = jobStatus.getSourceUserId(); Loading Loading @@ -892,7 +895,8 @@ public final class QuotaController extends StateController { // 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 return isTopStartedJobLocked(jobStatus) return jobStatus.shouldTreatAsUserInitiated() || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid()) || isWithinQuotaLocked( jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket); Loading Loading @@ -2116,6 +2120,13 @@ public final class QuotaController extends StateController { } void startTrackingJobLocked(@NonNull JobStatus jobStatus) { if (jobStatus.shouldTreatAsUserInitiated()) { if (DEBUG) { Slog.v(TAG, "Timer ignoring " + jobStatus.toShortString() + " because it's user-initiated"); } return; } if (isTopStartedJobLocked(jobStatus)) { // We intentionally don't pay attention to fg state changes after a TOP job has // started. Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java +20 −0 Original line number Diff line number Diff line Loading @@ -313,6 +313,11 @@ public class TareController extends StateController { @GuardedBy("mLock") public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { final long nowElapsed = sElapsedRealtimeClock.millis(); if (jobStatus.shouldTreatAsUserInitiated()) { // User-initiated jobs should always be allowed to run. jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true); return; } jobStatus.setTareWealthConstraintSatisfied(nowElapsed, hasEnoughWealthLocked(jobStatus)); setExpeditedTareApproved(jobStatus, nowElapsed, jobStatus.isRequestedExpeditedJob() && canAffordExpeditedBillLocked(jobStatus)); Loading @@ -326,6 +331,11 @@ public class TareController extends StateController { @Override @GuardedBy("mLock") public void prepareForExecutionLocked(JobStatus jobStatus) { if (jobStatus.shouldTreatAsUserInitiated()) { // TODO(202954395): consider noting execution with the EconomyManager even though it // won't affect this job return; } final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = Loading Loading @@ -355,6 +365,9 @@ public class TareController extends StateController { @Override @GuardedBy("mLock") public void unprepareFromExecutionLocked(JobStatus jobStatus) { if (jobStatus.shouldTreatAsUserInitiated()) { return; } final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); // If this method is called, then jobStatus.madeActive was never updated, so don't use it Loading Loading @@ -384,6 +397,9 @@ public class TareController extends StateController { @Override @GuardedBy("mLock") public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) { if (jobStatus.shouldTreatAsUserInitiated()) { return; } final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); if (!mTopStartedJobs.remove(jobStatus) && jobStatus.madeActive > 0) { Loading Loading @@ -637,6 +653,10 @@ public class TareController extends StateController { if (!mIsEnabled) { return true; } if (jobStatus.shouldTreatAsUserInitiated()) { // Always allow user-initiated jobs. return true; } if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP || isTopStartedJobLocked(jobStatus)) { // Jobs for the top app should always be allowed to run, and any jobs started while Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +25 −0 Original line number Diff line number Diff line Loading @@ -2162,6 +2162,31 @@ public class QuotaControllerTest { } } @Test public void testIsWithinQuotaLocked_UserInitiated() { // Put app in a state where regular jobs are out of quota. setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount), false); JobStatus job = createJobStatus("testIsWithinQuotaLocked_UserInitiated", 1); spyOn(job); synchronized (mQuotaController.mLock) { mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, jobCount); assertFalse(mQuotaController .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); doReturn(false).when(job).shouldTreatAsUserInitiated(); assertFalse(mQuotaController.isWithinQuotaLocked(job)); // User-initiated job should still be allowed. doReturn(true).when(job).shouldTreatAsUserInitiated(); assertTrue(mQuotaController.isWithinQuotaLocked(job)); } } @Test public void testIsWithinQuotaLocked_WithQuotaBump_Duration() { setDischarging(); Loading