Merge "Let EJs for TOP app run immediately and without limit." into tm-dev
This commit is contained in:
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package com.android.server.job;
|
package com.android.server.job;
|
||||||
|
|
||||||
import static com.android.server.job.JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -82,6 +81,11 @@ class JobConcurrencyManager {
|
|||||||
private static final String TAG = JobSchedulerService.TAG + ".Concurrency";
|
private static final String TAG = JobSchedulerService.TAG + ".Concurrency";
|
||||||
private static final boolean DEBUG = JobSchedulerService.DEBUG;
|
private static final boolean DEBUG = JobSchedulerService.DEBUG;
|
||||||
|
|
||||||
|
/** The maximum number of concurrent jobs we'll aim to run at one time. */
|
||||||
|
public static final int STANDARD_CONCURRENCY_LIMIT = 16;
|
||||||
|
/** The maximum number of objects we should retain in memory when not in use. */
|
||||||
|
private static final int MAX_RETAINED_OBJECTS = (int) (1.5 * STANDARD_CONCURRENCY_LIMIT);
|
||||||
|
|
||||||
static final String CONFIG_KEY_PREFIX_CONCURRENCY = "concurrency_";
|
static final String CONFIG_KEY_PREFIX_CONCURRENCY = "concurrency_";
|
||||||
private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS =
|
private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS =
|
||||||
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
|
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
|
||||||
@@ -93,7 +97,7 @@ class JobConcurrencyManager {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR =
|
static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR =
|
||||||
CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular";
|
CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular";
|
||||||
private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = MAX_JOB_CONTEXTS_COUNT / 2;
|
private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = STANDARD_CONCURRENCY_LIMIT / 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set of possible execution types that a job can have. The actual type(s) of a job are based
|
* Set of possible execution types that a job can have. The actual type(s) of a job are based
|
||||||
@@ -309,7 +313,7 @@ class JobConcurrencyManager {
|
|||||||
private final ArrayList<ContextAssignment> mRecycledStoppable = new ArrayList<>();
|
private final ArrayList<ContextAssignment> mRecycledStoppable = new ArrayList<>();
|
||||||
|
|
||||||
private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
|
private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
|
||||||
new Pools.SimplePool<>(MAX_JOB_CONTEXTS_COUNT);
|
new Pools.SimplePool<>(MAX_RETAINED_OBJECTS);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set of JobServiceContexts that are actively running jobs.
|
* Set of JobServiceContexts that are actively running jobs.
|
||||||
@@ -317,14 +321,16 @@ class JobConcurrencyManager {
|
|||||||
final List<JobServiceContext> mActiveServices = new ArrayList<>();
|
final List<JobServiceContext> mActiveServices = new ArrayList<>();
|
||||||
|
|
||||||
/** Set of JobServiceContexts that aren't currently running any jobs. */
|
/** Set of JobServiceContexts that aren't currently running any jobs. */
|
||||||
final ArraySet<JobServiceContext> mIdleContexts = new ArraySet<>();
|
private final ArraySet<JobServiceContext> mIdleContexts = new ArraySet<>();
|
||||||
|
|
||||||
|
private int mNumDroppedContexts = 0;
|
||||||
|
|
||||||
private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
|
private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
|
||||||
|
|
||||||
private final WorkCountTracker mWorkCountTracker = new WorkCountTracker();
|
private final WorkCountTracker mWorkCountTracker = new WorkCountTracker();
|
||||||
|
|
||||||
private final Pools.Pool<PackageStats> mPkgStatsPool =
|
private final Pools.Pool<PackageStats> mPkgStatsPool =
|
||||||
new Pools.SimplePool<>(MAX_JOB_CONTEXTS_COUNT);
|
new Pools.SimplePool<>(MAX_RETAINED_OBJECTS);
|
||||||
|
|
||||||
private final SparseArrayMap<String, PackageStats> mActivePkgStats = new SparseArrayMap<>();
|
private final SparseArrayMap<String, PackageStats> mActivePkgStats = new SparseArrayMap<>();
|
||||||
|
|
||||||
@@ -406,7 +412,7 @@ class JobConcurrencyManager {
|
|||||||
void onThirdPartyAppsCanStart() {
|
void onThirdPartyAppsCanStart() {
|
||||||
final IBatteryStats batteryStats = IBatteryStats.Stub.asInterface(
|
final IBatteryStats batteryStats = IBatteryStats.Stub.asInterface(
|
||||||
ServiceManager.getService(BatteryStats.SERVICE_NAME));
|
ServiceManager.getService(BatteryStats.SERVICE_NAME));
|
||||||
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
|
for (int i = 0; i < STANDARD_CONCURRENCY_LIMIT; i++) {
|
||||||
mIdleContexts.add(
|
mIdleContexts.add(
|
||||||
new JobServiceContext(mService, this, batteryStats,
|
new JobServiceContext(mService, this, batteryStats,
|
||||||
mService.mJobPackageTracker, mContext.getMainLooper()));
|
mService.mJobPackageTracker, mContext.getMainLooper()));
|
||||||
@@ -655,13 +661,13 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
preferredUidOnly.sort(sDeterminationComparator);
|
preferredUidOnly.sort(sDeterminationComparator);
|
||||||
stoppable.sort(sDeterminationComparator);
|
stoppable.sort(sDeterminationComparator);
|
||||||
for (int i = numRunningJobs; i < MAX_JOB_CONTEXTS_COUNT; ++i) {
|
for (int i = numRunningJobs; i < STANDARD_CONCURRENCY_LIMIT; ++i) {
|
||||||
final JobServiceContext jsc;
|
final JobServiceContext jsc;
|
||||||
final int numIdleContexts = mIdleContexts.size();
|
final int numIdleContexts = mIdleContexts.size();
|
||||||
if (numIdleContexts > 0) {
|
if (numIdleContexts > 0) {
|
||||||
jsc = mIdleContexts.removeAt(numIdleContexts - 1);
|
jsc = mIdleContexts.removeAt(numIdleContexts - 1);
|
||||||
} else {
|
} else {
|
||||||
Slog.wtf(TAG, "Had fewer than " + MAX_JOB_CONTEXTS_COUNT + " in existence");
|
Slog.wtf(TAG, "Had fewer than " + STANDARD_CONCURRENCY_LIMIT + " in existence");
|
||||||
jsc = createNewJobServiceContext();
|
jsc = createNewJobServiceContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,6 +687,7 @@ class JobConcurrencyManager {
|
|||||||
|
|
||||||
JobStatus nextPending;
|
JobStatus nextPending;
|
||||||
pendingJobQueue.resetIterator();
|
pendingJobQueue.resetIterator();
|
||||||
|
int projectedRunningCount = numRunningJobs;
|
||||||
while ((nextPending = pendingJobQueue.next()) != null) {
|
while ((nextPending = pendingJobQueue.next()) != null) {
|
||||||
if (mRunningJobs.contains(nextPending)) {
|
if (mRunningJobs.contains(nextPending)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -694,6 +701,9 @@ class JobConcurrencyManager {
|
|||||||
ContextAssignment selectedContext = null;
|
ContextAssignment selectedContext = null;
|
||||||
final int allWorkTypes = getJobWorkTypes(nextPending);
|
final int allWorkTypes = getJobWorkTypes(nextPending);
|
||||||
final boolean pkgConcurrencyOkay = !isPkgConcurrencyLimitedLocked(nextPending);
|
final boolean pkgConcurrencyOkay = !isPkgConcurrencyLimitedLocked(nextPending);
|
||||||
|
final boolean isTopEj = nextPending.shouldTreatAsExpeditedJob()
|
||||||
|
&& nextPending.lastEvaluatedBias == JobInfo.BIAS_TOP_APP;
|
||||||
|
final boolean isInOverage = projectedRunningCount > STANDARD_CONCURRENCY_LIMIT;
|
||||||
boolean startingJob = false;
|
boolean startingJob = false;
|
||||||
if (idle.size() > 0) {
|
if (idle.size() > 0) {
|
||||||
final int idx = idle.size() - 1;
|
final int idx = idle.size() - 1;
|
||||||
@@ -711,14 +721,36 @@ class JobConcurrencyManager {
|
|||||||
assignment.newWorkType = workType;
|
assignment.newWorkType = workType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectedContext == null) {
|
if (selectedContext == null && stoppable.size() > 0) {
|
||||||
|
int topEjCount = 0;
|
||||||
|
for (int r = mRunningJobs.size() - 1; r >= 0; --r) {
|
||||||
|
JobStatus js = mRunningJobs.valueAt(r);
|
||||||
|
if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
|
||||||
|
topEjCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int s = stoppable.size() - 1; s >= 0; --s) {
|
for (int s = stoppable.size() - 1; s >= 0; --s) {
|
||||||
final ContextAssignment assignment = stoppable.get(s);
|
final ContextAssignment assignment = stoppable.get(s);
|
||||||
final JobStatus runningJob = assignment.context.getRunningJobLocked();
|
final JobStatus runningJob = assignment.context.getRunningJobLocked();
|
||||||
// Maybe stop the job if it has had its day in the sun. Don't let a different
|
// Maybe stop the job if it has had its day in the sun. Only allow replacing
|
||||||
// app preempt jobs started for TOP apps though.
|
// for one of the following conditions:
|
||||||
if (runningJob.lastEvaluatedBias < JobInfo.BIAS_TOP_APP
|
// 1. We're putting in the current TOP app's EJ
|
||||||
&& assignment.shouldStopJobReason != null) {
|
// 2. There aren't too many jobs running AND the current job started when the
|
||||||
|
// app was in the background
|
||||||
|
// 3. There aren't too many jobs running AND the current job started when the
|
||||||
|
// app was on TOP, but the app has since left TOP
|
||||||
|
// 4. There aren't too many jobs running AND the current job started when the
|
||||||
|
// app was on TOP, the app is still TOP, but there are too many TOP+EJs
|
||||||
|
// running (because we don't want them to starve out other apps and the
|
||||||
|
// current job has already run for the minimum guaranteed time).
|
||||||
|
boolean canReplace = isTopEj; // Case 1
|
||||||
|
if (!canReplace && !isInOverage) {
|
||||||
|
final int currentJobBias = mService.evaluateJobBiasLocked(runningJob);
|
||||||
|
canReplace = runningJob.lastEvaluatedBias < JobInfo.BIAS_TOP_APP // Case 2
|
||||||
|
|| currentJobBias < JobInfo.BIAS_TOP_APP // Case 3
|
||||||
|
|| topEjCount > .5 * mWorkTypeConfig.getMaxTotal(); // Case 4
|
||||||
|
}
|
||||||
|
if (canReplace) {
|
||||||
int replaceWorkType = mWorkCountTracker.canJobStart(allWorkTypes,
|
int replaceWorkType = mWorkCountTracker.canJobStart(allWorkTypes,
|
||||||
assignment.context.getRunningJobWorkType());
|
assignment.context.getRunningJobWorkType());
|
||||||
if (replaceWorkType != WORK_TYPE_NONE) {
|
if (replaceWorkType != WORK_TYPE_NONE) {
|
||||||
@@ -736,7 +768,7 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectedContext == null) {
|
if (selectedContext == null && (!isInOverage || isTopEj)) {
|
||||||
int lowestBiasSeen = Integer.MAX_VALUE;
|
int lowestBiasSeen = Integer.MAX_VALUE;
|
||||||
for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
|
for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
|
||||||
final ContextAssignment assignment = preferredUidOnly.get(p);
|
final ContextAssignment assignment = preferredUidOnly.get(p);
|
||||||
@@ -765,10 +797,43 @@ class JobConcurrencyManager {
|
|||||||
preferredUidOnly.remove(selectedContext);
|
preferredUidOnly.remove(selectedContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Make sure to run EJs for the TOP app immediately.
|
||||||
|
if (isTopEj) {
|
||||||
|
if (selectedContext != null
|
||||||
|
&& selectedContext.context.getRunningJobLocked() != null) {
|
||||||
|
// We're "replacing" a currently running job, but we want TOP EJs to start
|
||||||
|
// immediately, so we'll start the EJ on a fresh available context and
|
||||||
|
// stop this currently running job to replace in two steps.
|
||||||
|
changed.add(selectedContext);
|
||||||
|
projectedRunningCount--;
|
||||||
|
selectedContext.newJob = null;
|
||||||
|
selectedContext.newWorkType = WORK_TYPE_NONE;
|
||||||
|
selectedContext = null;
|
||||||
|
}
|
||||||
|
if (selectedContext == null) {
|
||||||
|
selectedContext = mContextAssignmentPool.acquire();
|
||||||
|
if (selectedContext == null) {
|
||||||
|
selectedContext = new ContextAssignment();
|
||||||
|
}
|
||||||
|
selectedContext.context = mIdleContexts.size() > 0
|
||||||
|
? mIdleContexts.removeAt(mIdleContexts.size() - 1)
|
||||||
|
: createNewJobServiceContext();
|
||||||
|
selectedContext.newJob = nextPending;
|
||||||
|
final int workType = mWorkCountTracker.canJobStart(allWorkTypes);
|
||||||
|
selectedContext.newWorkType =
|
||||||
|
(workType != WORK_TYPE_NONE) ? workType : WORK_TYPE_TOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
final PackageStats packageStats = getPkgStatsLocked(
|
final PackageStats packageStats = getPkgStatsLocked(
|
||||||
nextPending.getSourceUserId(), nextPending.getSourcePackageName());
|
nextPending.getSourceUserId(), nextPending.getSourcePackageName());
|
||||||
if (selectedContext != null) {
|
if (selectedContext != null) {
|
||||||
changed.add(selectedContext);
|
changed.add(selectedContext);
|
||||||
|
if (selectedContext.context.getRunningJobLocked() != null) {
|
||||||
|
projectedRunningCount--;
|
||||||
|
}
|
||||||
|
if (selectedContext.newJob != null) {
|
||||||
|
projectedRunningCount++;
|
||||||
|
}
|
||||||
packageStats.adjustStagedCount(true, nextPending.shouldTreatAsExpeditedJob());
|
packageStats.adjustStagedCount(true, nextPending.shouldTreatAsExpeditedJob());
|
||||||
}
|
}
|
||||||
if (startingJob) {
|
if (startingJob) {
|
||||||
@@ -793,7 +858,7 @@ class JobConcurrencyManager {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "preempting job: " + js);
|
Slog.d(TAG, "preempting job: " + js);
|
||||||
}
|
}
|
||||||
// preferredUid will be set to uid of currently running job.
|
// preferredUid will be set to uid of currently running job, if appropriate.
|
||||||
assignment.context.cancelExecutingJobLocked(
|
assignment.context.cancelExecutingJobLocked(
|
||||||
assignment.preemptReasonCode,
|
assignment.preemptReasonCode,
|
||||||
JobParameters.INTERNAL_STOP_REASON_PREEMPT, assignment.preemptReason);
|
JobParameters.INTERNAL_STOP_REASON_PREEMPT, assignment.preemptReason);
|
||||||
@@ -811,9 +876,6 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
for (int s = stoppable.size() - 1; s >= 0; --s) {
|
for (int s = stoppable.size() - 1; s >= 0; --s) {
|
||||||
final ContextAssignment assignment = stoppable.get(s);
|
final ContextAssignment assignment = stoppable.get(s);
|
||||||
// The preferred UID is set when we cancel with PREEMPT reason, but don't preserve the
|
|
||||||
// UID for any stoppable contexts since we want to open the context up to any/all apps.
|
|
||||||
assignment.context.clearPreferredUid();
|
|
||||||
assignment.clear();
|
assignment.clear();
|
||||||
mContextAssignmentPool.release(assignment);
|
mContextAssignmentPool.release(assignment);
|
||||||
}
|
}
|
||||||
@@ -837,6 +899,21 @@ class JobConcurrencyManager {
|
|||||||
noteConcurrency();
|
noteConcurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
void onUidBiasChangedLocked(int prevBias, int newBias) {
|
||||||
|
if (prevBias != JobInfo.BIAS_TOP_APP && newBias != JobInfo.BIAS_TOP_APP) {
|
||||||
|
// TOP app didn't change. Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mService.getPendingJobQueue().size() == 0) {
|
||||||
|
// Nothing waiting for the top app to leave. Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Don't stop the TOP jobs directly. Instead, see if they would be replaced by some
|
||||||
|
// pending job (there may not always be something to replace them).
|
||||||
|
assignJobsToContextsLocked();
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
boolean stopJobOnServiceContextLocked(JobStatus job,
|
boolean stopJobOnServiceContextLocked(JobStatus job,
|
||||||
@JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
|
@JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
|
||||||
@@ -1041,7 +1118,13 @@ class JobConcurrencyManager {
|
|||||||
mWorkCountTracker.onJobFinished(workType);
|
mWorkCountTracker.onJobFinished(workType);
|
||||||
mRunningJobs.remove(jobStatus);
|
mRunningJobs.remove(jobStatus);
|
||||||
mActiveServices.remove(worker);
|
mActiveServices.remove(worker);
|
||||||
mIdleContexts.add(worker);
|
if (mIdleContexts.size() < MAX_RETAINED_OBJECTS) {
|
||||||
|
// Don't need to save all new contexts, but keep some extra around in case we need
|
||||||
|
// extras for another TOP+EJ overage.
|
||||||
|
mIdleContexts.add(worker);
|
||||||
|
} else {
|
||||||
|
mNumDroppedContexts++;
|
||||||
|
}
|
||||||
final PackageStats packageStats =
|
final PackageStats packageStats =
|
||||||
mActivePkgStats.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
|
mActivePkgStats.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
|
||||||
if (packageStats == null) {
|
if (packageStats == null) {
|
||||||
@@ -1054,6 +1137,14 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mActiveServices.size() >= STANDARD_CONCURRENCY_LIMIT) {
|
||||||
|
worker.clearPreferredUid();
|
||||||
|
// We're over the limit (because the TOP app scheduled a lot of EJs). Don't start
|
||||||
|
// running anything new until we get back below the limit.
|
||||||
|
noteConcurrency();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
|
final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue();
|
||||||
if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
|
if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
|
||||||
updateCounterConfigLocked();
|
updateCounterConfigLocked();
|
||||||
@@ -1245,6 +1336,18 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
} else if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
|
} else if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
|
||||||
return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
|
return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
|
||||||
|
} else if (js.startedAsExpeditedJob && js.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
|
||||||
|
// Try not to let TOP + EJ starve out other apps.
|
||||||
|
int topEjCount = 0;
|
||||||
|
for (int r = mRunningJobs.size() - 1; r >= 0; --r) {
|
||||||
|
JobStatus j = mRunningJobs.valueAt(r);
|
||||||
|
if (j.startedAsExpeditedJob && j.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
|
||||||
|
topEjCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (topEjCount > .5 * mWorkTypeConfig.getMaxTotal()) {
|
||||||
|
return "prevent top EJ dominance";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// No other pending EJs. Return null so we don't let regular jobs preempt an EJ.
|
// No other pending EJs. Return null so we don't let regular jobs preempt an EJ.
|
||||||
return null;
|
return null;
|
||||||
@@ -1361,10 +1464,10 @@ class JobConcurrencyManager {
|
|||||||
CONFIG_LIMITS_SCREEN_OFF.low.update(properties);
|
CONFIG_LIMITS_SCREEN_OFF.low.update(properties);
|
||||||
CONFIG_LIMITS_SCREEN_OFF.critical.update(properties);
|
CONFIG_LIMITS_SCREEN_OFF.critical.update(properties);
|
||||||
|
|
||||||
// Package concurrency limits must in the range [1, MAX_JOB_CONTEXTS_COUNT].
|
// Package concurrency limits must in the range [1, STANDARD_CONCURRENCY_LIMIT].
|
||||||
mPkgConcurrencyLimitEj = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT,
|
mPkgConcurrencyLimitEj = Math.max(1, Math.min(STANDARD_CONCURRENCY_LIMIT,
|
||||||
properties.getInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, DEFAULT_PKG_CONCURRENCY_LIMIT_EJ)));
|
properties.getInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, DEFAULT_PKG_CONCURRENCY_LIMIT_EJ)));
|
||||||
mPkgConcurrencyLimitRegular = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT,
|
mPkgConcurrencyLimitRegular = Math.max(1, Math.min(STANDARD_CONCURRENCY_LIMIT,
|
||||||
properties.getInt(
|
properties.getInt(
|
||||||
KEY_PKG_CONCURRENCY_LIMIT_REGULAR, DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR)));
|
KEY_PKG_CONCURRENCY_LIMIT_REGULAR, DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR)));
|
||||||
}
|
}
|
||||||
@@ -1492,6 +1595,13 @@ class JobConcurrencyManager {
|
|||||||
jsc.dumpLocked(pw, nowElapsed);
|
jsc.dumpLocked(pw, nowElapsed);
|
||||||
}
|
}
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
if (mNumDroppedContexts > 0) {
|
||||||
|
pw.println();
|
||||||
|
pw.print("Dropped ");
|
||||||
|
pw.print(mNumDroppedContexts);
|
||||||
|
pw.println(" contexts");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dumpProtoLocked(ProtoOutputStream proto, long tag, long now, long nowRealtime) {
|
public void dumpProtoLocked(ProtoOutputStream proto, long tag, long now, long nowRealtime) {
|
||||||
@@ -1600,7 +1710,7 @@ class JobConcurrencyManager {
|
|||||||
WorkTypeConfig(@NonNull String configIdentifier, int defaultMaxTotal,
|
WorkTypeConfig(@NonNull String configIdentifier, int defaultMaxTotal,
|
||||||
List<Pair<Integer, Integer>> defaultMin, List<Pair<Integer, Integer>> defaultMax) {
|
List<Pair<Integer, Integer>> defaultMin, List<Pair<Integer, Integer>> defaultMax) {
|
||||||
mConfigIdentifier = configIdentifier;
|
mConfigIdentifier = configIdentifier;
|
||||||
mDefaultMaxTotal = mMaxTotal = Math.min(defaultMaxTotal, MAX_JOB_CONTEXTS_COUNT);
|
mDefaultMaxTotal = mMaxTotal = Math.min(defaultMaxTotal, STANDARD_CONCURRENCY_LIMIT);
|
||||||
int numReserved = 0;
|
int numReserved = 0;
|
||||||
for (int i = defaultMin.size() - 1; i >= 0; --i) {
|
for (int i = defaultMin.size() - 1; i >= 0; --i) {
|
||||||
mDefaultMinReservedSlots.put(defaultMin.get(i).first, defaultMin.get(i).second);
|
mDefaultMinReservedSlots.put(defaultMin.get(i).first, defaultMin.get(i).second);
|
||||||
@@ -1621,8 +1731,8 @@ class JobConcurrencyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update(@NonNull DeviceConfig.Properties properties) {
|
void update(@NonNull DeviceConfig.Properties properties) {
|
||||||
// Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT].
|
// Ensure total in the range [1, STANDARD_CONCURRENCY_LIMIT].
|
||||||
mMaxTotal = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT,
|
mMaxTotal = Math.max(1, Math.min(STANDARD_CONCURRENCY_LIMIT,
|
||||||
properties.getInt(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mDefaultMaxTotal)));
|
properties.getInt(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mDefaultMaxTotal)));
|
||||||
|
|
||||||
mMaxAllowedSlots.clear();
|
mMaxAllowedSlots.clear();
|
||||||
|
|||||||
@@ -159,8 +159,6 @@ public class JobSchedulerService extends com.android.server.SystemService
|
|||||||
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
public static final boolean DEBUG_STANDBY = DEBUG || false;
|
public static final boolean DEBUG_STANDBY = DEBUG || false;
|
||||||
|
|
||||||
/** The maximum number of concurrent jobs we run at one time. */
|
|
||||||
static final int MAX_JOB_CONTEXTS_COUNT = 16;
|
|
||||||
/** The maximum number of jobs that we allow an app to schedule */
|
/** The maximum number of jobs that we allow an app to schedule */
|
||||||
private static final int MAX_JOBS_PER_APP = 150;
|
private static final int MAX_JOBS_PER_APP = 150;
|
||||||
/** The number of the most recently completed jobs to keep track of for debugging purposes. */
|
/** The number of the most recently completed jobs to keep track of for debugging purposes. */
|
||||||
@@ -1423,6 +1421,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
|||||||
for (int c = 0; c < mControllers.size(); ++c) {
|
for (int c = 0; c < mControllers.size(); ++c) {
|
||||||
mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
|
mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
|
||||||
}
|
}
|
||||||
|
mConcurrencyManager.onUidBiasChangedLocked(prevBias, newBias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -751,7 +751,8 @@ public final class JobServiceContext implements ServiceConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mParams.setStopReason(stopReasonCode, internalStopReasonCode, debugReason);
|
mParams.setStopReason(stopReasonCode, internalStopReasonCode, debugReason);
|
||||||
if (internalStopReasonCode == JobParameters.INTERNAL_STOP_REASON_PREEMPT) {
|
if (stopReasonCode == JobParameters.STOP_REASON_PREEMPT) {
|
||||||
|
// Only preserve the UID when we're preempting the job for another one of the same UID.
|
||||||
mPreferredUid = mRunningJob != null ? mRunningJob.getUid() : NO_PREFERRED_UID;
|
mPreferredUid = mRunningJob != null ? mRunningJob.getUid() : NO_PREFERRED_UID;
|
||||||
}
|
}
|
||||||
handleCancelLocked(debugReason);
|
handleCancelLocked(debugReason);
|
||||||
|
|||||||
@@ -297,9 +297,9 @@ public final class QuotaController extends StateController {
|
|||||||
private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
|
private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of jobs that started while the UID was in the TOP state. There will be no more than
|
* List of jobs that started while the UID was in the TOP state. There will usually be no more
|
||||||
* 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
|
* than {@value JobConcurrencyManager#MAX_STANDARD_JOB_CONCURRENCY} running at once, so an
|
||||||
* fine.
|
* ArraySet is fine.
|
||||||
*/
|
*/
|
||||||
private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
|
private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
|
||||||
|
|
||||||
|
|||||||
@@ -289,9 +289,9 @@ public class TareController extends StateController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of jobs that started while the UID was in the TOP state. There will be no more than
|
* List of jobs that started while the UID was in the TOP state. There will usually be no more
|
||||||
* 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
|
* than {@value JobConcurrencyManager#MAX_STANDARD_JOB_CONCURRENCY} running at once, so an
|
||||||
* fine.
|
* ArraySet is fine.
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
|
private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
|
||||||
|
|||||||
@@ -116,14 +116,14 @@ public final class JobConcurrencyManagerTest {
|
|||||||
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
|
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
|
||||||
|
|
||||||
// Pending jobs shouldn't affect TOP job's status.
|
// Pending jobs shouldn't affect TOP job's status.
|
||||||
for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
|
for (int i = 1; i <= JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
|
||||||
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
|
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
|
||||||
mPendingJobQueue.add(job);
|
mPendingJobQueue.add(job);
|
||||||
}
|
}
|
||||||
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
|
assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
|
||||||
|
|
||||||
// Already running jobs shouldn't affect TOP job's status.
|
// Already running jobs shouldn't affect TOP job's status.
|
||||||
for (int i = 1; i <= JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
|
for (int i = 1; i <= JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
|
||||||
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, i);
|
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, i);
|
||||||
mJobConcurrencyManager.addRunningJobForTesting(job);
|
mJobConcurrencyManager.addRunningJobForTesting(job);
|
||||||
}
|
}
|
||||||
@@ -183,9 +183,9 @@ public final class JobConcurrencyManagerTest {
|
|||||||
spyOn(testEj);
|
spyOn(testEj);
|
||||||
doReturn(true).when(testEj).shouldTreatAsExpeditedJob();
|
doReturn(true).when(testEj).shouldTreatAsExpeditedJob();
|
||||||
|
|
||||||
setConcurrencyConfig(JobSchedulerService.MAX_JOB_CONTEXTS_COUNT);
|
setConcurrencyConfig(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT);
|
||||||
|
|
||||||
for (int i = 0; i < JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
|
for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
|
||||||
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i, i + 1);
|
final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i, i + 1);
|
||||||
mPendingJobQueue.add(job);
|
mPendingJobQueue.add(job);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,14 +57,14 @@ public class BiasSchedulingTest extends AndroidTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testLowerBiasJobPreempted() throws Exception {
|
public void testLowerBiasJobPreempted() throws Exception {
|
||||||
for (int i = 0; i < JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
|
for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
|
||||||
JobInfo job = new JobInfo.Builder(100 + i, sJobServiceComponent)
|
JobInfo job = new JobInfo.Builder(100 + i, sJobServiceComponent)
|
||||||
.setBias(LOW_BIAS)
|
.setBias(LOW_BIAS)
|
||||||
.setOverrideDeadline(0)
|
.setOverrideDeadline(0)
|
||||||
.build();
|
.build();
|
||||||
mJobScheduler.schedule(job);
|
mJobScheduler.schedule(job);
|
||||||
}
|
}
|
||||||
final int higherBiasJobId = 100 + JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
|
final int higherBiasJobId = 100 + JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT;
|
||||||
JobInfo jobHigher = new JobInfo.Builder(higherBiasJobId, sJobServiceComponent)
|
JobInfo jobHigher = new JobInfo.Builder(higherBiasJobId, sJobServiceComponent)
|
||||||
.setBias(HIGH_BIAS)
|
.setBias(HIGH_BIAS)
|
||||||
.setMinimumLatency(2000)
|
.setMinimumLatency(2000)
|
||||||
@@ -88,14 +88,14 @@ public class BiasSchedulingTest extends AndroidTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testHigherBiasJobNotPreempted() throws Exception {
|
public void testHigherBiasJobNotPreempted() throws Exception {
|
||||||
for (int i = 0; i < JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; ++i) {
|
for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
|
||||||
JobInfo job = new JobInfo.Builder(100 + i, sJobServiceComponent)
|
JobInfo job = new JobInfo.Builder(100 + i, sJobServiceComponent)
|
||||||
.setBias(HIGH_BIAS)
|
.setBias(HIGH_BIAS)
|
||||||
.setOverrideDeadline(0)
|
.setOverrideDeadline(0)
|
||||||
.build();
|
.build();
|
||||||
mJobScheduler.schedule(job);
|
mJobScheduler.schedule(job);
|
||||||
}
|
}
|
||||||
final int lowerBiasJobId = 100 + JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
|
final int lowerBiasJobId = 100 + JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT;
|
||||||
JobInfo jobLower = new JobInfo.Builder(lowerBiasJobId, sJobServiceComponent)
|
JobInfo jobLower = new JobInfo.Builder(lowerBiasJobId, sJobServiceComponent)
|
||||||
.setBias(LOW_BIAS)
|
.setBias(LOW_BIAS)
|
||||||
.setMinimumLatency(2000)
|
.setMinimumLatency(2000)
|
||||||
|
|||||||
Reference in New Issue
Block a user