diff options
| author | 2021-03-03 20:28:16 +0000 | |
|---|---|---|
| committer | 2021-03-03 20:28:16 +0000 | |
| commit | 3f20cff956856ada0024e66d16f849971efe168b (patch) | |
| tree | 482a7ed8e6c3fd565e6fd8fa685dd84d8f92df14 | |
| parent | cce8e9ec91ad24c0646feff2c05d10322bffb062 (diff) | |
| parent | 9ded0ab7dcd7edab179ca1975db8b2e8eb921e39 (diff) | |
Merge "Reserve execution slots for FGS jobs." into sc-dev
3 files changed, 217 insertions, 182 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 e8e2c27f1554..0308d68d6a56 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -73,20 +73,48 @@ class JobConcurrencyManager { CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms"; private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000; - // Try to give higher priority types lower values. + /** + * Set of possible execution types that a job can have. The actual type(s) of a job are based + * on the {@link JobStatus#lastEvaluatedPriority}, which is typically evaluated right before + * execution (when we're trying to determine which jobs to run next) and won't change after the + * job has started executing. + * + * Try to give higher priority types lower values. + * + * @see #getJobWorkTypes(JobStatus) + */ + + /** Job shouldn't run or qualify as any other work type. */ static final int WORK_TYPE_NONE = 0; + /** The job is for an app in the TOP state for a currently active user. */ static final int WORK_TYPE_TOP = 1 << 0; - static final int WORK_TYPE_EJ = 1 << 1; - static final int WORK_TYPE_BG = 1 << 2; - static final int WORK_TYPE_BGUSER = 1 << 3; + /** + * The job is for an app in a {@link ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE} or higher + * state (excluding {@link ActivityManager#PROCESS_STATE_TOP} for a currently active user. + */ + static final int WORK_TYPE_FGS = 1 << 1; + /** The job is allowed to run as an expedited job for a currently active user. */ + static final int WORK_TYPE_EJ = 1 << 2; + /** + * The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP}, + * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a currently active user, so + * can run as a background job. + */ + static final int WORK_TYPE_BG = 1 << 3; + /** + * The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP}, + * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a completely background user, + * so can run as a background user job. + */ + static final int WORK_TYPE_BGUSER = 1 << 4; @VisibleForTesting - static final int NUM_WORK_TYPES = 4; - private static final int ALL_WORK_TYPES = - WORK_TYPE_TOP | WORK_TYPE_EJ | WORK_TYPE_BG | WORK_TYPE_BGUSER; + static final int NUM_WORK_TYPES = 5; + private static final int ALL_WORK_TYPES = (1 << NUM_WORK_TYPES) - 1; @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = { WORK_TYPE_NONE, WORK_TYPE_TOP, + WORK_TYPE_FGS, WORK_TYPE_EJ, WORK_TYPE_BG, WORK_TYPE_BGUSER @@ -95,12 +123,15 @@ class JobConcurrencyManager { public @interface WorkType { } - private static String workTypeToString(@WorkType int workType) { + @VisibleForTesting + static String workTypeToString(@WorkType int workType) { switch (workType) { case WORK_TYPE_NONE: return "NONE"; case WORK_TYPE_TOP: return "TOP"; + case WORK_TYPE_FGS: + return "FGS"; case WORK_TYPE_EJ: return "EJ"; case WORK_TYPE_BG: @@ -131,58 +162,60 @@ class JobConcurrencyManager { new WorkConfigLimitsPerMemoryTrimLevel( new WorkTypeConfig("screen_on_normal", 11, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3), - Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_FGS, 1), + 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", 9, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2), - Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), + 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", 6, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1), - Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), + Pair.create(WORK_TYPE_EJ, 1)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) ), - new WorkTypeConfig("screen_on_critical", 5, + new WorkTypeConfig("screen_on_critical", 6, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), + 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", 13, + new WorkTypeConfig("screen_off_normal", 15, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3), - Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 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_off_moderate", 13, + new WorkTypeConfig("screen_off_moderate", 15, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3), - Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_FGS, 2), + 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", 7, + new WorkTypeConfig("screen_off_low", 9, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2), - Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), + 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, + new WorkTypeConfig("screen_off_critical", 6, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), + Pair.create(WORK_TYPE_EJ, 1)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) ) @@ -977,10 +1010,11 @@ class JobConcurrencyManager { int getJobWorkTypes(@NonNull JobStatus js) { int classification = 0; - // TODO: create dedicated work type for FGS if (shouldRunAsFgUserJob(js)) { if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { classification |= WORK_TYPE_TOP; + } else if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_FOREGROUND_SERVICE) { + classification |= WORK_TYPE_FGS; } else { classification |= WORK_TYPE_BG; } @@ -1001,11 +1035,13 @@ 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_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "max_fgs_"; 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_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "min_fgs_"; 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 = @@ -1053,6 +1089,10 @@ class JobConcurrencyManager { properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal)))); mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop); + final int maxFgs = Math.max(1, Math.min(mMaxTotal, + properties.getInt(KEY_PREFIX_MAX_FGS + mConfigIdentifier, + mDefaultMaxAllowedSlots.get(WORK_TYPE_FGS, mMaxTotal)))); + mMaxAllowedSlots.put(WORK_TYPE_FGS, maxFgs); final int maxEj = Math.max(1, Math.min(mMaxTotal, properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal)))); @@ -1074,6 +1114,12 @@ class JobConcurrencyManager { mDefaultMinReservedSlots.get(WORK_TYPE_TOP)))); mMinReservedSlots.put(WORK_TYPE_TOP, minTop); remaining -= minTop; + // Ensure fgs is in the range [0, min(maxFgs, remaining)] + final int minFgs = Math.max(0, Math.min(Math.min(maxFgs, remaining), + properties.getInt(KEY_PREFIX_MIN_FGS + mConfigIdentifier, + mDefaultMinReservedSlots.get(WORK_TYPE_FGS)))); + mMinReservedSlots.put(WORK_TYPE_FGS, minFgs); + remaining -= minFgs; // 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, @@ -1111,6 +1157,10 @@ class JobConcurrencyManager { .println(); pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP)) .println(); + pw.print(KEY_PREFIX_MIN_FGS + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_FGS)) + .println(); + pw.print(KEY_PREFIX_MAX_FGS + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_FGS)) + .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)) @@ -1205,6 +1255,7 @@ class JobConcurrencyManager { private int mConfigMaxTotal; private final SparseIntArray mConfigNumReservedSlots = new SparseIntArray(NUM_WORK_TYPES); private final SparseIntArray mConfigAbsoluteMaxSlots = new SparseIntArray(NUM_WORK_TYPES); + private final SparseIntArray mRecycledReserved = new SparseIntArray(NUM_WORK_TYPES); /** * Numbers may be lower in this than in {@link #mConfigNumReservedSlots} if there aren't @@ -1220,11 +1271,14 @@ class JobConcurrencyManager { mConfigMaxTotal = workTypeConfig.getMaxTotal(); mConfigNumReservedSlots.put(WORK_TYPE_TOP, workTypeConfig.getMinReserved(WORK_TYPE_TOP)); + mConfigNumReservedSlots.put(WORK_TYPE_FGS, + workTypeConfig.getMinReserved(WORK_TYPE_FGS)); 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_FGS, workTypeConfig.getMax(WORK_TYPE_FGS)); 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)); @@ -1260,15 +1314,10 @@ class JobConcurrencyManager { // We don't need to adjust reservations if only one work type was modified // because that work type is the one we're using. - // 0 is WORK_TYPE_NONE. - int workType = 1; - int rem = workTypes; - while (rem > 0) { - if ((rem & 1) != 0) { + for (int workType = 1; workType <= workTypes; workType <<= 1) { + if ((workType & workTypes) == workType) { maybeAdjustReservations(workType); } - rem = rem >>> 1; - workType = workType << 1; } } } @@ -1280,21 +1329,11 @@ class JobConcurrencyManager { int numAdj = 0; // We don't know which type we'll classify the job as when we run it yet, so make sure // we have space in all applicable slots. - if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) { - mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + adj); - numAdj++; - } - if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) { - mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + adj); - numAdj++; - } - if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) { - mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + adj); - numAdj++; - } - if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) { - mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + adj); - numAdj++; + for (int workType = 1; workType <= workTypes; workType <<= 1) { + if ((workTypes & workType) == workType) { + mNumPendingJobs.put(workType, mNumPendingJobs.get(workType) + adj); + numAdj++; + } } return numAdj; @@ -1388,105 +1427,45 @@ class JobConcurrencyManager { 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; + for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) { + int run = mNumRunningJobs.get(workType); + mRecycledReserved.put(workType, run); + mNumUnspecializedRemaining -= run; + } // 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; + for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) { + int num = mNumRunningJobs.get(workType) + mNumPendingJobs.get(workType); + int res = mRecycledReserved.get(workType); + int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(num, mConfigNumReservedSlots.get(workType) - res))); + res += fillUp; + mRecycledReserved.put(workType, res); + 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) - - resBgUser)); - mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned); - mNumUnspecializedRemaining -= unspecializedAssigned; + for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) { + int num = mNumRunningJobs.get(workType) + mNumPendingJobs.get(workType); + int res = mRecycledReserved.get(workType); + int unspecializedAssigned = Math.max(0, + Math.min(mNumUnspecializedRemaining, + Math.min(mConfigAbsoluteMaxSlots.get(workType), num) - res)); + mNumActuallyReservedSlots.put(workType, res + unspecializedAssigned); + mNumUnspecializedRemaining -= unspecializedAssigned; + } } int canJobStart(int workTypes) { - if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) { - final int maxAllowed = Math.min( - mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), - mNumActuallyReservedSlots.get(WORK_TYPE_TOP) + mNumUnspecializedRemaining); - if (mNumRunningJobs.get(WORK_TYPE_TOP) + mNumStartingJobs.get(WORK_TYPE_TOP) - < maxAllowed) { - 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), - mNumActuallyReservedSlots.get(WORK_TYPE_BG) + mNumUnspecializedRemaining); - if (mNumRunningJobs.get(WORK_TYPE_BG) + mNumStartingJobs.get(WORK_TYPE_BG) - < maxAllowed) { - return WORK_TYPE_BG; - } - } - if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) { - final int maxAllowed = Math.min( - mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), - mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER) - + mNumUnspecializedRemaining); - if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER) - < maxAllowed) { - return WORK_TYPE_BGUSER; + for (int workType = 1; workType <= workTypes; workType <<= 1) { + if ((workTypes & workType) == workType) { + final int maxAllowed = Math.min( + mConfigAbsoluteMaxSlots.get(workType), + mNumActuallyReservedSlots.get(workType) + mNumUnspecializedRemaining); + if (mNumRunningJobs.get(workType) + mNumStartingJobs.get(workType) + < maxAllowed) { + return workType; + } } } return WORK_TYPE_NONE; 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 f87d5993c1b5..7d9ab3772733 100644 --- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java @@ -20,8 +20,10 @@ 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_FGS; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; +import static com.android.server.job.JobConcurrencyManager.workTypeToString; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -54,7 +56,7 @@ public class WorkCountTrackerTest { private static final double[] EQUAL_PROBABILITY_CDF = buildWorkTypeCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 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; @@ -66,8 +68,9 @@ public class WorkCountTrackerTest { } @NonNull - private static double[] buildWorkTypeCdf(double pTop, double pEj, double pBg, double pBgUser) { - return buildCdf(pTop, pEj, pBg, pBgUser); + private static double[] buildWorkTypeCdf( + double pTop, double pFgs, double pEj, double pBg, double pBgUser) { + return buildCdf(pTop, pFgs, pEj, pBg, pBgUser); } @NonNull @@ -102,10 +105,12 @@ public class WorkCountTrackerTest { case 0: return WORK_TYPE_TOP; case 1: - return WORK_TYPE_EJ; + return WORK_TYPE_FGS; case 2: - return WORK_TYPE_BG; + return WORK_TYPE_EJ; case 3: + return WORK_TYPE_BG; + case 4: return WORK_TYPE_BGUSER; default: throw new IllegalStateException("Unknown work type"); @@ -224,12 +229,15 @@ public class WorkCountTrackerTest { private void startPendingJobs(Jobs jobs) { while (hasStartablePendingJob(jobs)) { - final int startingWorkType = - getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble()); + final int workType = getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble()); + + if (jobs.pending.get(workType) > 0) { + final int pendingMultiType = getPendingMultiType(jobs, workType); + final int startingWorkType = mWorkCountTracker.canJobStart(pendingMultiType); + if (startingWorkType == WORK_TYPE_NONE) { + continue; + } - if (jobs.pending.get(startingWorkType) > 0 - && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) { - final int pendingMultiType = getPendingMultiType(jobs, startingWorkType); jobs.removePending(pendingMultiType); jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1); mWorkCountTracker.stageJob(startingWorkType, pendingMultiType); @@ -304,7 +312,7 @@ public class WorkCountTrackerTest { List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> minLimits = List.of(); final double probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0); + final double[] cdf = buildWorkTypeCdf(0.5, 0, 0, 0.5, 0); final double[] numTypesCdf = buildCdf(.5, .3, .15, .05); final double probStart = 0.5; @@ -322,7 +330,7 @@ public class WorkCountTrackerTest { 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 probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3); + final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3); final double[] numTypesCdf = buildCdf(.75, .2, .05); final double probStart = 0.5; @@ -340,7 +348,7 @@ public class WorkCountTrackerTest { List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> minLimits = List.of(); final double probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3); + final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3); final double[] numTypesCdf = buildCdf(.05, .95); final double probStart = 0.5; @@ -358,7 +366,7 @@ 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)); final double probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.8, .1); + final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.8, .1); final double[] numTypesCdf = buildCdf(.5, .3, .15, .05); final double probStart = 0.5; @@ -376,7 +384,7 @@ 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)); final double probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.1, 0); + final double[] cdf = buildWorkTypeCdf(0.85, 0.05, 0, 0.1, 0); final double[] numTypesCdf = buildCdf(1); final double probStart = 0.5; @@ -394,7 +402,7 @@ 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)); final double probStop = 0.4; - final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.1, .8); + final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.1, .8); final double[] numTypesCdf = buildCdf(0.5, 0.5); final double probStart = 0.5; @@ -413,7 +421,7 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final double probStop = 0.4; - final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.05, 0.05); + final double[] cdf = buildWorkTypeCdf(0.8, 0.1, 0, 0.05, 0.05); final double[] numTypesCdf = buildCdf(1); final double probStart = 0.5; @@ -432,7 +440,7 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final double probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(0, 0, 0.5, 0.5); + final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.5, 0.5); final double[] numTypesCdf = buildCdf(1); final double probStart = 0.5; @@ -451,7 +459,7 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final double probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(0, 0, 0.1, 0.9); + final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.1, 0.9); final double[] numTypesCdf = buildCdf(0.9, 0.1); final double probStart = 0.5; @@ -470,7 +478,7 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final double probStop = 0.5; - final double[] cdf = buildWorkTypeCdf(0, 0, 0.9, 0.1); + final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.9, 0.1); final double[] numTypesCdf = buildCdf(1); final double probStart = 0.5; @@ -488,7 +496,7 @@ public class WorkCountTrackerTest { 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 = buildWorkTypeCdf(0.5, 0.5, 0, 0); + final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0, 0); final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2); final double probStart = 0.5; @@ -511,7 +519,7 @@ public class WorkCountTrackerTest { final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)); final double probStop = 0.13; - final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.85); + final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.8, 0.05); final double probStart = 0.87; checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, @@ -528,7 +536,7 @@ public class WorkCountTrackerTest { 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 = buildWorkTypeCdf(.1, 0.5, 0.35, 0.05); + final double[] cdf = buildWorkTypeCdf(.1, 0, 0.5, 0.35, 0.05); final double[] numTypesCdf = buildCdf(1); final double probStart = 0.5; @@ -548,7 +556,7 @@ public class WorkCountTrackerTest { 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 = buildWorkTypeCdf(0.01, 0.49, 0.1, 0.4); + final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.4, 0.1, 0.4); final double[] numTypesCdf = buildCdf(0.7, 0.3); final double probStart = 0.5; @@ -576,11 +584,13 @@ public class WorkCountTrackerTest { startPendingJobs(jobs); for (Pair<Integer, Integer> run : resultRunning) { - assertWithMessage("Incorrect running result for work type " + run.first) + assertWithMessage( + "Incorrect running result for work type " + workTypeToString(run.first)) .that(jobs.running.get(run.first)).isEqualTo(run.second); } for (Pair<Integer, Integer> pend : resultPending) { - assertWithMessage("Incorrect pending result for work type " + pend.first) + assertWithMessage( + "Incorrect pending result for work type " + workTypeToString(pend.first)) .that(jobs.pending.get(pend.first)).isEqualTo(pend.second); } } @@ -938,10 +948,15 @@ public class WorkCountTrackerTest { assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(6); assertThat(jobs.running.get(WORK_TYPE_EJ)).isEqualTo(1); assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1); - assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(4); + // If run the TOP jobs as TOP first, and a TOP|EJ job as EJ, then we'll have 4 TOP jobs + // remaining. + assertThat(jobs.pending.get(WORK_TYPE_TOP)).isAtLeast(4); + // If we end up running the TOP|EJ jobs as TOP first, then we'll have 5 TOP jobs remaining. + assertThat(jobs.pending.get(WORK_TYPE_TOP)).isAtMost(5); // Can't equate pending EJ since some could be running as TOP and BG assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2); - assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9); + assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtLeast(8); + assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtMost(9); // Stop all jobs jobs.maybeFinishJobs(1); @@ -975,7 +990,7 @@ public class WorkCountTrackerTest { assertThat(jobs.running.get(WORK_TYPE_EJ)).isAtLeast(1); assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1); assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0); - assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2); + assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(1); assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(4); } } 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 2288a8925561..cc18317d0529 100644 --- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java @@ -18,7 +18,9 @@ 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_FGS; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; +import static com.android.server.job.JobConcurrencyManager.workTypeToString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -44,10 +46,12 @@ 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_FGS = "concurrency_max_fgs_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_FGS = "concurrency_min_fgs_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,15 +63,17 @@ public class WorkTypeConfigTest { private void resetConfig() { // 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); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_FGS, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_FGS, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, null, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false); } private void check(@Nullable DeviceConfig.Properties config, @@ -103,10 +109,12 @@ public class WorkTypeConfigTest { assertEquals(expectedTotal, counts.getMaxTotal()); for (Pair<Integer, Integer> min : expectedMinLimits) { - assertEquals((int) min.second, counts.getMinReserved(min.first)); + assertEquals("Incorrect min value for " + workTypeToString(min.first), + (int) min.second, counts.getMinReserved(min.first)); } for (Pair<Integer, Integer> max : expectedMaxLimits) { - assertEquals((int) max.second, counts.getMax(max.first)); + assertEquals("Incorrect max value for " + workTypeToString(max.first), + (int) max.second, counts.getMax(max.first)); } } @@ -193,6 +201,14 @@ public class WorkTypeConfigTest { /* 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*/ 10, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 3), Pair.create(WORK_TYPE_FGS, 2), + Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_FGS, 3)), + /*expected*/ true, 10, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 3), Pair.create(WORK_TYPE_FGS, 2), + Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_FGS, 3))); check(null, /*default*/ 15, /* min */ List.of(Pair.create(WORK_TYPE_BG, 15)), /* max */ List.of(Pair.create(WORK_TYPE_BG, 15)), @@ -289,5 +305,30 @@ public class WorkTypeConfigTest { /*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))); + + check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) + .setInt(KEY_MAX_TOTAL, 16) + .setInt(KEY_MAX_TOP, 16) + .setInt(KEY_MIN_TOP, 1) + .setInt(KEY_MAX_FGS, 15) + .setInt(KEY_MIN_FGS, 2) + .setInt(KEY_MAX_EJ, 14) + .setInt(KEY_MIN_EJ, 3) + .setInt(KEY_MAX_BG, 13) + .setInt(KEY_MIN_BG, 4) + .setInt(KEY_MAX_BGUSER, 12) + .setInt(KEY_MIN_BGUSER, 5) + .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_FGS, 2), + Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 5)), + /* max */ + List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_FGS, 15), + Pair.create(WORK_TYPE_EJ, 14), + Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 12))); } } |