diff options
| author | 2021-02-01 11:09:25 -0800 | |
|---|---|---|
| committer | 2021-02-08 19:18:40 -0800 | |
| commit | 20b0eee64fc47d16769939d8c2263b95101bd760 (patch) | |
| tree | 0a793c3f1ff338c8b8a0bb6ad3eef0a7454dab2a | |
| parent | 076669f905517c3637066dfa3caf6821e400358c (diff) | |
Reserve execution slots for expedited jobs.
Make sure we reserve at least 3 slots for expedited jobs (in the ideal
case) so we can get them to run as quickly as possible.
Also fix how we assign slots when there are already running jobs.
Bug: 171305774
Test: atest CtsJobSchedulerTestCases
Test: atest FrameworksMockingServicesTests:JobSchedulerServiceTest
Test: atest FrameworksServicesTests:PrioritySchedulingTest
Test: atest FrameworksServicesTests:WorkCountTrackerTest
Test: atest FrameworksServicesTests:WorkTypeConfigTest
Change-Id: Ic08bc9d665c4c016f01ddce8f952710e72bd27f9
4 files changed, 515 insertions, 331 deletions
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 edf2ce3c92cb..82e967ae1a0b 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; - - 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; - } + // 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 - // Assign remaining unspecialized based on ranking. - unspecializedAssigned = Math.max(0, + mNumUnspecializedRemaining = mConfigMaxTotal; + + // 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 4effa4d445bb..f2bb47bfb8ad 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 9c781922bf2e..353ac4bd2129 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 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; + } + 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(); - - 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); - } + 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<Pair<Integer, Integer>> minLimits, @NonNull List<Pair<Integer, Integer>> 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<Pair<Integer, Integer>> maxLimits = - List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); - final List<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List<Pair<Integer, Integer>> 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, probStart, + EQUAL_PROBABILITY_CDF, probStop); } @Test @@ -230,17 +245,11 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, probStart, cdf, probStop); } @Test @@ -252,17 +261,11 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, probStart, cdf, probStop); } @Test @@ -274,17 +277,11 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, probStart, cdf, probStop); } @Test @@ -296,17 +293,11 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, probStart, cdf, probStop); } @Test @@ -318,17 +309,11 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, probStart, cdf, probStop); } @Test @@ -340,17 +325,11 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, 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<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, 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<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, 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<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, 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<Pair<Integer, Integer>> 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; - - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, fgUserJobRatio, stopRatio); + 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, probStart, cdf, probStop); + } + + @Test + public void testRandom12() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List<Pair<Integer, Integer>> 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<Pair<Integer, Integer>> maxLimits = List.of( + Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4), + Pair.create(WORK_TYPE_BGUSER, 3)); + final List<Pair<Integer, Integer>> 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<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4)); + final List<Pair<Integer, Integer>> 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<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4), + Pair.create(WORK_TYPE_BGUSER, 1)); + final List<Pair<Integer, Integer>> 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 bbc1f1d26282..2288a8925561 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<Integer, Integer> defaultTopLimits, - @Nullable Pair<Integer, Integer> defaultBgLimits, - @Nullable Pair<Integer, Integer> defaultBgUserLimits, + @NonNull List<Pair<Integer, Integer>> defaultMin, + @NonNull List<Pair<Integer, Integer>> defaultMax, boolean expectedValid, int expectedTotal, - @NonNull Pair<Integer, Integer> expectedTopLimits, - @NonNull Pair<Integer, Integer> expectedBgLimits, - @NonNull Pair<Integer, Integer> expectedBgUserLimits) throws Exception { + @NonNull List<Pair<Integer, Integer>> expectedMinLimits, + @NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception { resetConfig(); if (config != null) { DeviceConfig.setProperties(config); } - List<Pair<Integer, Integer>> defaultMin = new ArrayList<>(); - List<Pair<Integer, Integer>> 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<Integer, Integer> min : expectedMinLimits) { + assertEquals((int) min.second, counts.getMinReserved(min.first)); + } + for (Pair<Integer, Integer> 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))); } } |