Merge "Introduce concept of work types." into sc-dev am: dd56fa93f6
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13431814 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Ib9983cbd420cf856014b62ec76160255baff0dad
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.server.job;
|
package com.android.server.job;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.job.JobInfo;
|
import android.app.job.JobInfo;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
@@ -26,8 +28,11 @@ import android.os.Handler;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
import android.util.ArraySet;
|
||||||
import android.util.IndentingPrintWriter;
|
import android.util.IndentingPrintWriter;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
import android.util.TimeUtils;
|
import android.util.TimeUtils;
|
||||||
import android.util.proto.ProtoOutputStream;
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
|
||||||
@@ -36,16 +41,17 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||||||
import com.android.internal.app.procstats.ProcessStats;
|
import com.android.internal.app.procstats.ProcessStats;
|
||||||
import com.android.internal.util.StatLogger;
|
import com.android.internal.util.StatLogger;
|
||||||
import com.android.server.JobSchedulerBackgroundThread;
|
import com.android.server.JobSchedulerBackgroundThread;
|
||||||
import com.android.server.job.JobSchedulerService.MaxJobCountsPerMemoryTrimLevel;
|
|
||||||
import com.android.server.job.controllers.JobStatus;
|
import com.android.server.job.controllers.JobStatus;
|
||||||
import com.android.server.job.controllers.StateController;
|
import com.android.server.job.controllers.StateController;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class decides, given the various configuration and the system status, how many more jobs
|
* This class decides, given the various configuration and the system status, which jobs can start
|
||||||
* can start.
|
* and which {@link JobServiceContext} to run each job on.
|
||||||
*/
|
*/
|
||||||
class JobConcurrencyManager {
|
class JobConcurrencyManager {
|
||||||
private static final String TAG = JobSchedulerService.TAG;
|
private static final String TAG = JobSchedulerService.TAG;
|
||||||
@@ -56,9 +62,22 @@ class JobConcurrencyManager {
|
|||||||
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;
|
||||||
|
|
||||||
|
static final int WORK_TYPE_NONE = 0;
|
||||||
|
static final int WORK_TYPE_TOP = 1 << 0;
|
||||||
|
static final int WORK_TYPE_BG = 1 << 1;
|
||||||
|
private static final int NUM_WORK_TYPES = 2;
|
||||||
|
|
||||||
|
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
|
||||||
|
WORK_TYPE_NONE,
|
||||||
|
WORK_TYPE_TOP,
|
||||||
|
WORK_TYPE_BG
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface WorkType {
|
||||||
|
}
|
||||||
|
|
||||||
private final Object mLock;
|
private final Object mLock;
|
||||||
private final JobSchedulerService mService;
|
private final JobSchedulerService mService;
|
||||||
private final JobSchedulerService.Constants mConstants;
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
|
|
||||||
@@ -72,6 +91,53 @@ class JobConcurrencyManager {
|
|||||||
|
|
||||||
private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
|
private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
|
||||||
|
|
||||||
|
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
|
||||||
|
new WorkConfigLimitsPerMemoryTrimLevel(
|
||||||
|
new WorkTypeConfig("screen_on_normal", 8,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 6))),
|
||||||
|
new WorkTypeConfig("screen_on_moderate", 8,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 4))),
|
||||||
|
new WorkTypeConfig("screen_on_low", 5,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 1))),
|
||||||
|
new WorkTypeConfig("screen_on_critical", 5,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 1)))
|
||||||
|
);
|
||||||
|
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
|
||||||
|
new WorkConfigLimitsPerMemoryTrimLevel(
|
||||||
|
new WorkTypeConfig("screen_off_normal", 10,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 6))),
|
||||||
|
new WorkTypeConfig("screen_off_moderate", 10,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 4))),
|
||||||
|
new WorkTypeConfig("screen_off_low", 5,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 1))),
|
||||||
|
new WorkTypeConfig("screen_off_critical", 5,
|
||||||
|
// defaultMin
|
||||||
|
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, 1)))
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This array essentially stores the state of mActiveServices array.
|
* This array essentially stores the state of mActiveServices array.
|
||||||
* The ith index stores the job present on the ith JobServiceContext.
|
* The ith index stores the job present on the ith JobServiceContext.
|
||||||
@@ -84,10 +150,11 @@ class JobConcurrencyManager {
|
|||||||
|
|
||||||
int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
|
int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
|
||||||
|
|
||||||
/** Max job counts according to the current system state. */
|
int[] mRecycledWorkTypeForContext = new int[MAX_JOB_CONTEXTS_COUNT];
|
||||||
private JobSchedulerService.MaxJobCounts mMaxJobCounts;
|
|
||||||
|
|
||||||
private final JobCountTracker mJobCountTracker = new JobCountTracker();
|
private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
|
||||||
|
|
||||||
|
private final WorkCountTracker mWorkCountTracker = new WorkCountTracker();
|
||||||
|
|
||||||
/** Wait for this long after screen off before adjusting the job concurrency. */
|
/** Wait for this long after screen off before adjusting the job concurrency. */
|
||||||
private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS;
|
private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS;
|
||||||
@@ -114,7 +181,6 @@ class JobConcurrencyManager {
|
|||||||
JobConcurrencyManager(JobSchedulerService service) {
|
JobConcurrencyManager(JobSchedulerService service) {
|
||||||
mService = service;
|
mService = service;
|
||||||
mLock = mService.mLock;
|
mLock = mService.mLock;
|
||||||
mConstants = service.mConstants;
|
|
||||||
mContext = service.getContext();
|
mContext = service.getContext();
|
||||||
|
|
||||||
mHandler = JobSchedulerBackgroundThread.getHandler();
|
mHandler = JobSchedulerBackgroundThread.getHandler();
|
||||||
@@ -209,12 +275,6 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFgJob(JobStatus job) {
|
|
||||||
// (It's super confusing PRIORITY_BOUND_FOREGROUND_SERVICE isn't FG here)
|
|
||||||
return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
|
|
||||||
|| job.shouldTreatAsExpeditedJob();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void refreshSystemStateLocked() {
|
private void refreshSystemStateLocked() {
|
||||||
final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis();
|
final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis();
|
||||||
@@ -237,28 +297,29 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void updateMaxCountsLocked() {
|
private void updateCounterConfigLocked() {
|
||||||
refreshSystemStateLocked();
|
refreshSystemStateLocked();
|
||||||
|
|
||||||
final MaxJobCountsPerMemoryTrimLevel jobCounts = mEffectiveInteractiveState
|
final WorkConfigLimitsPerMemoryTrimLevel workConfigs = mEffectiveInteractiveState
|
||||||
? mConstants.MAX_JOB_COUNTS_SCREEN_ON
|
? CONFIG_LIMITS_SCREEN_ON : CONFIG_LIMITS_SCREEN_OFF;
|
||||||
: mConstants.MAX_JOB_COUNTS_SCREEN_OFF;
|
|
||||||
|
|
||||||
|
|
||||||
|
WorkTypeConfig workTypeConfig;
|
||||||
switch (mLastMemoryTrimLevel) {
|
switch (mLastMemoryTrimLevel) {
|
||||||
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
|
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
|
||||||
mMaxJobCounts = jobCounts.moderate;
|
workTypeConfig = workConfigs.moderate;
|
||||||
break;
|
break;
|
||||||
case ProcessStats.ADJ_MEM_FACTOR_LOW:
|
case ProcessStats.ADJ_MEM_FACTOR_LOW:
|
||||||
mMaxJobCounts = jobCounts.low;
|
workTypeConfig = workConfigs.low;
|
||||||
break;
|
break;
|
||||||
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
|
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
|
||||||
mMaxJobCounts = jobCounts.critical;
|
workTypeConfig = workConfigs.critical;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mMaxJobCounts = jobCounts.normal;
|
workTypeConfig = workConfigs.normal;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mWorkCountTracker.setConfig(workTypeConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -282,31 +343,26 @@ class JobConcurrencyManager {
|
|||||||
Slog.d(TAG, printPendingQueueLocked());
|
Slog.d(TAG, printPendingQueueLocked());
|
||||||
}
|
}
|
||||||
|
|
||||||
final JobPackageTracker tracker = mService.mJobPackageTracker;
|
|
||||||
final List<JobStatus> pendingJobs = mService.mPendingJobs;
|
final List<JobStatus> pendingJobs = mService.mPendingJobs;
|
||||||
final List<JobServiceContext> activeServices = mService.mActiveServices;
|
final List<JobServiceContext> activeServices = mService.mActiveServices;
|
||||||
final List<StateController> controllers = mService.mControllers;
|
|
||||||
|
|
||||||
updateMaxCountsLocked();
|
|
||||||
|
|
||||||
// To avoid GC churn, we recycle the arrays.
|
// To avoid GC churn, we recycle the arrays.
|
||||||
JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
|
JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
|
||||||
boolean[] slotChanged = mRecycledSlotChanged;
|
boolean[] slotChanged = mRecycledSlotChanged;
|
||||||
int[] preferredUidForContext = mRecycledPreferredUidForContext;
|
int[] preferredUidForContext = mRecycledPreferredUidForContext;
|
||||||
|
int[] workTypeForContext = mRecycledWorkTypeForContext;
|
||||||
|
|
||||||
|
updateCounterConfigLocked();
|
||||||
|
// Reset everything since we'll re-evaluate the current state.
|
||||||
|
mWorkCountTracker.resetCounts();
|
||||||
|
|
||||||
// Initialize the work variables and also count running jobs.
|
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
|
||||||
mJobCountTracker.reset(
|
|
||||||
mMaxJobCounts.getMaxTotal(),
|
|
||||||
mMaxJobCounts.getMaxBg(),
|
|
||||||
mMaxJobCounts.getMinBg());
|
|
||||||
|
|
||||||
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) {
|
||||||
mJobCountTracker.incrementRunningJobCount(isFgJob(status));
|
mWorkCountTracker.incrementRunningJobCount(js.getRunningJobWorkType());
|
||||||
|
workTypeForContext[i] = js.getRunningJobWorkType();
|
||||||
}
|
}
|
||||||
|
|
||||||
slotChanged[i] = false;
|
slotChanged[i] = false;
|
||||||
@@ -317,41 +373,25 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next, update the job priorities, and also count the pending FG / BG jobs.
|
// Next, update the job priorities, and also count the pending FG / BG jobs.
|
||||||
for (int i = 0; i < pendingJobs.size(); i++) {
|
updateNonRunningPriorities(pendingJobs, true);
|
||||||
final JobStatus pending = pendingJobs.get(i);
|
|
||||||
|
|
||||||
// If job is already running, go to next job.
|
mWorkCountTracker.onCountDone();
|
||||||
int jobRunningContext = findJobContextIdFromMap(pending, contextIdToJobMap);
|
|
||||||
if (jobRunningContext != -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int priority = mService.evaluateJobPriorityLocked(pending);
|
|
||||||
pending.lastEvaluatedPriority = priority;
|
|
||||||
|
|
||||||
mJobCountTracker.incrementPendingJobCount(isFgJob(pending));
|
|
||||||
}
|
|
||||||
|
|
||||||
mJobCountTracker.onCountDone();
|
|
||||||
|
|
||||||
for (int i = 0; i < pendingJobs.size(); i++) {
|
for (int i = 0; i < pendingJobs.size(); i++) {
|
||||||
final JobStatus nextPending = pendingJobs.get(i);
|
final JobStatus nextPending = pendingJobs.get(i);
|
||||||
|
|
||||||
// Unfortunately we need to repeat this relatively expensive check.
|
if (mRunningJobs.contains(nextPending)) {
|
||||||
int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
|
|
||||||
if (jobRunningContext != -1) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them
|
// TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them
|
||||||
|
|
||||||
final boolean isPendingFg = isFgJob(nextPending);
|
|
||||||
|
|
||||||
// Find an available slot 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 minPriorityForPreemption = Integer.MAX_VALUE;
|
int minPriorityForPreemption = Integer.MAX_VALUE;
|
||||||
int selectedContextId = -1;
|
int selectedContextId = -1;
|
||||||
|
int workType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
|
||||||
boolean startingJob = false;
|
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];
|
||||||
@@ -360,7 +400,7 @@ class JobConcurrencyManager {
|
|||||||
final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
|
final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
|
||||||
|| (preferredUid == JobServiceContext.NO_PREFERRED_UID);
|
|| (preferredUid == JobServiceContext.NO_PREFERRED_UID);
|
||||||
|
|
||||||
if (preferredUidOkay && mJobCountTracker.canJobStart(isPendingFg)) {
|
if (preferredUidOkay && workType != WORK_TYPE_NONE) {
|
||||||
// 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.
|
||||||
selectedContextId = j;
|
selectedContextId = j;
|
||||||
@@ -396,19 +436,19 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
if (startingJob) {
|
if (startingJob) {
|
||||||
// Increase the counters when we're going to start a job.
|
// Increase the counters when we're going to start a job.
|
||||||
mJobCountTracker.onStartingNewJob(isPendingFg);
|
workTypeForContext[selectedContextId] = workType;
|
||||||
|
mWorkCountTracker.stageJob(workType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
|
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
|
||||||
}
|
}
|
||||||
|
|
||||||
mJobCountTracker.logStatus();
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "assignJobsToContexts: " + mWorkCountTracker.toString());
|
||||||
|
}
|
||||||
|
|
||||||
tracker.noteConcurrency(mJobCountTracker.getTotalRunningJobCountToNote(),
|
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
|
||||||
mJobCountTracker.getFgRunningJobCountToNote());
|
|
||||||
|
|
||||||
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
|
|
||||||
boolean preservePreferredUid = false;
|
boolean preservePreferredUid = false;
|
||||||
if (slotChanged[i]) {
|
if (slotChanged[i]) {
|
||||||
JobStatus js = activeServices.get(i).getRunningJobLocked();
|
JobStatus js = activeServices.get(i).getRunningJobLocked();
|
||||||
@@ -426,30 +466,58 @@ class JobConcurrencyManager {
|
|||||||
Slog.d(TAG, "About to run job on context "
|
Slog.d(TAG, "About to run job on context "
|
||||||
+ i + ", job: " + pendingJob);
|
+ i + ", job: " + pendingJob);
|
||||||
}
|
}
|
||||||
for (int ic=0; ic<controllers.size(); ic++) {
|
startJobLocked(activeServices.get(i), pendingJob, workTypeForContext[i]);
|
||||||
controllers.get(ic).prepareForExecutionLocked(pendingJob);
|
|
||||||
}
|
|
||||||
if (!activeServices.get(i).executeRunnableJob(pendingJob)) {
|
|
||||||
Slog.d(TAG, "Error executing " + pendingJob);
|
|
||||||
}
|
|
||||||
if (pendingJobs.remove(pendingJob)) {
|
|
||||||
tracker.noteNonpending(pendingJob);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!preservePreferredUid) {
|
if (!preservePreferredUid) {
|
||||||
activeServices.get(i).clearPreferredUid();
|
activeServices.get(i).clearPreferredUid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mWorkCountTracker.resetStagingCount();
|
||||||
|
noteConcurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
|
private void noteConcurrency() {
|
||||||
for (int i=0; i<map.length; i++) {
|
mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(),
|
||||||
if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
|
// TODO: log per type instead of only TOP
|
||||||
return i;
|
mWorkCountTracker.getRunningJobCount(WORK_TYPE_TOP));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNonRunningPriorities(@NonNull final List<JobStatus> pendingJobs,
|
||||||
|
boolean updateCounter) {
|
||||||
|
for (int i = 0; i < pendingJobs.size(); i++) {
|
||||||
|
final JobStatus pending = pendingJobs.get(i);
|
||||||
|
|
||||||
|
// If job is already running, go to next job.
|
||||||
|
if (mRunningJobs.contains(pending)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending.lastEvaluatedPriority = mService.evaluateJobPriorityLocked(pending);
|
||||||
|
|
||||||
|
if (updateCounter) {
|
||||||
|
mWorkCountTracker.incrementPendingJobCount(getJobWorkTypes(pending));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
|
||||||
|
@WorkType final int workType) {
|
||||||
|
final List<StateController> controllers = mService.mControllers;
|
||||||
|
for (int ic = 0; ic < controllers.size(); ic++) {
|
||||||
|
controllers.get(ic).prepareForExecutionLocked(jobStatus);
|
||||||
|
}
|
||||||
|
if (!worker.executeRunnableJob(jobStatus, workType)) {
|
||||||
|
Slog.e(TAG, "Error executing " + jobStatus);
|
||||||
|
mWorkCountTracker.onStagedJobFailed(workType);
|
||||||
|
} else {
|
||||||
|
mRunningJobs.add(jobStatus);
|
||||||
|
mWorkCountTracker.onJobStarted(workType);
|
||||||
|
}
|
||||||
|
final List<JobStatus> pendingJobs = mService.mPendingJobs;
|
||||||
|
if (pendingJobs.remove(jobStatus)) {
|
||||||
|
mService.mJobPackageTracker.noteNonpending(jobStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -484,6 +552,16 @@ class JobConcurrencyManager {
|
|||||||
|
|
||||||
mScreenOffAdjustmentDelayMs = properties.getLong(
|
mScreenOffAdjustmentDelayMs = properties.getLong(
|
||||||
KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS);
|
KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS);
|
||||||
|
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.normal.update(properties);
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.moderate.update(properties);
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.low.update(properties);
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.critical.update(properties);
|
||||||
|
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.normal.update(properties);
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.moderate.update(properties);
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.low.update(properties);
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.critical.update(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dumpLocked(IndentingPrintWriter pw, long now, long nowRealtime) {
|
public void dumpLocked(IndentingPrintWriter pw, long now, long nowRealtime) {
|
||||||
@@ -494,6 +572,14 @@ class JobConcurrencyManager {
|
|||||||
pw.print("Configuration:");
|
pw.print("Configuration:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println();
|
pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println();
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.normal.dump(pw);
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.moderate.dump(pw);
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.low.dump(pw);
|
||||||
|
CONFIG_LIMITS_SCREEN_ON.critical.dump(pw);
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.normal.dump(pw);
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.moderate.dump(pw);
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.low.dump(pw);
|
||||||
|
CONFIG_LIMITS_SCREEN_OFF.critical.dump(pw);
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
|
|
||||||
pw.print("Screen state: current ");
|
pw.print("Screen state: current ");
|
||||||
@@ -514,7 +600,7 @@ class JobConcurrencyManager {
|
|||||||
|
|
||||||
pw.println("Current max jobs:");
|
pw.println("Current max jobs:");
|
||||||
pw.println(" ");
|
pw.println(" ");
|
||||||
pw.println(mJobCountTracker);
|
pw.println(mWorkCountTracker);
|
||||||
|
|
||||||
pw.println();
|
pw.println();
|
||||||
|
|
||||||
@@ -540,8 +626,6 @@ class JobConcurrencyManager {
|
|||||||
proto.write(JobConcurrencyManagerProto.TIME_SINCE_LAST_SCREEN_OFF_MS,
|
proto.write(JobConcurrencyManagerProto.TIME_SINCE_LAST_SCREEN_OFF_MS,
|
||||||
nowRealtime - mLastScreenOffRealtime);
|
nowRealtime - mLastScreenOffRealtime);
|
||||||
|
|
||||||
mJobCountTracker.dumpProto(proto, JobConcurrencyManagerProto.JOB_COUNT_TRACKER);
|
|
||||||
|
|
||||||
proto.write(JobConcurrencyManagerProto.MEMORY_TRIM_LEVEL, mLastMemoryTrimLevel);
|
proto.write(JobConcurrencyManagerProto.MEMORY_TRIM_LEVEL, mLastMemoryTrimLevel);
|
||||||
|
|
||||||
mStatLogger.dumpProto(proto, JobConcurrencyManagerProto.STATS);
|
mStatLogger.dumpProto(proto, JobConcurrencyManagerProto.STATS);
|
||||||
@@ -549,199 +633,312 @@ class JobConcurrencyManager {
|
|||||||
proto.end(token);
|
proto.end(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getJobWorkTypes(@NonNull JobStatus js) {
|
||||||
|
int classification = 0;
|
||||||
|
// TODO(171305774): create dedicated work type for EJ and FGS
|
||||||
|
if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
|
||||||
|
|| js.shouldTreatAsExpeditedJob()) {
|
||||||
|
classification |= WORK_TYPE_TOP;
|
||||||
|
} else {
|
||||||
|
classification |= WORK_TYPE_BG;
|
||||||
|
}
|
||||||
|
return classification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class WorkTypeConfig {
|
||||||
|
private 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_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
|
||||||
|
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
|
||||||
|
private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
|
||||||
|
private final String mConfigIdentifier;
|
||||||
|
|
||||||
|
private int mMaxTotal;
|
||||||
|
private final SparseIntArray mMinReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
private final SparseIntArray mMaxAllowedSlots = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
private final int mDefaultMaxTotal;
|
||||||
|
private final SparseIntArray mDefaultMinReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
private final SparseIntArray mDefaultMaxAllowedSlots = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
|
||||||
|
WorkTypeConfig(@NonNull String configIdentifier, int defaultMaxTotal,
|
||||||
|
List<Pair<Integer, Integer>> defaultMin, List<Pair<Integer, Integer>> defaultMax) {
|
||||||
|
mConfigIdentifier = configIdentifier;
|
||||||
|
mDefaultMaxTotal = mMaxTotal = defaultMaxTotal;
|
||||||
|
for (int i = defaultMin.size() - 1; i >= 0; --i) {
|
||||||
|
mDefaultMinReservedSlots.put(defaultMin.get(i).first, defaultMin.get(i).second);
|
||||||
|
}
|
||||||
|
for (int i = defaultMax.size() - 1; i >= 0; --i) {
|
||||||
|
mDefaultMaxAllowedSlots.put(defaultMax.get(i).first, defaultMax.get(i).second);
|
||||||
|
}
|
||||||
|
update(new DeviceConfig.Properties.Builder(
|
||||||
|
DeviceConfig.NAMESPACE_JOB_SCHEDULER).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(@NonNull DeviceConfig.Properties properties) {
|
||||||
|
// Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT].
|
||||||
|
mMaxTotal = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT,
|
||||||
|
properties.getInt(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mDefaultMaxTotal)));
|
||||||
|
|
||||||
|
mMaxAllowedSlots.clear();
|
||||||
|
// Ensure they're in the range [1, total].
|
||||||
|
final int maxTop = Math.max(1, Math.min(mMaxTotal,
|
||||||
|
properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
|
||||||
|
mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
|
||||||
|
mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
|
||||||
|
final int maxBg = Math.max(1, Math.min(mMaxTotal,
|
||||||
|
properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
|
||||||
|
mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
|
||||||
|
mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
|
||||||
|
|
||||||
|
int remaining = mMaxTotal;
|
||||||
|
mMinReservedSlots.clear();
|
||||||
|
// Ensure top is in the range [1, min(maxTop, total)]
|
||||||
|
final int minTop = Math.max(1, Math.min(Math.min(maxTop, mMaxTotal),
|
||||||
|
properties.getInt(KEY_PREFIX_MIN_TOP + mConfigIdentifier,
|
||||||
|
mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
|
||||||
|
mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
|
||||||
|
remaining -= minTop;
|
||||||
|
// Ensure bg is in the range [0, min(maxBg, remaining)]
|
||||||
|
final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining),
|
||||||
|
properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
|
||||||
|
mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
|
||||||
|
mMinReservedSlots.put(WORK_TYPE_BG, minBg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMaxTotal() {
|
||||||
|
return mMaxTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMax(@WorkType int workType) {
|
||||||
|
return mMaxAllowedSlots.get(workType, mMaxTotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMinReserved(@WorkType int workType) {
|
||||||
|
return mMinReservedSlots.get(workType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump(IndentingPrintWriter pw) {
|
||||||
|
pw.print(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mMaxTotal).println();
|
||||||
|
pw.print(KEY_PREFIX_MIN_TOP + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_TOP))
|
||||||
|
.println();
|
||||||
|
pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
|
||||||
|
.println();
|
||||||
|
pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG))
|
||||||
|
.println();
|
||||||
|
pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
|
||||||
|
.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@link WorkTypeConfig} for each memory trim level. */
|
||||||
|
static class WorkConfigLimitsPerMemoryTrimLevel {
|
||||||
|
public final WorkTypeConfig normal;
|
||||||
|
public final WorkTypeConfig moderate;
|
||||||
|
public final WorkTypeConfig low;
|
||||||
|
public final WorkTypeConfig critical;
|
||||||
|
|
||||||
|
WorkConfigLimitsPerMemoryTrimLevel(WorkTypeConfig normal, WorkTypeConfig moderate,
|
||||||
|
WorkTypeConfig low, WorkTypeConfig critical) {
|
||||||
|
this.normal = normal;
|
||||||
|
this.moderate = moderate;
|
||||||
|
this.low = low;
|
||||||
|
this.critical = critical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class decides, taking into account {@link #mMaxJobCounts} and how mny jos are running /
|
* This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs
|
||||||
* pending, how many more job can start.
|
* are running/pending, how many more job can start.
|
||||||
*
|
*
|
||||||
* Extracted for testing and logging.
|
* Extracted for testing and logging.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static class JobCountTracker {
|
static class WorkCountTracker {
|
||||||
private int mConfigNumMaxTotalJobs;
|
private int mConfigMaxTotal;
|
||||||
private int mConfigNumMaxBgJobs;
|
private final SparseIntArray mConfigNumReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
private int mConfigNumMinBgJobs;
|
private final SparseIntArray mConfigAbsoluteMaxSlots = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
|
||||||
private int mNumRunningFgJobs;
|
/**
|
||||||
private int mNumRunningBgJobs;
|
* Numbers may be lower in this than in {@link #mConfigNumReservedSlots} if there aren't
|
||||||
|
* enough ready jobs of a type to take up all of the desired reserved slots.
|
||||||
|
*/
|
||||||
|
private final SparseIntArray mNumActuallyReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES);
|
||||||
|
private int mNumUnspecialized = 0;
|
||||||
|
private int mNumUnspecializedRemaining = 0;
|
||||||
|
|
||||||
private int mNumPendingFgJobs;
|
void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
|
||||||
private int mNumPendingBgJobs;
|
mConfigMaxTotal = workTypeConfig.getMaxTotal();
|
||||||
|
mConfigNumReservedSlots.put(WORK_TYPE_TOP,
|
||||||
|
workTypeConfig.getMinReserved(WORK_TYPE_TOP));
|
||||||
|
mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
|
||||||
|
mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
|
||||||
|
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
|
||||||
|
|
||||||
private int mNumStartingFgJobs;
|
mNumUnspecialized = mConfigMaxTotal;
|
||||||
private int mNumStartingBgJobs;
|
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
|
||||||
|
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
|
||||||
private int mNumReservedForBg;
|
mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP);
|
||||||
private int mNumActualMaxFgJobs;
|
mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG);
|
||||||
private int mNumActualMaxBgJobs;
|
calculateUnspecializedRemaining();
|
||||||
|
|
||||||
void reset(int numTotalMaxJobs, int numMaxBgJobs, int numMinBgJobs) {
|
|
||||||
mConfigNumMaxTotalJobs = numTotalMaxJobs;
|
|
||||||
mConfigNumMaxBgJobs = numMaxBgJobs;
|
|
||||||
mConfigNumMinBgJobs = numMinBgJobs;
|
|
||||||
|
|
||||||
mNumRunningFgJobs = 0;
|
|
||||||
mNumRunningBgJobs = 0;
|
|
||||||
|
|
||||||
mNumPendingFgJobs = 0;
|
|
||||||
mNumPendingBgJobs = 0;
|
|
||||||
|
|
||||||
mNumStartingFgJobs = 0;
|
|
||||||
mNumStartingBgJobs = 0;
|
|
||||||
|
|
||||||
mNumReservedForBg = 0;
|
|
||||||
mNumActualMaxFgJobs = 0;
|
|
||||||
mNumActualMaxBgJobs = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void incrementRunningJobCount(boolean isFg) {
|
private void calculateUnspecializedRemaining() {
|
||||||
if (isFg) {
|
mNumUnspecializedRemaining = mNumUnspecialized;
|
||||||
mNumRunningFgJobs++;
|
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
|
||||||
} else {
|
mNumUnspecializedRemaining -= mNumRunningJobs.valueAt(i);
|
||||||
mNumRunningBgJobs++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void incrementPendingJobCount(boolean isFg) {
|
void resetCounts() {
|
||||||
if (isFg) {
|
mNumActuallyReservedSlots.clear();
|
||||||
mNumPendingFgJobs++;
|
mNumPendingJobs.clear();
|
||||||
} else {
|
mNumRunningJobs.clear();
|
||||||
mNumPendingBgJobs++;
|
resetStagingCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetStagingCount() {
|
||||||
|
mNumStartingJobs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void incrementRunningJobCount(@WorkType int workType) {
|
||||||
|
mNumRunningJobs.put(workType, mNumRunningJobs.get(workType) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void incrementPendingJobCount(int workTypes) {
|
||||||
|
// We don't know which type we'll classify the job as when we run it yet, so make sure
|
||||||
|
// we have space in all applicable slots.
|
||||||
|
if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
|
||||||
|
mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
|
||||||
|
}
|
||||||
|
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
|
||||||
|
mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStartingNewJob(boolean isFg) {
|
void stageJob(@WorkType int workType) {
|
||||||
if (isFg) {
|
final int newNumStartingJobs = mNumStartingJobs.get(workType) + 1;
|
||||||
mNumStartingFgJobs++;
|
mNumStartingJobs.put(workType, newNumStartingJobs);
|
||||||
|
mNumPendingJobs.put(workType, Math.max(0, mNumPendingJobs.get(workType) - 1));
|
||||||
|
if (newNumStartingJobs + mNumRunningJobs.get(workType)
|
||||||
|
> mNumActuallyReservedSlots.get(workType)) {
|
||||||
|
mNumUnspecializedRemaining--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onStagedJobFailed(@WorkType int workType) {
|
||||||
|
final int oldNumStartingJobs = mNumStartingJobs.get(workType);
|
||||||
|
if (oldNumStartingJobs == 0) {
|
||||||
|
Slog.e(TAG, "# staged jobs for " + workType + " went negative.");
|
||||||
|
// We are in a bad state. We will eventually recover when the pending list is
|
||||||
|
// regenerated.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mNumStartingJobs.put(workType, oldNumStartingJobs - 1);
|
||||||
|
maybeAdjustReservations(workType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeAdjustReservations(@WorkType int workType) {
|
||||||
|
// Always make sure we reserve the minimum number of slots in case new jobs become ready
|
||||||
|
// soon.
|
||||||
|
final int numRemainingForType = Math.max(mConfigNumReservedSlots.get(workType),
|
||||||
|
mNumRunningJobs.get(workType) + mNumStartingJobs.get(workType)
|
||||||
|
+ mNumPendingJobs.get(workType));
|
||||||
|
if (numRemainingForType < mNumActuallyReservedSlots.get(workType)) {
|
||||||
|
// We've run all jobs for this type. Let another type use it now.
|
||||||
|
mNumActuallyReservedSlots.put(workType, numRemainingForType);
|
||||||
|
mNumUnspecializedRemaining++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onJobStarted(@WorkType int workType) {
|
||||||
|
mNumRunningJobs.put(workType, mNumRunningJobs.get(workType) + 1);
|
||||||
|
final int oldNumStartingJobs = mNumStartingJobs.get(workType);
|
||||||
|
if (oldNumStartingJobs == 0) {
|
||||||
|
Slog.e(TAG, "# stated jobs for " + workType + " went negative.");
|
||||||
|
// We are in a bad state. We will eventually recover when the pending list is
|
||||||
|
// regenerated. For now, only modify the running count.
|
||||||
} else {
|
} else {
|
||||||
mNumStartingBgJobs++;
|
mNumStartingJobs.put(workType, oldNumStartingJobs - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onCountDone() {
|
void onCountDone() {
|
||||||
// Note some variables are used only here but are made class members in order to have
|
// Calculate how many slots to reserve for each work type. "Unspecialized" slots will
|
||||||
// them on logcat / dumpsys.
|
// be reserved for higher importance types first (ie. top before bg).
|
||||||
|
mNumUnspecialized = mConfigMaxTotal;
|
||||||
|
final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
|
||||||
|
+ mNumPendingJobs.get(WORK_TYPE_TOP);
|
||||||
|
final int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
|
||||||
|
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
|
||||||
|
mNumUnspecialized -= resTop;
|
||||||
|
final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
|
||||||
|
final int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
|
||||||
|
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
|
||||||
|
mNumUnspecialized -= resBg;
|
||||||
|
calculateUnspecializedRemaining();
|
||||||
|
|
||||||
// How many slots should we allocate to BG jobs at least?
|
// Assign remaining unspecialized based on ranking.
|
||||||
// That's basically "getMinBg()", but if there are less jobs, decrease it.
|
int unspecializedAssigned = Math.max(0,
|
||||||
// (e.g. even if min-bg is 2, if there's only 1 running+pending job, this has to be 1.)
|
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
|
||||||
final int reservedForBg = Math.min(
|
Math.min(mNumUnspecializedRemaining, numTop - resTop)));
|
||||||
mConfigNumMinBgJobs,
|
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
|
||||||
mNumRunningBgJobs + mNumPendingBgJobs);
|
mNumUnspecializedRemaining -= unspecializedAssigned;
|
||||||
|
unspecializedAssigned = Math.max(0,
|
||||||
// However, if there are FG jobs already running, we have to adjust it.
|
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
|
||||||
mNumReservedForBg = Math.min(reservedForBg,
|
Math.min(mNumUnspecializedRemaining, numBg - resBg)));
|
||||||
mConfigNumMaxTotalJobs - mNumRunningFgJobs);
|
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
|
||||||
|
mNumUnspecializedRemaining -= unspecializedAssigned;
|
||||||
// Max FG is [total - [number needed for BG jobs]]
|
|
||||||
// [number needed for BG jobs] is the bigger one of [running BG] or [reserved BG]
|
|
||||||
final int maxFg =
|
|
||||||
mConfigNumMaxTotalJobs - Math.max(mNumRunningBgJobs, mNumReservedForBg);
|
|
||||||
|
|
||||||
// The above maxFg is the theoretical max. If there are less FG jobs, the actual
|
|
||||||
// max FG will be lower accordingly.
|
|
||||||
mNumActualMaxFgJobs = Math.min(
|
|
||||||
maxFg,
|
|
||||||
mNumRunningFgJobs + mNumPendingFgJobs);
|
|
||||||
|
|
||||||
// Max BG is [total - actual max FG], but cap at [config max BG].
|
|
||||||
final int maxBg = Math.min(
|
|
||||||
mConfigNumMaxBgJobs,
|
|
||||||
mConfigNumMaxTotalJobs - mNumActualMaxFgJobs);
|
|
||||||
|
|
||||||
// If there are less BG jobs than maxBg, then reduce the actual max BG accordingly.
|
|
||||||
// This isn't needed for the logic to work, but this will give consistent output
|
|
||||||
// on logcat and dumpsys.
|
|
||||||
mNumActualMaxBgJobs = Math.min(
|
|
||||||
maxBg,
|
|
||||||
mNumRunningBgJobs + mNumPendingBgJobs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean canJobStart(boolean isFg) {
|
int canJobStart(int workTypes) {
|
||||||
if (isFg) {
|
if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
|
||||||
return mNumRunningFgJobs + mNumStartingFgJobs < mNumActualMaxFgJobs;
|
final int maxAllowed = Math.min(
|
||||||
} else {
|
mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
|
||||||
return mNumRunningBgJobs + mNumStartingBgJobs < mNumActualMaxBgJobs;
|
mNumActuallyReservedSlots.get(WORK_TYPE_TOP) + mNumUnspecializedRemaining);
|
||||||
|
if (mNumRunningJobs.get(WORK_TYPE_TOP) + mNumStartingJobs.get(WORK_TYPE_TOP)
|
||||||
|
< maxAllowed) {
|
||||||
|
return WORK_TYPE_TOP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
|
||||||
|
final int maxAllowed = Math.min(
|
||||||
public int getNumStartingFgJobs() {
|
mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
|
||||||
return mNumStartingFgJobs;
|
mNumActuallyReservedSlots.get(WORK_TYPE_BG) + mNumUnspecializedRemaining);
|
||||||
}
|
if (mNumRunningJobs.get(WORK_TYPE_BG) + mNumStartingJobs.get(WORK_TYPE_BG)
|
||||||
|
< maxAllowed) {
|
||||||
public int getNumStartingBgJobs() {
|
return WORK_TYPE_BG;
|
||||||
return mNumStartingBgJobs;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int getTotalRunningJobCountToNote() {
|
|
||||||
return mNumRunningFgJobs + mNumRunningBgJobs
|
|
||||||
+ mNumStartingFgJobs + mNumStartingBgJobs;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getFgRunningJobCountToNote() {
|
|
||||||
return mNumRunningFgJobs + mNumStartingFgJobs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void logStatus() {
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.d(TAG, "assignJobsToContexts: " + this);
|
|
||||||
}
|
}
|
||||||
|
return WORK_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRunningJobCount(@WorkType final int workType) {
|
||||||
|
return mNumRunningJobs.get(workType, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final int totalFg = mNumRunningFgJobs + mNumStartingFgJobs;
|
StringBuilder sb = new StringBuilder();
|
||||||
final int totalBg = mNumRunningBgJobs + mNumStartingBgJobs;
|
|
||||||
return String.format(
|
|
||||||
"Config={tot=%d bg min/max=%d/%d}"
|
|
||||||
+ " Running[FG/BG (total)]: %d / %d (%d)"
|
|
||||||
+ " Pending: %d / %d (%d)"
|
|
||||||
+ " Actual max: %d%s / %d%s (%d%s)"
|
|
||||||
+ " Res BG: %d"
|
|
||||||
+ " Starting: %d / %d (%d)"
|
|
||||||
+ " Total: %d%s / %d%s (%d%s)",
|
|
||||||
mConfigNumMaxTotalJobs, mConfigNumMinBgJobs, mConfigNumMaxBgJobs,
|
|
||||||
|
|
||||||
mNumRunningFgJobs, mNumRunningBgJobs, mNumRunningFgJobs + mNumRunningBgJobs,
|
sb.append("Config={");
|
||||||
|
sb.append("tot=").append(mConfigMaxTotal);
|
||||||
|
sb.append(" mins=");
|
||||||
|
sb.append(mConfigNumReservedSlots);
|
||||||
|
sb.append(" maxs=");
|
||||||
|
sb.append(mConfigAbsoluteMaxSlots);
|
||||||
|
sb.append("}");
|
||||||
|
|
||||||
mNumPendingFgJobs, mNumPendingBgJobs, mNumPendingFgJobs + mNumPendingBgJobs,
|
sb.append(", act res=").append(mNumActuallyReservedSlots);
|
||||||
|
sb.append(", Pending=").append(mNumPendingJobs);
|
||||||
|
sb.append(", Running=").append(mNumRunningJobs);
|
||||||
|
sb.append(", Staged=").append(mNumStartingJobs);
|
||||||
|
sb.append(", # unspecialized remaining=").append(mNumUnspecializedRemaining);
|
||||||
|
|
||||||
mNumActualMaxFgJobs, (totalFg <= mConfigNumMaxTotalJobs) ? "" : "*",
|
return sb.toString();
|
||||||
mNumActualMaxBgJobs, (totalBg <= mConfigNumMaxBgJobs) ? "" : "*",
|
|
||||||
mNumActualMaxFgJobs + mNumActualMaxBgJobs,
|
|
||||||
(mNumActualMaxFgJobs + mNumActualMaxBgJobs <= mConfigNumMaxTotalJobs)
|
|
||||||
? "" : "*",
|
|
||||||
|
|
||||||
mNumReservedForBg,
|
|
||||||
|
|
||||||
mNumStartingFgJobs, mNumStartingBgJobs, mNumStartingFgJobs + mNumStartingBgJobs,
|
|
||||||
|
|
||||||
totalFg, (totalFg <= mNumActualMaxFgJobs) ? "" : "*",
|
|
||||||
totalBg, (totalBg <= mNumActualMaxBgJobs) ? "" : "*",
|
|
||||||
totalFg + totalBg, (totalFg + totalBg <= mConfigNumMaxTotalJobs) ? "" : "*"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dumpProto(ProtoOutputStream proto, long fieldId) {
|
|
||||||
final long token = proto.start(fieldId);
|
|
||||||
|
|
||||||
proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_TOTAL_JOBS, mConfigNumMaxTotalJobs);
|
|
||||||
proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_BG_JOBS, mConfigNumMaxBgJobs);
|
|
||||||
proto.write(JobCountTrackerProto.CONFIG_NUM_MIN_BG_JOBS, mConfigNumMinBgJobs);
|
|
||||||
|
|
||||||
proto.write(JobCountTrackerProto.NUM_RUNNING_FG_JOBS, mNumRunningFgJobs);
|
|
||||||
proto.write(JobCountTrackerProto.NUM_RUNNING_BG_JOBS, mNumRunningBgJobs);
|
|
||||||
|
|
||||||
proto.write(JobCountTrackerProto.NUM_PENDING_FG_JOBS, mNumPendingFgJobs);
|
|
||||||
proto.write(JobCountTrackerProto.NUM_PENDING_BG_JOBS, mNumPendingBgJobs);
|
|
||||||
|
|
||||||
proto.write(JobCountTrackerProto.NUM_ACTUAL_MAX_FG_JOBS, mNumActualMaxFgJobs);
|
|
||||||
proto.write(JobCountTrackerProto.NUM_ACTUAL_MAX_BG_JOBS, mNumActualMaxBgJobs);
|
|
||||||
|
|
||||||
proto.write(JobCountTrackerProto.NUM_RESERVED_FOR_BG, mNumReservedForBg);
|
|
||||||
|
|
||||||
proto.write(JobCountTrackerProto.NUM_STARTING_FG_JOBS, mNumStartingFgJobs);
|
|
||||||
proto.write(JobCountTrackerProto.NUM_STARTING_BG_JOBS, mNumStartingBgJobs);
|
|
||||||
|
|
||||||
proto.end(token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
@@ -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,
|
||||||
@@ -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();
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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.");
|
||||||
@@ -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() &&
|
||||||
@@ -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;
|
||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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 com.android.server.job.JobConcurrencyManager.JobCountTracker;
|
import androidx.test.filters.MediumTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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);
|
||||||
|
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,22 +15,34 @@
|
|||||||
*/
|
*/
|
||||||
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_TOP;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
import com.android.server.job.JobSchedulerService.MaxJobCounts;
|
import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public class MaxJobCountsTest {
|
public class WorkTypeConfigTest {
|
||||||
|
private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
|
||||||
|
private static final String KEY_MAX_TOP = "concurrency_max_top_test";
|
||||||
|
private static final String KEY_MAX_BG = "concurrency_max_bg_test";
|
||||||
|
private static final String KEY_MIN_TOP = "concurrency_min_top_test";
|
||||||
|
private static final String KEY_MIN_BG = "concurrency_min_bg_test";
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
resetConfig();
|
resetConfig();
|
||||||
@@ -38,9 +50,11 @@ public class MaxJobCountsTest {
|
|||||||
|
|
||||||
private void resetConfig() {
|
private void resetConfig() {
|
||||||
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
|
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
|
||||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "total", "", false);
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
|
||||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "maxbg", "", false);
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
|
||||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "minbg", "", false);
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void check(@Nullable DeviceConfig.Properties config,
|
private void check(@Nullable DeviceConfig.Properties config,
|
||||||
@@ -51,16 +65,19 @@ public class MaxJobCountsTest {
|
|||||||
DeviceConfig.setProperties(config);
|
DeviceConfig.setProperties(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
final MaxJobCounts counts = new JobSchedulerService.MaxJobCounts(
|
final WorkTypeConfig counts = new WorkTypeConfig("test",
|
||||||
defaultTotal, "total",
|
defaultTotal,
|
||||||
defaultMaxBg, "maxbg",
|
// defaultMin
|
||||||
defaultMinBg, "minbg");
|
List.of(Pair.create(WORK_TYPE_TOP, defaultTotal - defaultMaxBg),
|
||||||
|
Pair.create(WORK_TYPE_BG, defaultMinBg)),
|
||||||
|
// defaultMax
|
||||||
|
List.of(Pair.create(WORK_TYPE_BG, defaultMaxBg)));
|
||||||
|
|
||||||
counts.update();
|
counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
|
||||||
|
|
||||||
Assert.assertEquals(expectedTotal, counts.getMaxTotal());
|
Assert.assertEquals(expectedTotal, counts.getMaxTotal());
|
||||||
Assert.assertEquals(expectedMaxBg, counts.getMaxBg());
|
Assert.assertEquals(expectedMaxBg, counts.getMax(WORK_TYPE_BG));
|
||||||
Assert.assertEquals(expectedMinBg, counts.getMinBg());
|
Assert.assertEquals(expectedMinBg, counts.getMinReserved(WORK_TYPE_BG));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -80,19 +97,19 @@ public class MaxJobCountsTest {
|
|||||||
|
|
||||||
// Test for overriding with a setting string.
|
// Test for overriding with a setting string.
|
||||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||||
.setInt("total", 5)
|
.setInt(KEY_MAX_TOTAL, 5)
|
||||||
.setInt("maxbg", 4)
|
.setInt(KEY_MAX_BG, 4)
|
||||||
.setInt("minbg", 3)
|
.setInt(KEY_MIN_BG, 3)
|
||||||
.build(),
|
.build(),
|
||||||
/*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
|
/*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
|
||||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||||
.setInt("total", 5).build(),
|
.setInt(KEY_MAX_TOTAL, 5).build(),
|
||||||
/*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
|
/*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
|
||||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||||
.setInt("maxbg", 4).build(),
|
.setInt(KEY_MAX_BG, 4).build(),
|
||||||
/*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
|
/*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
|
||||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||||
.setInt("minbg", 3).build(),
|
.setInt(KEY_MIN_BG, 3).build(),
|
||||||
/*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
|
/*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user