Merge "Reserve execution slot for important bg user jobs." into sc-dev

This commit is contained in:
Kweku Adams
2021-03-06 02:04:22 +00:00
committed by Android (Google) Code Review
3 changed files with 153 additions and 62 deletions

View File

@@ -102,14 +102,19 @@ class JobConcurrencyManager {
* can run as a background job. * can run as a background job.
*/ */
static final int WORK_TYPE_BG = 1 << 3; static final int WORK_TYPE_BG = 1 << 3;
/**
* The job is for an app in a {@link ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE} or higher
* state, or is allowed to run as an expedited job, but is for a completely background user.
*/
static final int WORK_TYPE_BGUSER_IMPORTANT = 1 << 4;
/** /**
* The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP}, * 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, * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a completely background user,
* so can run as a background user job. * so can run as a background user job.
*/ */
static final int WORK_TYPE_BGUSER = 1 << 4; static final int WORK_TYPE_BGUSER = 1 << 5;
@VisibleForTesting @VisibleForTesting
static final int NUM_WORK_TYPES = 5; static final int NUM_WORK_TYPES = 6;
private static final int ALL_WORK_TYPES = (1 << NUM_WORK_TYPES) - 1; private static final int ALL_WORK_TYPES = (1 << NUM_WORK_TYPES) - 1;
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = { @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
@@ -118,6 +123,7 @@ class JobConcurrencyManager {
WORK_TYPE_FGS, WORK_TYPE_FGS,
WORK_TYPE_EJ, WORK_TYPE_EJ,
WORK_TYPE_BG, WORK_TYPE_BG,
WORK_TYPE_BGUSER_IMPORTANT,
WORK_TYPE_BGUSER WORK_TYPE_BGUSER
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@@ -139,6 +145,8 @@ class JobConcurrencyManager {
return "BG"; return "BG";
case WORK_TYPE_BGUSER: case WORK_TYPE_BGUSER:
return "BGUSER"; return "BGUSER";
case WORK_TYPE_BGUSER_IMPORTANT:
return "BGUSER_IMPORTANT";
default: default:
return "WORK(" + workType + ")"; return "WORK(" + workType + ")";
} }
@@ -164,30 +172,40 @@ class JobConcurrencyManager {
new WorkTypeConfig("screen_on_normal", 11, new WorkTypeConfig("screen_on_normal", 11,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_FGS, 1), 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)), Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4)) List.of(Pair.create(WORK_TYPE_BG, 6),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 2),
Pair.create(WORK_TYPE_BGUSER, 3))
), ),
new WorkTypeConfig("screen_on_moderate", 9, new WorkTypeConfig("screen_on_moderate", 9,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 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, 2)), Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)) List.of(Pair.create(WORK_TYPE_BG, 4),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
Pair.create(WORK_TYPE_BGUSER, 1))
), ),
new WorkTypeConfig("screen_on_low", 6, new WorkTypeConfig("screen_on_low", 6,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
Pair.create(WORK_TYPE_EJ, 1)), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) List.of(Pair.create(WORK_TYPE_BG, 1),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
Pair.create(WORK_TYPE_BGUSER, 1))
), ),
new WorkTypeConfig("screen_on_critical", 6, new WorkTypeConfig("screen_on_critical", 6,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
Pair.create(WORK_TYPE_EJ, 1)), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) List.of(Pair.create(WORK_TYPE_BG, 1),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
Pair.create(WORK_TYPE_BGUSER, 1))
) )
); );
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF = private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
@@ -195,30 +213,40 @@ class JobConcurrencyManager {
new WorkTypeConfig("screen_off_normal", 15, new WorkTypeConfig("screen_off_normal", 15,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 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)), Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4)) List.of(Pair.create(WORK_TYPE_BG, 6),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 2),
Pair.create(WORK_TYPE_BGUSER, 3))
), ),
new WorkTypeConfig("screen_off_moderate", 15, new WorkTypeConfig("screen_off_moderate", 15,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_FGS, 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)), Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)) List.of(Pair.create(WORK_TYPE_BG, 4),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
Pair.create(WORK_TYPE_BGUSER, 1))
), ),
new WorkTypeConfig("screen_off_low", 9, new WorkTypeConfig("screen_off_low", 9,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 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)), Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) List.of(Pair.create(WORK_TYPE_BG, 1),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
Pair.create(WORK_TYPE_BGUSER, 1))
), ),
new WorkTypeConfig("screen_off_critical", 6, new WorkTypeConfig("screen_off_critical", 6,
// defaultMin // defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1), List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
Pair.create(WORK_TYPE_EJ, 1)), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax // defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) List.of(Pair.create(WORK_TYPE_BG, 1),
Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
Pair.create(WORK_TYPE_BGUSER, 1))
) )
); );
@@ -823,17 +851,22 @@ class JobConcurrencyManager {
// Only expedited jobs can replace expedited jobs. // Only expedited jobs can replace expedited jobs.
if (js.shouldTreatAsExpeditedJob()) { if (js.shouldTreatAsExpeditedJob()) {
// Keep fg/bg user distinction. // Keep fg/bg user distinction.
if (workType == WORK_TYPE_BGUSER) { if (workType == WORK_TYPE_BGUSER_IMPORTANT || workType == WORK_TYPE_BGUSER) {
// For now, let any bg user job replace a bg user expedited job. // Let any important bg user job replace a bg user expedited job.
// TODO: limit to ej once we have dedicated bg user ej slots. if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER_IMPORTANT) > 0) {
if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER) > 0) { return "blocking " + workTypeToString(WORK_TYPE_BGUSER_IMPORTANT) + " queue";
return "blocking " + workTypeToString(workType) + " queue";
} }
} else { // Let a fg user EJ preempt a bg user EJ (if able), but not the other way around.
if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) { if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0
return "blocking " + workTypeToString(workType) + " queue"; && mWorkCountTracker.canJobStart(WORK_TYPE_EJ, workType)
!= WORK_TYPE_NONE) {
return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
} }
} else if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
} }
// No other pending EJs. Return null so we don't let regular jobs preempt an EJ.
return null;
} }
// Easy check. If there are pending jobs of the same work type, then we know that // Easy check. If there are pending jobs of the same work type, then we know that
@@ -1017,6 +1050,7 @@ class JobConcurrencyManager {
int getJobWorkTypes(@NonNull JobStatus js) { int getJobWorkTypes(@NonNull JobStatus js) {
int classification = 0; int classification = 0;
if (shouldRunAsFgUserJob(js)) { if (shouldRunAsFgUserJob(js)) {
if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
classification |= WORK_TYPE_TOP; classification |= WORK_TYPE_TOP;
@@ -1030,7 +1064,11 @@ class JobConcurrencyManager {
classification |= WORK_TYPE_EJ; classification |= WORK_TYPE_EJ;
} }
} else { } else {
// TODO(171305774): create dedicated slots for EJs of bg user if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_FOREGROUND_SERVICE
|| js.shouldTreatAsExpeditedJob()) {
classification |= WORK_TYPE_BGUSER_IMPORTANT;
}
// BGUSER_IMPORTANT jobs can also run as BGUSER jobs, so not an 'else' here.
classification |= WORK_TYPE_BGUSER; classification |= WORK_TYPE_BGUSER;
} }
@@ -1047,12 +1085,16 @@ class JobConcurrencyManager {
private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_"; private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
private static final String KEY_PREFIX_MAX_BGUSER = private static final String KEY_PREFIX_MAX_BGUSER =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_"; CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
private static final String KEY_PREFIX_MAX_BGUSER_IMPORTANT =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_important_";
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_"; 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_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_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_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
private static final String KEY_PREFIX_MIN_BGUSER = private static final String KEY_PREFIX_MIN_BGUSER =
CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_"; CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
private static final String KEY_PREFIX_MIN_BGUSER_IMPORTANT =
CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_important_";
private final String mConfigIdentifier; private final String mConfigIdentifier;
private int mMaxTotal; private int mMaxTotal;
@@ -1108,6 +1150,10 @@ class JobConcurrencyManager {
properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier, properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal)))); mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg); mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
final int maxBgUserImp = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_BGUSER_IMPORTANT + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER_IMPORTANT, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_BGUSER_IMPORTANT, maxBgUserImp);
final int maxBgUser = Math.max(1, Math.min(mMaxTotal, final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier, properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal)))); mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
@@ -1139,6 +1185,11 @@ class JobConcurrencyManager {
mDefaultMinReservedSlots.get(WORK_TYPE_BG)))); mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
mMinReservedSlots.put(WORK_TYPE_BG, minBg); mMinReservedSlots.put(WORK_TYPE_BG, minBg);
remaining -= minBg; remaining -= minBg;
// Ensure bg user imp is in the range [0, min(maxBgUserImp, remaining)]
final int minBgUserImp = Math.max(0, Math.min(Math.min(maxBgUserImp, remaining),
properties.getInt(KEY_PREFIX_MIN_BGUSER_IMPORTANT + mConfigIdentifier,
mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER_IMPORTANT, 0))));
mMinReservedSlots.put(WORK_TYPE_BGUSER_IMPORTANT, minBgUserImp);
// Ensure bg user is in the range [0, min(maxBgUser, remaining)] // Ensure bg user is in the range [0, min(maxBgUser, remaining)]
final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining), final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier, properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
@@ -1176,6 +1227,10 @@ class JobConcurrencyManager {
.println(); .println();
pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG)) pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
.println(); .println();
pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
mMinReservedSlots.get(WORK_TYPE_BGUSER_IMPORTANT)).println();
pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
mMaxAllowedSlots.get(WORK_TYPE_BGUSER_IMPORTANT)).println();
pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier, pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
mMinReservedSlots.get(WORK_TYPE_BGUSER)).println(); mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier, pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
@@ -1276,19 +1331,10 @@ class JobConcurrencyManager {
void setConfig(@NonNull WorkTypeConfig workTypeConfig) { void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
mConfigMaxTotal = workTypeConfig.getMaxTotal(); mConfigMaxTotal = workTypeConfig.getMaxTotal();
mConfigNumReservedSlots.put(WORK_TYPE_TOP, for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) {
workTypeConfig.getMinReserved(WORK_TYPE_TOP)); mConfigNumReservedSlots.put(workType, workTypeConfig.getMinReserved(workType));
mConfigNumReservedSlots.put(WORK_TYPE_FGS, mConfigAbsoluteMaxSlots.put(workType, workTypeConfig.getMax(workType));
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));
mNumUnspecializedRemaining = mConfigMaxTotal; mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) { for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {

View File

@@ -19,6 +19,7 @@ package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES; 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_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER_IMPORTANT;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ; 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_FGS;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
@@ -29,6 +30,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.util.SparseIntArray; import android.util.SparseIntArray;
@@ -52,11 +54,11 @@ import java.util.Random;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@MediumTest @MediumTest
public class WorkCountTrackerTest { public class WorkCountTrackerTest {
private static final String TAG = "WorkerCountTrackerTest"; private static final String TAG = "WorkCountTrackerTest";
private static final double[] EQUAL_PROBABILITY_CDF = private static final double[] EQUAL_PROBABILITY_CDF =
buildWorkTypeCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 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, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES);
private Random mRandom; private Random mRandom;
private WorkCountTracker mWorkCountTracker; private WorkCountTracker mWorkCountTracker;
@@ -69,12 +71,15 @@ public class WorkCountTrackerTest {
@NonNull @NonNull
private static double[] buildWorkTypeCdf( private static double[] buildWorkTypeCdf(
double pTop, double pFgs, double pEj, double pBg, double pBgUser) { double pTop, double pFgs, double pEj, double pBg, double pBgUserImp, double pBgUser) {
return buildCdf(pTop, pFgs, pEj, pBg, pBgUser); return buildCdf(pTop, pFgs, pEj, pBg, pBgUserImp, pBgUser);
} }
@NonNull @NonNull
private static double[] buildCdf(double... probs) { private static double[] buildCdf(double... probs) {
if (probs.length == 0) {
throw new IllegalArgumentException("Must supply at least one probability");
}
double[] cdf = new double[probs.length]; double[] cdf = new double[probs.length];
double sum = 0; double sum = 0;
@@ -84,7 +89,9 @@ public class WorkCountTrackerTest {
} }
if (Double.compare(1, sum) != 0) { if (Double.compare(1, sum) != 0) {
throw new IllegalArgumentException("probabilities don't sum to one: " + sum); Log.e(TAG, "probabilities don't sum to one: " + sum);
// 1.0/6 doesn't work well in code :/
cdf[cdf.length - 1] = 1;
} }
return cdf; return cdf;
} }
@@ -111,6 +118,8 @@ public class WorkCountTrackerTest {
case 3: case 3:
return WORK_TYPE_BG; return WORK_TYPE_BG;
case 4: case 4:
return WORK_TYPE_BGUSER_IMPORTANT;
case 5:
return WORK_TYPE_BGUSER; return WORK_TYPE_BGUSER;
default: default:
throw new IllegalStateException("Unknown work type"); throw new IllegalStateException("Unknown work type");
@@ -312,7 +321,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of(); final List<Pair<Integer, Integer>> minLimits = List.of();
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(0.5, 0, 0, 0.5, 0); final double[] cdf = buildWorkTypeCdf(0.5, 0, 0, 0.5, 0, 0);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05); final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5; final double probStart = 0.5;
@@ -330,7 +339,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); 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 List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3); final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 0, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.75, .2, .05); final double[] numTypesCdf = buildCdf(.75, .2, .05);
final double probStart = 0.5; final double probStart = 0.5;
@@ -348,7 +357,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of(); final List<Pair<Integer, Integer>> minLimits = List.of();
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3); final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 0, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.05, .95); final double[] numTypesCdf = buildCdf(.05, .95);
final double probStart = 0.5; final double probStart = 0.5;
@@ -366,7 +375,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); 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 List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.8, .1); final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.8, 0.02, .08);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05); final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5; final double probStart = 0.5;
@@ -384,7 +393,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); 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 List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(0.85, 0.05, 0, 0.1, 0); final double[] cdf = buildWorkTypeCdf(0.85, 0.05, 0, 0.1, 0, 0);
final double[] numTypesCdf = buildCdf(1); final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5; final double probStart = 0.5;
@@ -402,7 +411,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); 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 List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4; final double probStop = 0.4;
final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.1, .8); final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.1, 0.05, .75);
final double[] numTypesCdf = buildCdf(0.5, 0.5); final double[] numTypesCdf = buildCdf(0.5, 0.5);
final double probStart = 0.5; final double probStart = 0.5;
@@ -421,7 +430,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits = final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.4; final double probStop = 0.4;
final double[] cdf = buildWorkTypeCdf(0.8, 0.1, 0, 0.05, 0.05); final double[] cdf = buildWorkTypeCdf(0.8, 0.1, 0, 0.05, 0, 0.05);
final double[] numTypesCdf = buildCdf(1); final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5; final double probStart = 0.5;
@@ -440,7 +449,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits = final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.5, 0.5); final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.5, 0, 0.5);
final double[] numTypesCdf = buildCdf(1); final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5; final double probStart = 0.5;
@@ -459,7 +468,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits = final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.1, 0.9); final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.1, 0, 0.9);
final double[] numTypesCdf = buildCdf(0.9, 0.1); final double[] numTypesCdf = buildCdf(0.9, 0.1);
final double probStart = 0.5; final double probStart = 0.5;
@@ -478,7 +487,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits = final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5; final double probStop = 0.5;
final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.9, 0.1); final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.9, 0, 0.1);
final double[] numTypesCdf = buildCdf(1); final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5; final double probStart = 0.5;
@@ -496,7 +505,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits = final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)); List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4; final double probStop = 0.4;
final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0, 0); final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0, 0, 0);
final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2); final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2);
final double probStart = 0.5; final double probStart = 0.5;
@@ -519,7 +528,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits = final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)); List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
final double probStop = 0.13; final double probStop = 0.13;
final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.8, 0.05); final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.7, 0.1, 0.05);
final double probStart = 0.87; final double probStart = 0.87;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
@@ -536,7 +545,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4)); 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 List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4; final double probStop = 0.4;
final double[] cdf = buildWorkTypeCdf(.1, 0, 0.5, 0.35, 0.05); final double[] cdf = buildWorkTypeCdf(.1, 0, 0.5, 0.35, 0, 0.05);
final double[] numTypesCdf = buildCdf(1); final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5; final double probStart = 0.5;
@@ -556,7 +565,28 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits = final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)); List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4; final double probStop = 0.4;
final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.4, 0.1, 0.4); final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.4, 0.1, 0, 0.4);
final double[] numTypesCdf = buildCdf(0.7, 0.3);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
cdf, numTypesCdf, probStop);
}
@Test
public void testRandom16() {
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 7;
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_IMPORTANT, 1),
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 = buildWorkTypeCdf(0.01, 0.09, 0.25, 0.05, 0.3, 0.3);
final double[] numTypesCdf = buildCdf(0.7, 0.3); final double[] numTypesCdf = buildCdf(0.7, 0.3);
final double probStart = 0.5; final double probStart = 0.5;
@@ -748,6 +778,7 @@ public class WorkCountTrackerTest {
/* resPen */ List.of( /* resPen */ List.of(
Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2))); Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
Log.d(TAG, "START***#*#*#*#*#*#**#*");
// Test multi-types // Test multi-types
checkSimple(6, checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)), /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
@@ -764,7 +795,10 @@ public class WorkCountTrackerTest {
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2),
Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)), Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
/* resPen */ List.of( /* resPen */ List.of(
Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1))); // Not checking BG count because the test starts jobs in random order
// and if it tries to start 4 BG jobs (2 will run as EJ from EJ|BG), but
// the resulting pending will be 3 BG instead of 4 BG.
Pair.create(WORK_TYPE_BGUSER, 1)));
} }
/** Tests that the counter updates properly when jobs are stopped. */ /** Tests that the counter updates properly when jobs are stopped. */

View File

@@ -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_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER_IMPORTANT;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ; 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_FGS;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
@@ -49,11 +50,13 @@ public class WorkTypeConfigTest {
private static final String KEY_MAX_FGS = "concurrency_max_fgs_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_EJ = "concurrency_max_ej_test";
private static final String KEY_MAX_BG = "concurrency_max_bg_test"; private static final String KEY_MAX_BG = "concurrency_max_bg_test";
private static final String KEY_MAX_BGUSER_IMPORTANT = "concurrency_max_bguser_important_test";
private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_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_TOP = "concurrency_min_top_test";
private static final String KEY_MIN_FGS = "concurrency_min_fgs_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_EJ = "concurrency_min_ej_test";
private static final String KEY_MIN_BG = "concurrency_min_bg_test"; private static final String KEY_MIN_BG = "concurrency_min_bg_test";
private static final String KEY_MIN_BGUSER_IMPORTANT = "concurrency_min_bguser_important_test";
private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test"; private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
@After @After
@@ -68,11 +71,15 @@ public class WorkTypeConfigTest {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_FGS, 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_EJ, null, false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, null, false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, null, false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_MAX_BGUSER_IMPORTANT, null, false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, 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_TOP, null, false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_FGS, 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_EJ, null, false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, null, false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, null, false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_MIN_BGUSER_IMPORTANT, null, false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false);
} }
@@ -316,19 +323,23 @@ public class WorkTypeConfigTest {
.setInt(KEY_MIN_EJ, 3) .setInt(KEY_MIN_EJ, 3)
.setInt(KEY_MAX_BG, 13) .setInt(KEY_MAX_BG, 13)
.setInt(KEY_MIN_BG, 4) .setInt(KEY_MIN_BG, 4)
.setInt(KEY_MAX_BGUSER, 12) .setInt(KEY_MAX_BGUSER_IMPORTANT, 12)
.setInt(KEY_MIN_BGUSER, 5) .setInt(KEY_MIN_BGUSER_IMPORTANT, 5)
.setInt(KEY_MAX_BGUSER, 11)
.setInt(KEY_MIN_BGUSER, 6)
.build(), .build(),
/*default*/ 9, /*default*/ 9,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/*expected*/ true, 16, /*expected*/ true, 16,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_FGS, 2), /* 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_EJ, 3), Pair.create(WORK_TYPE_BG, 4),
Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 5)), Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 5),
Pair.create(WORK_TYPE_BGUSER, 6)),
/* max */ /* max */
List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_FGS, 15), List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_FGS, 15),
Pair.create(WORK_TYPE_EJ, 14), Pair.create(WORK_TYPE_EJ, 14), Pair.create(WORK_TYPE_BG, 13),
Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 12))); Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 12),
Pair.create(WORK_TYPE_BGUSER, 11)));
} }
} }