Merge "Give user-initiated jobs immediacy privilege for TOP app."
This commit is contained in:
committed by
Android (Google) Code Review
commit
e254ca958b
@@ -761,8 +761,8 @@ class JobConcurrencyManager {
|
||||
if (js != null) {
|
||||
mWorkCountTracker.incrementRunningJobCount(jsc.getRunningJobWorkType());
|
||||
assignment.workType = jsc.getRunningJobWorkType();
|
||||
if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
|
||||
info.numRunningTopEj++;
|
||||
if (js.startedWithImmediacyPrivilege) {
|
||||
info.numRunningImmediacyPrivileged++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,11 +829,9 @@ class JobConcurrencyManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isTopEj = nextPending.shouldTreatAsExpeditedJob()
|
||||
&& nextPending.lastEvaluatedBias == JobInfo.BIAS_TOP_APP;
|
||||
final boolean hasImmediacyPrivilege = hasImmediacyPrivilegeLocked(nextPending);
|
||||
if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
|
||||
Slog.w(TAG, "Already running similar " + (isTopEj ? "TOP-EJ" : "job")
|
||||
+ " to: " + nextPending);
|
||||
Slog.w(TAG, "Already running similar job to: " + nextPending);
|
||||
}
|
||||
|
||||
// Factoring minChangedWaitingTimeMs into the min waiting time effectively limits
|
||||
@@ -876,23 +874,25 @@ class JobConcurrencyManager {
|
||||
final JobStatus runningJob = assignment.context.getRunningJobLocked();
|
||||
// Maybe stop the job if it has had its day in the sun. Only allow replacing
|
||||
// for one of the following conditions:
|
||||
// 1. We're putting in the current TOP app's EJ
|
||||
// 1. We're putting in a job that has the privilege of running immediately
|
||||
// 2. There aren't too many jobs running AND the current job started when the
|
||||
// app was in the background
|
||||
// 3. There aren't too many jobs running AND the current job started when the
|
||||
// app was on TOP, but the app has since left TOP
|
||||
// 4. There aren't too many jobs running AND the current job started when the
|
||||
// app was on TOP, the app is still TOP, but there are too many TOP+EJs
|
||||
// app was on TOP, the app is still TOP, but there are too many
|
||||
// immediacy-privileged jobs
|
||||
// running (because we don't want them to starve out other apps and the
|
||||
// current job has already run for the minimum guaranteed time).
|
||||
// 5. This new job could be waiting for too long for a slot to open up
|
||||
boolean canReplace = isTopEj; // Case 1
|
||||
boolean canReplace = hasImmediacyPrivilege; // Case 1
|
||||
if (!canReplace && !isInOverage) {
|
||||
final int currentJobBias = mService.evaluateJobBiasLocked(runningJob);
|
||||
canReplace = runningJob.lastEvaluatedBias < JobInfo.BIAS_TOP_APP // Case 2
|
||||
|| currentJobBias < JobInfo.BIAS_TOP_APP // Case 3
|
||||
// Case 4
|
||||
|| info.numRunningTopEj > .5 * mWorkTypeConfig.getMaxTotal();
|
||||
|| info.numRunningImmediacyPrivileged
|
||||
> (mWorkTypeConfig.getMaxTotal() / 2);
|
||||
}
|
||||
if (!canReplace && mMaxWaitTimeBypassEnabled) { // Case 5
|
||||
if (nextPending.shouldTreatAsExpeditedJob()) {
|
||||
@@ -919,7 +919,7 @@ class JobConcurrencyManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedContext == null && (!isInOverage || isTopEj)) {
|
||||
if (selectedContext == null && (!isInOverage || hasImmediacyPrivilege)) {
|
||||
int lowestBiasSeen = Integer.MAX_VALUE;
|
||||
long newMinPreferredUidOnlyWaitingTimeMs = Long.MAX_VALUE;
|
||||
for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
|
||||
@@ -962,12 +962,13 @@ class JobConcurrencyManager {
|
||||
info.minPreferredUidOnlyWaitingTimeMs = newMinPreferredUidOnlyWaitingTimeMs;
|
||||
}
|
||||
}
|
||||
// Make sure to run EJs for the TOP app immediately.
|
||||
if (isTopEj) {
|
||||
// Make sure to run jobs with special privilege immediately.
|
||||
if (hasImmediacyPrivilege) {
|
||||
if (selectedContext != null
|
||||
&& selectedContext.context.getRunningJobLocked() != null) {
|
||||
// We're "replacing" a currently running job, but we want TOP EJs to start
|
||||
// immediately, so we'll start the EJ on a fresh available context and
|
||||
// We're "replacing" a currently running job, but we want immediacy-privileged
|
||||
// jobs to start immediately, so we'll start the privileged jobs on a fresh
|
||||
// available context and
|
||||
// stop this currently running job to replace in two steps.
|
||||
changed.add(selectedContext);
|
||||
projectedRunningCount--;
|
||||
@@ -1029,6 +1030,7 @@ class JobConcurrencyManager {
|
||||
projectedRunningCount--;
|
||||
}
|
||||
if (selectedContext.newJob != null) {
|
||||
selectedContext.newJob.startedWithImmediacyPrivilege = hasImmediacyPrivilege;
|
||||
projectedRunningCount++;
|
||||
minChangedWaitingTimeMs = Math.min(minChangedWaitingTimeMs,
|
||||
mService.getMinJobExecutionGuaranteeMs(selectedContext.newJob));
|
||||
@@ -1103,6 +1105,18 @@ class JobConcurrencyManager {
|
||||
mActivePkgStats.forEach(mPackageStatsStagingCountClearer);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mLock")
|
||||
boolean hasImmediacyPrivilegeLocked(@NonNull JobStatus job) {
|
||||
// EJs & user-initiated jobs for the TOP app should run immediately.
|
||||
// However, even for user-initiated jobs, if the app has not recently been in TOP or BAL
|
||||
// state, we don't give the immediacy privilege so that we can try and maintain
|
||||
// reasonably concurrency behavior.
|
||||
return job.lastEvaluatedBias == JobInfo.BIAS_TOP_APP
|
||||
// TODO(): include BAL state for user-initiated jobs
|
||||
&& (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiated());
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
void onUidBiasChangedLocked(int prevBias, int newBias) {
|
||||
if (prevBias != JobInfo.BIAS_TOP_APP && newBias != JobInfo.BIAS_TOP_APP) {
|
||||
@@ -1361,7 +1375,7 @@ class JobConcurrencyManager {
|
||||
mActiveServices.remove(worker);
|
||||
if (mIdleContexts.size() < MAX_RETAINED_OBJECTS) {
|
||||
// Don't need to save all new contexts, but keep some extra around in case we need
|
||||
// extras for another TOP+EJ overage.
|
||||
// extras for another immediacy privileged overage.
|
||||
mIdleContexts.add(worker);
|
||||
} else {
|
||||
mNumDroppedContexts++;
|
||||
@@ -1403,7 +1417,8 @@ class JobConcurrencyManager {
|
||||
}
|
||||
if (respectConcurrencyLimit) {
|
||||
worker.clearPreferredUid();
|
||||
// We're over the limit (because the TOP app scheduled a lot of EJs), but we should
|
||||
// We're over the limit (because there were a lot of immediacy-privileged jobs
|
||||
// scheduled), but we should
|
||||
// be able to stop the other jobs soon so don't start running anything new until we
|
||||
// get back below the limit.
|
||||
noteConcurrency();
|
||||
@@ -1627,17 +1642,17 @@ class JobConcurrencyManager {
|
||||
}
|
||||
} else if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
|
||||
return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
|
||||
} else if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
|
||||
// Try not to let TOP + EJ starve out other apps.
|
||||
int topEjCount = 0;
|
||||
} else if (js.startedWithImmediacyPrivilege) {
|
||||
// Try not to let jobs with immediacy privilege starve out other apps.
|
||||
int immediacyPrivilegeCount = 0;
|
||||
for (int r = mRunningJobs.size() - 1; r >= 0; --r) {
|
||||
JobStatus j = mRunningJobs.valueAt(r);
|
||||
if (j.startedAsExpeditedJob && j.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
|
||||
topEjCount++;
|
||||
if (j.startedWithImmediacyPrivilege) {
|
||||
immediacyPrivilegeCount++;
|
||||
}
|
||||
}
|
||||
if (topEjCount > .5 * mWorkTypeConfig.getMaxTotal()) {
|
||||
return "prevent top EJ dominance";
|
||||
if (immediacyPrivilegeCount > mWorkTypeConfig.getMaxTotal() / 2) {
|
||||
return "prevent immediacy privilege dominance";
|
||||
}
|
||||
}
|
||||
// No other pending EJs. Return null so we don't let regular jobs preempt an EJ.
|
||||
@@ -2566,11 +2581,11 @@ class JobConcurrencyManager {
|
||||
@VisibleForTesting
|
||||
static final class AssignmentInfo {
|
||||
public long minPreferredUidOnlyWaitingTimeMs;
|
||||
public int numRunningTopEj;
|
||||
public int numRunningImmediacyPrivileged;
|
||||
|
||||
void clear() {
|
||||
minPreferredUidOnlyWaitingTimeMs = 0;
|
||||
numRunningTopEj = 0;
|
||||
numRunningImmediacyPrivileged = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -388,6 +388,8 @@ public final class JobStatus {
|
||||
*/
|
||||
public boolean startedAsExpeditedJob = false;
|
||||
|
||||
public boolean startedWithImmediacyPrivilege = false;
|
||||
|
||||
// If non-null, this is work that has been enqueued for the job.
|
||||
public ArrayList<JobWorkItem> pendingWork;
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ public final class JobConcurrencyManagerTest {
|
||||
assertEquals(0, preferredUidOnly.size());
|
||||
assertEquals(0, stoppable.size());
|
||||
assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
|
||||
assertEquals(0, assignmentInfo.numRunningTopEj);
|
||||
assertEquals(0, assignmentInfo.numRunningImmediacyPrivileged);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -239,7 +239,7 @@ public final class JobConcurrencyManagerTest {
|
||||
assertEquals(0, preferredUidOnly.size());
|
||||
assertEquals(0, stoppable.size());
|
||||
assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
|
||||
assertEquals(0, assignmentInfo.numRunningTopEj);
|
||||
assertEquals(0, assignmentInfo.numRunningImmediacyPrivileged);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -265,15 +265,14 @@ public final class JobConcurrencyManagerTest {
|
||||
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
|
||||
assertEquals(0, stoppable.size());
|
||||
assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
|
||||
assertEquals(0, assignmentInfo.numRunningTopEj);
|
||||
assertEquals(0, assignmentInfo.numRunningImmediacyPrivileged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrepareForAssignmentDetermination_onlyRunningTopEjs() {
|
||||
public void testPrepareForAssignmentDetermination_onlyStartedWithImmediacyPrivilege() {
|
||||
for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
|
||||
JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
|
||||
job.startedAsExpeditedJob = true;
|
||||
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
|
||||
job.startedWithImmediacyPrivilege = true;
|
||||
mJobConcurrencyManager.addRunningJobForTesting(job);
|
||||
}
|
||||
|
||||
@@ -294,7 +293,7 @@ public final class JobConcurrencyManagerTest {
|
||||
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT / 2, stoppable.size());
|
||||
assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
|
||||
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT,
|
||||
assignmentInfo.numRunningTopEj);
|
||||
assignmentInfo.numRunningImmediacyPrivileged);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -499,6 +498,38 @@ public final class JobConcurrencyManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasImmediacyPrivilege() {
|
||||
JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
|
||||
spyOn(job);
|
||||
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
|
||||
|
||||
doReturn(false).when(job).shouldTreatAsExpeditedJob();
|
||||
doReturn(false).when(job).shouldTreatAsUserInitiated();
|
||||
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
|
||||
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
|
||||
|
||||
doReturn(true).when(job).shouldTreatAsExpeditedJob();
|
||||
doReturn(false).when(job).shouldTreatAsUserInitiated();
|
||||
job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
|
||||
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
|
||||
|
||||
doReturn(false).when(job).shouldTreatAsExpeditedJob();
|
||||
doReturn(true).when(job).shouldTreatAsUserInitiated();
|
||||
job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
|
||||
assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
|
||||
|
||||
doReturn(false).when(job).shouldTreatAsExpeditedJob();
|
||||
doReturn(true).when(job).shouldTreatAsUserInitiated();
|
||||
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
|
||||
assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
|
||||
|
||||
doReturn(true).when(job).shouldTreatAsExpeditedJob();
|
||||
doReturn(false).when(job).shouldTreatAsUserInitiated();
|
||||
job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
|
||||
assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsPkgConcurrencyLimited_top() {
|
||||
final JobStatus topJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
|
||||
|
||||
Reference in New Issue
Block a user