From 12fce9938eb06e6b994b35990f1974876ea58b1b Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Fri, 23 Oct 2020 11:36:07 -0700 Subject: [PATCH] Migrate QuotaController to DeviceConfig. This migrates flags inside QuotaController to DeviceConfig. DeviceConfig is the recommended way and using it means that we only operate/parse the set of flags that changed instead of every single flag whenever one of them changes. This uses the JobScheduler DeviceConfig namespace instead of creating a wholly separate namespace just for the controller. Bug: 124466289 Test: atest CtsJobSchedulerTestCases Test: atest FrameworksMockingServicesTests:ConnectivityControllerTest Test: atest FrameworksMockingServicesTests:JobSchedulerServiceTest Test: atest FrameworksMockingServicesTests:JobStatusTest Test: atest FrameworksMockingServicesTests:QuotaControllerTest Test: atest FrameworksServicesTests:JobStoreTest Change-Id: Id397dccc6d9c40bac1a9837addcab514f509a721 --- .../server/job/JobSchedulerService.java | 10 + .../server/job/controllers/JobStatus.java | 5 + .../job/controllers/QuotaController.java | 636 ++++++++++-------- .../job/controllers/StateController.java | 9 + core/java/android/provider/Settings.java | 15 - .../android/providers/settings/global.proto | 2 +- .../settings/SettingsProtoDumpUtil.java | 3 - .../android/provider/SettingsBackupTest.java | 1 - .../job/controllers/QuotaControllerTest.java | 254 ++++--- 9 files changed, 545 insertions(+), 390 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 6c14233dba139..34e82b0ce45e5 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -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; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index c7cc2f03b0622..00dbb8235d295 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -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(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index c06e19cbf6870..b7ace70f0cd4e 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -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; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java index 71c759931f576..56b30907b2a1f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java @@ -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. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a4212b53c9059..2308d4121b700 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11912,21 +11912,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" - * - *

- * 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: diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 16a691c9c4ec6..9291a90574cd1 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -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 ]; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index f1fb527245a13..8aaa32365ac6d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -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); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 8aad467a24406..93139fd37be43 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -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, diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 18bd6b17f3403..b98021bc2cabb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -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) invocationOnMock -> null) + .when(() -> DeviceConfig.addOnPropertiesChangedListener( + anyString(), any(Executor.class), + any(DeviceConfig.OnPropertiesChangedListener.class))); + mDeviceConfigPropertiesBuilder = + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); + doAnswer( + (Answer) invocationOnMock + -> mDeviceConfigPropertiesBuilder.build()) + .when(() -> DeviceConfig.getProperties( + eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.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);