Merge "Fix concurrency limits." into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-28 18:17:26 +00:00
committed by Android (Google) Code Review
4 changed files with 317 additions and 30 deletions

View File

@@ -84,10 +84,12 @@ class JobConcurrencyManager {
private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS = private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS =
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms"; CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000; private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
private static final String KEY_PKG_CONCURRENCY_LIMIT_EJ = @VisibleForTesting
static final String KEY_PKG_CONCURRENCY_LIMIT_EJ =
CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_ej"; CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_ej";
private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_EJ = 3; private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_EJ = 3;
private static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR = @VisibleForTesting
static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR =
CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular"; CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular";
private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = MAX_JOB_CONTEXTS_COUNT / 2; private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = MAX_JOB_CONTEXTS_COUNT / 2;
@@ -299,13 +301,13 @@ class JobConcurrencyManager {
* The maximum number of expedited jobs a single userId-package can have running simultaneously. * The maximum number of expedited jobs a single userId-package can have running simultaneously.
* TOP apps are not limited. * TOP apps are not limited.
*/ */
private long mPkgConcurrencyLimitEj = DEFAULT_PKG_CONCURRENCY_LIMIT_EJ; private int mPkgConcurrencyLimitEj = DEFAULT_PKG_CONCURRENCY_LIMIT_EJ;
/** /**
* The maximum number of regular jobs a single userId-package can have running simultaneously. * The maximum number of regular jobs a single userId-package can have running simultaneously.
* TOP apps are not limited. * TOP apps are not limited.
*/ */
private long mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR; private int mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR;
/** Current memory trim level. */ /** Current memory trim level. */
private int mLastMemoryTrimLevel; private int mLastMemoryTrimLevel;
@@ -568,7 +570,7 @@ class JobConcurrencyManager {
Slog.d(TAG, printPendingQueueLocked()); Slog.d(TAG, printPendingQueueLocked());
} }
final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
final List<JobServiceContext> activeServices = mActiveServices; final List<JobServiceContext> activeServices = mActiveServices;
// To avoid GC churn, we recycle the arrays. // To avoid GC churn, we recycle the arrays.
@@ -916,7 +918,8 @@ class JobConcurrencyManager {
} }
@GuardedBy("mLock") @GuardedBy("mLock")
private boolean isPkgConcurrencyLimitedLocked(@NonNull JobStatus jobStatus) { @VisibleForTesting
boolean isPkgConcurrencyLimitedLocked(@NonNull JobStatus jobStatus) {
if (jobStatus.lastEvaluatedBias >= JobInfo.BIAS_TOP_APP) { if (jobStatus.lastEvaluatedBias >= JobInfo.BIAS_TOP_APP) {
// Don't restrict top apps' concurrency. The work type limits will make sure // Don't restrict top apps' concurrency. The work type limits will make sure
// background jobs have slots to run if the system has resources. // background jobs have slots to run if the system has resources.
@@ -924,7 +927,7 @@ class JobConcurrencyManager {
} }
// Use < instead of <= as that gives us a little wiggle room in case a new job comes // Use < instead of <= as that gives us a little wiggle room in case a new job comes
// along very shortly. // along very shortly.
if (mService.mPendingJobQueue.size() + mRunningJobs.size() if (mService.getPendingJobQueue().size() + mRunningJobs.size()
< mWorkTypeConfig.getMaxTotal()) { < mWorkTypeConfig.getMaxTotal()) {
// Don't artificially limit a single package if we don't even have enough jobs to use // Don't artificially limit a single package if we don't even have enough jobs to use
// the maximum number of slots. We'll preempt the job later if we need the slot. // the maximum number of slots. We'll preempt the job later if we need the slot.
@@ -937,10 +940,10 @@ class JobConcurrencyManager {
return false; return false;
} }
if (jobStatus.shouldTreatAsExpeditedJob()) { if (jobStatus.shouldTreatAsExpeditedJob()) {
return packageStats.numRunningEj + packageStats.numStagedEj < mPkgConcurrencyLimitEj; return packageStats.numRunningEj + packageStats.numStagedEj >= mPkgConcurrencyLimitEj;
} else { } else {
return packageStats.numRunningRegular + packageStats.numStagedRegular return packageStats.numRunningRegular + packageStats.numStagedRegular
< mPkgConcurrencyLimitRegular; >= mPkgConcurrencyLimitRegular;
} }
} }
@@ -981,7 +984,7 @@ class JobConcurrencyManager {
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
packageStats); packageStats);
} }
if (mService.mPendingJobQueue.remove(jobStatus)) { if (mService.getPendingJobQueue().remove(jobStatus)) {
mService.mJobPackageTracker.noteNonpending(jobStatus); mService.mJobPackageTracker.noteNonpending(jobStatus);
} }
} finally { } finally {
@@ -1008,7 +1011,7 @@ class JobConcurrencyManager {
} }
} }
final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) { if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
updateCounterConfigLocked(); updateCounterConfigLocked();
// Preemption case needs special care. // Preemption case needs special care.
@@ -1176,7 +1179,7 @@ class JobConcurrencyManager {
return "too many jobs running"; return "too many jobs running";
} }
final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
final int numPending = pendingJobQueue.size(); final int numPending = pendingJobQueue.size();
if (numPending == 0) { if (numPending == 0) {
// All quiet. We can let this job run to completion. // All quiet. We can let this job run to completion.
@@ -1259,7 +1262,7 @@ class JobConcurrencyManager {
@GuardedBy("mLock") @GuardedBy("mLock")
private String printPendingQueueLocked() { private String printPendingQueueLocked() {
StringBuilder s = new StringBuilder("Pending queue: "); StringBuilder s = new StringBuilder("Pending queue: ");
PendingJobQueue pendingJobQueue = mService.mPendingJobQueue; PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
JobStatus js; JobStatus js;
pendingJobQueue.resetIterator(); pendingJobQueue.resetIterator();
while ((js = pendingJobQueue.next()) != null) { while ((js = pendingJobQueue.next()) != null) {
@@ -1523,8 +1526,8 @@ class JobConcurrencyManager {
@VisibleForTesting @VisibleForTesting
static class WorkTypeConfig { static class WorkTypeConfig {
private static final String KEY_PREFIX_MAX_TOTAL = @VisibleForTesting
CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_"; 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_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_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_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
@@ -2025,7 +2028,8 @@ class JobConcurrencyManager {
} }
} }
private static class PackageStats { @VisibleForTesting
static class PackageStats {
public int userId; public int userId;
public String packageName; public String packageName;
public int numRunningEj; public int numRunningEj;
@@ -2095,4 +2099,32 @@ class JobConcurrencyManager {
newWorkType = WORK_TYPE_NONE; newWorkType = WORK_TYPE_NONE;
} }
} }
// TESTING HELPERS
@VisibleForTesting
void addRunningJobForTesting(@NonNull JobStatus job) {
mRunningJobs.add(job);
final PackageStats packageStats =
getPackageStatsForTesting(job.getSourceUserId(), job.getSourcePackageName());
packageStats.adjustRunningCount(true, job.shouldTreatAsExpeditedJob());
}
@VisibleForTesting
int getPackageConcurrencyLimitEj() {
return mPkgConcurrencyLimitEj;
}
int getPackageConcurrencyLimitRegular() {
return mPkgConcurrencyLimitRegular;
}
/** Gets the {@link PackageStats} object for the app and saves it for testing use. */
@NonNull
@VisibleForTesting
PackageStats getPackageStatsForTesting(int userId, @NonNull String packageName) {
final PackageStats packageStats = getPkgStatsLocked(userId, packageName);
mActivePkgStats.add(userId, packageName, packageStats);
return packageStats;
}
} }

View File

@@ -286,7 +286,7 @@ public class JobSchedulerService extends com.android.server.SystemService
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
* when ready to execute them. * when ready to execute them.
*/ */
final PendingJobQueue mPendingJobQueue = new PendingJobQueue(); private final PendingJobQueue mPendingJobQueue = new PendingJobQueue();
int[] mStartedUsers = EmptyArray.INT; int[] mStartedUsers = EmptyArray.INT;
@@ -1027,6 +1027,11 @@ public class JobSchedulerService extends com.android.server.SystemService
return mConstants; return mConstants;
} }
@NonNull
PendingJobQueue getPendingJobQueue() {
return mPendingJobQueue;
}
@NonNull @NonNull
public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) { public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) { if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {

View File

@@ -16,6 +16,10 @@
package com.android.server.job; package com.android.server.job;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_EJ;
import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_REGULAR;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
@@ -30,6 +34,7 @@ import android.content.Context;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.DeviceConfig;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
@@ -37,9 +42,11 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R; import com.android.internal.R;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.job.JobConcurrencyManager.GracePeriodObserver; import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.JobStatus;
import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerInternal;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@@ -54,9 +61,12 @@ public final class JobConcurrencyManagerTest {
private UserManagerInternal mUserManagerInternal; private UserManagerInternal mUserManagerInternal;
private ActivityManagerInternal mActivityManagerInternal; private ActivityManagerInternal mActivityManagerInternal;
private int mNextUserId; private int mNextUserId;
private int mDefaultUserId;
private GracePeriodObserver mGracePeriodObserver; private GracePeriodObserver mGracePeriodObserver;
private Context mContext; private Context mContext;
private Resources mResources; private Resources mResources;
private PendingJobQueue mPendingJobQueue;
private DeviceConfig.Properties.Builder mConfigBuilder;
@BeforeClass @BeforeClass
public static void setUpOnce() { public static void setUpOnce() {
@@ -80,14 +90,224 @@ public final class JobConcurrencyManagerTest {
R.bool.config_jobSchedulerRestrictBackgroundUser); R.bool.config_jobSchedulerRestrictBackgroundUser);
when(mContext.getResources()).thenReturn(mResources); when(mContext.getResources()).thenReturn(mResources);
doReturn(mContext).when(jobSchedulerService).getTestableContext(); doReturn(mContext).when(jobSchedulerService).getTestableContext();
mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
mPendingJobQueue = new PendingJobQueue();
doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue();
mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService); mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService);
mGracePeriodObserver = mock(GracePeriodObserver.class); mGracePeriodObserver = mock(GracePeriodObserver.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mDefaultUserId = mNextUserId;
createCurrentUser(true);
mNextUserId = 10; mNextUserId = 10;
mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver; mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
} }
@After
public void tearDown() throws Exception {
resetConfig();
}
@Test
public void testIsPkgConcurrencyLimited_top() {
final JobStatus topJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
topJob.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
// Pending jobs shouldn't affect TOP job's status.
for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
mPendingJobQueue.add(job);
}
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
// Already running jobs shouldn't affect TOP job's status.
for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, i);
mJobConcurrencyManager.addRunningJobForTesting(job);
}
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
// Currently running or staged jobs shouldn't affect TOP job's status.
final JobConcurrencyManager.PackageStats packageStats =
mJobConcurrencyManager.getPackageStatsForTesting(
topJob.getSourceUserId(), topJob.getSourcePackageName());
packageStats.numStagedEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj();
packageStats.numStagedRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular();
packageStats.numRunningEj = 0;
packageStats.numRunningRegular = 0;
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
packageStats.numStagedEj = 0;
packageStats.numStagedRegular = 0;
packageStats.numRunningEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj();
packageStats.numRunningRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
}
@Test
public void testIsPkgConcurrencyLimited_belowTotalLimit() throws Exception {
final JobStatus testJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE);
setConcurrencyConfig(8);
// Pending jobs below limit shouldn't affect job's status.
for (int i = 0; i < 5; ++i) {
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
mPendingJobQueue.add(job);
}
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
mPendingJobQueue.clear();
// Already running jobs below limit shouldn't affect job's status.
for (int i = 0; i < 4; ++i) {
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
mJobConcurrencyManager.addRunningJobForTesting(job);
}
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
// Mix of pending + running.
for (int i = 4; i < 8; ++i) {
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
mPendingJobQueue.add(job);
}
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
}
@Test
public void testIsPkgConcurrencyLimited() throws Exception {
final JobStatus testReg = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
final JobStatus testEj = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 1);
spyOn(testEj);
doReturn(true).when(testEj).shouldTreatAsExpeditedJob();
setConcurrencyConfig(JobSchedulerService.MAX_JOB_CONTEXTS_COUNT);
for (int i = 0; i < JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i, i + 1);
mPendingJobQueue.add(job);
}
// App has no running jobs, so shouldn't be limited.
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
// Already running jobs shouldn't affect TOP job's status.
final JobConcurrencyManager.PackageStats packageStats =
mJobConcurrencyManager.getPackageStatsForTesting(
testReg.getSourceUserId(), testReg.getSourcePackageName());
// Only running counts
packageStats.numStagedEj = 0;
packageStats.numStagedRegular = 0;
packageStats.numRunningEj = 4;
packageStats.numRunningRegular = 4;
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
// Only staged counts
packageStats.numStagedEj = 4;
packageStats.numStagedRegular = 4;
packageStats.numRunningEj = 0;
packageStats.numRunningRegular = 0;
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
// Running + staged counts
packageStats.numStagedEj = 2;
packageStats.numStagedRegular = 1;
packageStats.numRunningEj = 2;
packageStats.numRunningRegular = 3;
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
updateDeviceConfig();
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
updateDeviceConfig();
assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
}
@Test @Test
public void testShouldRunAsFgUserJob_currentUser() { public void testShouldRunAsFgUserJob_currentUser() {
assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
@@ -179,10 +399,40 @@ public final class JobConcurrencyManagerTest {
} }
private static JobStatus createJob(UserInfo userInfo) { private static JobStatus createJob(UserInfo userInfo) {
JobStatus jobStatus = JobStatus.createFromJobInfo( return createJob(userInfo.id * UserHandle.PER_USER_RANGE);
new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(), }
userInfo.id * UserHandle.PER_USER_RANGE,
null, userInfo.id, "JobConcurrencyManagerTest"); private static JobStatus createJob(int uid) {
return jobStatus; return createJob(uid, 1);
}
private static JobStatus createJob(int uid, int jobId) {
return JobStatus.createFromJobInfo(
new JobInfo.Builder(jobId, new ComponentName("foo", "bar")).build(), uid,
null, UserHandle.getUserId(uid), "JobConcurrencyManagerTest");
}
private void setConcurrencyConfig(int total) throws Exception {
// Set the values for all memory states so we don't have to worry about memory on the device
// during testing.
final String[] identifiers = {
"screen_on_normal", "screen_on_moderate", "screen_on_low", "screen_on_critical",
"screen_off_normal", "screen_off_moderate", "screen_off_low", "screen_off_critical"
};
for (String identifier : identifiers) {
mConfigBuilder
.setInt(WorkTypeConfig.KEY_PREFIX_MAX_TOTAL + identifier, total);
}
updateDeviceConfig();
}
private void updateDeviceConfig() throws Exception {
DeviceConfig.setProperties(mConfigBuilder.build());
mJobConcurrencyManager.updateConfigLocked();
}
private void resetConfig() throws Exception {
mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
updateDeviceConfig();
} }
} }

View File

@@ -757,7 +757,7 @@ public class JobSchedulerServiceTest {
job.setStandbyBucket(RARE_INDEX); job.setStandbyBucket(RARE_INDEX);
// Not enough RARE jobs to run. // Not enough RARE jobs to run.
mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear();
maybeQueueFunctor.reset(); maybeQueueFunctor.reset();
for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) {
maybeQueueFunctor.accept(job); maybeQueueFunctor.accept(job);
@@ -766,10 +766,10 @@ public class JobSchedulerServiceTest {
assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
} }
maybeQueueFunctor.postProcessLocked(); maybeQueueFunctor.postProcessLocked();
assertEquals(0, mService.mPendingJobQueue.size()); assertEquals(0, mService.getPendingJobQueue().size());
// Enough RARE jobs to run. // Enough RARE jobs to run.
mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear();
maybeQueueFunctor.reset(); maybeQueueFunctor.reset();
for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) { for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) {
maybeQueueFunctor.accept(job); maybeQueueFunctor.accept(job);
@@ -778,10 +778,10 @@ public class JobSchedulerServiceTest {
assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
} }
maybeQueueFunctor.postProcessLocked(); maybeQueueFunctor.postProcessLocked();
assertEquals(5, mService.mPendingJobQueue.size()); assertEquals(5, mService.getPendingJobQueue().size());
// Not enough RARE jobs to run, but a non-batched job saves the day. // Not enough RARE jobs to run, but a non-batched job saves the day.
mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear();
maybeQueueFunctor.reset(); maybeQueueFunctor.reset();
JobStatus activeJob = createJobStatus( JobStatus activeJob = createJobStatus(
"testRareJobBatching", "testRareJobBatching",
@@ -795,10 +795,10 @@ public class JobSchedulerServiceTest {
} }
maybeQueueFunctor.accept(activeJob); maybeQueueFunctor.accept(activeJob);
maybeQueueFunctor.postProcessLocked(); maybeQueueFunctor.postProcessLocked();
assertEquals(3, mService.mPendingJobQueue.size()); assertEquals(3, mService.getPendingJobQueue().size());
// Not enough RARE jobs to run, but an old RARE job saves the day. // Not enough RARE jobs to run, but an old RARE job saves the day.
mService.mPendingJobQueue.clear(); mService.getPendingJobQueue().clear();
maybeQueueFunctor.reset(); maybeQueueFunctor.reset();
JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo()); JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo());
oldRareJob.setStandbyBucket(RARE_INDEX); oldRareJob.setStandbyBucket(RARE_INDEX);
@@ -814,7 +814,7 @@ public class JobSchedulerServiceTest {
maybeQueueFunctor.accept(oldRareJob); maybeQueueFunctor.accept(oldRareJob);
assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed()); assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed());
maybeQueueFunctor.postProcessLocked(); maybeQueueFunctor.postProcessLocked();
assertEquals(3, mService.mPendingJobQueue.size()); assertEquals(3, mService.getPendingJobQueue().size());
} }
/** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */ /** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */