Loading apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +48 −16 Original line number Diff line number Diff line Loading @@ -84,10 +84,12 @@ class JobConcurrencyManager { private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS = CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms"; private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000; private static final String KEY_PKG_CONCURRENCY_LIMIT_EJ = @VisibleForTesting static final String KEY_PKG_CONCURRENCY_LIMIT_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_ej"; private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_EJ = 3; private static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR = @VisibleForTesting static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR = CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular"; private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = MAX_JOB_CONTEXTS_COUNT / 2; Loading Loading @@ -299,13 +301,13 @@ class JobConcurrencyManager { * The maximum number of expedited jobs a single userId-package can have running simultaneously. * TOP apps are not limited. */ private long mPkgConcurrencyLimitEj = DEFAULT_PKG_CONCURRENCY_LIMIT_EJ; private int mPkgConcurrencyLimitEj = DEFAULT_PKG_CONCURRENCY_LIMIT_EJ; /** * The maximum number of regular jobs a single userId-package can have running simultaneously. * TOP apps are not limited. */ private long mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR; private int mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR; /** Current memory trim level. */ private int mLastMemoryTrimLevel; Loading Loading @@ -568,7 +570,7 @@ class JobConcurrencyManager { Slog.d(TAG, printPendingQueueLocked()); } final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); final List<JobServiceContext> activeServices = mActiveServices; // To avoid GC churn, we recycle the arrays. Loading Loading @@ -916,7 +918,8 @@ class JobConcurrencyManager { } @GuardedBy("mLock") private boolean isPkgConcurrencyLimitedLocked(@NonNull JobStatus jobStatus) { @VisibleForTesting boolean isPkgConcurrencyLimitedLocked(@NonNull JobStatus jobStatus) { if (jobStatus.lastEvaluatedBias >= JobInfo.BIAS_TOP_APP) { // Don't restrict top apps' concurrency. The work type limits will make sure // background jobs have slots to run if the system has resources. Loading @@ -924,7 +927,7 @@ class JobConcurrencyManager { } // Use < instead of <= as that gives us a little wiggle room in case a new job comes // along very shortly. if (mService.mPendingJobQueue.size() + mRunningJobs.size() if (mService.getPendingJobQueue().size() + mRunningJobs.size() < mWorkTypeConfig.getMaxTotal()) { // Don't artificially limit a single package if we don't even have enough jobs to use // the maximum number of slots. We'll preempt the job later if we need the slot. Loading @@ -937,10 +940,10 @@ class JobConcurrencyManager { return false; } if (jobStatus.shouldTreatAsExpeditedJob()) { return packageStats.numRunningEj + packageStats.numStagedEj < mPkgConcurrencyLimitEj; return packageStats.numRunningEj + packageStats.numStagedEj >= mPkgConcurrencyLimitEj; } else { return packageStats.numRunningRegular + packageStats.numStagedRegular < mPkgConcurrencyLimitRegular; >= mPkgConcurrencyLimitRegular; } } Loading Loading @@ -981,7 +984,7 @@ class JobConcurrencyManager { jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), packageStats); } if (mService.mPendingJobQueue.remove(jobStatus)) { if (mService.getPendingJobQueue().remove(jobStatus)) { mService.mJobPackageTracker.noteNonpending(jobStatus); } } finally { Loading @@ -1008,7 +1011,7 @@ class JobConcurrencyManager { } } final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) { updateCounterConfigLocked(); // Preemption case needs special care. Loading Loading @@ -1176,7 +1179,7 @@ class JobConcurrencyManager { return "too many jobs running"; } final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); final int numPending = pendingJobQueue.size(); if (numPending == 0) { // All quiet. We can let this job run to completion. Loading Loading @@ -1259,7 +1262,7 @@ class JobConcurrencyManager { @GuardedBy("mLock") private String printPendingQueueLocked() { StringBuilder s = new StringBuilder("Pending queue: "); PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); JobStatus js; pendingJobQueue.resetIterator(); while ((js = pendingJobQueue.next()) != null) { Loading Loading @@ -1523,8 +1526,8 @@ class JobConcurrencyManager { @VisibleForTesting static class WorkTypeConfig { private static final String KEY_PREFIX_MAX_TOTAL = CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_"; @VisibleForTesting static final String KEY_PREFIX_MAX_TOTAL = CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_"; private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_"; private static final String KEY_PREFIX_MAX_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "max_fgs_"; private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_"; Loading Loading @@ -2025,7 +2028,8 @@ class JobConcurrencyManager { } } private static class PackageStats { @VisibleForTesting static class PackageStats { public int userId; public String packageName; public int numRunningEj; Loading Loading @@ -2095,4 +2099,32 @@ class JobConcurrencyManager { newWorkType = WORK_TYPE_NONE; } } // TESTING HELPERS @VisibleForTesting void addRunningJobForTesting(@NonNull JobStatus job) { mRunningJobs.add(job); final PackageStats packageStats = getPackageStatsForTesting(job.getSourceUserId(), job.getSourcePackageName()); packageStats.adjustRunningCount(true, job.shouldTreatAsExpeditedJob()); } @VisibleForTesting int getPackageConcurrencyLimitEj() { return mPkgConcurrencyLimitEj; } int getPackageConcurrencyLimitRegular() { return mPkgConcurrencyLimitRegular; } /** Gets the {@link PackageStats} object for the app and saves it for testing use. */ @NonNull @VisibleForTesting PackageStats getPackageStatsForTesting(int userId, @NonNull String packageName) { final PackageStats packageStats = getPkgStatsLocked(userId, packageName); mActivePkgStats.add(userId, packageName, packageStats); return packageStats; } } apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +6 −1 Original line number Diff line number Diff line Loading @@ -286,7 +286,7 @@ public class JobSchedulerService extends com.android.server.SystemService * Queue of pending jobs. The JobServiceContext class will receive jobs from this list * when ready to execute them. */ final PendingJobQueue mPendingJobQueue = new PendingJobQueue(); private final PendingJobQueue mPendingJobQueue = new PendingJobQueue(); int[] mStartedUsers = EmptyArray.INT; Loading Loading @@ -1027,6 +1027,11 @@ public class JobSchedulerService extends com.android.server.SystemService return mConstants; } @NonNull PendingJobQueue getPendingJobQueue() { return mPendingJobQueue; } @NonNull public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) { if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) { Loading services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java +255 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.job; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_EJ; import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_REGULAR; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; Loading @@ -30,6 +34,7 @@ import android.content.Context; import android.content.pm.UserInfo; import android.content.res.Resources; import android.os.UserHandle; import android.provider.DeviceConfig; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -37,9 +42,11 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.job.JobConcurrencyManager.GracePeriodObserver; import com.android.server.job.JobConcurrencyManager.WorkTypeConfig; import com.android.server.job.controllers.JobStatus; import com.android.server.pm.UserManagerInternal; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; Loading @@ -54,9 +61,12 @@ public final class JobConcurrencyManagerTest { private UserManagerInternal mUserManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private int mNextUserId; private int mDefaultUserId; private GracePeriodObserver mGracePeriodObserver; private Context mContext; private Resources mResources; private PendingJobQueue mPendingJobQueue; private DeviceConfig.Properties.Builder mConfigBuilder; @BeforeClass public static void setUpOnce() { Loading @@ -80,14 +90,224 @@ public final class JobConcurrencyManagerTest { R.bool.config_jobSchedulerRestrictBackgroundUser); when(mContext.getResources()).thenReturn(mResources); doReturn(mContext).when(jobSchedulerService).getTestableContext(); mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); mPendingJobQueue = new PendingJobQueue(); doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue(); mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService); mGracePeriodObserver = mock(GracePeriodObserver.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mDefaultUserId = mNextUserId; createCurrentUser(true); mNextUserId = 10; mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver; } @After public void tearDown() throws Exception { resetConfig(); } @Test public void testIsPkgConcurrencyLimited_top() { final JobStatus topJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0); topJob.lastEvaluatedBias = JobInfo.BIAS_TOP_APP; assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); // Pending jobs shouldn't affect TOP job's status. for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mPendingJobQueue.add(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); // Already running jobs shouldn't affect TOP job's status. for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, i); mJobConcurrencyManager.addRunningJobForTesting(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); // Currently running or staged jobs shouldn't affect TOP job's status. final JobConcurrencyManager.PackageStats packageStats = mJobConcurrencyManager.getPackageStatsForTesting( topJob.getSourceUserId(), topJob.getSourcePackageName()); packageStats.numStagedEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj(); packageStats.numStagedRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular(); packageStats.numRunningEj = 0; packageStats.numRunningRegular = 0; assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); packageStats.numStagedEj = 0; packageStats.numStagedRegular = 0; packageStats.numRunningEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj(); packageStats.numRunningRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); } @Test public void testIsPkgConcurrencyLimited_belowTotalLimit() throws Exception { final JobStatus testJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE); setConcurrencyConfig(8); // Pending jobs below limit shouldn't affect job's status. for (int i = 0; i < 5; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mPendingJobQueue.add(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob)); mPendingJobQueue.clear(); // Already running jobs below limit shouldn't affect job's status. for (int i = 0; i < 4; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mJobConcurrencyManager.addRunningJobForTesting(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob)); // Mix of pending + running. for (int i = 4; i < 8; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mPendingJobQueue.add(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob)); } @Test public void testIsPkgConcurrencyLimited() throws Exception { final JobStatus testReg = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0); final JobStatus testEj = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 1); spyOn(testEj); doReturn(true).when(testEj).shouldTreatAsExpeditedJob(); setConcurrencyConfig(JobSchedulerService.MAX_JOB_CONTEXTS_COUNT); for (int i = 0; i < JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i, i + 1); mPendingJobQueue.add(job); } // App has no running jobs, so shouldn't be limited. assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); // Already running jobs shouldn't affect TOP job's status. final JobConcurrencyManager.PackageStats packageStats = mJobConcurrencyManager.getPackageStatsForTesting( testReg.getSourceUserId(), testReg.getSourcePackageName()); // Only running counts packageStats.numStagedEj = 0; packageStats.numStagedRegular = 0; packageStats.numRunningEj = 4; packageStats.numRunningRegular = 4; mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); // Only staged counts packageStats.numStagedEj = 4; packageStats.numStagedRegular = 4; packageStats.numRunningEj = 0; packageStats.numRunningRegular = 0; mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); // Running + staged counts packageStats.numStagedEj = 2; packageStats.numStagedRegular = 1; packageStats.numRunningEj = 2; packageStats.numRunningRegular = 3; mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); } @Test public void testShouldRunAsFgUserJob_currentUser() { assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( Loading Loading @@ -179,10 +399,40 @@ public final class JobConcurrencyManagerTest { } private static JobStatus createJob(UserInfo userInfo) { JobStatus jobStatus = JobStatus.createFromJobInfo( new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(), userInfo.id * UserHandle.PER_USER_RANGE, null, userInfo.id, "JobConcurrencyManagerTest"); return jobStatus; return createJob(userInfo.id * UserHandle.PER_USER_RANGE); } private static JobStatus createJob(int uid) { return createJob(uid, 1); } private static JobStatus createJob(int uid, int jobId) { return JobStatus.createFromJobInfo( new JobInfo.Builder(jobId, new ComponentName("foo", "bar")).build(), uid, null, UserHandle.getUserId(uid), "JobConcurrencyManagerTest"); } private void setConcurrencyConfig(int total) throws Exception { // Set the values for all memory states so we don't have to worry about memory on the device // during testing. final String[] identifiers = { "screen_on_normal", "screen_on_moderate", "screen_on_low", "screen_on_critical", "screen_off_normal", "screen_off_moderate", "screen_off_low", "screen_off_critical" }; for (String identifier : identifiers) { mConfigBuilder .setInt(WorkTypeConfig.KEY_PREFIX_MAX_TOTAL + identifier, total); } updateDeviceConfig(); } private void updateDeviceConfig() throws Exception { DeviceConfig.setProperties(mConfigBuilder.build()); mJobConcurrencyManager.updateConfigLocked(); } private void resetConfig() throws Exception { mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); updateDeviceConfig(); } } services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +8 −8 Original line number Diff line number Diff line Loading @@ -757,7 +757,7 @@ public class JobSchedulerServiceTest { job.setStandbyBucket(RARE_INDEX); // Not enough RARE jobs to run. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { maybeQueueFunctor.accept(job); Loading @@ -766,10 +766,10 @@ public class JobSchedulerServiceTest { assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); } maybeQueueFunctor.postProcessLocked(); assertEquals(0, mService.mPendingJobQueue.size()); assertEquals(0, mService.getPendingJobQueue().size()); // Enough RARE jobs to run. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) { maybeQueueFunctor.accept(job); Loading @@ -778,10 +778,10 @@ public class JobSchedulerServiceTest { assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); } maybeQueueFunctor.postProcessLocked(); assertEquals(5, mService.mPendingJobQueue.size()); assertEquals(5, mService.getPendingJobQueue().size()); // Not enough RARE jobs to run, but a non-batched job saves the day. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); JobStatus activeJob = createJobStatus( "testRareJobBatching", Loading @@ -795,10 +795,10 @@ public class JobSchedulerServiceTest { } maybeQueueFunctor.accept(activeJob); maybeQueueFunctor.postProcessLocked(); assertEquals(3, mService.mPendingJobQueue.size()); assertEquals(3, mService.getPendingJobQueue().size()); // Not enough RARE jobs to run, but an old RARE job saves the day. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo()); oldRareJob.setStandbyBucket(RARE_INDEX); Loading @@ -814,7 +814,7 @@ public class JobSchedulerServiceTest { maybeQueueFunctor.accept(oldRareJob); assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed()); maybeQueueFunctor.postProcessLocked(); assertEquals(3, mService.mPendingJobQueue.size()); assertEquals(3, mService.getPendingJobQueue().size()); } /** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */ Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +48 −16 Original line number Diff line number Diff line Loading @@ -84,10 +84,12 @@ class JobConcurrencyManager { private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS = CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms"; private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000; private static final String KEY_PKG_CONCURRENCY_LIMIT_EJ = @VisibleForTesting static final String KEY_PKG_CONCURRENCY_LIMIT_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_ej"; private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_EJ = 3; private static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR = @VisibleForTesting static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR = CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular"; private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = MAX_JOB_CONTEXTS_COUNT / 2; Loading Loading @@ -299,13 +301,13 @@ class JobConcurrencyManager { * The maximum number of expedited jobs a single userId-package can have running simultaneously. * TOP apps are not limited. */ private long mPkgConcurrencyLimitEj = DEFAULT_PKG_CONCURRENCY_LIMIT_EJ; private int mPkgConcurrencyLimitEj = DEFAULT_PKG_CONCURRENCY_LIMIT_EJ; /** * The maximum number of regular jobs a single userId-package can have running simultaneously. * TOP apps are not limited. */ private long mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR; private int mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR; /** Current memory trim level. */ private int mLastMemoryTrimLevel; Loading Loading @@ -568,7 +570,7 @@ class JobConcurrencyManager { Slog.d(TAG, printPendingQueueLocked()); } final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); final List<JobServiceContext> activeServices = mActiveServices; // To avoid GC churn, we recycle the arrays. Loading Loading @@ -916,7 +918,8 @@ class JobConcurrencyManager { } @GuardedBy("mLock") private boolean isPkgConcurrencyLimitedLocked(@NonNull JobStatus jobStatus) { @VisibleForTesting boolean isPkgConcurrencyLimitedLocked(@NonNull JobStatus jobStatus) { if (jobStatus.lastEvaluatedBias >= JobInfo.BIAS_TOP_APP) { // Don't restrict top apps' concurrency. The work type limits will make sure // background jobs have slots to run if the system has resources. Loading @@ -924,7 +927,7 @@ class JobConcurrencyManager { } // Use < instead of <= as that gives us a little wiggle room in case a new job comes // along very shortly. if (mService.mPendingJobQueue.size() + mRunningJobs.size() if (mService.getPendingJobQueue().size() + mRunningJobs.size() < mWorkTypeConfig.getMaxTotal()) { // Don't artificially limit a single package if we don't even have enough jobs to use // the maximum number of slots. We'll preempt the job later if we need the slot. Loading @@ -937,10 +940,10 @@ class JobConcurrencyManager { return false; } if (jobStatus.shouldTreatAsExpeditedJob()) { return packageStats.numRunningEj + packageStats.numStagedEj < mPkgConcurrencyLimitEj; return packageStats.numRunningEj + packageStats.numStagedEj >= mPkgConcurrencyLimitEj; } else { return packageStats.numRunningRegular + packageStats.numStagedRegular < mPkgConcurrencyLimitRegular; >= mPkgConcurrencyLimitRegular; } } Loading Loading @@ -981,7 +984,7 @@ class JobConcurrencyManager { jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), packageStats); } if (mService.mPendingJobQueue.remove(jobStatus)) { if (mService.getPendingJobQueue().remove(jobStatus)) { mService.mJobPackageTracker.noteNonpending(jobStatus); } } finally { Loading @@ -1008,7 +1011,7 @@ class JobConcurrencyManager { } } final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) { updateCounterConfigLocked(); // Preemption case needs special care. Loading Loading @@ -1176,7 +1179,7 @@ class JobConcurrencyManager { return "too many jobs running"; } final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); final int numPending = pendingJobQueue.size(); if (numPending == 0) { // All quiet. We can let this job run to completion. Loading Loading @@ -1259,7 +1262,7 @@ class JobConcurrencyManager { @GuardedBy("mLock") private String printPendingQueueLocked() { StringBuilder s = new StringBuilder("Pending queue: "); PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); JobStatus js; pendingJobQueue.resetIterator(); while ((js = pendingJobQueue.next()) != null) { Loading Loading @@ -1523,8 +1526,8 @@ class JobConcurrencyManager { @VisibleForTesting static class WorkTypeConfig { private static final String KEY_PREFIX_MAX_TOTAL = CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_"; @VisibleForTesting static final String KEY_PREFIX_MAX_TOTAL = CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_"; private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_"; private static final String KEY_PREFIX_MAX_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "max_fgs_"; private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_"; Loading Loading @@ -2025,7 +2028,8 @@ class JobConcurrencyManager { } } private static class PackageStats { @VisibleForTesting static class PackageStats { public int userId; public String packageName; public int numRunningEj; Loading Loading @@ -2095,4 +2099,32 @@ class JobConcurrencyManager { newWorkType = WORK_TYPE_NONE; } } // TESTING HELPERS @VisibleForTesting void addRunningJobForTesting(@NonNull JobStatus job) { mRunningJobs.add(job); final PackageStats packageStats = getPackageStatsForTesting(job.getSourceUserId(), job.getSourcePackageName()); packageStats.adjustRunningCount(true, job.shouldTreatAsExpeditedJob()); } @VisibleForTesting int getPackageConcurrencyLimitEj() { return mPkgConcurrencyLimitEj; } int getPackageConcurrencyLimitRegular() { return mPkgConcurrencyLimitRegular; } /** Gets the {@link PackageStats} object for the app and saves it for testing use. */ @NonNull @VisibleForTesting PackageStats getPackageStatsForTesting(int userId, @NonNull String packageName) { final PackageStats packageStats = getPkgStatsLocked(userId, packageName); mActivePkgStats.add(userId, packageName, packageStats); return packageStats; } }
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +6 −1 Original line number Diff line number Diff line Loading @@ -286,7 +286,7 @@ public class JobSchedulerService extends com.android.server.SystemService * Queue of pending jobs. The JobServiceContext class will receive jobs from this list * when ready to execute them. */ final PendingJobQueue mPendingJobQueue = new PendingJobQueue(); private final PendingJobQueue mPendingJobQueue = new PendingJobQueue(); int[] mStartedUsers = EmptyArray.INT; Loading Loading @@ -1027,6 +1027,11 @@ public class JobSchedulerService extends com.android.server.SystemService return mConstants; } @NonNull PendingJobQueue getPendingJobQueue() { return mPendingJobQueue; } @NonNull public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) { if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) { Loading
services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java +255 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.job; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_EJ; import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_REGULAR; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; Loading @@ -30,6 +34,7 @@ import android.content.Context; import android.content.pm.UserInfo; import android.content.res.Resources; import android.os.UserHandle; import android.provider.DeviceConfig; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -37,9 +42,11 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.job.JobConcurrencyManager.GracePeriodObserver; import com.android.server.job.JobConcurrencyManager.WorkTypeConfig; import com.android.server.job.controllers.JobStatus; import com.android.server.pm.UserManagerInternal; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; Loading @@ -54,9 +61,12 @@ public final class JobConcurrencyManagerTest { private UserManagerInternal mUserManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private int mNextUserId; private int mDefaultUserId; private GracePeriodObserver mGracePeriodObserver; private Context mContext; private Resources mResources; private PendingJobQueue mPendingJobQueue; private DeviceConfig.Properties.Builder mConfigBuilder; @BeforeClass public static void setUpOnce() { Loading @@ -80,14 +90,224 @@ public final class JobConcurrencyManagerTest { R.bool.config_jobSchedulerRestrictBackgroundUser); when(mContext.getResources()).thenReturn(mResources); doReturn(mContext).when(jobSchedulerService).getTestableContext(); mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); mPendingJobQueue = new PendingJobQueue(); doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue(); mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService); mGracePeriodObserver = mock(GracePeriodObserver.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mDefaultUserId = mNextUserId; createCurrentUser(true); mNextUserId = 10; mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver; } @After public void tearDown() throws Exception { resetConfig(); } @Test public void testIsPkgConcurrencyLimited_top() { final JobStatus topJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0); topJob.lastEvaluatedBias = JobInfo.BIAS_TOP_APP; assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); // Pending jobs shouldn't affect TOP job's status. for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mPendingJobQueue.add(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); // Already running jobs shouldn't affect TOP job's status. for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, i); mJobConcurrencyManager.addRunningJobForTesting(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); // Currently running or staged jobs shouldn't affect TOP job's status. final JobConcurrencyManager.PackageStats packageStats = mJobConcurrencyManager.getPackageStatsForTesting( topJob.getSourceUserId(), topJob.getSourcePackageName()); packageStats.numStagedEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj(); packageStats.numStagedRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular(); packageStats.numRunningEj = 0; packageStats.numRunningRegular = 0; assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); packageStats.numStagedEj = 0; packageStats.numStagedRegular = 0; packageStats.numRunningEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj(); packageStats.numRunningRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob)); } @Test public void testIsPkgConcurrencyLimited_belowTotalLimit() throws Exception { final JobStatus testJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE); setConcurrencyConfig(8); // Pending jobs below limit shouldn't affect job's status. for (int i = 0; i < 5; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mPendingJobQueue.add(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob)); mPendingJobQueue.clear(); // Already running jobs below limit shouldn't affect job's status. for (int i = 0; i < 4; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mJobConcurrencyManager.addRunningJobForTesting(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob)); // Mix of pending + running. for (int i = 4; i < 8; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i); mPendingJobQueue.add(job); } assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob)); } @Test public void testIsPkgConcurrencyLimited() throws Exception { final JobStatus testReg = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0); final JobStatus testEj = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 1); spyOn(testEj); doReturn(true).when(testEj).shouldTreatAsExpeditedJob(); setConcurrencyConfig(JobSchedulerService.MAX_JOB_CONTEXTS_COUNT); for (int i = 0; i < JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) { final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i, i + 1); mPendingJobQueue.add(job); } // App has no running jobs, so shouldn't be limited. assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); // Already running jobs shouldn't affect TOP job's status. final JobConcurrencyManager.PackageStats packageStats = mJobConcurrencyManager.getPackageStatsForTesting( testReg.getSourceUserId(), testReg.getSourcePackageName()); // Only running counts packageStats.numStagedEj = 0; packageStats.numStagedRegular = 0; packageStats.numRunningEj = 4; packageStats.numRunningRegular = 4; mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); // Only staged counts packageStats.numStagedEj = 4; packageStats.numStagedRegular = 4; packageStats.numRunningEj = 0; packageStats.numRunningRegular = 0; mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); // Running + staged counts packageStats.numStagedEj = 2; packageStats.numStagedRegular = 1; packageStats.numRunningEj = 2; packageStats.numRunningRegular = 3; mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3); updateDeviceConfig(); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3); mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8); updateDeviceConfig(); assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj)); assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg)); } @Test public void testShouldRunAsFgUserJob_currentUser() { assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( Loading Loading @@ -179,10 +399,40 @@ public final class JobConcurrencyManagerTest { } private static JobStatus createJob(UserInfo userInfo) { JobStatus jobStatus = JobStatus.createFromJobInfo( new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(), userInfo.id * UserHandle.PER_USER_RANGE, null, userInfo.id, "JobConcurrencyManagerTest"); return jobStatus; return createJob(userInfo.id * UserHandle.PER_USER_RANGE); } private static JobStatus createJob(int uid) { return createJob(uid, 1); } private static JobStatus createJob(int uid, int jobId) { return JobStatus.createFromJobInfo( new JobInfo.Builder(jobId, new ComponentName("foo", "bar")).build(), uid, null, UserHandle.getUserId(uid), "JobConcurrencyManagerTest"); } private void setConcurrencyConfig(int total) throws Exception { // Set the values for all memory states so we don't have to worry about memory on the device // during testing. final String[] identifiers = { "screen_on_normal", "screen_on_moderate", "screen_on_low", "screen_on_critical", "screen_off_normal", "screen_off_moderate", "screen_off_low", "screen_off_critical" }; for (String identifier : identifiers) { mConfigBuilder .setInt(WorkTypeConfig.KEY_PREFIX_MAX_TOTAL + identifier, total); } updateDeviceConfig(); } private void updateDeviceConfig() throws Exception { DeviceConfig.setProperties(mConfigBuilder.build()); mJobConcurrencyManager.updateConfigLocked(); } private void resetConfig() throws Exception { mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); updateDeviceConfig(); } }
services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +8 −8 Original line number Diff line number Diff line Loading @@ -757,7 +757,7 @@ public class JobSchedulerServiceTest { job.setStandbyBucket(RARE_INDEX); // Not enough RARE jobs to run. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { maybeQueueFunctor.accept(job); Loading @@ -766,10 +766,10 @@ public class JobSchedulerServiceTest { assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); } maybeQueueFunctor.postProcessLocked(); assertEquals(0, mService.mPendingJobQueue.size()); assertEquals(0, mService.getPendingJobQueue().size()); // Enough RARE jobs to run. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) { maybeQueueFunctor.accept(job); Loading @@ -778,10 +778,10 @@ public class JobSchedulerServiceTest { assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); } maybeQueueFunctor.postProcessLocked(); assertEquals(5, mService.mPendingJobQueue.size()); assertEquals(5, mService.getPendingJobQueue().size()); // Not enough RARE jobs to run, but a non-batched job saves the day. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); JobStatus activeJob = createJobStatus( "testRareJobBatching", Loading @@ -795,10 +795,10 @@ public class JobSchedulerServiceTest { } maybeQueueFunctor.accept(activeJob); maybeQueueFunctor.postProcessLocked(); assertEquals(3, mService.mPendingJobQueue.size()); assertEquals(3, mService.getPendingJobQueue().size()); // Not enough RARE jobs to run, but an old RARE job saves the day. mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear(); maybeQueueFunctor.reset(); JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo()); oldRareJob.setStandbyBucket(RARE_INDEX); Loading @@ -814,7 +814,7 @@ public class JobSchedulerServiceTest { maybeQueueFunctor.accept(oldRareJob); assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed()); maybeQueueFunctor.postProcessLocked(); assertEquals(3, mService.mPendingJobQueue.size()); assertEquals(3, mService.getPendingJobQueue().size()); } /** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */ Loading