Merge "Migrate QuotaController to DeviceConfig."

This commit is contained in:
TreeHugger Robot
2020-10-27 22:36:29 +00:00
committed by Android (Google) Code Review
9 changed files with 545 additions and 390 deletions

View File

@@ -339,6 +339,11 @@ public class JobSchedulerService extends com.android.server.SystemService
public void onPropertiesChanged(DeviceConfig.Properties properties) {
boolean apiQuotaScheduleUpdated = false;
boolean concurrencyUpdated = false;
for (int controller = 0; controller < mControllers.size(); controller++) {
final StateController sc = mControllers.get(controller);
sc.prepareForUpdatedConstantsLocked();
}
synchronized (mLock) {
for (String name : properties.getKeyset()) {
if (name == null) {
@@ -384,6 +389,11 @@ public class JobSchedulerService extends com.android.server.SystemService
&& !concurrencyUpdated) {
mConstants.updateConcurrencyConstantsLocked();
concurrencyUpdated = true;
} else {
for (int ctrlr = 0; ctrlr < mControllers.size(); ctrlr++) {
final StateController sc = mControllers.get(ctrlr);
sc.processConstantLocked(properties, name);
}
}
break;
}

View File

@@ -1388,6 +1388,11 @@ public final class JobStatus {
}
if (isReady()) {
sb.append(" READY");
} else {
sb.append(" satisfied:0x").append(Integer.toHexString(satisfiedConstraints));
sb.append(" unsatisfied:0x").append(Integer.toHexString(
(satisfiedConstraints & mRequiredConstraintsOfInterest)
^ mRequiredConstraintsOfInterest));
}
sb.append("}");
return sb.toString();

View File

@@ -37,12 +37,9 @@ import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.Handler;
@@ -50,10 +47,9 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -494,7 +490,7 @@ public final class QuotaController extends StateController {
mChargeTracker.startTracking();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mQcConstants = new QcConstants(mHandler);
mQcConstants = new QcConstants();
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null);
@@ -512,11 +508,6 @@ public final class QuotaController extends StateController {
}
}
@Override
public void onSystemServicesReady() {
mQcConstants.start(mContext.getContentResolver());
}
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
final int userId = jobStatus.getSourceUserId();
@@ -2028,38 +2019,109 @@ public final class QuotaController extends StateController {
}
}
@VisibleForTesting
class QcConstants extends ContentObserver {
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@Override
public void prepareForUpdatedConstantsLocked() {
mQcConstants.mShouldReevaluateConstraints = false;
mQcConstants.mRateLimitingConstantsUpdated = false;
mQcConstants.mExecutionPeriodConstantsUpdated = false;
}
private static final String KEY_ALLOWED_TIME_PER_PERIOD_MS = "allowed_time_per_period_ms";
private static final String KEY_IN_QUOTA_BUFFER_MS = "in_quota_buffer_ms";
private static final String KEY_WINDOW_SIZE_ACTIVE_MS = "window_size_active_ms";
private static final String KEY_WINDOW_SIZE_WORKING_MS = "window_size_working_ms";
private static final String KEY_WINDOW_SIZE_FREQUENT_MS = "window_size_frequent_ms";
private static final String KEY_WINDOW_SIZE_RARE_MS = "window_size_rare_ms";
private static final String KEY_WINDOW_SIZE_RESTRICTED_MS = "window_size_restricted_ms";
private static final String KEY_MAX_EXECUTION_TIME_MS = "max_execution_time_ms";
private static final String KEY_MAX_JOB_COUNT_ACTIVE = "max_job_count_active";
private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working";
private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent";
private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
private static final String KEY_MAX_JOB_COUNT_RESTRICTED = "max_job_count_restricted";
private static final String KEY_RATE_LIMITING_WINDOW_MS = "rate_limiting_window_ms";
private static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
"max_job_count_per_rate_limiting_window";
private static final String KEY_MAX_SESSION_COUNT_ACTIVE = "max_session_count_active";
private static final String KEY_MAX_SESSION_COUNT_WORKING = "max_session_count_working";
private static final String KEY_MAX_SESSION_COUNT_FREQUENT = "max_session_count_frequent";
private static final String KEY_MAX_SESSION_COUNT_RARE = "max_session_count_rare";
private static final String KEY_MAX_SESSION_COUNT_RESTRICTED =
"max_session_count_restricted";
private static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
"max_session_count_per_rate_limiting_window";
private static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
"timing_session_coalescing_duration_ms";
private static final String KEY_MIN_QUOTA_CHECK_DELAY_MS = "min_quota_check_delay_ms";
@Override
public void processConstantLocked(DeviceConfig.Properties properties, String key) {
mQcConstants.processConstantLocked(properties, key);
}
@Override
public void onConstantsUpdatedLocked() {
if (mQcConstants.mShouldReevaluateConstraints) {
// Update job bookkeeping out of band.
JobSchedulerBackgroundThread.getHandler().post(() -> {
synchronized (mLock) {
invalidateAllExecutionStatsLocked();
maybeUpdateAllConstraintsLocked();
}
});
}
}
@VisibleForTesting
class QcConstants {
private boolean mShouldReevaluateConstraints = false;
private boolean mRateLimitingConstantsUpdated = false;
private boolean mExecutionPeriodConstantsUpdated = false;
/** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
private static final String QC_CONSTANT_PREFIX = "qc_";
@VisibleForTesting
static final String KEY_ALLOWED_TIME_PER_PERIOD_MS =
QC_CONSTANT_PREFIX + "allowed_time_per_period_ms";
@VisibleForTesting
static final String KEY_IN_QUOTA_BUFFER_MS =
QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@VisibleForTesting
static final String KEY_WINDOW_SIZE_ACTIVE_MS =
QC_CONSTANT_PREFIX + "window_size_active_ms";
@VisibleForTesting
static final String KEY_WINDOW_SIZE_WORKING_MS =
QC_CONSTANT_PREFIX + "window_size_working_ms";
@VisibleForTesting
static final String KEY_WINDOW_SIZE_FREQUENT_MS =
QC_CONSTANT_PREFIX + "window_size_frequent_ms";
@VisibleForTesting
static final String KEY_WINDOW_SIZE_RARE_MS =
QC_CONSTANT_PREFIX + "window_size_rare_ms";
@VisibleForTesting
static final String KEY_WINDOW_SIZE_RESTRICTED_MS =
QC_CONSTANT_PREFIX + "window_size_restricted_ms";
@VisibleForTesting
static final String KEY_MAX_EXECUTION_TIME_MS =
QC_CONSTANT_PREFIX + "max_execution_time_ms";
@VisibleForTesting
static final String KEY_MAX_JOB_COUNT_ACTIVE =
QC_CONSTANT_PREFIX + "max_job_count_active";
@VisibleForTesting
static final String KEY_MAX_JOB_COUNT_WORKING =
QC_CONSTANT_PREFIX + "max_job_count_working";
@VisibleForTesting
static final String KEY_MAX_JOB_COUNT_FREQUENT =
QC_CONSTANT_PREFIX + "max_job_count_frequent";
@VisibleForTesting
static final String KEY_MAX_JOB_COUNT_RARE =
QC_CONSTANT_PREFIX + "max_job_count_rare";
@VisibleForTesting
static final String KEY_MAX_JOB_COUNT_RESTRICTED =
QC_CONSTANT_PREFIX + "max_job_count_restricted";
@VisibleForTesting
static final String KEY_RATE_LIMITING_WINDOW_MS =
QC_CONSTANT_PREFIX + "rate_limiting_window_ms";
@VisibleForTesting
static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
QC_CONSTANT_PREFIX + "max_job_count_per_rate_limiting_window";
@VisibleForTesting
static final String KEY_MAX_SESSION_COUNT_ACTIVE =
QC_CONSTANT_PREFIX + "max_session_count_active";
@VisibleForTesting
static final String KEY_MAX_SESSION_COUNT_WORKING =
QC_CONSTANT_PREFIX + "max_session_count_working";
@VisibleForTesting
static final String KEY_MAX_SESSION_COUNT_FREQUENT =
QC_CONSTANT_PREFIX + "max_session_count_frequent";
@VisibleForTesting
static final String KEY_MAX_SESSION_COUNT_RARE =
QC_CONSTANT_PREFIX + "max_session_count_rare";
@VisibleForTesting
static final String KEY_MAX_SESSION_COUNT_RESTRICTED =
QC_CONSTANT_PREFIX + "max_session_count_restricted";
@VisibleForTesting
static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
QC_CONSTANT_PREFIX + "max_session_count_per_rate_limiting_window";
@VisibleForTesting
static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
QC_CONSTANT_PREFIX + "timing_session_coalescing_duration_ms";
@VisibleForTesting
static final String KEY_MIN_QUOTA_CHECK_DELAY_MS =
QC_CONSTANT_PREFIX + "min_quota_check_delay_ms";
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
10 * 60 * 1000L; // 10 minutes
@@ -2260,238 +2322,273 @@ public final class QuotaController extends StateController {
/** The minimum value that {@link #RATE_LIMITING_WINDOW_MS} can have. */
private static final long MIN_RATE_LIMITING_WINDOW_MS = 30 * SECOND_IN_MILLIS;
QcConstants(Handler handler) {
super(handler);
}
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
switch (key) {
case KEY_ALLOWED_TIME_PER_PERIOD_MS:
case KEY_IN_QUOTA_BUFFER_MS:
case KEY_MAX_EXECUTION_TIME_MS:
case KEY_WINDOW_SIZE_ACTIVE_MS:
case KEY_WINDOW_SIZE_WORKING_MS:
case KEY_WINDOW_SIZE_FREQUENT_MS:
case KEY_WINDOW_SIZE_RARE_MS:
case KEY_WINDOW_SIZE_RESTRICTED_MS:
updateExecutionPeriodConstantsLocked();
break;
private void start(ContentResolver resolver) {
mResolver = resolver;
mResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS), false, this);
onChange(true, null);
}
case KEY_RATE_LIMITING_WINDOW_MS:
case KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW:
case KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW:
updateRateLimitingConstantsLocked();
break;
@Override
public void onChange(boolean selfChange, Uri uri) {
final String constants = Settings.Global.getString(
mResolver, Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
try {
mParser.setString(constants);
} catch (Exception e) {
// Failed to parse the settings string, log this and move on with defaults.
Slog.e(TAG, "Bad jobscheduler quota controller settings", e);
case KEY_MAX_JOB_COUNT_ACTIVE:
MAX_JOB_COUNT_ACTIVE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_ACTIVE);
int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_JOB_COUNT_WORKING:
MAX_JOB_COUNT_WORKING = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_WORKING);
int newWorkingMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
MAX_JOB_COUNT_WORKING);
if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_JOB_COUNT_FREQUENT:
MAX_JOB_COUNT_FREQUENT = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_FREQUENT);
int newFrequentMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
MAX_JOB_COUNT_FREQUENT);
if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_JOB_COUNT_RARE:
MAX_JOB_COUNT_RARE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_RARE);
int newRareMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE);
if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_JOB_COUNT_RESTRICTED:
MAX_JOB_COUNT_RESTRICTED =
properties.getInt(key, DEFAULT_MAX_JOB_COUNT_RESTRICTED);
int newRestrictedMaxJobCount =
Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RESTRICTED);
if (mMaxBucketJobCounts[RESTRICTED_INDEX] != newRestrictedMaxJobCount) {
mMaxBucketJobCounts[RESTRICTED_INDEX] = newRestrictedMaxJobCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_SESSION_COUNT_ACTIVE:
MAX_SESSION_COUNT_ACTIVE =
properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
int newActiveMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_ACTIVE);
if (mMaxBucketSessionCounts[ACTIVE_INDEX] != newActiveMaxSessionCount) {
mMaxBucketSessionCounts[ACTIVE_INDEX] = newActiveMaxSessionCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_SESSION_COUNT_WORKING:
MAX_SESSION_COUNT_WORKING =
properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_WORKING);
int newWorkingMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_WORKING);
if (mMaxBucketSessionCounts[WORKING_INDEX] != newWorkingMaxSessionCount) {
mMaxBucketSessionCounts[WORKING_INDEX] = newWorkingMaxSessionCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_SESSION_COUNT_FREQUENT:
MAX_SESSION_COUNT_FREQUENT =
properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
int newFrequentMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_FREQUENT);
if (mMaxBucketSessionCounts[FREQUENT_INDEX] != newFrequentMaxSessionCount) {
mMaxBucketSessionCounts[FREQUENT_INDEX] = newFrequentMaxSessionCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_SESSION_COUNT_RARE:
MAX_SESSION_COUNT_RARE = properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_RARE);
int newRareMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_RARE);
if (mMaxBucketSessionCounts[RARE_INDEX] != newRareMaxSessionCount) {
mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MAX_SESSION_COUNT_RESTRICTED:
MAX_SESSION_COUNT_RESTRICTED =
properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_RESTRICTED);
int newRestrictedMaxSessionCount = Math.max(0, MAX_SESSION_COUNT_RESTRICTED);
if (mMaxBucketSessionCounts[RESTRICTED_INDEX] != newRestrictedMaxSessionCount) {
mMaxBucketSessionCounts[RESTRICTED_INDEX] = newRestrictedMaxSessionCount;
mShouldReevaluateConstraints = true;
}
break;
case KEY_TIMING_SESSION_COALESCING_DURATION_MS:
TIMING_SESSION_COALESCING_DURATION_MS =
properties.getLong(key, DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS);
long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
mTimingSessionCoalescingDurationMs = newSessionCoalescingDurationMs;
mShouldReevaluateConstraints = true;
}
break;
case KEY_MIN_QUOTA_CHECK_DELAY_MS:
MIN_QUOTA_CHECK_DELAY_MS =
properties.getLong(key, DEFAULT_MIN_QUOTA_CHECK_DELAY_MS);
// We don't need to re-evaluate execution stats or constraint status for this.
// Limit the delay to the range [0, 15] minutes.
mInQuotaAlarmListener.setMinQuotaCheckDelayMs(
Math.min(15 * MINUTE_IN_MILLIS, Math.max(0, MIN_QUOTA_CHECK_DELAY_MS)));
break;
}
ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
KEY_ALLOWED_TIME_PER_PERIOD_MS, DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
KEY_IN_QUOTA_BUFFER_MS, DEFAULT_IN_QUOTA_BUFFER_MS);
WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
KEY_WINDOW_SIZE_ACTIVE_MS, DEFAULT_WINDOW_SIZE_ACTIVE_MS);
WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS);
WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
KEY_WINDOW_SIZE_FREQUENT_MS, DEFAULT_WINDOW_SIZE_FREQUENT_MS);
WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS);
WINDOW_SIZE_RESTRICTED_MS = mParser.getDurationMillis(
KEY_WINDOW_SIZE_RESTRICTED_MS, DEFAULT_WINDOW_SIZE_RESTRICTED_MS);
MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS);
MAX_JOB_COUNT_ACTIVE = mParser.getInt(
KEY_MAX_JOB_COUNT_ACTIVE, DEFAULT_MAX_JOB_COUNT_ACTIVE);
MAX_JOB_COUNT_WORKING = mParser.getInt(
KEY_MAX_JOB_COUNT_WORKING, DEFAULT_MAX_JOB_COUNT_WORKING);
MAX_JOB_COUNT_FREQUENT = mParser.getInt(
KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT);
MAX_JOB_COUNT_RARE = mParser.getInt(
KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
MAX_JOB_COUNT_RESTRICTED = mParser.getInt(
KEY_MAX_JOB_COUNT_RESTRICTED, DEFAULT_MAX_JOB_COUNT_RESTRICTED);
RATE_LIMITING_WINDOW_MS = mParser.getLong(
KEY_RATE_LIMITING_WINDOW_MS, DEFAULT_RATE_LIMITING_WINDOW_MS);
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
MAX_SESSION_COUNT_ACTIVE = mParser.getInt(
KEY_MAX_SESSION_COUNT_ACTIVE, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
MAX_SESSION_COUNT_WORKING = mParser.getInt(
KEY_MAX_SESSION_COUNT_WORKING, DEFAULT_MAX_SESSION_COUNT_WORKING);
MAX_SESSION_COUNT_FREQUENT = mParser.getInt(
KEY_MAX_SESSION_COUNT_FREQUENT, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
MAX_SESSION_COUNT_RARE = mParser.getInt(
KEY_MAX_SESSION_COUNT_RARE, DEFAULT_MAX_SESSION_COUNT_RARE);
MAX_SESSION_COUNT_RESTRICTED = mParser.getInt(
KEY_MAX_SESSION_COUNT_RESTRICTED, DEFAULT_MAX_SESSION_COUNT_RESTRICTED);
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
TIMING_SESSION_COALESCING_DURATION_MS = mParser.getLong(
KEY_TIMING_SESSION_COALESCING_DURATION_MS,
DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS);
MIN_QUOTA_CHECK_DELAY_MS = mParser.getDurationMillis(KEY_MIN_QUOTA_CHECK_DELAY_MS,
DEFAULT_MIN_QUOTA_CHECK_DELAY_MS);
updateConstants();
}
@VisibleForTesting
void updateConstants() {
synchronized (mLock) {
boolean changed = false;
private void updateExecutionPeriodConstantsLocked() {
if (mExecutionPeriodConstantsUpdated) {
return;
}
mExecutionPeriodConstantsUpdated = true;
long newMaxExecutionTimeMs = Math.max(MIN_MAX_EXECUTION_TIME_MS,
Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
mMaxExecutionTimeMs = newMaxExecutionTimeMs;
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
changed = true;
}
long newAllowedTimeMs = Math.min(mMaxExecutionTimeMs,
Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
mAllowedTimePerPeriodMs = newAllowedTimeMs;
mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
changed = true;
}
// Make sure quota buffer is non-negative, not greater than allowed time per period,
// and no more than 5 minutes.
long newQuotaBufferMs = Math.max(0, Math.min(mAllowedTimePerPeriodMs,
Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)));
if (mQuotaBufferMs != newQuotaBufferMs) {
mQuotaBufferMs = newQuotaBufferMs;
mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
changed = true;
}
long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
changed = true;
}
long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
changed = true;
}
long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
changed = true;
}
long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS));
if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
changed = true;
}
// Fit in the range [allowed time (10 mins), 1 week].
long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
changed = true;
}
long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS,
Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS));
if (mRateLimitingWindowMs != newRateLimitingWindowMs) {
mRateLimitingWindowMs = newRateLimitingWindowMs;
changed = true;
}
int newMaxJobCountPerRateLimitingWindow = Math.max(
MIN_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
if (mMaxJobCountPerRateLimitingWindow != newMaxJobCountPerRateLimitingWindow) {
mMaxJobCountPerRateLimitingWindow = newMaxJobCountPerRateLimitingWindow;
changed = true;
}
int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
changed = true;
}
int newWorkingMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_WORKING);
if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
changed = true;
}
int newFrequentMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_FREQUENT);
if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
changed = true;
}
int newRareMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE);
if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
changed = true;
}
int newRestrictedMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
MAX_JOB_COUNT_RESTRICTED);
if (mMaxBucketJobCounts[RESTRICTED_INDEX] != newRestrictedMaxJobCount) {
mMaxBucketJobCounts[RESTRICTED_INDEX] = newRestrictedMaxJobCount;
changed = true;
}
int newMaxSessionCountPerRateLimitPeriod = Math.max(
MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
if (mMaxSessionCountPerRateLimitingWindow != newMaxSessionCountPerRateLimitPeriod) {
mMaxSessionCountPerRateLimitingWindow = newMaxSessionCountPerRateLimitPeriod;
changed = true;
}
int newActiveMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_ACTIVE);
if (mMaxBucketSessionCounts[ACTIVE_INDEX] != newActiveMaxSessionCount) {
mMaxBucketSessionCounts[ACTIVE_INDEX] = newActiveMaxSessionCount;
changed = true;
}
int newWorkingMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_WORKING);
if (mMaxBucketSessionCounts[WORKING_INDEX] != newWorkingMaxSessionCount) {
mMaxBucketSessionCounts[WORKING_INDEX] = newWorkingMaxSessionCount;
changed = true;
}
int newFrequentMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_FREQUENT);
if (mMaxBucketSessionCounts[FREQUENT_INDEX] != newFrequentMaxSessionCount) {
mMaxBucketSessionCounts[FREQUENT_INDEX] = newFrequentMaxSessionCount;
changed = true;
}
int newRareMaxSessionCount =
Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_RARE);
if (mMaxBucketSessionCounts[RARE_INDEX] != newRareMaxSessionCount) {
mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount;
changed = true;
}
int newRestrictedMaxSessionCount = Math.max(0, MAX_SESSION_COUNT_RESTRICTED);
if (mMaxBucketSessionCounts[RESTRICTED_INDEX] != newRestrictedMaxSessionCount) {
mMaxBucketSessionCounts[RESTRICTED_INDEX] = newRestrictedMaxSessionCount;
changed = true;
}
long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
mTimingSessionCoalescingDurationMs = newSessionCoalescingDurationMs;
changed = true;
}
// Don't set changed to true for this one since we don't need to re-evaluate
// execution stats or constraint status. Limit the delay to the range [0, 15]
// minutes.
mInQuotaAlarmListener.setMinQuotaCheckDelayMs(
Math.min(15 * MINUTE_IN_MILLIS, Math.max(0, MIN_QUOTA_CHECK_DELAY_MS)));
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_ALLOWED_TIME_PER_PERIOD_MS, KEY_IN_QUOTA_BUFFER_MS,
KEY_MAX_EXECUTION_TIME_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
KEY_WINDOW_SIZE_WORKING_MS,
KEY_WINDOW_SIZE_FREQUENT_MS, KEY_WINDOW_SIZE_RARE_MS,
KEY_WINDOW_SIZE_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
DEFAULT_WINDOW_SIZE_ACTIVE_MS);
WINDOW_SIZE_WORKING_MS =
properties.getLong(KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS);
WINDOW_SIZE_FREQUENT_MS =
properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS,
DEFAULT_WINDOW_SIZE_FREQUENT_MS);
WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS,
DEFAULT_WINDOW_SIZE_RARE_MS);
WINDOW_SIZE_RESTRICTED_MS =
properties.getLong(KEY_WINDOW_SIZE_RESTRICTED_MS,
DEFAULT_WINDOW_SIZE_RESTRICTED_MS);
if (changed) {
// Update job bookkeeping out of band.
JobSchedulerBackgroundThread.getHandler().post(() -> {
synchronized (mLock) {
invalidateAllExecutionStatsLocked();
maybeUpdateAllConstraintsLocked();
}
});
}
long newMaxExecutionTimeMs = Math.max(MIN_MAX_EXECUTION_TIME_MS,
Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
mMaxExecutionTimeMs = newMaxExecutionTimeMs;
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
long newAllowedTimeMs = Math.min(mMaxExecutionTimeMs,
Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
mAllowedTimePerPeriodMs = newAllowedTimeMs;
mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
// Make sure quota buffer is non-negative, not greater than allowed time per period,
// and no more than 5 minutes.
long newQuotaBufferMs = Math.max(0, Math.min(mAllowedTimePerPeriodMs,
Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)));
if (mQuotaBufferMs != newQuotaBufferMs) {
mQuotaBufferMs = newQuotaBufferMs;
mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
mShouldReevaluateConstraints = true;
}
long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
mShouldReevaluateConstraints = true;
}
long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
mShouldReevaluateConstraints = true;
}
long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS));
if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
mShouldReevaluateConstraints = true;
}
// Fit in the range [allowed time (10 mins), 1 week].
long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
mShouldReevaluateConstraints = true;
}
}
private void updateRateLimitingConstantsLocked() {
if (mRateLimitingConstantsUpdated) {
return;
}
mRateLimitingConstantsUpdated = true;
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_RATE_LIMITING_WINDOW_MS, KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
RATE_LIMITING_WINDOW_MS =
properties.getLong(KEY_RATE_LIMITING_WINDOW_MS,
DEFAULT_RATE_LIMITING_WINDOW_MS);
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
properties.getInt(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
properties.getInt(KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS,
Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS));
if (mRateLimitingWindowMs != newRateLimitingWindowMs) {
mRateLimitingWindowMs = newRateLimitingWindowMs;
mShouldReevaluateConstraints = true;
}
int newMaxJobCountPerRateLimitingWindow = Math.max(
MIN_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
if (mMaxJobCountPerRateLimitingWindow != newMaxJobCountPerRateLimitingWindow) {
mMaxJobCountPerRateLimitingWindow = newMaxJobCountPerRateLimitingWindow;
mShouldReevaluateConstraints = true;
}
int newMaxSessionCountPerRateLimitPeriod = Math.max(
MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
if (mMaxSessionCountPerRateLimitingWindow != newMaxSessionCountPerRateLimitPeriod) {
mMaxSessionCountPerRateLimitingWindow = newMaxSessionCountPerRateLimitPeriod;
mShouldReevaluateConstraints = true;
}
}
@@ -2633,6 +2730,11 @@ public final class QuotaController extends StateController {
return mMaxSessionCountPerRateLimitingWindow;
}
@VisibleForTesting
long getMinQuotaCheckDelayMs() {
return mInQuotaAlarmListener.mMinQuotaCheckDelayMs;
}
@VisibleForTesting
long getRateLimitingWindowMs() {
return mRateLimitingWindowMs;

View File

@@ -18,7 +18,9 @@ package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.DEBUG;
import android.annotation.NonNull;
import android.content.Context;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -84,6 +86,13 @@ public abstract class StateController {
public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) {
}
/** Notice that updated configuration constants are about to be read. */
public void prepareForUpdatedConstantsLocked() {}
/** Process the specified constant and update internal constants if relevant. */
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {}
/**
* Called when the JobScheduler.Constants are updated.
*/

View File

@@ -11887,21 +11887,6 @@ public final class Settings {
*/
public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants";
/**
* Job scheduler QuotaController specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
* "max_job_count_working=5,max_job_count_rare=2"
*
* <p>
* Type: string
*
* @hide
* @see com.android.server.job.JobSchedulerService.Constants
*/
public static final String JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS =
"job_scheduler_quota_controller_constants";
/**
* ShortcutManager specific settings.
* This is encoded as a key=value list, separated by commas. Ex:

View File

@@ -510,7 +510,7 @@ message GlobalSettingsProto {
optional IntentFirewall intent_firewall = 65;
reserved 66; // job_scheduler_constants
optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
reserved 149; // job_scheduler_quota_controller_constants
reserved 150; // job_scheduler_time_controller_constants
optional SettingProto keep_profile_in_background = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];

View File

@@ -862,9 +862,6 @@ class SettingsProtoDumpUtil {
GlobalSettingsProto.IntentFirewall.UPDATE_METADATA_URL);
p.end(intentFirewallToken);
dumpSetting(s, p,
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
dumpSetting(s, p,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
GlobalSettingsProto.KEEP_PROFILE_IN_BACKGROUND);

View File

@@ -309,7 +309,6 @@ public class SettingsBackupTest {
Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
Settings.Global.KERNEL_CPU_THREAD_READER,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,

View File

@@ -16,6 +16,7 @@
package com.android.server.job.controllers;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
@@ -27,6 +28,7 @@ import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
@@ -39,6 +41,7 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
@@ -68,6 +71,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.SparseBooleanArray;
import androidx.test.runner.AndroidJUnit4;
@@ -78,6 +82,7 @@ import com.android.server.job.JobSchedulerService.Constants;
import com.android.server.job.JobServiceContext;
import com.android.server.job.JobStore;
import com.android.server.job.controllers.QuotaController.ExecutionStats;
import com.android.server.job.controllers.QuotaController.QcConstants;
import com.android.server.job.controllers.QuotaController.TimingSession;
import com.android.server.usage.AppStandbyInternal;
@@ -86,16 +91,19 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.time.Clock;
import java.time.Duration;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
public class QuotaControllerTest {
@@ -113,6 +121,7 @@ public class QuotaControllerTest {
private QuotaController.QcConstants mQcConstants;
private int mSourceUid;
private IUidObserver mUidObserver;
DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private MockitoSession mMockingSession;
@Mock
@@ -133,6 +142,7 @@ public class QuotaControllerTest {
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
.spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
.startMocking();
@@ -164,6 +174,18 @@ public class QuotaControllerTest {
// Used in QuotaController.Handler.
mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir());
when(mJobSchedulerService.getJobStore()).thenReturn(mJobStore);
// Used in QuotaController.QcConstants
doAnswer((Answer<Void>) invocationOnMock -> null)
.when(() -> DeviceConfig.addOnPropertiesChangedListener(
anyString(), any(Executor.class),
any(DeviceConfig.OnPropertiesChangedListener.class)));
mDeviceConfigPropertiesBuilder =
new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
doAnswer(
(Answer<DeviceConfig.Properties>) invocationOnMock
-> mDeviceConfigPropertiesBuilder.build())
.when(() -> DeviceConfig.getProperties(
eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.<String>any()));
// Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
// in the past, and QuotaController sometimes floors values at 0, so if the test time
@@ -313,6 +335,18 @@ public class QuotaControllerTest {
return new TimingSession(start, start + duration, count);
}
private void setDeviceConfigLong(String key, long val) {
mQuotaController.prepareForUpdatedConstantsLocked();
mDeviceConfigPropertiesBuilder.setLong(key, val);
mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
}
private void setDeviceConfigInt(String key, int val) {
mQuotaController.prepareForUpdatedConstantsLocked();
mDeviceConfigPropertiesBuilder.setInt(key, val);
mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
}
@Test
public void testSaveTimingSession() {
assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
@@ -858,8 +892,7 @@ public class QuotaControllerTest {
advanceElapsedClock(40 * MINUTE_IN_MILLIS);
}
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 0;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -871,8 +904,7 @@ public class QuotaControllerTest {
assertEquals(160, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 500;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -884,8 +916,7 @@ public class QuotaControllerTest {
assertEquals(110, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 1000;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -897,8 +928,8 @@ public class QuotaControllerTest {
assertEquals(110, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * SECOND_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
5 * SECOND_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -910,8 +941,8 @@ public class QuotaControllerTest {
assertEquals(70, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = MINUTE_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
MINUTE_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -923,8 +954,8 @@ public class QuotaControllerTest {
assertEquals(20, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * MINUTE_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
5 * MINUTE_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -936,8 +967,8 @@ public class QuotaControllerTest {
assertEquals(10, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 15 * MINUTE_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
15 * MINUTE_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -951,8 +982,7 @@ public class QuotaControllerTest {
// QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
// between an hour and 15 minutes.
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = HOUR_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -994,8 +1024,7 @@ public class QuotaControllerTest {
// Advance clock so that the working stats shouldn't be the same.
advanceElapsedClock(MINUTE_IN_MILLIS);
// Change frequent bucket size so that the stats need to be recalculated.
mQcConstants.WINDOW_SIZE_FREQUENT_MS = 6 * HOUR_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 6 * HOUR_IN_MILLIS);
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
@@ -1413,11 +1442,10 @@ public class QuotaControllerTest {
public void testIsWithinQuotaLocked_TimingSession() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQcConstants.MAX_SESSION_COUNT_RARE = 3;
mQcConstants.MAX_SESSION_COUNT_FREQUENT = 4;
mQcConstants.MAX_SESSION_COUNT_WORKING = 5;
mQcConstants.MAX_SESSION_COUNT_ACTIVE = 6;
mQcConstants.updateConstants();
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 3);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 4);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 5);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 6);
for (int i = 0; i < 7; ++i) {
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1629,8 +1657,7 @@ public class QuotaControllerTest {
final int standbyBucket = RARE_INDEX;
// Prevent timing session throttling from affecting the test.
mQcConstants.MAX_SESSION_COUNT_RARE = 50;
mQcConstants.updateConstants();
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 50);
// No sessions saved yet.
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -1749,9 +1776,8 @@ public class QuotaControllerTest {
public void testMaybeScheduleStartAlarmLocked_JobCount_RateLimitingWindow() {
// Set rate limiting period different from allowed time to confirm code sets based on
// the former.
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 10 * MINUTE_IN_MILLIS;
mQcConstants.RATE_LIMITING_WINDOW_MS = 5 * MINUTE_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 5 * MINUTE_IN_MILLIS);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int standbyBucket = WORKING_INDEX;
@@ -1790,8 +1816,9 @@ public class QuotaControllerTest {
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedBufferSize() {
// Make sure any new value is used correctly.
mQcConstants.IN_QUOTA_BUFFER_MS *= 2;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS,
mQcConstants.IN_QUOTA_BUFFER_MS * 2);
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1800,8 +1827,9 @@ public class QuotaControllerTest {
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() {
// Make sure any new value is used correctly.
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1810,8 +1838,9 @@ public class QuotaControllerTest {
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedMaxTime() {
// Make sure any new value is used correctly.
mQcConstants.MAX_EXECUTION_TIME_MS /= 2;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS,
mQcConstants.MAX_EXECUTION_TIME_MS / 2);
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1820,10 +1849,13 @@ public class QuotaControllerTest {
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedEverything() {
// Make sure any new value is used correctly.
mQcConstants.IN_QUOTA_BUFFER_MS *= 2;
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2;
mQcConstants.MAX_EXECUTION_TIME_MS /= 2;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS,
mQcConstants.IN_QUOTA_BUFFER_MS * 2);
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS,
mQcConstants.MAX_EXECUTION_TIME_MS / 2);
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1891,27 +1923,30 @@ public class QuotaControllerTest {
@Test
public void testConstantsUpdating_ValidValues() {
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS;
mQcConstants.IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
mQcConstants.MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS;
mQcConstants.MAX_JOB_COUNT_ACTIVE = 5000;
mQcConstants.MAX_JOB_COUNT_WORKING = 4000;
mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
mQcConstants.MAX_JOB_COUNT_RARE = 2000;
mQcConstants.RATE_LIMITING_WINDOW_MS = 15 * MINUTE_IN_MILLIS;
mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 500;
mQcConstants.MAX_SESSION_COUNT_ACTIVE = 500;
mQcConstants.MAX_SESSION_COUNT_WORKING = 400;
mQcConstants.MAX_SESSION_COUNT_FREQUENT = 300;
mQcConstants.MAX_SESSION_COUNT_RARE = 200;
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 50;
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 10 * SECOND_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 5 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 45 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 60 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 120 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 3 * HOUR_IN_MILLIS);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, 5000);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 4000);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 3000);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RARE, 2000);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, 2000);
setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 500);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 500);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 400);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 300);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 200);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RESTRICTED, 100);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 50);
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
10 * SECOND_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 7 * MINUTE_IN_MILLIS);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -1920,6 +1955,8 @@ public class QuotaControllerTest {
assertEquals(45 * MINUTE_IN_MILLIS,
mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(120 * MINUTE_IN_MILLIS,
mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]);
assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(500, mQuotaController.getMaxJobCountPerRateLimitingWindow());
@@ -1927,39 +1964,44 @@ public class QuotaControllerTest {
assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
assertEquals(50, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
assertEquals(200, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
assertEquals(100, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]);
assertEquals(10 * SECOND_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
}
@Test
public void testConstantsUpdating_InvalidValues() {
// Test negatives/too low.
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
mQcConstants.IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
mQcConstants.WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
mQcConstants.MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS;
mQcConstants.MAX_JOB_COUNT_ACTIVE = -1;
mQcConstants.MAX_JOB_COUNT_WORKING = 1;
mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
mQcConstants.MAX_JOB_COUNT_RARE = 1;
mQcConstants.RATE_LIMITING_WINDOW_MS = 15 * SECOND_IN_MILLIS;
mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 0;
mQcConstants.MAX_SESSION_COUNT_ACTIVE = -1;
mQcConstants.MAX_SESSION_COUNT_WORKING = 0;
mQcConstants.MAX_SESSION_COUNT_FREQUENT = -3;
mQcConstants.MAX_SESSION_COUNT_RARE = 0;
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 0;
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = -1;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, -MINUTE_IN_MILLIS);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, -1);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 1);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 1);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RARE, 1);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, -1);
setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * SECOND_IN_MILLIS);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 0);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, -1);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 0);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, -3);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 0);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RESTRICTED, -5);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 0);
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, -1);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, -1);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(0, mQuotaController.getInQuotaBufferMs());
@@ -1967,6 +2009,7 @@ public class QuotaControllerTest {
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]);
assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(30 * SECOND_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(10, mQuotaController.getMaxJobCountPerRateLimitingWindow());
@@ -1974,35 +2017,37 @@ public class QuotaControllerTest {
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
assertEquals(10, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
assertEquals(0, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(0, mQuotaController.getMinQuotaCheckDelayMs());
// Invalid configurations.
// In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 2 * MINUTE_IN_MILLIS;
mQcConstants.IN_QUOTA_BUFFER_MS = 5 * MINUTE_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 2 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 5 * MINUTE_IN_MILLIS);
assertTrue(mQuotaController.getInQuotaBufferMs()
<= mQuotaController.getAllowedTimePerPeriodMs());
// Test larger than a day. Controller should cap at one day.
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.RATE_LIMITING_WINDOW_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.updateConstants();
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 30 * 24 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 25 * HOUR_IN_MILLIS);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -2010,10 +2055,13 @@ public class QuotaControllerTest {
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(7 * 24 * HOUR_IN_MILLIS,
mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(15 * MINUTE_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
}
/** Tests that TimingSessions aren't saved when the device is charging. */
@@ -2619,9 +2667,9 @@ public class QuotaControllerTest {
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
// Essentially disable session throttling.
mQcConstants.MAX_SESSION_COUNT_WORKING =
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = Integer.MAX_VALUE;
mQcConstants.updateConstants();
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, Integer.MAX_VALUE);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
Integer.MAX_VALUE);
final int standbyBucket = WORKING_INDEX;
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
@@ -2671,12 +2719,12 @@ public class QuotaControllerTest {
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
// Essentially disable job count throttling.
mQcConstants.MAX_JOB_COUNT_FREQUENT =
mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = Integer.MAX_VALUE;
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, Integer.MAX_VALUE);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
Integer.MAX_VALUE);
// Make sure throttling is because of COUNT_PER_RATE_LIMITING_WINDOW.
mQcConstants.MAX_SESSION_COUNT_FREQUENT =
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW + 1;
mQcConstants.updateConstants();
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT,
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW + 1);
final int standbyBucket = FREQUENT_INDEX;
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);