Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d05ed8c5 authored by Kweku Adams's avatar Kweku Adams
Browse files

Fix concurrency limits.

The concurrency manager was accidentally limiting background apps to one
job at a time instead of correctly letting them run multiple jobs and
limiting them when they hit the package concurrency limit.

Bug: 204924801
Test: atest com.android.server.job.JobConcurrencyManagerTest
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/job
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/job
Change-Id: I51d9c41ce0c47fc1f634f3df8753730d7d3c641a
parent c88a1ff3
Loading
Loading
Loading
Loading
+48 −16
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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.
@@ -919,7 +921,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.
@@ -927,7 +930,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.
@@ -940,10 +943,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;
        }
    }

@@ -984,7 +987,7 @@ class JobConcurrencyManager {
                        jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
                        packageStats);
            }
            if (mService.mPendingJobQueue.remove(jobStatus)) {
            if (mService.getPendingJobQueue().remove(jobStatus)) {
                mService.mJobPackageTracker.noteNonpending(jobStatus);
            }
        } finally {
@@ -1011,7 +1014,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.
@@ -1179,7 +1182,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.
@@ -1262,7 +1265,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) {
@@ -1526,8 +1529,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_";
@@ -2028,7 +2031,8 @@ class JobConcurrencyManager {
        }
    }

    private static class PackageStats {
    @VisibleForTesting
    static class PackageStats {
        public int userId;
        public String packageName;
        public int numRunningEj;
@@ -2098,4 +2102,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;
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -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;

@@ -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())) {
+255 −5
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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;
@@ -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() {
@@ -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(
@@ -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();
    }
}
+8 −8
Original line number Diff line number Diff line
@@ -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);
@@ -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);
@@ -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",
@@ -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);
@@ -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. */