summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java60
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java113
2 files changed, 126 insertions, 47 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 53f5c6e64129..f9dd0b379cb2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -327,6 +327,7 @@ class JobConcurrencyManager {
private final ArraySet<ContextAssignment> mRecycledIdle = new ArraySet<>();
private final ArrayList<ContextAssignment> mRecycledPreferredUidOnly = new ArrayList<>();
private final ArrayList<ContextAssignment> mRecycledStoppable = new ArrayList<>();
+ private final AssignmentInfo mRecycledAssignmentInfo = new AssignmentInfo();
private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
new Pools.SimplePool<>(MAX_RETAINED_OBJECTS);
@@ -414,7 +415,7 @@ class JobConcurrencyManager {
@VisibleForTesting
JobConcurrencyManager(JobSchedulerService service, Injector injector) {
mService = service;
- mLock = mService.mLock;
+ mLock = mService.getLock();
mContext = service.getTestableContext();
mInjector = injector;
@@ -693,8 +694,9 @@ class JobConcurrencyManager {
return;
}
- final long minPreferredUidOnlyWaitingTimeMs = prepareForAssignmentDeterminationLocked(
- mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable);
+ prepareForAssignmentDeterminationLocked(
+ mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
+ mRecycledAssignmentInfo);
if (DEBUG) {
Slog.d(TAG, printAssignments("running jobs initial",
@@ -703,7 +705,7 @@ class JobConcurrencyManager {
determineAssignmentsLocked(
mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
- minPreferredUidOnlyWaitingTimeMs);
+ mRecycledAssignmentInfo);
if (DEBUG) {
Slog.d(TAG, printAssignments("running jobs final",
@@ -715,17 +717,18 @@ class JobConcurrencyManager {
carryOutAssignmentChangesLocked(mRecycledChanged);
cleanUpAfterAssignmentChangesLocked(
- mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable);
+ mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
+ mRecycledAssignmentInfo);
noteConcurrency();
}
- /** @return the minimum remaining execution time for preferred UID only JobServiceContexts. */
@VisibleForTesting
@GuardedBy("mLock")
- long prepareForAssignmentDeterminationLocked(final ArraySet<ContextAssignment> idle,
+ void prepareForAssignmentDeterminationLocked(final ArraySet<ContextAssignment> idle,
final List<ContextAssignment> preferredUidOnly,
- final List<ContextAssignment> stoppable) {
+ final List<ContextAssignment> stoppable,
+ final AssignmentInfo info) {
final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
final List<JobServiceContext> activeServices = mActiveServices;
@@ -755,6 +758,9 @@ class JobConcurrencyManager {
if (js != null) {
mWorkCountTracker.incrementRunningJobCount(jsc.getRunningJobWorkType());
assignment.workType = jsc.getRunningJobWorkType();
+ if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
+ info.numRunningTopEj++;
+ }
}
assignment.preferredUid = jsc.getPreferredUid();
@@ -789,10 +795,11 @@ class JobConcurrencyManager {
}
mWorkCountTracker.onCountDone();
- // Return 0 if there were no preferred UID only contexts to indicate no waiting time due
+ // Set 0 if there were no preferred UID only contexts to indicate no waiting time due
// to such jobs.
- return minPreferredUidOnlyWaitingTimeMs == Long.MAX_VALUE
- ? 0 : minPreferredUidOnlyWaitingTimeMs;
+ info.minPreferredUidOnlyWaitingTimeMs =
+ minPreferredUidOnlyWaitingTimeMs == Long.MAX_VALUE
+ ? 0 : minPreferredUidOnlyWaitingTimeMs;
}
@VisibleForTesting
@@ -801,7 +808,7 @@ class JobConcurrencyManager {
final ArraySet<ContextAssignment> idle,
final List<ContextAssignment> preferredUidOnly,
final List<ContextAssignment> stoppable,
- long minPreferredUidOnlyWaitingTimeMs) {
+ @NonNull AssignmentInfo info) {
final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
final List<JobServiceContext> activeServices = mActiveServices;
pendingJobQueue.resetIterator();
@@ -832,7 +839,7 @@ class JobConcurrencyManager {
// pending jobs that could be designated as waiting too long, and those other jobs
// would only have to wait for the new slots to become available.
final long minWaitingTimeMs =
- Math.min(minPreferredUidOnlyWaitingTimeMs, minChangedWaitingTimeMs);
+ Math.min(info.minPreferredUidOnlyWaitingTimeMs, minChangedWaitingTimeMs);
// Find an available slot for nextPending. The context should be one of the following:
// 1. Unused
@@ -861,13 +868,6 @@ class JobConcurrencyManager {
}
}
if (selectedContext == null && stoppable.size() > 0) {
- int topEjCount = 0;
- for (int r = mRunningJobs.size() - 1; r >= 0; --r) {
- JobStatus js = mRunningJobs.valueAt(r);
- if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
- topEjCount++;
- }
- }
for (int s = stoppable.size() - 1; s >= 0; --s) {
final ContextAssignment assignment = stoppable.get(s);
final JobStatus runningJob = assignment.context.getRunningJobLocked();
@@ -888,7 +888,8 @@ class JobConcurrencyManager {
final int currentJobBias = mService.evaluateJobBiasLocked(runningJob);
canReplace = runningJob.lastEvaluatedBias < JobInfo.BIAS_TOP_APP // Case 2
|| currentJobBias < JobInfo.BIAS_TOP_APP // Case 3
- || topEjCount > .5 * mWorkTypeConfig.getMaxTotal(); // Case 4
+ // Case 4
+ || info.numRunningTopEj > .5 * mWorkTypeConfig.getMaxTotal();
}
if (!canReplace && mMaxWaitTimeBypassEnabled) { // Case 5
if (nextPending.shouldTreatAsExpeditedJob()) {
@@ -955,7 +956,7 @@ class JobConcurrencyManager {
if (selectedContext != null) {
selectedContext.newJob = nextPending;
preferredUidOnly.remove(selectedContext);
- minPreferredUidOnlyWaitingTimeMs = newMinPreferredUidOnlyWaitingTimeMs;
+ info.minPreferredUidOnlyWaitingTimeMs = newMinPreferredUidOnlyWaitingTimeMs;
}
}
// Make sure to run EJs for the TOP app immediately.
@@ -1072,7 +1073,8 @@ class JobConcurrencyManager {
private void cleanUpAfterAssignmentChangesLocked(final ArraySet<ContextAssignment> changed,
final ArraySet<ContextAssignment> idle,
final List<ContextAssignment> preferredUidOnly,
- final List<ContextAssignment> stoppable) {
+ final List<ContextAssignment> stoppable,
+ final AssignmentInfo assignmentInfo) {
for (int s = stoppable.size() - 1; s >= 0; --s) {
final ContextAssignment assignment = stoppable.get(s);
assignment.clear();
@@ -1093,6 +1095,7 @@ class JobConcurrencyManager {
idle.clear();
stoppable.clear();
preferredUidOnly.clear();
+ assignmentInfo.clear();
mWorkCountTracker.resetStagingCount();
mActivePkgStats.forEach(mPackageStatsStagingCountClearer);
}
@@ -2540,6 +2543,17 @@ class JobConcurrencyManager {
}
}
+ @VisibleForTesting
+ static final class AssignmentInfo {
+ public long minPreferredUidOnlyWaitingTimeMs;
+ public int numRunningTopEj;
+
+ void clear() {
+ minPreferredUidOnlyWaitingTimeMs = 0;
+ numRunningTopEj = 0;
+ }
+ }
+
// TESTING HELPERS
@VisibleForTesting
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
index 6279b87eb603..6e4d21456cd0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -17,6 +17,7 @@
package com.android.server.job;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -34,16 +35,20 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.IActivityManager;
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -51,6 +56,8 @@ import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
@@ -149,12 +156,14 @@ public final class JobConcurrencyManagerTest {
R.bool.config_jobSchedulerRestrictBackgroundUser);
when(mContext.getResources()).thenReturn(mResources);
doReturn(mContext).when(jobSchedulerService).getTestableContext();
+ doReturn(jobSchedulerService).when(jobSchedulerService).getLock();
mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock -> mConfigBuilder.build())
.when(() -> DeviceConfig.getProperties(eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER)));
mPendingJobQueue = new PendingJobQueue();
doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue();
doReturn(mIPackageManager).when(AppGlobals::getPackageManager);
+ doReturn(mock(PowerManager.class)).when(mContext).getSystemService(PowerManager.class);
mInjector = new InjectorForTest();
doAnswer((Answer<Long>) invocationOnMock -> {
Object[] args = invocationOnMock.getArguments();
@@ -171,6 +180,16 @@ public final class JobConcurrencyManagerTest {
createCurrentUser(true);
mNextUserId = 10;
mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
+
+ IActivityManager activityManager = ActivityManager.getService();
+ spyOn(activityManager);
+ try {
+ doNothing().when(activityManager).registerUserSwitchObserver(any(), anyString());
+ } catch (RemoteException e) {
+ fail("registerUserSwitchObserver threw exception: " + e.getMessage());
+ }
+
+ mJobConcurrencyManager.onSystemReady();
}
@After
@@ -188,13 +207,16 @@ public final class JobConcurrencyManagerTest {
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
- final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
- .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, idle.size());
assertEquals(0, preferredUidOnly.size());
assertEquals(0, stoppable.size());
- assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(0, assignmentInfo.numRunningTopEj);
}
@Test
@@ -207,13 +229,16 @@ public final class JobConcurrencyManagerTest {
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
- final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
- .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, idle.size());
assertEquals(0, preferredUidOnly.size());
assertEquals(0, stoppable.size());
- assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(0, assignmentInfo.numRunningTopEj);
}
@Test
@@ -230,13 +255,45 @@ public final class JobConcurrencyManagerTest {
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
- final long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
- .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
assertEquals(0, idle.size());
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
assertEquals(0, stoppable.size());
- assertEquals(0, minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(0, assignmentInfo.numRunningTopEj);
+ }
+
+ @Test
+ public void testPrepareForAssignmentDetermination_onlyRunningTopEjs() {
+ 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;
+ mJobConcurrencyManager.addRunningJobForTesting(job);
+ }
+
+ for (int i = 0; i < mInjector.contexts.size(); ++i) {
+ doReturn(i % 2 == 0).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
+ }
+
+ final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
+ final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
+ final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
+
+ assertEquals(0, idle.size());
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT / 2, preferredUidOnly.size());
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT / 2, stoppable.size());
+ assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
+ assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT,
+ assignmentInfo.numRunningTopEj);
}
@Test
@@ -257,11 +314,13 @@ public final class JobConcurrencyManagerTest {
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
- mJobConcurrencyManager
- .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
mJobConcurrencyManager
.determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
- Long.MAX_VALUE);
+ assignmentInfo);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, changed.size());
for (int i = changed.size() - 1; i >= 0; --i) {
@@ -301,15 +360,17 @@ public final class JobConcurrencyManagerTest {
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
- long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
- .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
- assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
+ assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
mJobConcurrencyManager
.determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
- minPreferredUidOnlyWaitingTimeMs);
+ assignmentInfo);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
assertEquals(0, changed.size());
@@ -350,15 +411,17 @@ public final class JobConcurrencyManagerTest {
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
- long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
- .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
- assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
+ assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
mJobConcurrencyManager
.determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
- minPreferredUidOnlyWaitingTimeMs);
+ assignmentInfo);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
for (int i = changed.size() - 1; i >= 0; --i) {
@@ -404,15 +467,17 @@ public final class JobConcurrencyManagerTest {
final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
+ final JobConcurrencyManager.AssignmentInfo assignmentInfo =
+ new JobConcurrencyManager.AssignmentInfo();
- long minPreferredUidOnlyWaitingTimeMs = mJobConcurrencyManager
- .prepareForAssignmentDeterminationLocked(idle, preferredUidOnly, stoppable);
- assertEquals(remainingTimeMs, minPreferredUidOnlyWaitingTimeMs);
+ mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
+ idle, preferredUidOnly, stoppable, assignmentInfo);
+ assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
mJobConcurrencyManager
.determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
- minPreferredUidOnlyWaitingTimeMs);
+ assignmentInfo);
assertEquals(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT, preferredUidOnly.size());
// Depending on iteration order, we may create 1 or 2 contexts.