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 edf2ce3c92cb7..82e967ae1a0ba 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -75,13 +75,16 @@ class JobConcurrencyManager { // Try to give higher priority types lower values. static final int WORK_TYPE_NONE = 0; static final int WORK_TYPE_TOP = 1 << 0; - static final int WORK_TYPE_BG = 1 << 1; - static final int WORK_TYPE_BGUSER = 1 << 2; - private static final int NUM_WORK_TYPES = 3; + static final int WORK_TYPE_EJ = 1 << 1; + static final int WORK_TYPE_BG = 1 << 2; + static final int WORK_TYPE_BGUSER = 1 << 3; + @VisibleForTesting + static final int NUM_WORK_TYPES = 4; @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = { WORK_TYPE_NONE, WORK_TYPE_TOP, + WORK_TYPE_EJ, WORK_TYPE_BG, WORK_TYPE_BGUSER }) @@ -106,54 +109,60 @@ class JobConcurrencyManager { private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON = new WorkConfigLimitsPerMemoryTrimLevel( - new WorkTypeConfig("screen_on_normal", 8, + new WorkTypeConfig("screen_on_normal", 11, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4)) ), - new WorkTypeConfig("screen_on_moderate", 8, + new WorkTypeConfig("screen_on_moderate", 9, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)) ), - new WorkTypeConfig("screen_on_low", 5, + new WorkTypeConfig("screen_on_low", 6, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1), + Pair.create(WORK_TYPE_BG, 1)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) ), new WorkTypeConfig("screen_on_critical", 5, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) ) ); private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF = new WorkConfigLimitsPerMemoryTrimLevel( - new WorkTypeConfig("screen_off_normal", 10, + new WorkTypeConfig("screen_off_normal", 13, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4)) ), - new WorkTypeConfig("screen_off_moderate", 10, + new WorkTypeConfig("screen_off_moderate", 13, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)) ), - new WorkTypeConfig("screen_off_low", 5, + new WorkTypeConfig("screen_off_low", 7, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2), + Pair.create(WORK_TYPE_BG, 1)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) ), new WorkTypeConfig("screen_off_critical", 5, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) ) @@ -818,15 +827,22 @@ class JobConcurrencyManager { int getJobWorkTypes(@NonNull JobStatus js) { int classification = 0; - // TODO(171305774): create dedicated work type for EJ and FGS - if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP - || js.shouldTreatAsExpeditedJob()) { - classification |= WORK_TYPE_TOP; - } else if (shouldRunAsFgUserJob(js)) { - classification |= WORK_TYPE_BG; + // TODO: create dedicated work type for FGS + if (shouldRunAsFgUserJob(js)) { + if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { + classification |= WORK_TYPE_TOP; + } else { + classification |= WORK_TYPE_BG; + } + + if (js.shouldTreatAsExpeditedJob()) { + classification |= WORK_TYPE_EJ; + } } else { + // TODO(171305774): create dedicated slots for EJs of bg user classification |= WORK_TYPE_BGUSER; } + return classification; } @@ -835,10 +851,12 @@ class JobConcurrencyManager { private 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_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_"; private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_"; private static final String KEY_PREFIX_MAX_BGUSER = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_"; private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_"; + private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_"; private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_"; private static final String KEY_PREFIX_MIN_BGUSER = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_"; @@ -885,6 +903,10 @@ class JobConcurrencyManager { properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal)))); mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop); + final int maxEj = Math.max(1, Math.min(mMaxTotal, + properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier, + mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal)))); + mMaxAllowedSlots.put(WORK_TYPE_EJ, maxEj); final int maxBg = Math.max(1, Math.min(mMaxTotal, properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier, mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal)))); @@ -902,6 +924,12 @@ class JobConcurrencyManager { mDefaultMinReservedSlots.get(WORK_TYPE_TOP)))); mMinReservedSlots.put(WORK_TYPE_TOP, minTop); remaining -= minTop; + // Ensure ej is in the range [0, min(maxEj, remaining)] + final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining), + properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier, + mDefaultMinReservedSlots.get(WORK_TYPE_EJ)))); + mMinReservedSlots.put(WORK_TYPE_EJ, minEj); + remaining -= minEj; // Ensure bg is in the range [0, min(maxBg, remaining)] final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining), properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier, @@ -933,6 +961,10 @@ class JobConcurrencyManager { .println(); pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP)) .println(); + pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ)) + .println(); + pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ)) + .println(); pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG)) .println(); pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG)) @@ -1032,24 +1064,21 @@ class JobConcurrencyManager { private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES); private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES); private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES); - private int mNumUnspecialized = 0; private int mNumUnspecializedRemaining = 0; void setConfig(@NonNull WorkTypeConfig workTypeConfig) { mConfigMaxTotal = workTypeConfig.getMaxTotal(); mConfigNumReservedSlots.put(WORK_TYPE_TOP, workTypeConfig.getMinReserved(WORK_TYPE_TOP)); + mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ)); mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG)); mConfigNumReservedSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMinReserved(WORK_TYPE_BGUSER)); mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP)); + mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ)); mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG)); mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER)); - mNumUnspecialized = mConfigMaxTotal; - mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP); - mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG); - mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BGUSER); mNumUnspecializedRemaining = mConfigMaxTotal; for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) { mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i), @@ -1078,6 +1107,9 @@ class JobConcurrencyManager { if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) { mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1); } + if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) { + mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + 1); + } if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) { mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1); } @@ -1165,61 +1197,70 @@ class JobConcurrencyManager { void onCountDone() { // Calculate how many slots to reserve for each work type. "Unspecialized" slots will - // be reserved for higher importance types first (ie. top before bg). - mNumUnspecialized = mConfigMaxTotal; - final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP) - + mNumPendingJobs.get(WORK_TYPE_TOP); - int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop); - mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop); - mNumUnspecialized -= resTop; - final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG); - int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg); - mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg); - mNumUnspecialized -= resBg; - final int numBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER) - + mNumPendingJobs.get(WORK_TYPE_BGUSER); - int resBgUser = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BGUSER), numBgUser); - mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser); - mNumUnspecialized -= resBgUser; + // be reserved for higher importance types first (ie. top before ej before bg). + // Steps: + // 1. Account for slots for already running jobs + // 2. Use remaining unaccounted slots to try and ensure minimum reserved slots + // 3. Allocate remaining up to max, based on importance - mNumUnspecializedRemaining = mNumUnspecialized; - // Account for already running jobs after we've assigned the minimum number of slots. - int unspecializedAssigned; - int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop); - if (extraRunning > 0) { - unspecializedAssigned = Math.max(0, - Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop, - extraRunning)); - resTop += unspecializedAssigned; - mNumUnspecializedRemaining -= extraRunning; - } - extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg); - if (extraRunning > 0) { - unspecializedAssigned = Math.max(0, - Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning)); - resBg += unspecializedAssigned; - mNumUnspecializedRemaining -= extraRunning; - } - extraRunning = (mNumRunningJobs.get(WORK_TYPE_BGUSER) - resBgUser); - if (extraRunning > 0) { - unspecializedAssigned = Math.max(0, - Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER) - resBgUser, - extraRunning)); - resBgUser += unspecializedAssigned; - mNumUnspecializedRemaining -= extraRunning; - } + mNumUnspecializedRemaining = mConfigMaxTotal; - // Assign remaining unspecialized based on ranking. - unspecializedAssigned = Math.max(0, + // Step 1 + int runTop = mNumRunningJobs.get(WORK_TYPE_TOP); + int resTop = runTop; + mNumUnspecializedRemaining -= resTop; + int runEj = mNumRunningJobs.get(WORK_TYPE_EJ); + int resEj = runEj; + mNumUnspecializedRemaining -= resEj; + int runBg = mNumRunningJobs.get(WORK_TYPE_BG); + int resBg = runBg; + mNumUnspecializedRemaining -= resBg; + int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER); + int resBgUser = runBgUser; + mNumUnspecializedRemaining -= resBgUser; + + // Step 2 + final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP); + int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop))); + resTop += fillUp; + mNumUnspecializedRemaining -= fillUp; + final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ); + fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj))); + resEj += fillUp; + mNumUnspecializedRemaining -= fillUp; + final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG); + fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg))); + resBg += fillUp; + mNumUnspecializedRemaining -= fillUp; + final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER); + fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numBgUser, + mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser))); + resBgUser += fillUp; + mNumUnspecializedRemaining -= fillUp; + + // Step 3 + int unspecializedAssigned = Math.max(0, Math.min(mNumUnspecializedRemaining, Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop)); mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned); mNumUnspecializedRemaining -= unspecializedAssigned; + + unspecializedAssigned = Math.max(0, + Math.min(mNumUnspecializedRemaining, + Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj)); + mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned); + mNumUnspecializedRemaining -= unspecializedAssigned; + unspecializedAssigned = Math.max(0, Math.min(mNumUnspecializedRemaining, Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg)); mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned); mNumUnspecializedRemaining -= unspecializedAssigned; + unspecializedAssigned = Math.max(0, Math.min(mNumUnspecializedRemaining, Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser) @@ -1238,6 +1279,15 @@ class JobConcurrencyManager { return WORK_TYPE_TOP; } } + if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) { + final int maxAllowed = Math.min( + mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), + mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining); + if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ) + < maxAllowed) { + return WORK_TYPE_EJ; + } + } if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) { final int maxAllowed = Math.min( mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index 4effa4d445bbe..f2bb47bfb8ad1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -62,6 +62,7 @@ import com.android.server.LocalServices; import com.android.server.PowerAllowlistInternal; import com.android.server.SystemServiceManager; import com.android.server.job.controllers.JobStatus; +import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal; import org.junit.After; @@ -128,6 +129,9 @@ public class JobSchedulerServiceTest { // Called in DeviceIdleJobsController constructor. doReturn(mock(DeviceIdleInternal.class)) .when(() -> LocalServices.getService(DeviceIdleInternal.class)); + // Used in JobConcurrencyManager. + doReturn(mock(UserManagerInternal.class)) + .when(() -> LocalServices.getService(UserManagerInternal.class)); // Used in JobStatus. doReturn(mock(PackageManagerInternal.class)) .when(() -> LocalServices.getService(PackageManagerInternal.class)); diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java index 9c781922bf2e3..353ac4bd2129e 100644 --- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java @@ -16,8 +16,10 @@ package com.android.server.job; +import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; @@ -48,6 +50,10 @@ import java.util.Random; public class WorkCountTrackerTest { private static final String TAG = "WorkerCountTrackerTest"; + private static final double[] EQUAL_PROBABILITY_CDF = + buildCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, + 1.0 / NUM_WORK_TYPES); + private Random mRandom; private WorkCountTracker mWorkCountTracker; @@ -57,6 +63,47 @@ public class WorkCountTrackerTest { mWorkCountTracker = new WorkCountTracker(); } + @NonNull + private static double[] buildCdf(double pTop, double pEj, double pBg, double pBgUser) { + double[] cdf = new double[JobConcurrencyManager.NUM_WORK_TYPES]; + double sum = 0; + + sum += pTop; + cdf[0] = sum; + sum += pEj; + cdf[1] = sum; + sum += pBg; + cdf[2] = sum; + sum += pBgUser; + cdf[3] = sum; + + if (Double.compare(1, sum) != 0) { + throw new IllegalArgumentException("probabilities don't sum to one: " + sum); + } + return cdf; + } + + @JobConcurrencyManager.WorkType + static int getRandomWorkType(double[] cdf, double rand) { + for (int i = cdf.length - 1; i >= 0; --i) { + if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) { + switch (i) { + case 0: + return WORK_TYPE_TOP; + case 1: + return WORK_TYPE_EJ; + case 2: + return WORK_TYPE_BG; + case 3: + return WORK_TYPE_BGUSER; + default: + throw new IllegalStateException("Unknown work type"); + } + } + } + throw new IllegalStateException("Couldn't pick random work type"); + } + /** * Represents running and pending jobs. */ @@ -64,35 +111,22 @@ public class WorkCountTrackerTest { public final SparseIntArray running = new SparseIntArray(); public final SparseIntArray pending = new SparseIntArray(); - public void maybeEnqueueJobs(double startRatio, double fgJobRatio, double fgUserJobRatio) { - // fgUserJobRatio should always be at least fgJobRatio, otherwise no WORK_TYPE_BG will - // be enqueued. - while (mRandom.nextDouble() < startRatio) { - final double random = mRandom.nextDouble(); - if (random < fgJobRatio) { - pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1); - } else if (random < fgUserJobRatio) { - pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1); - } else { - pending.put(WORK_TYPE_BGUSER, pending.get(WORK_TYPE_BGUSER) + 1); - } + public void maybeEnqueueJobs(double probStart, double[] typeCdf) { + while (mRandom.nextDouble() < probStart) { + final int workType = getRandomWorkType(typeCdf, mRandom.nextDouble()); + pending.put(workType, pending.get(workType) + 1); } } - public void maybeFinishJobs(double stopRatio) { - for (int i = running.get(WORK_TYPE_BGUSER); i > 0; i--) { - if (mRandom.nextDouble() < stopRatio) { - running.put(WORK_TYPE_BGUSER, running.get(WORK_TYPE_BGUSER) - 1); - } - } + public void maybeFinishJobs(double probStop) { for (int i = running.get(WORK_TYPE_BG); i > 0; i--) { - if (mRandom.nextDouble() < stopRatio) { + if (mRandom.nextDouble() < probStop) { running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1); mWorkCountTracker.onJobFinished(WORK_TYPE_BG); } } for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) { - if (mRandom.nextDouble() < stopRatio) { + if (mRandom.nextDouble() < probStop) { running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1); mWorkCountTracker.onJobFinished(WORK_TYPE_TOP); } @@ -127,40 +161,27 @@ public class WorkCountTrackerTest { mWorkCountTracker.onCountDone(); } - private void startPendingJobs(Jobs jobs) { - while ((jobs.pending.get(WORK_TYPE_TOP) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) - || (jobs.pending.get(WORK_TYPE_BG) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) - || (jobs.pending.get(WORK_TYPE_BGUSER) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_BGUSER) != WORK_TYPE_NONE)) { - final boolean isStartingFg = mRandom.nextBoolean(); - final boolean isStartingFgUser = mRandom.nextBoolean(); + private boolean hasStartablePendingJob(Jobs jobs) { + for (int i = 0; i < jobs.pending.size(); ++i) { + if (jobs.pending.valueAt(i) > 0 + && mWorkCountTracker.canJobStart(jobs.pending.keyAt(i)) != WORK_TYPE_NONE) { + return true; + } + } + return false; + } - if (isStartingFg) { - if (jobs.pending.get(WORK_TYPE_TOP) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) { - jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1); - jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1); - mWorkCountTracker.stageJob(WORK_TYPE_TOP); - mWorkCountTracker.onJobStarted(WORK_TYPE_TOP); - } - } else if (isStartingFgUser) { - if (jobs.pending.get(WORK_TYPE_BG) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) { - jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1); - jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1); - mWorkCountTracker.stageJob(WORK_TYPE_BG); - mWorkCountTracker.onJobStarted(WORK_TYPE_BG); - } - } else { - if (jobs.pending.get(WORK_TYPE_BGUSER) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_BGUSER) != WORK_TYPE_NONE) { - jobs.pending.put(WORK_TYPE_BGUSER, jobs.pending.get(WORK_TYPE_BGUSER) - 1); - jobs.running.put(WORK_TYPE_BGUSER, jobs.running.get(WORK_TYPE_BGUSER) + 1); - mWorkCountTracker.stageJob(WORK_TYPE_BGUSER); - mWorkCountTracker.onJobStarted(WORK_TYPE_BGUSER); - } + private void startPendingJobs(Jobs jobs) { + while (hasStartablePendingJob(jobs)) { + final int startingWorkType = + getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble()); + + if (jobs.pending.get(startingWorkType) > 0 + && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) { + jobs.pending.put(startingWorkType, jobs.pending.get(startingWorkType) - 1); + jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1); + mWorkCountTracker.stageJob(startingWorkType); + mWorkCountTracker.onJobStarted(startingWorkType); } } } @@ -171,10 +192,10 @@ public class WorkCountTrackerTest { private void checkRandom(Jobs jobs, int numTests, int totalMax, @NonNull List> minLimits, @NonNull List> maxLimits, - double startRatio, double fgJobRatio, double fgUserJobRatio, double stopRatio) { + double probStart, double[] typeCdf, double probStop) { for (int i = 0; i < numTests; i++) { - jobs.maybeFinishJobs(stopRatio); - jobs.maybeEnqueueJobs(startRatio, fgJobRatio, fgUserJobRatio); + jobs.maybeFinishJobs(probStop); + jobs.maybeEnqueueJobs(probStart, typeCdf); recount(jobs, totalMax, minLimits, maxLimits); startPendingJobs(jobs); @@ -200,25 +221,19 @@ public class WorkCountTrackerTest { */ @Test public void testRandom1() { + assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES); + final Jobs jobs = new Jobs(); final int numTests = 5000; final int totalMax = 6; - final List> maxLimits = - List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); - final List> minLimits = - List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); - final double stopRatio = 0.1; - // WorkType probabilities: - // WORK_TYPE_TOP -- 50% - // WORK_TYPE_BG -- 50% - // WORK_TYPE_BGUSER -- 0% - final double fgJobRatio = 0.5; - final double fgUserJobRatio = 1; - final double startRatio = 0.1; + final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); + final double probStop = 0.1; + final double probStart = 0.1; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, + EQUAL_PROBABILITY_CDF, probStop); } @Test @@ -230,17 +245,11 @@ public class WorkCountTrackerTest { final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List> minLimits = List.of(); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 50% - // WORK_TYPE_BG -- 50% - // WORK_TYPE_BGUSER -- 0% - final double fgJobRatio = 0.5; - final double fgUserJobRatio = 1; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0.5, 0, 0.5, 0); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -252,17 +261,11 @@ public class WorkCountTrackerTest { final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 33% - // WORK_TYPE_BG -- 33% - // WORK_TYPE_BGUSER -- 33% - final double fgJobRatio = 1 / 3.0; - final double fgUserJobRatio = 2 / 3.0; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -274,17 +277,11 @@ public class WorkCountTrackerTest { final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List> minLimits = List.of(); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 33% - // WORK_TYPE_BG -- 33% - // WORK_TYPE_BGUSER -- 33% - final double fgJobRatio = 1 / 3.0; - final double fgUserJobRatio = 2 / 3.0; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -296,17 +293,11 @@ public class WorkCountTrackerTest { final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 10% - // WORK_TYPE_BG -- 80% - // WORK_TYPE_BGUSER -- 10% - final double fgJobRatio = 0.1; - final double fgUserJobRatio = 0.9; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0.1, 0, 0.8, .1); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -318,17 +309,11 @@ public class WorkCountTrackerTest { final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 90% - // WORK_TYPE_BG -- 10% - // WORK_TYPE_BGUSER -- 0% - final double fgJobRatio = 0.9; - final double fgUserJobRatio = 1; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0.9, 0, 0.1, 0); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -340,17 +325,11 @@ public class WorkCountTrackerTest { final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.4; - // WorkType probabilities: - // WORK_TYPE_TOP -- 10% - // WORK_TYPE_BG -- 10% - // WORK_TYPE_BGUSER -- 80% - final double fgJobRatio = 0.1; - final double fgUserJobRatio = 0.2; - final double startRatio = 0.5; + final double probStop = 0.4; + final double[] cdf = buildCdf(0.1, 0, 0.1, .8); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -363,17 +342,11 @@ public class WorkCountTrackerTest { List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); - final double stopRatio = 0.4; - // WorkType probabilities: - // WORK_TYPE_TOP -- 90% - // WORK_TYPE_BG -- 5% - // WORK_TYPE_BGUSER -- 5% - final double fgJobRatio = 0.9; - final double fgUserJobRatio = 0.95; - final double startRatio = 0.5; + final double probStop = 0.4; + final double[] cdf = buildCdf(0.9, 0, 0.05, 0.05); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -386,17 +359,11 @@ public class WorkCountTrackerTest { List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 0% - // WORK_TYPE_BG -- 50% - // WORK_TYPE_BGUSER -- 50% - final double fgJobRatio = 0; - final double fgUserJobRatio = 0.5; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0, 0, 0.5, 0.5); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -409,17 +376,11 @@ public class WorkCountTrackerTest { List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 0% - // WORK_TYPE_BG -- 10% - // WORK_TYPE_BGUSER -- 90% - final double fgJobRatio = 0; - final double fgUserJobRatio = 0.1; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0, 0, 0.1, 0.9); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -432,17 +393,81 @@ public class WorkCountTrackerTest { List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); - final double stopRatio = 0.5; - // WorkType probabilities: - // WORK_TYPE_TOP -- 0% - // WORK_TYPE_BG -- 90% - // WORK_TYPE_BGUSER -- 10% - final double fgJobRatio = 0; - final double fgUserJobRatio = 0.9; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0, 0, 0.9, 0.1); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom12() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List> minLimits = + List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)); + final double probStop = 0.4; + final double[] cdf = buildCdf(0.5, 0.5, 0, 0); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom13() { + assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES); + + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 13; + final List> maxLimits = List.of( + Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4), + Pair.create(WORK_TYPE_BGUSER, 3)); + final List> minLimits = + List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)); + final double probStop = 0.01; + final double probStart = 0.99; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, + EQUAL_PROBABILITY_CDF, probStop); + } + + @Test + public void testRandom14() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List> maxLimits = + List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4)); + final List> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); + final double probStop = 0.4; + final double[] cdf = buildCdf(.1, 0.5, 0.35, 0.05); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom15() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List> maxLimits = + List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4), + Pair.create(WORK_TYPE_BGUSER, 1)); + final List> minLimits = + List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)); + final double probStop = 0.4; + final double[] cdf = buildCdf(0.01, 0.49, 0.1, 0.4); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } /** Used by the following tests */ @@ -574,6 +599,16 @@ public class WorkCountTrackerTest { /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)), /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3))); + checkSimple(8, + /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)), + /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)), + /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_EJ, 5), + Pair.create(WORK_TYPE_BG, 3)), + /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 2)), + /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3), + Pair.create(WORK_TYPE_BG, 3))); + // This could happen if we lower the effective config due to higher memory pressure after // we've already started running jobs. We shouldn't stop already running jobs, but also // shouldn't start new ones. diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java index bbc1f1d262828..2288a89255619 100644 --- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java @@ -17,6 +17,7 @@ package com.android.server.job; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; import static org.junit.Assert.assertEquals; @@ -36,7 +37,6 @@ import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; import java.util.List; @RunWith(AndroidJUnit4.class) @@ -44,9 +44,11 @@ import java.util.List; public class WorkTypeConfigTest { private static final String KEY_MAX_TOTAL = "concurrency_max_total_test"; private static final String KEY_MAX_TOP = "concurrency_max_top_test"; + private static final String KEY_MAX_EJ = "concurrency_max_ej_test"; private static final String KEY_MAX_BG = "concurrency_max_bg_test"; private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test"; private static final String KEY_MIN_TOP = "concurrency_min_top_test"; + private static final String KEY_MIN_EJ = "concurrency_min_ej_test"; private static final String KEY_MIN_BG = "concurrency_min_bg_test"; private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test"; @@ -59,55 +61,27 @@ public class WorkTypeConfigTest { // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually. DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false); } private void check(@Nullable DeviceConfig.Properties config, int defaultTotal, - @Nullable Pair defaultTopLimits, - @Nullable Pair defaultBgLimits, - @Nullable Pair defaultBgUserLimits, + @NonNull List> defaultMin, + @NonNull List> defaultMax, boolean expectedValid, int expectedTotal, - @NonNull Pair expectedTopLimits, - @NonNull Pair expectedBgLimits, - @NonNull Pair expectedBgUserLimits) throws Exception { + @NonNull List> expectedMinLimits, + @NonNull List> expectedMaxLimits) throws Exception { resetConfig(); if (config != null) { DeviceConfig.setProperties(config); } - List> defaultMin = new ArrayList<>(); - List> defaultMax = new ArrayList<>(); - Integer val; - if (defaultTopLimits != null) { - if ((val = defaultTopLimits.first) != null) { - defaultMin.add(Pair.create(WORK_TYPE_TOP, val)); - } - if ((val = defaultTopLimits.second) != null) { - defaultMax.add(Pair.create(WORK_TYPE_TOP, val)); - } - } - if (defaultBgLimits != null) { - if ((val = defaultBgLimits.first) != null) { - defaultMin.add(Pair.create(WORK_TYPE_BG, val)); - } - if ((val = defaultBgLimits.second) != null) { - defaultMax.add(Pair.create(WORK_TYPE_BG, val)); - } - } - if (defaultBgUserLimits != null) { - if ((val = defaultBgUserLimits.first) != null) { - defaultMin.add(Pair.create(WORK_TYPE_BGUSER, val)); - } - if ((val = defaultBgUserLimits.second) != null) { - defaultMax.add(Pair.create(WORK_TYPE_BGUSER, val)); - } - } - final WorkTypeConfig counts; try { counts = new WorkTypeConfig("test", @@ -128,44 +102,125 @@ public class WorkTypeConfigTest { counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); assertEquals(expectedTotal, counts.getMaxTotal()); - assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP)); - assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP)); - assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG)); - assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG)); - assertEquals((int) expectedBgUserLimits.first, counts.getMinReserved(WORK_TYPE_BGUSER)); - assertEquals((int) expectedBgUserLimits.second, counts.getMax(WORK_TYPE_BGUSER)); + for (Pair min : expectedMinLimits) { + assertEquals((int) min.second, counts.getMinReserved(min.first)); + } + for (Pair max : expectedMaxLimits) { + assertEquals((int) max.second, counts.getMax(max.first)); + } } @Test public void test() throws Exception { // Tests with various combinations. - check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1), Pair.create(0, 1), - /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1), Pair.create(0, 1)); - check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0), Pair.create(0, 0), - /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1), Pair.create(0, 1)); - check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0), Pair.create(0, 0), - /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1), Pair.create(0, 1)); - check(null, /*default*/ -1, null, Pair.create(-1, -1), Pair.create(-1, -1), - /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1), Pair.create(0, 1)); - check(null, /*default*/ 5, null, Pair.create(5, 5), Pair.create(0, 5), - /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5), Pair.create(0, 5)); - check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5), Pair.create(2, 1), - /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5), Pair.create(0, 1)); - check(null, /*default*/ 4, null, Pair.create(6, 5), Pair.create(6, 5), - /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4), Pair.create(0, 4)); - check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1), Pair.create(0, 5), - /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1), Pair.create(0, 5)); - check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1), Pair.create(1, 5), - /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1), Pair.create(1, 5)); - check(null, /*default*/ 15, null, Pair.create(15, 15), Pair.create(0, 15), - /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15), Pair.create(0, 15)); - check(null, /*default*/ 16, null, Pair.create(16, 16), Pair.create(0, 16), - /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16)); - check(null, /*default*/ 20, null, Pair.create(20, 20), Pair.create(10, 20), + check(null, /*default*/ 13, + /* min */ List.of(), + /* max */ List.of(), + /*expected*/ true, 13, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 0), + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 13), Pair.create(WORK_TYPE_EJ, 13), + Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 13))); + check(null, /*default*/ 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 1)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))); + check(null, /*default*/ 0, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 0)), + /*expected*/ false, 1, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ -1, + /* min */ List.of(Pair.create(WORK_TYPE_BG, -1)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, -1)), + /*expected*/ false, 1, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 5, + /* min */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5))); + check(null, /*default*/ 6, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 2)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)), + /*expected*/ false, 6, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1))); + check(null, /*default*/ 4, + /* min */ List.of( + Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 6)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)), + /*expected*/ false, 4, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 4), + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 4))); + check(null, /*default*/ 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 10, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)), + /*expected*/ true, 10, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 15, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 15)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 15)), + /*expected*/ true, 15, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 14)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 15), Pair.create(WORK_TYPE_BG, 15))); + check(null, /*default*/ 16, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16))); + check(null, /*default*/ 20, + /* min */ List.of( + Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 10)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 20)), /*expected*/ false, 16, - Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16)); - check(null, /*default*/ 20, null, Pair.create(16, 16), Pair.create(0, 16), - /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16)); + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 15), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), + Pair.create(WORK_TYPE_BG, 16), Pair.create(WORK_TYPE_BGUSER, 16))); + check(null, /*default*/ 20, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16))); // Test for overriding with a setting string. check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) @@ -173,26 +228,66 @@ public class WorkTypeConfigTest { .setInt(KEY_MAX_BG, 4) .setInt(KEY_MIN_BG, 3) .build(), - /*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 2), - /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4), Pair.create(0, 2)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 9), Pair.create(WORK_TYPE_BGUSER, 2)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))); check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MAX_TOTAL, 5).build(), - /*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 0), - /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5), Pair.create(0, 1)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 5))); + check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MAX_BG, 4).build(), - /*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 9), - /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4), Pair.create(0, 9)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 9, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 4))); check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MIN_BG, 3).build(), - /*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 6), - /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9), Pair.create(0, 6)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 9, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 9))); + check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) + .setInt(KEY_MAX_TOTAL, 20) + .setInt(KEY_MAX_EJ, 5) + .setInt(KEY_MIN_EJ, 2) + .setInt(KEY_MAX_BG, 16) + .setInt(KEY_MIN_BG, 8) + .build(), + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 2), + Pair.create(WORK_TYPE_BG, 8)), + /* max */ + List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_EJ, 5), + Pair.create(WORK_TYPE_BG, 16))); + check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MAX_TOTAL, 20) .setInt(KEY_MAX_BG, 20) .setInt(KEY_MIN_BG, 8) .build(), - /*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 8), - /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16), Pair.create(0, 8)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16))); } }