Merge "Reserve execution slots for FGS jobs." into sc-dev

This commit is contained in:
Kweku Adams
2021-03-03 20:28:16 +00:00
committed by Android (Google) Code Review
3 changed files with 217 additions and 182 deletions

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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)));
}
}