Loading apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +434 −237 File changed.Preview size limit exceeded, changes collapsed. Show changes apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +0 −182 Original line number Original line Diff line number Diff line Loading @@ -380,7 +380,6 @@ public class JobSchedulerService extends com.android.server.SystemService default: default: if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) && !concurrencyUpdated) { && !concurrencyUpdated) { mConstants.updateConcurrencyConstantsLocked(); mConcurrencyManager.updateConfigLocked(); mConcurrencyManager.updateConfigLocked(); concurrencyUpdated = true; concurrencyUpdated = true; } else { } else { Loading Loading @@ -408,119 +407,6 @@ public class JobSchedulerService extends com.android.server.SystemService mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); } } static class MaxJobCounts { private final int mTotalDefault; private final String mTotalKey; private final int mMaxBgDefault; private final String mMaxBgKey; private final int mMinBgDefault; private final String mMinBgKey; private int mTotal; private int mMaxBg; private int mMinBg; MaxJobCounts(int totalDefault, String totalKey, int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) { mTotalKey = totalKey; mTotal = mTotalDefault = totalDefault; mMaxBgKey = maxBgKey; mMaxBg = mMaxBgDefault = maxBgDefault; mMinBgKey = minBgKey; mMinBg = mMinBgDefault = minBgDefault; } public void update() { mTotal = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, mTotalKey, mTotalDefault); mMaxBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, mMaxBgKey, mMaxBgDefault); mMinBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, mMinBgKey, mMinBgDefault); // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT]. mTotal = Math.min(Math.max(1, mTotal), MAX_JOB_CONTEXTS_COUNT); // Ensure maxBg in the range [1, total]. mMaxBg = Math.min(Math.max(1, mMaxBg), mTotal); // Ensure minBg in the range [0, min(maxBg, total - 1)] mMinBg = Math.min(Math.max(0, mMinBg), Math.min(mMaxBg, mTotal - 1)); } /** Total number of jobs to run simultaneously. */ public int getMaxTotal() { return mTotal; } /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */ public int getMaxBg() { return mMaxBg; } /** * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any * pending, rather than always running the TOTAL number of FG jobs. */ public int getMinBg() { return mMinBg; } public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print(mTotalKey); pw.print("="); pw.print(mTotal); pw.println(); pw.print(prefix); pw.print(mMaxBgKey); pw.print("="); pw.print(mMaxBg); pw.println(); pw.print(prefix); pw.print(mMinBgKey); pw.print("="); pw.print(mMinBg); pw.println(); } public void dumpProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(MaxJobCountsProto.TOTAL_JOBS, mTotal); proto.write(MaxJobCountsProto.MAX_BG, mMaxBg); proto.write(MaxJobCountsProto.MIN_BG, mMinBg); proto.end(token); } } /** {@link MaxJobCounts} for each memory trim level. */ static class MaxJobCountsPerMemoryTrimLevel { public final MaxJobCounts normal; public final MaxJobCounts moderate; public final MaxJobCounts low; public final MaxJobCounts critical; MaxJobCountsPerMemoryTrimLevel( MaxJobCounts normal, MaxJobCounts moderate, MaxJobCounts low, MaxJobCounts critical) { this.normal = normal; this.moderate = moderate; this.low = low; this.critical = critical; } public void dumpProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL); moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE); low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW); critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL); proto.end(token); } } /** /** * All times are in milliseconds. Any access to this class or its fields should be done while * All times are in milliseconds. Any access to this class or its fields should be done while * holding the JobSchedulerService.mLock lock. * holding the JobSchedulerService.mLock lock. Loading Loading @@ -580,49 +466,6 @@ public class JobSchedulerService extends com.android.server.SystemService */ */ float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; /** Prefix for all of the max_job constants. */ private static final String KEY_PREFIX_MAX_JOB = JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY + "max_job_"; // Max job counts for screen on / off, for each memory trim level. final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON = new MaxJobCountsPerMemoryTrimLevel( new MaxJobCounts( 8, KEY_PREFIX_MAX_JOB + "total_on_normal", 6, KEY_PREFIX_MAX_JOB + "max_bg_on_normal", 2, KEY_PREFIX_MAX_JOB + "min_bg_on_normal"), new MaxJobCounts( 8, KEY_PREFIX_MAX_JOB + "total_on_moderate", 4, KEY_PREFIX_MAX_JOB + "max_bg_on_moderate", 2, KEY_PREFIX_MAX_JOB + "min_bg_on_moderate"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_on_low", 1, KEY_PREFIX_MAX_JOB + "max_bg_on_low", 1, KEY_PREFIX_MAX_JOB + "min_bg_on_low"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_on_critical", 1, KEY_PREFIX_MAX_JOB + "max_bg_on_critical", 1, KEY_PREFIX_MAX_JOB + "min_bg_on_critical")); final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF = new MaxJobCountsPerMemoryTrimLevel( new MaxJobCounts( 10, KEY_PREFIX_MAX_JOB + "total_off_normal", 6, KEY_PREFIX_MAX_JOB + "max_bg_off_normal", 2, KEY_PREFIX_MAX_JOB + "min_bg_off_normal"), new MaxJobCounts( 10, KEY_PREFIX_MAX_JOB + "total_off_moderate", 4, KEY_PREFIX_MAX_JOB + "max_bg_off_moderate", 2, KEY_PREFIX_MAX_JOB + "min_bg_off_moderate"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_off_low", 1, KEY_PREFIX_MAX_JOB + "max_bg_off_low", 1, KEY_PREFIX_MAX_JOB + "min_bg_off_low"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_off_critical", 1, KEY_PREFIX_MAX_JOB + "max_bg_off_critical", 1, KEY_PREFIX_MAX_JOB + "min_bg_off_critical")); /** /** * The minimum backoff time to allow for linear backoff. * The minimum backoff time to allow for linear backoff. */ */ Loading Loading @@ -686,18 +529,6 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_MODERATE_USE_FACTOR); DEFAULT_MODERATE_USE_FACTOR); } } void updateConcurrencyConstantsLocked() { MAX_JOB_COUNTS_SCREEN_ON.normal.update(); MAX_JOB_COUNTS_SCREEN_ON.moderate.update(); MAX_JOB_COUNTS_SCREEN_ON.low.update(); MAX_JOB_COUNTS_SCREEN_ON.critical.update(); MAX_JOB_COUNTS_SCREEN_OFF.normal.update(); MAX_JOB_COUNTS_SCREEN_OFF.moderate.update(); MAX_JOB_COUNTS_SCREEN_OFF.low.update(); MAX_JOB_COUNTS_SCREEN_OFF.critical.update(); } private void updateBackoffConstantsLocked() { private void updateBackoffConstantsLocked() { MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER, MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_LINEAR_BACKOFF_TIME_MS, KEY_MIN_LINEAR_BACKOFF_TIME_MS, Loading Loading @@ -747,16 +578,6 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, ""); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); Loading @@ -781,9 +602,6 @@ public class JobSchedulerService extends com.android.server.SystemService proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON); MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF); proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS); proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); Loading apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +20 −12 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.job; package com.android.server.job; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; Loading Loading @@ -124,9 +125,12 @@ public final class JobServiceContext implements ServiceConnection { * * * Any reads (dereferences) not done from the handler thread must be synchronized on * Any reads (dereferences) not done from the handler thread must be synchronized on * {@link #mLock}. * {@link #mLock}. * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}. * Writes can only be done from the handler thread, * or {@link #executeRunnableJob(JobStatus, int)}. */ */ private JobStatus mRunningJob; private JobStatus mRunningJob; @JobConcurrencyManager.WorkType private int mRunningJobWorkType; private JobCallback mRunningCallback; private JobCallback mRunningCallback; /** Used to store next job to run when current job is to be preempted. */ /** Used to store next job to run when current job is to be preempted. */ private int mPreferredUid; private int mPreferredUid; Loading Loading @@ -181,30 +185,26 @@ public final class JobServiceContext implements ServiceConnection { JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) { JobPackageTracker tracker, Looper looper) { this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper); mContext = service.getContext(); } mLock = service.getLock(); @VisibleForTesting JobServiceContext(Context context, Object lock, IBatteryStats batteryStats, JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) { mContext = context; mLock = lock; mBatteryStats = batteryStats; mBatteryStats = batteryStats; mJobPackageTracker = tracker; mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); mCallbackHandler = new JobServiceHandler(looper); mCompletedListener = completedListener; mCompletedListener = service; mAvailable = true; mAvailable = true; mVerb = VERB_FINISHED; mVerb = VERB_FINISHED; mPreferredUid = NO_PREFERRED_UID; mPreferredUid = NO_PREFERRED_UID; } } /** /** * Give a job to this context for execution. Callers must first check {@link #getRunningJobLocked()} * Give a job to this context for execution. Callers must first check {@link * #getRunningJobLocked()} * and ensure it is null to make sure this is a valid context. * and ensure it is null to make sure this is a valid context. * * @param job The status of the job that we are going to run. * @param job The status of the job that we are going to run. * @return True if the job is valid and is running. False if the job cannot be executed. * @return True if the job is valid and is running. False if the job cannot be executed. */ */ boolean executeRunnableJob(JobStatus job) { boolean executeRunnableJob(JobStatus job, @JobConcurrencyManager.WorkType int workType) { synchronized (mLock) { synchronized (mLock) { if (!mAvailable) { if (!mAvailable) { Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); Loading @@ -214,6 +214,7 @@ public final class JobServiceContext implements ServiceConnection { mPreferredUid = NO_PREFERRED_UID; mPreferredUid = NO_PREFERRED_UID; mRunningJob = job; mRunningJob = job; mRunningJobWorkType = workType; mRunningCallback = new JobCallback(); mRunningCallback = new JobCallback(); final boolean isDeadlineExpired = final boolean isDeadlineExpired = job.hasDeadlineConstraint() && job.hasDeadlineConstraint() && Loading Loading @@ -282,6 +283,7 @@ public final class JobServiceContext implements ServiceConnection { Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable."); Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable."); } } mRunningJob = null; mRunningJob = null; mRunningJobWorkType = WORK_TYPE_NONE; mRunningCallback = null; mRunningCallback = null; mParams = null; mParams = null; mExecutionStartTimeElapsed = 0L; mExecutionStartTimeElapsed = 0L; Loading Loading @@ -326,6 +328,11 @@ public final class JobServiceContext implements ServiceConnection { return mRunningJob; return mRunningJob; } } @JobConcurrencyManager.WorkType int getRunningJobWorkType() { return mRunningJobWorkType; } /** /** * Used only for debugging. Will return <code>"<null>"</code> if there is no job running. * Used only for debugging. Will return <code>"<null>"</code> if there is no job running. */ */ Loading Loading @@ -831,6 +838,7 @@ public final class JobServiceContext implements ServiceConnection { mContext.unbindService(JobServiceContext.this); mContext.unbindService(JobServiceContext.this); mWakeLock = null; mWakeLock = null; mRunningJob = null; mRunningJob = null; mRunningJobWorkType = WORK_TYPE_NONE; mRunningCallback = null; mRunningCallback = null; mParams = null; mParams = null; mVerb = VERB_FINISHED; mVerb = VERB_FINISHED; Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +1 −0 Original line number Original line Diff line number Diff line Loading @@ -305,6 +305,7 @@ public final class JobStatus { public Network network; public Network network; public ServiceInfo serviceInfo; public ServiceInfo serviceInfo; /** The evaluated priority of the job when it started running. */ public int lastEvaluatedPriority; public int lastEvaluatedPriority; // If non-null, this is work that has been enqueued for the job. // If non-null, this is work that has been enqueued for the job. Loading services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java→services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java +44 −22 Original line number Original line Diff line number Diff line Loading @@ -16,36 +16,42 @@ package com.android.server.job; package com.android.server.job; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat; import android.util.Log; import android.util.Log; import android.util.Pair; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.job.JobConcurrencyManager.JobCountTracker; import com.android.server.job.JobConcurrencyManager.WorkCountTracker; import org.junit.Before; import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import java.util.List; import java.util.Random; import java.util.Random; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; /** /** * Test for {@link com.android.server.job.JobConcurrencyManager.JobCountTracker}. * Test for {@link WorkCountTracker}. */ */ @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) @MediumTest @MediumTest public class JobCountTrackerTest { public class WorkCountTrackerTest { private static final String TAG = "JobCountTrackerTest"; private static final String TAG = "WorkerCountTrackerTest"; private Random mRandom; private Random mRandom; private JobCountTracker mJobCountTracker; private WorkCountTracker mWorkCountTracker; @Before @Before public void setUp() { public void setUp() { mRandom = new Random(1); // Always use the same series of pseudo random values. mRandom = new Random(1); // Always use the same series of pseudo random values. mJobCountTracker = new JobCountTracker(); mWorkCountTracker = new WorkCountTracker(); } } /** /** Loading Loading @@ -83,44 +89,57 @@ public class JobCountTrackerTest { private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) { private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) { mJobCountTracker.reset(totalMax, maxBg, minBg); mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig("critical", totalMax, // defaultMin List.of(Pair.create(WORK_TYPE_TOP, totalMax - maxBg), Pair.create(WORK_TYPE_BG, minBg)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, maxBg)))); mWorkCountTracker.resetCounts(); for (int i = 0; i < jobs.runningFg; i++) { for (int i = 0; i < jobs.runningFg; i++) { mJobCountTracker.incrementRunningJobCount(true); mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_TOP); } } for (int i = 0; i < jobs.runningBg; i++) { for (int i = 0; i < jobs.runningBg; i++) { mJobCountTracker.incrementRunningJobCount(false); mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_BG); } } for (int i = 0; i < jobs.pendingFg; i++) { for (int i = 0; i < jobs.pendingFg; i++) { mJobCountTracker.incrementPendingJobCount(true); mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_TOP); } } for (int i = 0; i < jobs.pendingBg; i++) { for (int i = 0; i < jobs.pendingBg; i++) { mJobCountTracker.incrementPendingJobCount(false); mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_BG); } } mJobCountTracker.onCountDone(); mWorkCountTracker.onCountDone(); while ((jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) while ((jobs.pendingFg > 0 || (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false))) { && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) || (jobs.pendingBg > 0 && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) { final boolean isStartingFg = mRandom.nextBoolean(); final boolean isStartingFg = mRandom.nextBoolean(); if (isStartingFg) { if (isStartingFg) { if (jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) { if (jobs.pendingFg > 0 && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) { jobs.pendingFg--; jobs.pendingFg--; jobs.runningFg++; jobs.runningFg++; mJobCountTracker.onStartingNewJob(true); mWorkCountTracker.stageJob(WORK_TYPE_TOP); mWorkCountTracker.onJobStarted(WORK_TYPE_TOP); } } } else { } else { if (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false)) { if (jobs.pendingBg > 0 && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) { jobs.pendingBg--; jobs.pendingBg--; jobs.runningBg++; jobs.runningBg++; mJobCountTracker.onStartingNewJob(false); mWorkCountTracker.stageJob(WORK_TYPE_BG); mWorkCountTracker.onJobStarted(WORK_TYPE_BG); } } } } } } Log.i(TAG, "" + mJobCountTracker); Log.i(TAG, "" + mWorkCountTracker); } } /** /** Loading Loading @@ -277,6 +296,7 @@ public class JobCountTrackerTest { startPendingJobs(jobs, totalMax, maxBg, minBg); startPendingJobs(jobs, totalMax, maxBg, minBg); // fail(mWorkerCountTracker.toString()); assertThat(jobs.runningFg).isEqualTo(resultRunningFg); assertThat(jobs.runningFg).isEqualTo(resultRunningFg); assertThat(jobs.runningBg).isEqualTo(resultRunningBg); assertThat(jobs.runningBg).isEqualTo(resultRunningBg); Loading @@ -300,6 +320,8 @@ public class JobCountTrackerTest { checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0); checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0); checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1); checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1); checkSimple(8, 6, 2, /*run=*/ 0, 0, /*pen=*/ 0, 49, /*res run/pen=*/ 0, 6, 0, 43); checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3); checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3); } } } } Loading
apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +434 −237 File changed.Preview size limit exceeded, changes collapsed. Show changes
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +0 −182 Original line number Original line Diff line number Diff line Loading @@ -380,7 +380,6 @@ public class JobSchedulerService extends com.android.server.SystemService default: default: if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) && !concurrencyUpdated) { && !concurrencyUpdated) { mConstants.updateConcurrencyConstantsLocked(); mConcurrencyManager.updateConfigLocked(); mConcurrencyManager.updateConfigLocked(); concurrencyUpdated = true; concurrencyUpdated = true; } else { } else { Loading Loading @@ -408,119 +407,6 @@ public class JobSchedulerService extends com.android.server.SystemService mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); } } static class MaxJobCounts { private final int mTotalDefault; private final String mTotalKey; private final int mMaxBgDefault; private final String mMaxBgKey; private final int mMinBgDefault; private final String mMinBgKey; private int mTotal; private int mMaxBg; private int mMinBg; MaxJobCounts(int totalDefault, String totalKey, int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) { mTotalKey = totalKey; mTotal = mTotalDefault = totalDefault; mMaxBgKey = maxBgKey; mMaxBg = mMaxBgDefault = maxBgDefault; mMinBgKey = minBgKey; mMinBg = mMinBgDefault = minBgDefault; } public void update() { mTotal = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, mTotalKey, mTotalDefault); mMaxBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, mMaxBgKey, mMaxBgDefault); mMinBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, mMinBgKey, mMinBgDefault); // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT]. mTotal = Math.min(Math.max(1, mTotal), MAX_JOB_CONTEXTS_COUNT); // Ensure maxBg in the range [1, total]. mMaxBg = Math.min(Math.max(1, mMaxBg), mTotal); // Ensure minBg in the range [0, min(maxBg, total - 1)] mMinBg = Math.min(Math.max(0, mMinBg), Math.min(mMaxBg, mTotal - 1)); } /** Total number of jobs to run simultaneously. */ public int getMaxTotal() { return mTotal; } /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */ public int getMaxBg() { return mMaxBg; } /** * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any * pending, rather than always running the TOTAL number of FG jobs. */ public int getMinBg() { return mMinBg; } public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print(mTotalKey); pw.print("="); pw.print(mTotal); pw.println(); pw.print(prefix); pw.print(mMaxBgKey); pw.print("="); pw.print(mMaxBg); pw.println(); pw.print(prefix); pw.print(mMinBgKey); pw.print("="); pw.print(mMinBg); pw.println(); } public void dumpProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(MaxJobCountsProto.TOTAL_JOBS, mTotal); proto.write(MaxJobCountsProto.MAX_BG, mMaxBg); proto.write(MaxJobCountsProto.MIN_BG, mMinBg); proto.end(token); } } /** {@link MaxJobCounts} for each memory trim level. */ static class MaxJobCountsPerMemoryTrimLevel { public final MaxJobCounts normal; public final MaxJobCounts moderate; public final MaxJobCounts low; public final MaxJobCounts critical; MaxJobCountsPerMemoryTrimLevel( MaxJobCounts normal, MaxJobCounts moderate, MaxJobCounts low, MaxJobCounts critical) { this.normal = normal; this.moderate = moderate; this.low = low; this.critical = critical; } public void dumpProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL); moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE); low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW); critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL); proto.end(token); } } /** /** * All times are in milliseconds. Any access to this class or its fields should be done while * All times are in milliseconds. Any access to this class or its fields should be done while * holding the JobSchedulerService.mLock lock. * holding the JobSchedulerService.mLock lock. Loading Loading @@ -580,49 +466,6 @@ public class JobSchedulerService extends com.android.server.SystemService */ */ float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; /** Prefix for all of the max_job constants. */ private static final String KEY_PREFIX_MAX_JOB = JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY + "max_job_"; // Max job counts for screen on / off, for each memory trim level. final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON = new MaxJobCountsPerMemoryTrimLevel( new MaxJobCounts( 8, KEY_PREFIX_MAX_JOB + "total_on_normal", 6, KEY_PREFIX_MAX_JOB + "max_bg_on_normal", 2, KEY_PREFIX_MAX_JOB + "min_bg_on_normal"), new MaxJobCounts( 8, KEY_PREFIX_MAX_JOB + "total_on_moderate", 4, KEY_PREFIX_MAX_JOB + "max_bg_on_moderate", 2, KEY_PREFIX_MAX_JOB + "min_bg_on_moderate"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_on_low", 1, KEY_PREFIX_MAX_JOB + "max_bg_on_low", 1, KEY_PREFIX_MAX_JOB + "min_bg_on_low"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_on_critical", 1, KEY_PREFIX_MAX_JOB + "max_bg_on_critical", 1, KEY_PREFIX_MAX_JOB + "min_bg_on_critical")); final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF = new MaxJobCountsPerMemoryTrimLevel( new MaxJobCounts( 10, KEY_PREFIX_MAX_JOB + "total_off_normal", 6, KEY_PREFIX_MAX_JOB + "max_bg_off_normal", 2, KEY_PREFIX_MAX_JOB + "min_bg_off_normal"), new MaxJobCounts( 10, KEY_PREFIX_MAX_JOB + "total_off_moderate", 4, KEY_PREFIX_MAX_JOB + "max_bg_off_moderate", 2, KEY_PREFIX_MAX_JOB + "min_bg_off_moderate"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_off_low", 1, KEY_PREFIX_MAX_JOB + "max_bg_off_low", 1, KEY_PREFIX_MAX_JOB + "min_bg_off_low"), new MaxJobCounts( 5, KEY_PREFIX_MAX_JOB + "total_off_critical", 1, KEY_PREFIX_MAX_JOB + "max_bg_off_critical", 1, KEY_PREFIX_MAX_JOB + "min_bg_off_critical")); /** /** * The minimum backoff time to allow for linear backoff. * The minimum backoff time to allow for linear backoff. */ */ Loading Loading @@ -686,18 +529,6 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_MODERATE_USE_FACTOR); DEFAULT_MODERATE_USE_FACTOR); } } void updateConcurrencyConstantsLocked() { MAX_JOB_COUNTS_SCREEN_ON.normal.update(); MAX_JOB_COUNTS_SCREEN_ON.moderate.update(); MAX_JOB_COUNTS_SCREEN_ON.low.update(); MAX_JOB_COUNTS_SCREEN_ON.critical.update(); MAX_JOB_COUNTS_SCREEN_OFF.normal.update(); MAX_JOB_COUNTS_SCREEN_OFF.moderate.update(); MAX_JOB_COUNTS_SCREEN_OFF.low.update(); MAX_JOB_COUNTS_SCREEN_OFF.critical.update(); } private void updateBackoffConstantsLocked() { private void updateBackoffConstantsLocked() { MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER, MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_LINEAR_BACKOFF_TIME_MS, KEY_MIN_LINEAR_BACKOFF_TIME_MS, Loading Loading @@ -747,16 +578,6 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, ""); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); Loading @@ -781,9 +602,6 @@ public class JobSchedulerService extends com.android.server.SystemService proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON); MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF); proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS); proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); Loading
apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +20 −12 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.job; package com.android.server.job; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; Loading Loading @@ -124,9 +125,12 @@ public final class JobServiceContext implements ServiceConnection { * * * Any reads (dereferences) not done from the handler thread must be synchronized on * Any reads (dereferences) not done from the handler thread must be synchronized on * {@link #mLock}. * {@link #mLock}. * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}. * Writes can only be done from the handler thread, * or {@link #executeRunnableJob(JobStatus, int)}. */ */ private JobStatus mRunningJob; private JobStatus mRunningJob; @JobConcurrencyManager.WorkType private int mRunningJobWorkType; private JobCallback mRunningCallback; private JobCallback mRunningCallback; /** Used to store next job to run when current job is to be preempted. */ /** Used to store next job to run when current job is to be preempted. */ private int mPreferredUid; private int mPreferredUid; Loading Loading @@ -181,30 +185,26 @@ public final class JobServiceContext implements ServiceConnection { JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) { JobPackageTracker tracker, Looper looper) { this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper); mContext = service.getContext(); } mLock = service.getLock(); @VisibleForTesting JobServiceContext(Context context, Object lock, IBatteryStats batteryStats, JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) { mContext = context; mLock = lock; mBatteryStats = batteryStats; mBatteryStats = batteryStats; mJobPackageTracker = tracker; mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); mCallbackHandler = new JobServiceHandler(looper); mCompletedListener = completedListener; mCompletedListener = service; mAvailable = true; mAvailable = true; mVerb = VERB_FINISHED; mVerb = VERB_FINISHED; mPreferredUid = NO_PREFERRED_UID; mPreferredUid = NO_PREFERRED_UID; } } /** /** * Give a job to this context for execution. Callers must first check {@link #getRunningJobLocked()} * Give a job to this context for execution. Callers must first check {@link * #getRunningJobLocked()} * and ensure it is null to make sure this is a valid context. * and ensure it is null to make sure this is a valid context. * * @param job The status of the job that we are going to run. * @param job The status of the job that we are going to run. * @return True if the job is valid and is running. False if the job cannot be executed. * @return True if the job is valid and is running. False if the job cannot be executed. */ */ boolean executeRunnableJob(JobStatus job) { boolean executeRunnableJob(JobStatus job, @JobConcurrencyManager.WorkType int workType) { synchronized (mLock) { synchronized (mLock) { if (!mAvailable) { if (!mAvailable) { Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); Loading @@ -214,6 +214,7 @@ public final class JobServiceContext implements ServiceConnection { mPreferredUid = NO_PREFERRED_UID; mPreferredUid = NO_PREFERRED_UID; mRunningJob = job; mRunningJob = job; mRunningJobWorkType = workType; mRunningCallback = new JobCallback(); mRunningCallback = new JobCallback(); final boolean isDeadlineExpired = final boolean isDeadlineExpired = job.hasDeadlineConstraint() && job.hasDeadlineConstraint() && Loading Loading @@ -282,6 +283,7 @@ public final class JobServiceContext implements ServiceConnection { Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable."); Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable."); } } mRunningJob = null; mRunningJob = null; mRunningJobWorkType = WORK_TYPE_NONE; mRunningCallback = null; mRunningCallback = null; mParams = null; mParams = null; mExecutionStartTimeElapsed = 0L; mExecutionStartTimeElapsed = 0L; Loading Loading @@ -326,6 +328,11 @@ public final class JobServiceContext implements ServiceConnection { return mRunningJob; return mRunningJob; } } @JobConcurrencyManager.WorkType int getRunningJobWorkType() { return mRunningJobWorkType; } /** /** * Used only for debugging. Will return <code>"<null>"</code> if there is no job running. * Used only for debugging. Will return <code>"<null>"</code> if there is no job running. */ */ Loading Loading @@ -831,6 +838,7 @@ public final class JobServiceContext implements ServiceConnection { mContext.unbindService(JobServiceContext.this); mContext.unbindService(JobServiceContext.this); mWakeLock = null; mWakeLock = null; mRunningJob = null; mRunningJob = null; mRunningJobWorkType = WORK_TYPE_NONE; mRunningCallback = null; mRunningCallback = null; mParams = null; mParams = null; mVerb = VERB_FINISHED; mVerb = VERB_FINISHED; Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +1 −0 Original line number Original line Diff line number Diff line Loading @@ -305,6 +305,7 @@ public final class JobStatus { public Network network; public Network network; public ServiceInfo serviceInfo; public ServiceInfo serviceInfo; /** The evaluated priority of the job when it started running. */ public int lastEvaluatedPriority; public int lastEvaluatedPriority; // If non-null, this is work that has been enqueued for the job. // If non-null, this is work that has been enqueued for the job. Loading
services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java→services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java +44 −22 Original line number Original line Diff line number Diff line Loading @@ -16,36 +16,42 @@ package com.android.server.job; package com.android.server.job; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat; import android.util.Log; import android.util.Log; import android.util.Pair; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.job.JobConcurrencyManager.JobCountTracker; import com.android.server.job.JobConcurrencyManager.WorkCountTracker; import org.junit.Before; import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import java.util.List; import java.util.Random; import java.util.Random; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; /** /** * Test for {@link com.android.server.job.JobConcurrencyManager.JobCountTracker}. * Test for {@link WorkCountTracker}. */ */ @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) @MediumTest @MediumTest public class JobCountTrackerTest { public class WorkCountTrackerTest { private static final String TAG = "JobCountTrackerTest"; private static final String TAG = "WorkerCountTrackerTest"; private Random mRandom; private Random mRandom; private JobCountTracker mJobCountTracker; private WorkCountTracker mWorkCountTracker; @Before @Before public void setUp() { public void setUp() { mRandom = new Random(1); // Always use the same series of pseudo random values. mRandom = new Random(1); // Always use the same series of pseudo random values. mJobCountTracker = new JobCountTracker(); mWorkCountTracker = new WorkCountTracker(); } } /** /** Loading Loading @@ -83,44 +89,57 @@ public class JobCountTrackerTest { private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) { private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) { mJobCountTracker.reset(totalMax, maxBg, minBg); mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig("critical", totalMax, // defaultMin List.of(Pair.create(WORK_TYPE_TOP, totalMax - maxBg), Pair.create(WORK_TYPE_BG, minBg)), // defaultMax List.of(Pair.create(WORK_TYPE_BG, maxBg)))); mWorkCountTracker.resetCounts(); for (int i = 0; i < jobs.runningFg; i++) { for (int i = 0; i < jobs.runningFg; i++) { mJobCountTracker.incrementRunningJobCount(true); mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_TOP); } } for (int i = 0; i < jobs.runningBg; i++) { for (int i = 0; i < jobs.runningBg; i++) { mJobCountTracker.incrementRunningJobCount(false); mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_BG); } } for (int i = 0; i < jobs.pendingFg; i++) { for (int i = 0; i < jobs.pendingFg; i++) { mJobCountTracker.incrementPendingJobCount(true); mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_TOP); } } for (int i = 0; i < jobs.pendingBg; i++) { for (int i = 0; i < jobs.pendingBg; i++) { mJobCountTracker.incrementPendingJobCount(false); mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_BG); } } mJobCountTracker.onCountDone(); mWorkCountTracker.onCountDone(); while ((jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) while ((jobs.pendingFg > 0 || (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false))) { && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) || (jobs.pendingBg > 0 && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) { final boolean isStartingFg = mRandom.nextBoolean(); final boolean isStartingFg = mRandom.nextBoolean(); if (isStartingFg) { if (isStartingFg) { if (jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) { if (jobs.pendingFg > 0 && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) { jobs.pendingFg--; jobs.pendingFg--; jobs.runningFg++; jobs.runningFg++; mJobCountTracker.onStartingNewJob(true); mWorkCountTracker.stageJob(WORK_TYPE_TOP); mWorkCountTracker.onJobStarted(WORK_TYPE_TOP); } } } else { } else { if (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false)) { if (jobs.pendingBg > 0 && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) { jobs.pendingBg--; jobs.pendingBg--; jobs.runningBg++; jobs.runningBg++; mJobCountTracker.onStartingNewJob(false); mWorkCountTracker.stageJob(WORK_TYPE_BG); mWorkCountTracker.onJobStarted(WORK_TYPE_BG); } } } } } } Log.i(TAG, "" + mJobCountTracker); Log.i(TAG, "" + mWorkCountTracker); } } /** /** Loading Loading @@ -277,6 +296,7 @@ public class JobCountTrackerTest { startPendingJobs(jobs, totalMax, maxBg, minBg); startPendingJobs(jobs, totalMax, maxBg, minBg); // fail(mWorkerCountTracker.toString()); assertThat(jobs.runningFg).isEqualTo(resultRunningFg); assertThat(jobs.runningFg).isEqualTo(resultRunningFg); assertThat(jobs.runningBg).isEqualTo(resultRunningBg); assertThat(jobs.runningBg).isEqualTo(resultRunningBg); Loading @@ -300,6 +320,8 @@ public class JobCountTrackerTest { checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0); checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0); checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1); checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1); checkSimple(8, 6, 2, /*run=*/ 0, 0, /*pen=*/ 0, 49, /*res run/pen=*/ 0, 6, 0, 43); checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3); checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3); } } } }