From 714f97d1b679fa82a94e2037c3beb8d182b0d3cf Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Wed, 5 Dec 2018 11:18:13 -0800 Subject: JobScheduler: Move the concurrency calculation to a separate class Bug: 111360323 Test: atest CtsJobSchedulerTestCases Summary ------- CtsJobSchedulerTestCases: Passed: 28, Failed: 5 5 tests failed -------------- android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_metered android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withMobile android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withWifi android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintExecutes_withWifi android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintFails_withMobile * Connectivity test failures are expected. Change-Id: I0cf7553dc846b310887da6ae48abd5c19d25b4b4 --- .../android/server/job/JobConcurrencyManager.java | 251 +++++++++++++++++++++ .../android/server/job/JobSchedulerService.java | 209 +---------------- 2 files changed, 258 insertions(+), 202 deletions(-) create mode 100644 services/core/java/com/android/server/job/JobConcurrencyManager.java diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java new file mode 100644 index 000000000000..ee1d8476c471 --- /dev/null +++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.job; + +import android.app.ActivityManager; +import android.app.job.JobInfo; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.app.procstats.ProcessStats; +import com.android.server.job.controllers.JobStatus; +import com.android.server.job.controllers.StateController; + +import java.util.Iterator; +import java.util.List; + +class JobConcurrencyManager { + private static final String TAG = JobSchedulerService.TAG; + private static final boolean DEBUG = JobSchedulerService.DEBUG; + + private final Object mLock; + private final JobSchedulerService mService; + private final JobSchedulerService.Constants mConstants; + + private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; + + /** + * This array essentially stores the state of mActiveServices array. + * The ith index stores the job present on the ith JobServiceContext. + * We manipulate this array until we arrive at what jobs should be running on + * what JobServiceContext. + */ + JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; + + /** + * Indicates whether we need to act on this jobContext id + */ + boolean[] mTmpAssignAct = new boolean[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) { + mService = service; + mLock = mService.mLock; + mConstants = service.mConstants; + } + + /** + * Takes jobs from pending queue and runs them on available contexts. + * If no contexts are available, preempts lower priority jobs to + * run higher priority ones. + * Lock on mJobs before calling this function. + */ + void assignJobsToContextsLocked() { + if (DEBUG) { + Slog.d(TAG, printPendingQueueLocked()); + } + + final JobPackageTracker tracker = mService.mJobPackageTracker; + final List pendingJobs = mService.mPendingJobs; + final List activeServices = mService.mActiveServices; + final List controllers = mService.mControllers; + + int memLevel; + try { + memLevel = ActivityManager.getService().getMemoryTrimLevel(); + } catch (RemoteException e) { + memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; + } + switch (memLevel) { + case ProcessStats.ADJ_MEM_FACTOR_MODERATE: + mService.mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT; + break; + case ProcessStats.ADJ_MEM_FACTOR_LOW: + mService.mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT; + break; + case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: + mService.mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT; + break; + default: + mService.mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT; + break; + } + + JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; + boolean[] act = mTmpAssignAct; + int[] preferredUidForContext = mTmpAssignPreferredUidForContext; + int numActive = 0; + int numForeground = 0; + for (int i=0; i= JobInfo.PRIORITY_TOP_APP) { + numForeground++; + } + } + act[i] = false; + preferredUidForContext[i] = js.getPreferredUid(); + } + if (DEBUG) { + Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); + } + for (int i=0; i= JobInfo.PRIORITY_TOP_APP && + numForeground < mConstants.FG_JOB_COUNT)) && + (preferredUid == nextPending.getUid() || + preferredUid == JobServiceContext.NO_PREFERRED_UID)) { + // This slot is free, and we haven't yet hit the limit on + // concurrent jobs... we can just throw the job in to here. + minPriorityContextId = j; + break; + } + // No job on this context, but nextPending can't run here because + // the context has a preferred Uid or we have reached the limit on + // concurrent jobs. + continue; + } + if (job.getUid() != nextPending.getUid()) { + continue; + } + if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { + continue; + } + if (minPriority > nextPending.lastEvaluatedPriority) { + minPriority = nextPending.lastEvaluatedPriority; + minPriorityContextId = j; + } + } + if (minPriorityContextId != -1) { + contextIdToJobMap[minPriorityContextId] = nextPending; + act[minPriorityContextId] = true; + numActive++; + if (priority >= JobInfo.PRIORITY_TOP_APP) { + numForeground++; + } + } + } + if (DEBUG) { + Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); + } + tracker.noteConcurrency(numActive, numForeground); + for (int i=0; i it = mService.mPendingJobs.iterator(); + while (it.hasNext()) { + JobStatus js = it.next(); + s.append("(") + .append(js.getJob().getId()) + .append(", ") + .append(js.getUid()) + .append(") "); + } + return s.toString(); + } + + private String printContextIdToJobMap(JobStatus[] map, String initial) { + StringBuilder s = new StringBuilder(initial + ": "); + for (int i=0; i mActiveServices = new ArrayList<>(); /** List of controllers that will notify this service of updates to jobs. */ - private final List mControllers; + final List mControllers; /** Need direct access to this for testing. */ private final BatteryController mBatteryController; /** Need direct access to this for testing. */ @@ -268,22 +267,6 @@ public class JobSchedulerService extends com.android.server.SystemService // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked -- - /** - * This array essentially stores the state of mActiveServices array. - * The ith index stores the job present on the ith JobServiceContext. - * We manipulate this array until we arrive at what jobs should be running on - * what JobServiceContext. - */ - JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; - /** - * Indicates whether we need to act on this jobContext id - */ - boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT]; - /** - * The uid whose jobs we would like to assign to a context. - */ - int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; - private class ConstantsObserver extends ContentObserver { private ContentResolver mResolver; @@ -1267,6 +1250,8 @@ public class JobSchedulerService extends com.android.server.SystemService mConstantsObserver = new ConstantsObserver(mHandler); mJobSchedulerStub = new JobSchedulerStub(); + mConcurrencyManager = new JobConcurrencyManager(this); + // Set up the app standby bucketing tracker mStandbyTracker = new StandbyTracker(); mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); @@ -2193,7 +2178,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs."); } - assignJobsToContextsLocked(); + mConcurrencyManager.assignJobsToContextsLocked(); reportActiveLocked(); } @@ -2209,7 +2194,7 @@ public class JobSchedulerService extends com.android.server.SystemService return curPriority; } - private int evaluateJobPriorityLocked(JobStatus job) { + int evaluateJobPriorityLocked(JobStatus job) { int priority = job.getPriority(); if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) { return adjustJobPriority(priority, job); @@ -2221,161 +2206,6 @@ public class JobSchedulerService extends com.android.server.SystemService return adjustJobPriority(priority, job); } - /** - * Takes jobs from pending queue and runs them on available contexts. - * If no contexts are available, preempts lower priority jobs to - * run higher priority ones. - * Lock on mJobs before calling this function. - */ - private void assignJobsToContextsLocked() { - if (DEBUG) { - Slog.d(TAG, printPendingQueue()); - } - - int memLevel; - try { - memLevel = ActivityManager.getService().getMemoryTrimLevel(); - } catch (RemoteException e) { - memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; - } - switch (memLevel) { - case ProcessStats.ADJ_MEM_FACTOR_MODERATE: - mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT; - break; - case ProcessStats.ADJ_MEM_FACTOR_LOW: - mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT; - break; - case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: - mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT; - break; - default: - mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT; - break; - } - - JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; - boolean[] act = mTmpAssignAct; - int[] preferredUidForContext = mTmpAssignPreferredUidForContext; - int numActive = 0; - int numForeground = 0; - for (int i=0; i= JobInfo.PRIORITY_TOP_APP) { - numForeground++; - } - } - act[i] = false; - preferredUidForContext[i] = js.getPreferredUid(); - } - if (DEBUG) { - Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); - } - for (int i=0; i= JobInfo.PRIORITY_TOP_APP && - numForeground < mConstants.FG_JOB_COUNT)) && - (preferredUid == nextPending.getUid() || - preferredUid == JobServiceContext.NO_PREFERRED_UID)) { - // This slot is free, and we haven't yet hit the limit on - // concurrent jobs... we can just throw the job in to here. - minPriorityContextId = j; - break; - } - // No job on this context, but nextPending can't run here because - // the context has a preferred Uid or we have reached the limit on - // concurrent jobs. - continue; - } - if (job.getUid() != nextPending.getUid()) { - continue; - } - if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { - continue; - } - if (minPriority > nextPending.lastEvaluatedPriority) { - minPriority = nextPending.lastEvaluatedPriority; - minPriorityContextId = j; - } - } - if (minPriorityContextId != -1) { - contextIdToJobMap[minPriorityContextId] = nextPending; - act[minPriorityContextId] = true; - numActive++; - if (priority >= JobInfo.PRIORITY_TOP_APP) { - numForeground++; - } - } - } - if (DEBUG) { - Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); - } - mJobPackageTracker.noteConcurrency(numActive, numForeground); - for (int i=0; i it = mPendingJobs.iterator(); - while (it.hasNext()) { - JobStatus js = it.next(); - s.append("(") - .append(js.getJob().getId()) - .append(", ") - .append(js.getUid()) - .append(") "); - } - return s.toString(); - } - static void dumpHelp(PrintWriter pw) { pw.println("Job Scheduler (jobscheduler) dump options:"); pw.println(" [-h] [package] ..."); -- cgit v1.2.3-59-g8ed1b