Loading services/core/java/com/android/server/job/JobConcurrencyManager.java +53 −39 Original line number Original line Diff line number Diff line Loading @@ -44,17 +44,11 @@ class JobConcurrencyManager { * We manipulate this array until we arrive at what jobs should be running on * We manipulate this array until we arrive at what jobs should be running on * what JobServiceContext. * what JobServiceContext. */ */ JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; /** boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT]; * Indicates whether we need to act on this jobContext id */ boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT]; /** int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; * The uid whose jobs we would like to assign to a context. */ int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; JobConcurrencyManager(JobSchedulerService service) { JobConcurrencyManager(JobSchedulerService service) { mService = service; mService = service; Loading Loading @@ -99,28 +93,30 @@ class JobConcurrencyManager { break; break; } } JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; // To avoid GC churn, we recycle the arrays. boolean[] act = mTmpAssignAct; JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap; int[] preferredUidForContext = mTmpAssignPreferredUidForContext; boolean[] slotChanged = mRecycledSlotChanged; int numActive = 0; int[] preferredUidForContext = mRecycledPreferredUidForContext; int numForeground = 0; int numTotalRunningJobs = 0; int numForegroundJobs = 0; for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { final JobServiceContext js = mService.mActiveServices.get(i); final JobServiceContext js = mService.mActiveServices.get(i); final JobStatus status = js.getRunningJobLocked(); final JobStatus status = js.getRunningJobLocked(); if ((contextIdToJobMap[i] = status) != null) { if ((contextIdToJobMap[i] = status) != null) { numActive++; numTotalRunningJobs++; if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { numForeground++; numForegroundJobs++; } } } } act[i] = false; slotChanged[i] = false; preferredUidForContext[i] = js.getPreferredUid(); preferredUidForContext[i] = js.getPreferredUid(); } } if (DEBUG) { if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); } } for (int i=0; i<pendingJobs.size(); i++) { for (int i=0; i<pendingJobs.size(); i++) { JobStatus nextPending = pendingJobs.get(i); final JobStatus nextPending = pendingJobs.get(i); // If job is already running, go to next job. // If job is already running, go to next job. int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); Loading @@ -131,23 +127,32 @@ class JobConcurrencyManager { final int priority = mService.evaluateJobPriorityLocked(nextPending); final int priority = mService.evaluateJobPriorityLocked(nextPending); nextPending.lastEvaluatedPriority = priority; nextPending.lastEvaluatedPriority = priority; // Find a context for nextPending. The context should be available OR // Find an available slot for nextPending. The context should be available OR // it should have lowest priority among all running jobs // it should have lowest priority among all running jobs // (sharing the same Uid as nextPending) // (sharing the same Uid as nextPending) int minPriority = Integer.MAX_VALUE; int minPriorityForPreemption = Integer.MAX_VALUE; int minPriorityContextId = -1; int selectedContextId = -1; boolean startingJob = false; for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { JobStatus job = contextIdToJobMap[j]; JobStatus job = contextIdToJobMap[j]; int preferredUid = preferredUidForContext[j]; int preferredUid = preferredUidForContext[j]; if (job == null) { if (job == null) { if ((numActive < mService.mMaxActiveJobs || final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs; (priority >= JobInfo.PRIORITY_TOP_APP && final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP) numForeground < mConstants.FG_JOB_COUNT)) && && (numForegroundJobs < mConstants.FG_JOB_COUNT); (preferredUid == nextPending.getUid() || final boolean preferredUidOkay = (preferredUid == nextPending.getUid()) preferredUid == JobServiceContext.NO_PREFERRED_UID)) { || (preferredUid == JobServiceContext.NO_PREFERRED_UID); // TODO: The following check is slightly wrong. // Depending on how the pending jobs are sorted, we sometimes cap the total // job count at mMaxActiveJobs (when all jobs are FG jobs), or // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs // and then FG_JOB_COUNT FG jobs.) if ((totalCountOk || fgCountOk) && preferredUidOkay) { // This slot is free, and we haven't yet hit the limit on // This slot is free, and we haven't yet hit the limit on // concurrent jobs... we can just throw the job in to here. // concurrent jobs... we can just throw the job in to here. minPriorityContextId = j; selectedContextId = j; startingJob = true; break; break; } } // No job on this context, but nextPending can't run here because // No job on this context, but nextPending can't run here because Loading @@ -158,30 +163,39 @@ class JobConcurrencyManager { if (job.getUid() != nextPending.getUid()) { if (job.getUid() != nextPending.getUid()) { continue; continue; } } if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { final int jobPriority = mService.evaluateJobPriorityLocked(job); if (jobPriority >= nextPending.lastEvaluatedPriority) { continue; continue; } } if (minPriority > nextPending.lastEvaluatedPriority) { minPriority = nextPending.lastEvaluatedPriority; // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it) minPriorityContextId = j; if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) { minPriorityForPreemption = nextPending.lastEvaluatedPriority; selectedContextId = j; // In this case, we're just going to preempt a low priority job, we're not // actually starting a job, so don't set startingJob. } } } if (selectedContextId != -1) { contextIdToJobMap[selectedContextId] = nextPending; slotChanged[selectedContextId] = true; } } if (minPriorityContextId != -1) { if (startingJob) { contextIdToJobMap[minPriorityContextId] = nextPending; // Increase the counters when we're going to start a job. act[minPriorityContextId] = true; numTotalRunningJobs++; numActive++; if (priority >= JobInfo.PRIORITY_TOP_APP) { if (priority >= JobInfo.PRIORITY_TOP_APP) { numForeground++; numForegroundJobs++; } } } } } } if (DEBUG) { if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); } } tracker.noteConcurrency(numActive, numForeground); tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs); for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { boolean preservePreferredUid = false; boolean preservePreferredUid = false; if (act[i]) { if (slotChanged[i]) { JobStatus js = activeServices.get(i).getRunningJobLocked(); JobStatus js = activeServices.get(i).getRunningJobLocked(); if (js != null) { if (js != null) { if (DEBUG) { if (DEBUG) { Loading @@ -195,7 +209,7 @@ class JobConcurrencyManager { final JobStatus pendingJob = contextIdToJobMap[i]; final JobStatus pendingJob = contextIdToJobMap[i]; if (DEBUG) { if (DEBUG) { Slog.d(TAG, "About to run job on context " Slog.d(TAG, "About to run job on context " + String.valueOf(i) + ", job: " + pendingJob); + i + ", job: " + pendingJob); } } for (int ic=0; ic<controllers.size(); ic++) { for (int ic=0; ic<controllers.size(); ic++) { controllers.get(ic).prepareForExecutionLocked(pendingJob); controllers.get(ic).prepareForExecutionLocked(pendingJob); Loading services/core/java/com/android/server/job/JobSchedulerService.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -1261,6 +1261,9 @@ public class JobSchedulerService extends com.android.server.SystemService @Override @Override public void onDeviceIdleStateChanged(boolean deviceIdle) { public void onDeviceIdleStateChanged(boolean deviceIdle) { synchronized (mLock) { synchronized (mLock) { if (DEBUG) { Slog.d(TAG, "Doze state changed: " + deviceIdle); } if (deviceIdle) { if (deviceIdle) { // When becoming idle, make sure no jobs are actively running, // When becoming idle, make sure no jobs are actively running, // except those using the idle exemption flag. // except those using the idle exemption flag. Loading Loading @@ -1829,6 +1832,9 @@ public class JobSchedulerService extends com.android.server.SystemService } } } break; } break; case MSG_CHECK_JOB: case MSG_CHECK_JOB: if (DEBUG) { Slog.d(TAG, "MSG_CHECK_JOB"); } if (mReportedActive) { if (mReportedActive) { // if jobs are currently being run, queue all ready jobs for execution. // if jobs are currently being run, queue all ready jobs for execution. queueReadyJobsForExecutionLocked(); queueReadyJobsForExecutionLocked(); Loading @@ -1838,6 +1844,9 @@ public class JobSchedulerService extends com.android.server.SystemService } } break; break; case MSG_CHECK_JOB_GREEDY: case MSG_CHECK_JOB_GREEDY: if (DEBUG) { Slog.d(TAG, "MSG_CHECK_JOB_GREEDY"); } queueReadyJobsForExecutionLocked(); queueReadyJobsForExecutionLocked(); break; break; case MSG_STOP_JOB: case MSG_STOP_JOB: Loading Loading
services/core/java/com/android/server/job/JobConcurrencyManager.java +53 −39 Original line number Original line Diff line number Diff line Loading @@ -44,17 +44,11 @@ class JobConcurrencyManager { * We manipulate this array until we arrive at what jobs should be running on * We manipulate this array until we arrive at what jobs should be running on * what JobServiceContext. * what JobServiceContext. */ */ JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; /** boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT]; * Indicates whether we need to act on this jobContext id */ boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT]; /** int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; * The uid whose jobs we would like to assign to a context. */ int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; JobConcurrencyManager(JobSchedulerService service) { JobConcurrencyManager(JobSchedulerService service) { mService = service; mService = service; Loading Loading @@ -99,28 +93,30 @@ class JobConcurrencyManager { break; break; } } JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; // To avoid GC churn, we recycle the arrays. boolean[] act = mTmpAssignAct; JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap; int[] preferredUidForContext = mTmpAssignPreferredUidForContext; boolean[] slotChanged = mRecycledSlotChanged; int numActive = 0; int[] preferredUidForContext = mRecycledPreferredUidForContext; int numForeground = 0; int numTotalRunningJobs = 0; int numForegroundJobs = 0; for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { final JobServiceContext js = mService.mActiveServices.get(i); final JobServiceContext js = mService.mActiveServices.get(i); final JobStatus status = js.getRunningJobLocked(); final JobStatus status = js.getRunningJobLocked(); if ((contextIdToJobMap[i] = status) != null) { if ((contextIdToJobMap[i] = status) != null) { numActive++; numTotalRunningJobs++; if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { numForeground++; numForegroundJobs++; } } } } act[i] = false; slotChanged[i] = false; preferredUidForContext[i] = js.getPreferredUid(); preferredUidForContext[i] = js.getPreferredUid(); } } if (DEBUG) { if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); } } for (int i=0; i<pendingJobs.size(); i++) { for (int i=0; i<pendingJobs.size(); i++) { JobStatus nextPending = pendingJobs.get(i); final JobStatus nextPending = pendingJobs.get(i); // If job is already running, go to next job. // If job is already running, go to next job. int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); Loading @@ -131,23 +127,32 @@ class JobConcurrencyManager { final int priority = mService.evaluateJobPriorityLocked(nextPending); final int priority = mService.evaluateJobPriorityLocked(nextPending); nextPending.lastEvaluatedPriority = priority; nextPending.lastEvaluatedPriority = priority; // Find a context for nextPending. The context should be available OR // Find an available slot for nextPending. The context should be available OR // it should have lowest priority among all running jobs // it should have lowest priority among all running jobs // (sharing the same Uid as nextPending) // (sharing the same Uid as nextPending) int minPriority = Integer.MAX_VALUE; int minPriorityForPreemption = Integer.MAX_VALUE; int minPriorityContextId = -1; int selectedContextId = -1; boolean startingJob = false; for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { JobStatus job = contextIdToJobMap[j]; JobStatus job = contextIdToJobMap[j]; int preferredUid = preferredUidForContext[j]; int preferredUid = preferredUidForContext[j]; if (job == null) { if (job == null) { if ((numActive < mService.mMaxActiveJobs || final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs; (priority >= JobInfo.PRIORITY_TOP_APP && final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP) numForeground < mConstants.FG_JOB_COUNT)) && && (numForegroundJobs < mConstants.FG_JOB_COUNT); (preferredUid == nextPending.getUid() || final boolean preferredUidOkay = (preferredUid == nextPending.getUid()) preferredUid == JobServiceContext.NO_PREFERRED_UID)) { || (preferredUid == JobServiceContext.NO_PREFERRED_UID); // TODO: The following check is slightly wrong. // Depending on how the pending jobs are sorted, we sometimes cap the total // job count at mMaxActiveJobs (when all jobs are FG jobs), or // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs // and then FG_JOB_COUNT FG jobs.) if ((totalCountOk || fgCountOk) && preferredUidOkay) { // This slot is free, and we haven't yet hit the limit on // This slot is free, and we haven't yet hit the limit on // concurrent jobs... we can just throw the job in to here. // concurrent jobs... we can just throw the job in to here. minPriorityContextId = j; selectedContextId = j; startingJob = true; break; break; } } // No job on this context, but nextPending can't run here because // No job on this context, but nextPending can't run here because Loading @@ -158,30 +163,39 @@ class JobConcurrencyManager { if (job.getUid() != nextPending.getUid()) { if (job.getUid() != nextPending.getUid()) { continue; continue; } } if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { final int jobPriority = mService.evaluateJobPriorityLocked(job); if (jobPriority >= nextPending.lastEvaluatedPriority) { continue; continue; } } if (minPriority > nextPending.lastEvaluatedPriority) { minPriority = nextPending.lastEvaluatedPriority; // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it) minPriorityContextId = j; if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) { minPriorityForPreemption = nextPending.lastEvaluatedPriority; selectedContextId = j; // In this case, we're just going to preempt a low priority job, we're not // actually starting a job, so don't set startingJob. } } } if (selectedContextId != -1) { contextIdToJobMap[selectedContextId] = nextPending; slotChanged[selectedContextId] = true; } } if (minPriorityContextId != -1) { if (startingJob) { contextIdToJobMap[minPriorityContextId] = nextPending; // Increase the counters when we're going to start a job. act[minPriorityContextId] = true; numTotalRunningJobs++; numActive++; if (priority >= JobInfo.PRIORITY_TOP_APP) { if (priority >= JobInfo.PRIORITY_TOP_APP) { numForeground++; numForegroundJobs++; } } } } } } if (DEBUG) { if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); } } tracker.noteConcurrency(numActive, numForeground); tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs); for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { boolean preservePreferredUid = false; boolean preservePreferredUid = false; if (act[i]) { if (slotChanged[i]) { JobStatus js = activeServices.get(i).getRunningJobLocked(); JobStatus js = activeServices.get(i).getRunningJobLocked(); if (js != null) { if (js != null) { if (DEBUG) { if (DEBUG) { Loading @@ -195,7 +209,7 @@ class JobConcurrencyManager { final JobStatus pendingJob = contextIdToJobMap[i]; final JobStatus pendingJob = contextIdToJobMap[i]; if (DEBUG) { if (DEBUG) { Slog.d(TAG, "About to run job on context " Slog.d(TAG, "About to run job on context " + String.valueOf(i) + ", job: " + pendingJob); + i + ", job: " + pendingJob); } } for (int ic=0; ic<controllers.size(); ic++) { for (int ic=0; ic<controllers.size(); ic++) { controllers.get(ic).prepareForExecutionLocked(pendingJob); controllers.get(ic).prepareForExecutionLocked(pendingJob); Loading
services/core/java/com/android/server/job/JobSchedulerService.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -1261,6 +1261,9 @@ public class JobSchedulerService extends com.android.server.SystemService @Override @Override public void onDeviceIdleStateChanged(boolean deviceIdle) { public void onDeviceIdleStateChanged(boolean deviceIdle) { synchronized (mLock) { synchronized (mLock) { if (DEBUG) { Slog.d(TAG, "Doze state changed: " + deviceIdle); } if (deviceIdle) { if (deviceIdle) { // When becoming idle, make sure no jobs are actively running, // When becoming idle, make sure no jobs are actively running, // except those using the idle exemption flag. // except those using the idle exemption flag. Loading Loading @@ -1829,6 +1832,9 @@ public class JobSchedulerService extends com.android.server.SystemService } } } break; } break; case MSG_CHECK_JOB: case MSG_CHECK_JOB: if (DEBUG) { Slog.d(TAG, "MSG_CHECK_JOB"); } if (mReportedActive) { if (mReportedActive) { // if jobs are currently being run, queue all ready jobs for execution. // if jobs are currently being run, queue all ready jobs for execution. queueReadyJobsForExecutionLocked(); queueReadyJobsForExecutionLocked(); Loading @@ -1838,6 +1844,9 @@ public class JobSchedulerService extends com.android.server.SystemService } } break; break; case MSG_CHECK_JOB_GREEDY: case MSG_CHECK_JOB_GREEDY: if (DEBUG) { Slog.d(TAG, "MSG_CHECK_JOB_GREEDY"); } queueReadyJobsForExecutionLocked(); queueReadyJobsForExecutionLocked(); break; break; case MSG_STOP_JOB: case MSG_STOP_JOB: Loading