diff options
2 files changed, 60 insertions, 18 deletions
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 1c29982dbd48..4dc37de8a633 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 @@ -36,6 +36,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.UidObserver; +import android.app.job.JobInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.UsageEventListener; @@ -772,18 +773,23 @@ public final class QuotaController extends StateController { if (!jobStatus.shouldTreatAsExpeditedJob()) { // If quota is currently "free", then the job can run for the full amount of time, // regardless of bucket (hence using charging instead of isQuotaFreeLocked()). - if (mService.isBatteryCharging() - // The top and foreground cases here were added because apps in those states - // aren't really restricted and the work could be something the user is - // waiting for. Now that user-initiated jobs are a defined concept, we may - // not need these exemptions as much. However, UIJs are currently limited - // (as of UDC) to data transfer work. There may be other work that could - // rely on this exception. Once we add more UIJ types, we can re-evaluate - // the need for these exceptions. - // TODO: re-evaluate the need for these exceptions - || mTopAppCache.get(jobStatus.getSourceUid()) + if (mService.isBatteryCharging()) { + return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; + } + // The top and foreground cases here were added because apps in those states + // aren't really restricted and the work could be something the user is + // waiting for. Now that user-initiated jobs are a defined concept, we may + // not need these exemptions as much. However, UIJs are currently limited + // (as of UDC) to data transfer work. There may be other work that could + // rely on this exception. Once we add more UIJ types, we can re-evaluate + // the need for these exceptions. + // TODO: re-evaluate the need for these exceptions + final boolean isInPrivilegedState = mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus) - || isUidInForeground(jobStatus.getSourceUid())) { + || isUidInForeground(jobStatus.getSourceUid()); + final boolean isJobImportant = jobStatus.getEffectivePriority() >= JobInfo.PRIORITY_HIGH + || (jobStatus.getFlags() & JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0; + if (isInPrivilegedState && isJobImportant) { return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; } return getTimeUntilQuotaConsumedLocked( 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 a250ac75635b..07027645411d 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 @@ -368,10 +368,15 @@ public class QuotaControllerTest { } } + private JobInfo.Builder createJobInfoBuilder(int jobId) { + return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestQuotaJobService")); + } + private JobStatus createJobStatus(String testTag, int jobId) { - JobInfo jobInfo = new JobInfo.Builder(jobId, - new ComponentName(mContext, "TestQuotaJobService")) - .build(); + return createJobStatus(testTag, createJobInfoBuilder(jobId).build()); + } + + private JobStatus createJobStatus(String testTag, JobInfo jobInfo) { return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); } @@ -1333,39 +1338,70 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); + final long timeUntilQuotaConsumedMs = 7 * MINUTE_IN_MILLIS; JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); + //noinspection deprecation + JobStatus jobDefIWF = createJobStatus("testGetMaxJobExecutionTimeLocked", + createJobInfoBuilder(1) + .setImportantWhileForeground(true) + .setPriority(JobInfo.PRIORITY_DEFAULT) + .build()); + JobStatus jobHigh = createJobStatus("testGetMaxJobExecutionTimeLocked", + createJobInfoBuilder(2).setPriority(JobInfo.PRIORITY_HIGH).build()); setStandbyBucket(RARE_INDEX, job); + setStandbyBucket(RARE_INDEX, jobDefIWF); + setStandbyBucket(RARE_INDEX, jobHigh); setCharging(); synchronized (mQuotaController.mLock) { assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); } setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); synchronized (mQuotaController.mLock) { - assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(timeUntilQuotaConsumedMs, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); } // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job, null); + trackJobs(job, jobDefIWF, jobHigh); mQuotaController.prepareForExecutionLocked(job); + mQuotaController.prepareForExecutionLocked(jobDefIWF); + mQuotaController.prepareForExecutionLocked(jobHigh); } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { - assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(timeUntilQuotaConsumedMs, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); mQuotaController.maybeStopTrackingJobLocked(job, null); + mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null); + mQuotaController.maybeStopTrackingJobLocked(jobHigh, null); } setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); synchronized (mQuotaController.mLock) { - assertEquals(7 * MINUTE_IN_MILLIS, + assertEquals(timeUntilQuotaConsumedMs, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF)); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh)); } } |