Merge "Fix concurrency limits." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
89d773811e
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())) {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
|||||||
Reference in New Issue
Block a user