Merge "Changing default limits, clarify flags, fix bug." into qt-dev
This commit is contained in:
@@ -273,9 +273,11 @@ message ConstantsProto {
|
||||
// The maximum number of jobs an app can run within this particular standby bucket's
|
||||
// window size.
|
||||
optional int32 max_job_count_rare = 11;
|
||||
// The period of time used to rate limit recently run jobs.
|
||||
optional int32 rate_limiting_window_ms = 19;
|
||||
// The maximum number of jobs that should be allowed to run in the past
|
||||
// {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
|
||||
optional int32 max_job_count_per_allowed_time = 12;
|
||||
// rate_limiting_window_ms.
|
||||
optional int32 max_job_count_per_rate_limiting_window = 12;
|
||||
// The maximum number of timing sessions an app can run within this particular standby
|
||||
// bucket's window size.
|
||||
optional int32 max_session_count_active = 13;
|
||||
@@ -289,8 +291,8 @@ message ConstantsProto {
|
||||
// bucket's window size.
|
||||
optional int32 max_session_count_rare = 16;
|
||||
// The maximum number of timing sessions that should be allowed to run in the past
|
||||
// {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
|
||||
optional int32 max_session_count_per_allowed_time = 17;
|
||||
// rate_limiting_window_ms.
|
||||
optional int32 max_session_count_per_rate_limiting_window = 17;
|
||||
// Treat two distinct {@link TimingSession}s as the same if they start and end within this
|
||||
// amount of time of each other.
|
||||
optional int64 timing_session_coalescing_duration_ms = 18;
|
||||
@@ -517,63 +519,56 @@ message StateControllerProto {
|
||||
optional int64 expiration_time_elapsed = 2;
|
||||
optional int64 window_size_ms = 3;
|
||||
|
||||
/** The total amount of time the app ran in its respective bucket window size. */
|
||||
optional int32 job_count_limit = 14;
|
||||
optional int32 session_count_limit = 15;
|
||||
|
||||
// The total amount of time the app ran in its respective bucket window size.
|
||||
optional int64 execution_time_in_window_ms = 4;
|
||||
optional int32 bg_job_count_in_window = 5;
|
||||
|
||||
/**
|
||||
* The total amount of time the app ran in the last
|
||||
* {@link QuotaController#MAX_PERIOD_MS}.
|
||||
*/
|
||||
// The total amount of time the app ran in the last
|
||||
// {@link QuotaController#MAX_PERIOD_MS}.
|
||||
optional int64 execution_time_in_max_period_ms = 6;
|
||||
optional int32 bg_job_count_in_max_period = 7;
|
||||
|
||||
/**
|
||||
* The number of {@link TimingSession}s within the bucket window size. This will include
|
||||
* sessions that started before the window as long as they end within the window.
|
||||
*/
|
||||
// The number of {@link TimingSession}s within the bucket window size. This will include
|
||||
// sessions that started before the window as long as they end within the window.
|
||||
optional int32 session_count_in_window = 11;
|
||||
|
||||
/**
|
||||
* The time after which the sum of all the app's sessions plus
|
||||
* ConstantsProto.QuotaController.in_quota_buffer_ms equals the quota. This is only
|
||||
* valid if
|
||||
* execution_time_in_window_ms >=
|
||||
* ConstantsProto.QuotaController.allowed_time_per_period_ms
|
||||
* or
|
||||
* execution_time_in_max_period_ms >=
|
||||
* ConstantsProto.QuotaController.max_execution_time_ms.
|
||||
*/
|
||||
optional int64 quota_cutoff_time_elapsed = 8;
|
||||
// The time after which the app will be under the bucket quota. This is only valid if
|
||||
// execution_time_in_window_ms >=
|
||||
// ConstantsProto.QuotaController.allowed_time_per_period_ms
|
||||
// or
|
||||
// execution_time_in_max_period_ms >=
|
||||
// ConstantsProto.QuotaController.max_execution_time_ms
|
||||
// or
|
||||
// bg_job_count_in_window >= job_count_limit
|
||||
// or
|
||||
// session_count_in_window >= session_count_limit.
|
||||
optional int64 in_quota_time_elapsed = 8;
|
||||
|
||||
/**
|
||||
* The time after which job_count_in_allowed_time should be considered invalid, in the
|
||||
* elapsed realtime timebase.
|
||||
*/
|
||||
// The time after which job_count_in_rate_limiting_window should be considered invalid,
|
||||
// in the elapsed realtime timebase.
|
||||
optional int64 job_count_expiration_time_elapsed = 9;
|
||||
|
||||
/**
|
||||
* The number of jobs that ran in at least the last
|
||||
* ConstantsProto.QuotaController.allowed_time_per_period_ms.
|
||||
* It may contain a few stale entries since cleanup won't happen exactly every
|
||||
* ConstantsProto.QuotaController.allowed_time_per_period_ms.
|
||||
*/
|
||||
optional int32 job_count_in_allowed_time = 10;
|
||||
// The number of jobs that ran in at least the last
|
||||
// ConstantsProto.QuotaController.rate_limiting_window_ms.
|
||||
// It may contain a few stale entries since cleanup won't happen exactly every
|
||||
// ConstantsProto.QuotaController.rate_limiting_window_ms. This should only be
|
||||
// considered valid before elapsed realtime has reached
|
||||
// job_count_expiration_time_elapsed.
|
||||
optional int32 job_count_in_rate_limiting_window = 10;
|
||||
|
||||
/**
|
||||
* The time after which {@link #timingSessionCountInAllowedTime} should be considered
|
||||
* invalid, in the elapsed realtime timebase.
|
||||
*/
|
||||
// The time after which {@link #timingSessionCountInAllowedTime} should be considered
|
||||
// invalid, in the elapsed realtime timebase.
|
||||
optional int64 session_count_expiration_time_elapsed = 12;
|
||||
|
||||
/**
|
||||
* The number of {@link TimingSession}s that ran in at least the last
|
||||
* {@link #mAllowedTimePerPeriodMs}. It may contain a few stale entries since cleanup won't
|
||||
* happen exactly every {@link #mAllowedTimePerPeriodMs}. This should only be considered
|
||||
* valid before elapsed realtime has reached
|
||||
* {@link #timingSessionCountExpirationTimeElapsed}.
|
||||
*/
|
||||
optional int32 session_count_in_allowed_time = 13;
|
||||
// The number of {@link TimingSession}s that ran in at least the last
|
||||
// ConstantsProto.QuotaController.rate_limiting_window_ms. It may contain a few stale
|
||||
// entries since cleanup won't happen exactly every
|
||||
// ConstantsProto.QuotaController.rate_limiting_window_ms. This should only be considered
|
||||
// valid before elapsed realtime has reached session_count_expiration_time_elapsed.
|
||||
optional int32 session_count_in_rate_limiting_window = 13;
|
||||
}
|
||||
|
||||
message Package {
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package com.android.server.job.controllers;
|
||||
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
|
||||
|
||||
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;
|
||||
@@ -75,17 +79,28 @@ import java.util.function.Predicate;
|
||||
/**
|
||||
* Controller that tracks whether an app has exceeded its standby bucket quota.
|
||||
*
|
||||
* Each job in each bucket is given 10 minutes to run within its respective time window. Active
|
||||
* jobs can run indefinitely, working set jobs can run for 10 minutes within a 2 hour window,
|
||||
* frequent jobs get to run 10 minutes in an 8 hour window, and rare jobs get to run 10 minutes in
|
||||
* a 24 hour window. The windows are rolling, so as soon as a job would have some quota based on its
|
||||
* bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately
|
||||
* applied to it.
|
||||
* With initial defaults, each app in each bucket is given 10 minutes to run within its respective
|
||||
* time window. Active jobs can run indefinitely, working set jobs can run for 10 minutes within a
|
||||
* 2 hour window, frequent jobs get to run 10 minutes in an 8 hour window, and rare jobs get to run
|
||||
* 10 minutes in a 24 hour window. The windows are rolling, so as soon as a job would have some
|
||||
* quota based on its bucket, it will be eligible to run. When a job's bucket changes, its new
|
||||
* quota is immediately applied to it.
|
||||
*
|
||||
* Job and session count limits are included to prevent abuse/spam. Each bucket has its own limit on
|
||||
* the number of jobs or sessions that can run within the window. Regardless of bucket, apps will
|
||||
* not be allowed to run more than 20 jobs within the past 10 minutes.
|
||||
*
|
||||
* Jobs are throttled while an app is not in a foreground state. All jobs are allowed to run
|
||||
* freely when an app enters the foreground state and are restricted when the app leaves the
|
||||
* foreground state. However, jobs that are started while the app is in the TOP state are not
|
||||
* restricted regardless of the app's state change.
|
||||
* foreground state. However, jobs that are started while the app is in the TOP state do not count
|
||||
* towards any quota and are not restricted regardless of the app's state change.
|
||||
*
|
||||
* Jobs will not be throttled when the device is charging. The device is considered to be charging
|
||||
* once the {@link BatteryManager#ACTION_CHARGING} intent has been broadcast.
|
||||
*
|
||||
* Note: all limits are enforced per bucket window unless explicitly stated otherwise.
|
||||
* All stated values are configurable and subject to change. See {@link QcConstants} for current
|
||||
* defaults.
|
||||
*
|
||||
* Test: atest com.android.server.job.controllers.QuotaControllerTest
|
||||
*/
|
||||
@@ -94,8 +109,6 @@ public final class QuotaController extends StateController {
|
||||
private static final boolean DEBUG = JobSchedulerService.DEBUG
|
||||
|| Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private static final long MINUTE_IN_MILLIS = 60 * 1000L;
|
||||
|
||||
private static final String ALARM_TAG_CLEANUP = "*job.cleanup*";
|
||||
private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*";
|
||||
|
||||
@@ -244,6 +257,8 @@ public final class QuotaController extends StateController {
|
||||
public long expirationTimeElapsed;
|
||||
|
||||
public long windowSizeMs;
|
||||
public int jobCountLimit;
|
||||
public int sessionCountLimit;
|
||||
|
||||
/** The total amount of time the app ran in its respective bucket window size. */
|
||||
public long executionTimeInWindowMs;
|
||||
@@ -260,54 +275,58 @@ public final class QuotaController extends StateController {
|
||||
public int sessionCountInWindow;
|
||||
|
||||
/**
|
||||
* The time after which the sum of all the app's sessions plus {@link #mQuotaBufferMs}
|
||||
* equals the quota. This is only valid if
|
||||
* executionTimeInWindowMs >= {@link #mAllowedTimePerPeriodMs} or
|
||||
* executionTimeInMaxPeriodMs >= {@link #mMaxExecutionTimeMs}.
|
||||
* The time after which the app will be under the bucket quota and can start running jobs
|
||||
* again. This is only valid if
|
||||
* {@link #executionTimeInWindowMs} >= {@link #mAllowedTimePerPeriodMs},
|
||||
* {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
|
||||
* {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
|
||||
* {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
|
||||
*/
|
||||
public long quotaCutoffTimeElapsed;
|
||||
public long inQuotaTimeElapsed;
|
||||
|
||||
/**
|
||||
* The time after which {@link #jobCountInAllowedTime} should be considered invalid, in the
|
||||
* elapsed realtime timebase.
|
||||
* The time after which {@link #jobCountInRateLimitingWindow} should be considered invalid,
|
||||
* in the elapsed realtime timebase.
|
||||
*/
|
||||
public long jobCountExpirationTimeElapsed;
|
||||
public long jobRateLimitExpirationTimeElapsed;
|
||||
|
||||
/**
|
||||
* The number of jobs that ran in at least the last {@link #mAllowedTimePerPeriodMs}.
|
||||
* The number of jobs that ran in at least the last {@link #mRateLimitingWindowMs}.
|
||||
* It may contain a few stale entries since cleanup won't happen exactly every
|
||||
* {@link #mAllowedTimePerPeriodMs}.
|
||||
* {@link #mRateLimitingWindowMs}.
|
||||
*/
|
||||
public int jobCountInAllowedTime;
|
||||
public int jobCountInRateLimitingWindow;
|
||||
|
||||
/**
|
||||
* The time after which {@link #sessionCountInAllowedTime} should be considered
|
||||
* The time after which {@link #sessionCountInRateLimitingWindow} should be considered
|
||||
* invalid, in the elapsed realtime timebase.
|
||||
*/
|
||||
public long sessionCountExpirationTimeElapsed;
|
||||
public long sessionRateLimitExpirationTimeElapsed;
|
||||
|
||||
/**
|
||||
* The number of {@link TimingSession}s that ran in at least the last
|
||||
* {@link #mAllowedTimePerPeriodMs}. It may contain a few stale entries since cleanup won't
|
||||
* happen exactly every {@link #mAllowedTimePerPeriodMs}. This should only be considered
|
||||
* valid before elapsed realtime has reached {@link #sessionCountExpirationTimeElapsed}.
|
||||
* {@link #mRateLimitingWindowMs}. It may contain a few stale entries since cleanup won't
|
||||
* happen exactly every {@link #mRateLimitingWindowMs}. This should only be considered
|
||||
* valid before elapsed realtime has reached {@link #sessionRateLimitExpirationTimeElapsed}.
|
||||
*/
|
||||
public int sessionCountInAllowedTime;
|
||||
public int sessionCountInRateLimitingWindow;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "expirationTime=" + expirationTimeElapsed + ", "
|
||||
+ "windowSize=" + windowSizeMs + ", "
|
||||
+ "windowSizeMs=" + windowSizeMs + ", "
|
||||
+ "jobCountLimit=" + jobCountLimit + ", "
|
||||
+ "sessionCountLimit=" + sessionCountLimit + ", "
|
||||
+ "executionTimeInWindow=" + executionTimeInWindowMs + ", "
|
||||
+ "bgJobCountInWindow=" + bgJobCountInWindow + ", "
|
||||
+ "executionTimeInMaxPeriod=" + executionTimeInMaxPeriodMs + ", "
|
||||
+ "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
|
||||
+ "sessionCountInWindow=" + sessionCountInWindow + ", "
|
||||
+ "quotaCutoffTime=" + quotaCutoffTimeElapsed + ", "
|
||||
+ "jobCountExpirationTime=" + jobCountExpirationTimeElapsed + ", "
|
||||
+ "jobCountInAllowedTime=" + jobCountInAllowedTime + ", "
|
||||
+ "sessionCountExpirationTime=" + sessionCountExpirationTimeElapsed + ", "
|
||||
+ "sessionCountInAllowedTime=" + sessionCountInAllowedTime;
|
||||
+ "inQuotaTime=" + inQuotaTimeElapsed + ", "
|
||||
+ "jobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
|
||||
+ "jobCountInRateLimitingWindow=" + jobCountInRateLimitingWindow + ", "
|
||||
+ "sessionCountExpirationTime=" + sessionRateLimitExpirationTimeElapsed + ", "
|
||||
+ "sessionCountInRateLimitingWindow=" + sessionCountInRateLimitingWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -316,18 +335,21 @@ public final class QuotaController extends StateController {
|
||||
ExecutionStats other = (ExecutionStats) obj;
|
||||
return this.expirationTimeElapsed == other.expirationTimeElapsed
|
||||
&& this.windowSizeMs == other.windowSizeMs
|
||||
&& this.jobCountLimit == other.jobCountLimit
|
||||
&& this.sessionCountLimit == other.sessionCountLimit
|
||||
&& this.executionTimeInWindowMs == other.executionTimeInWindowMs
|
||||
&& this.bgJobCountInWindow == other.bgJobCountInWindow
|
||||
&& this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
|
||||
&& this.sessionCountInWindow == other.sessionCountInWindow
|
||||
&& this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
|
||||
&& this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed
|
||||
&& this.jobCountExpirationTimeElapsed == other.jobCountExpirationTimeElapsed
|
||||
&& this.jobCountInAllowedTime == other.jobCountInAllowedTime
|
||||
&& this.sessionCountExpirationTimeElapsed
|
||||
== other.sessionCountExpirationTimeElapsed
|
||||
&& this.sessionCountInAllowedTime
|
||||
== other.sessionCountInAllowedTime;
|
||||
&& this.inQuotaTimeElapsed == other.inQuotaTimeElapsed
|
||||
&& this.jobRateLimitExpirationTimeElapsed
|
||||
== other.jobRateLimitExpirationTimeElapsed
|
||||
&& this.jobCountInRateLimitingWindow == other.jobCountInRateLimitingWindow
|
||||
&& this.sessionRateLimitExpirationTimeElapsed
|
||||
== other.sessionRateLimitExpirationTimeElapsed
|
||||
&& this.sessionCountInRateLimitingWindow
|
||||
== other.sessionCountInRateLimitingWindow;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -338,16 +360,18 @@ public final class QuotaController extends StateController {
|
||||
int result = 0;
|
||||
result = 31 * result + hashLong(expirationTimeElapsed);
|
||||
result = 31 * result + hashLong(windowSizeMs);
|
||||
result = 31 * result + hashLong(jobCountLimit);
|
||||
result = 31 * result + hashLong(sessionCountLimit);
|
||||
result = 31 * result + hashLong(executionTimeInWindowMs);
|
||||
result = 31 * result + bgJobCountInWindow;
|
||||
result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
|
||||
result = 31 * result + bgJobCountInMaxPeriod;
|
||||
result = 31 * result + sessionCountInWindow;
|
||||
result = 31 * result + hashLong(quotaCutoffTimeElapsed);
|
||||
result = 31 * result + hashLong(jobCountExpirationTimeElapsed);
|
||||
result = 31 * result + jobCountInAllowedTime;
|
||||
result = 31 * result + hashLong(sessionCountExpirationTimeElapsed);
|
||||
result = 31 * result + sessionCountInAllowedTime;
|
||||
result = 31 * result + hashLong(inQuotaTimeElapsed);
|
||||
result = 31 * result + hashLong(jobRateLimitExpirationTimeElapsed);
|
||||
result = 31 * result + jobCountInRateLimitingWindow;
|
||||
result = 31 * result + hashLong(sessionRateLimitExpirationTimeElapsed);
|
||||
result = 31 * result + sessionCountInRateLimitingWindow;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -399,19 +423,19 @@ public final class QuotaController extends StateController {
|
||||
private boolean mShouldThrottle;
|
||||
|
||||
/** How much time each app will have to run jobs within their standby bucket window. */
|
||||
private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
|
||||
private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
|
||||
|
||||
/**
|
||||
* The maximum amount of time an app can have its jobs running within a {@link #MAX_PERIOD_MS}
|
||||
* window.
|
||||
*/
|
||||
private long mMaxExecutionTimeMs = 4 * 60 * MINUTE_IN_MILLIS;
|
||||
private long mMaxExecutionTimeMs = QcConstants.DEFAULT_MAX_EXECUTION_TIME_MS;
|
||||
|
||||
/**
|
||||
* How much time the app should have before transitioning from out-of-quota to in-quota.
|
||||
* This should not affect processing if the app is already in-quota.
|
||||
*/
|
||||
private long mQuotaBufferMs = 30 * 1000L; // 30 seconds
|
||||
private long mQuotaBufferMs = QcConstants.DEFAULT_IN_QUOTA_BUFFER_MS;
|
||||
|
||||
/**
|
||||
* {@link #mAllowedTimePerPeriodMs} - {@link #mQuotaBufferMs}. This can be used to determine
|
||||
@@ -425,14 +449,19 @@ public final class QuotaController extends StateController {
|
||||
*/
|
||||
private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
|
||||
|
||||
/** The maximum number of jobs that can run within the past {@link #mAllowedTimePerPeriodMs}. */
|
||||
private int mMaxJobCountPerAllowedTime = 20;
|
||||
/** The period of time used to rate limit recently run jobs. */
|
||||
private long mRateLimitingWindowMs = QcConstants.DEFAULT_RATE_LIMITING_WINDOW_MS;
|
||||
|
||||
/** The maximum number of jobs that can run within the past {@link #mRateLimitingWindowMs}. */
|
||||
private int mMaxJobCountPerRateLimitingWindow =
|
||||
QcConstants.DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
|
||||
|
||||
/**
|
||||
* The maximum number of {@link TimingSession}s that can run within the past {@link
|
||||
* #mAllowedTimePerPeriodMs}.
|
||||
* #mRateLimitingWindowMs}.
|
||||
*/
|
||||
private int mMaxSessionCountPerAllowedTime = 20;
|
||||
private int mMaxSessionCountPerRateLimitingWindow =
|
||||
QcConstants.DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
|
||||
|
||||
private long mNextCleanupTimeElapsed = 0;
|
||||
private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
|
||||
@@ -486,11 +515,11 @@ public final class QuotaController extends StateController {
|
||||
* The rolling window size for each standby bucket. Within each window, an app will have 10
|
||||
* minutes to run its jobs.
|
||||
*/
|
||||
private final long[] mBucketPeriodsMs = new long[] {
|
||||
10 * MINUTE_IN_MILLIS, // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
|
||||
2 * 60 * MINUTE_IN_MILLIS, // 2 hours for WORKING
|
||||
8 * 60 * MINUTE_IN_MILLIS, // 8 hours for FREQUENT
|
||||
24 * 60 * MINUTE_IN_MILLIS // 24 hours for RARE
|
||||
private final long[] mBucketPeriodsMs = new long[]{
|
||||
QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS,
|
||||
QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS,
|
||||
QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
|
||||
QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS
|
||||
};
|
||||
|
||||
/** The maximum period any bucket can have. */
|
||||
@@ -503,16 +532,13 @@ public final class QuotaController extends StateController {
|
||||
*
|
||||
* @see #mBucketPeriodsMs
|
||||
*/
|
||||
private final int[] mMaxBucketJobCounts = new int[] {
|
||||
200, // ACTIVE -- 1200/hr
|
||||
1200, // WORKING -- 600/hr
|
||||
1800, // FREQUENT -- 225/hr
|
||||
2400 // RARE -- 100/hr
|
||||
private final int[] mMaxBucketJobCounts = new int[]{
|
||||
QcConstants.DEFAULT_MAX_JOB_COUNT_ACTIVE,
|
||||
QcConstants.DEFAULT_MAX_JOB_COUNT_WORKING,
|
||||
QcConstants.DEFAULT_MAX_JOB_COUNT_FREQUENT,
|
||||
QcConstants.DEFAULT_MAX_JOB_COUNT_RARE
|
||||
};
|
||||
|
||||
/** The minimum number of jobs that any bucket will be allowed to run. */
|
||||
private static final int MIN_BUCKET_JOB_COUNT = 100;
|
||||
|
||||
/**
|
||||
* The maximum number of {@link TimingSession}s based on its standby bucket. For each max value
|
||||
* count in the array, the app will not be allowed to have more than that many number of
|
||||
@@ -527,14 +553,12 @@ public final class QuotaController extends StateController {
|
||||
QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE
|
||||
};
|
||||
|
||||
/** The minimum number of {@link TimingSession}s that any bucket will be allowed to run. */
|
||||
private static final int MIN_BUCKET_SESSION_COUNT = 3;
|
||||
|
||||
/**
|
||||
* Treat two distinct {@link TimingSession}s as the same if they start and end within this
|
||||
* amount of time of each other.
|
||||
*/
|
||||
private long mTimingSessionCoalescingDurationMs = 0;
|
||||
private long mTimingSessionCoalescingDurationMs =
|
||||
QcConstants.DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
|
||||
|
||||
/** An app has reached its quota. The message should contain a {@link Package} object. */
|
||||
private static final int MSG_REACHED_QUOTA = 0;
|
||||
@@ -761,8 +785,8 @@ public final class QuotaController extends StateController {
|
||||
final int standbyBucket) {
|
||||
final long now = sElapsedRealtimeClock.millis();
|
||||
final boolean isUnderAllowedTimeQuota =
|
||||
(stats.jobCountExpirationTimeElapsed <= now
|
||||
|| stats.jobCountInAllowedTime < mMaxJobCountPerAllowedTime);
|
||||
(stats.jobRateLimitExpirationTimeElapsed <= now
|
||||
|| stats.jobCountInRateLimitingWindow < mMaxJobCountPerRateLimitingWindow);
|
||||
return isUnderAllowedTimeQuota
|
||||
&& (stats.bgJobCountInWindow < mMaxBucketJobCounts[standbyBucket]);
|
||||
}
|
||||
@@ -770,10 +794,8 @@ public final class QuotaController extends StateController {
|
||||
private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats,
|
||||
final int standbyBucket) {
|
||||
final long now = sElapsedRealtimeClock.millis();
|
||||
final boolean isUnderAllowedTimeQuota =
|
||||
(stats.sessionCountExpirationTimeElapsed <= now
|
||||
|| stats.sessionCountInAllowedTime
|
||||
< mMaxSessionCountPerAllowedTime);
|
||||
final boolean isUnderAllowedTimeQuota = (stats.sessionRateLimitExpirationTimeElapsed <= now
|
||||
|| stats.sessionCountInRateLimitingWindow < mMaxSessionCountPerRateLimitingWindow);
|
||||
return isUnderAllowedTimeQuota
|
||||
&& stats.sessionCountInWindow < mMaxBucketSessionCounts[standbyBucket];
|
||||
}
|
||||
@@ -924,12 +946,18 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
if (refreshStatsIfOld) {
|
||||
final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
|
||||
final int jobCountLimit = mMaxBucketJobCounts[standbyBucket];
|
||||
final int sessionCountLimit = mMaxBucketSessionCounts[standbyBucket];
|
||||
Timer timer = mPkgTimers.get(userId, packageName);
|
||||
if ((timer != null && timer.isActive())
|
||||
|| stats.expirationTimeElapsed <= sElapsedRealtimeClock.millis()
|
||||
|| stats.windowSizeMs != bucketWindowSizeMs) {
|
||||
|| stats.windowSizeMs != bucketWindowSizeMs
|
||||
|| stats.jobCountLimit != jobCountLimit
|
||||
|| stats.sessionCountLimit != sessionCountLimit) {
|
||||
// The stats are no longer valid.
|
||||
stats.windowSizeMs = bucketWindowSizeMs;
|
||||
stats.jobCountLimit = jobCountLimit;
|
||||
stats.sessionCountLimit = sessionCountLimit;
|
||||
updateExecutionStatsLocked(userId, packageName, stats);
|
||||
}
|
||||
}
|
||||
@@ -945,7 +973,7 @@ public final class QuotaController extends StateController {
|
||||
stats.executionTimeInMaxPeriodMs = 0;
|
||||
stats.bgJobCountInMaxPeriod = 0;
|
||||
stats.sessionCountInWindow = 0;
|
||||
stats.quotaCutoffTimeElapsed = 0;
|
||||
stats.inQuotaTimeElapsed = 0;
|
||||
|
||||
Timer timer = mPkgTimers.get(userId, packageName);
|
||||
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||
@@ -958,12 +986,12 @@ public final class QuotaController extends StateController {
|
||||
// invalidate now.
|
||||
stats.expirationTimeElapsed = nowElapsed;
|
||||
if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
|
||||
stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
|
||||
nowElapsed - mAllowedTimeIntoQuotaMs);
|
||||
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
|
||||
nowElapsed - mAllowedTimeIntoQuotaMs + stats.windowSizeMs);
|
||||
}
|
||||
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
|
||||
stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
|
||||
nowElapsed - mMaxExecutionTimeIntoQuotaMs);
|
||||
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
|
||||
nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,36 +1013,40 @@ public final class QuotaController extends StateController {
|
||||
TimingSession session = sessions.get(i);
|
||||
|
||||
// Window management.
|
||||
if (startWindowElapsed < session.startTimeElapsed) {
|
||||
stats.executionTimeInWindowMs += session.endTimeElapsed - session.startTimeElapsed;
|
||||
if (startWindowElapsed < session.endTimeElapsed) {
|
||||
final long start;
|
||||
if (startWindowElapsed < session.startTimeElapsed) {
|
||||
start = session.startTimeElapsed;
|
||||
emptyTimeMs =
|
||||
Math.min(emptyTimeMs, session.startTimeElapsed - startWindowElapsed);
|
||||
} else {
|
||||
// The session started before the window but ended within the window. Only
|
||||
// include the portion that was within the window.
|
||||
start = startWindowElapsed;
|
||||
emptyTimeMs = 0;
|
||||
}
|
||||
|
||||
stats.executionTimeInWindowMs += session.endTimeElapsed - start;
|
||||
stats.bgJobCountInWindow += session.bgJobCount;
|
||||
emptyTimeMs = Math.min(emptyTimeMs, session.startTimeElapsed - startWindowElapsed);
|
||||
if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
|
||||
stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
|
||||
session.startTimeElapsed + stats.executionTimeInWindowMs
|
||||
- mAllowedTimeIntoQuotaMs);
|
||||
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
|
||||
start + stats.executionTimeInWindowMs - mAllowedTimeIntoQuotaMs
|
||||
+ stats.windowSizeMs);
|
||||
}
|
||||
if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
|
||||
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
|
||||
session.endTimeElapsed + stats.windowSizeMs);
|
||||
}
|
||||
if (i == loopStart
|
||||
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
|
||||
> mTimingSessionCoalescingDurationMs) {
|
||||
// Coalesce sessions if they are very close to each other in time
|
||||
sessionCountInWindow++;
|
||||
}
|
||||
} else if (startWindowElapsed < session.endTimeElapsed) {
|
||||
// The session started before the window but ended within the window. Only include
|
||||
// the portion that was within the window.
|
||||
stats.executionTimeInWindowMs += session.endTimeElapsed - startWindowElapsed;
|
||||
stats.bgJobCountInWindow += session.bgJobCount;
|
||||
emptyTimeMs = 0;
|
||||
if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
|
||||
stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
|
||||
startWindowElapsed + stats.executionTimeInWindowMs
|
||||
- mAllowedTimeIntoQuotaMs);
|
||||
}
|
||||
if (i == loopStart
|
||||
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
|
||||
> mTimingSessionCoalescingDurationMs) {
|
||||
sessionCountInWindow++;
|
||||
|
||||
if (sessionCountInWindow >= stats.sessionCountLimit) {
|
||||
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
|
||||
session.endTimeElapsed + stats.windowSizeMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,9 +1057,9 @@ public final class QuotaController extends StateController {
|
||||
stats.bgJobCountInMaxPeriod += session.bgJobCount;
|
||||
emptyTimeMs = Math.min(emptyTimeMs, session.startTimeElapsed - startMaxElapsed);
|
||||
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
|
||||
stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
|
||||
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
|
||||
session.startTimeElapsed + stats.executionTimeInMaxPeriodMs
|
||||
- mMaxExecutionTimeIntoQuotaMs);
|
||||
- mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
|
||||
}
|
||||
} else if (startMaxElapsed < session.endTimeElapsed) {
|
||||
// The session started before the window but ended within the window. Only include
|
||||
@@ -1036,9 +1068,9 @@ public final class QuotaController extends StateController {
|
||||
stats.bgJobCountInMaxPeriod += session.bgJobCount;
|
||||
emptyTimeMs = 0;
|
||||
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
|
||||
stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
|
||||
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
|
||||
startMaxElapsed + stats.executionTimeInMaxPeriodMs
|
||||
- mMaxExecutionTimeIntoQuotaMs);
|
||||
- mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
|
||||
}
|
||||
} else {
|
||||
// This session ended before the window. No point in going any further.
|
||||
@@ -1094,11 +1126,11 @@ public final class QuotaController extends StateController {
|
||||
stats = new ExecutionStats();
|
||||
appStats[i] = stats;
|
||||
}
|
||||
if (stats.jobCountExpirationTimeElapsed <= now) {
|
||||
stats.jobCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
|
||||
stats.jobCountInAllowedTime = 0;
|
||||
if (stats.jobRateLimitExpirationTimeElapsed <= now) {
|
||||
stats.jobRateLimitExpirationTimeElapsed = now + mRateLimitingWindowMs;
|
||||
stats.jobCountInRateLimitingWindow = 0;
|
||||
}
|
||||
stats.jobCountInAllowedTime += count;
|
||||
stats.jobCountInRateLimitingWindow += count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1115,11 +1147,11 @@ public final class QuotaController extends StateController {
|
||||
stats = new ExecutionStats();
|
||||
appStats[i] = stats;
|
||||
}
|
||||
if (stats.sessionCountExpirationTimeElapsed <= now) {
|
||||
stats.sessionCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
|
||||
stats.sessionCountInAllowedTime = 0;
|
||||
if (stats.sessionRateLimitExpirationTimeElapsed <= now) {
|
||||
stats.sessionRateLimitExpirationTimeElapsed = now + mRateLimitingWindowMs;
|
||||
stats.sessionCountInRateLimitingWindow = 0;
|
||||
}
|
||||
stats.sessionCountInAllowedTime++;
|
||||
stats.sessionCountInRateLimitingWindow++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1367,18 +1399,17 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
|
||||
// The time this app will have quota again.
|
||||
long inQuotaTimeElapsed = stats.quotaCutoffTimeElapsed + stats.windowSizeMs;
|
||||
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeMs) {
|
||||
long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
|
||||
if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
|
||||
// App hit the rate limit.
|
||||
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
|
||||
stats.quotaCutoffTimeElapsed + MAX_PERIOD_MS);
|
||||
stats.jobRateLimitExpirationTimeElapsed);
|
||||
}
|
||||
if (!isUnderJobCountQuota) {
|
||||
if (!isUnderTimingSessionCountQuota
|
||||
&& stats.sessionCountInWindow < stats.sessionCountLimit) {
|
||||
// App hit the rate limit.
|
||||
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
|
||||
stats.jobCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
|
||||
}
|
||||
if (!isUnderTimingSessionCountQuota) {
|
||||
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
|
||||
stats.sessionCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
|
||||
stats.sessionRateLimitExpirationTimeElapsed);
|
||||
}
|
||||
// Only schedule the alarm if:
|
||||
// 1. There isn't one currently scheduled
|
||||
@@ -1399,7 +1430,7 @@ public final class QuotaController extends StateController {
|
||||
ALARM_TAG_QUOTA_CHECK, alarmListener, mHandler);
|
||||
alarmListener.setTriggerTime(inQuotaTimeElapsed);
|
||||
} else if (DEBUG) {
|
||||
Slog.d(TAG, "No need to scheduling start alarm for " + pkgString);
|
||||
Slog.d(TAG, "No need to schedule start alarm for " + pkgString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1968,14 +1999,15 @@ public final class QuotaController extends StateController {
|
||||
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_PER_ALLOWED_TIME =
|
||||
"max_count_per_allowed_time";
|
||||
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_PER_ALLOWED_TIME =
|
||||
"max_session_count_per_allowed_time";
|
||||
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";
|
||||
|
||||
@@ -1984,7 +2016,7 @@ public final class QuotaController extends StateController {
|
||||
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
|
||||
30 * 1000L; // 30 seconds
|
||||
private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
|
||||
10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
|
||||
DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; // ACTIVE apps can run jobs at any time
|
||||
private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
|
||||
2 * 60 * 60 * 1000L; // 2 hours
|
||||
private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS =
|
||||
@@ -1992,16 +2024,18 @@ public final class QuotaController extends StateController {
|
||||
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
|
||||
24 * 60 * 60 * 1000L; // 24 hours
|
||||
private static final long DEFAULT_MAX_EXECUTION_TIME_MS =
|
||||
4 * 60 * 60 * 1000L; // 4 hours
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE =
|
||||
200; // 1200/hr
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_WORKING =
|
||||
1200; // 600/hr
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT =
|
||||
1800; // 225/hr
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_RARE =
|
||||
2400; // 100/hr
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
|
||||
4 * HOUR_IN_MILLIS;
|
||||
private static final long DEFAULT_RATE_LIMITING_WINDOW_MS =
|
||||
10 * MINUTE_IN_MILLIS;
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 20;
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = // 20/window = 120/hr = 1/session
|
||||
DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session
|
||||
(int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session
|
||||
(int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
|
||||
private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
|
||||
(int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
|
||||
private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
|
||||
20; // 120/hr
|
||||
private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
|
||||
@@ -2010,8 +2044,8 @@ public final class QuotaController extends StateController {
|
||||
8; // 1/hr
|
||||
private static final int DEFAULT_MAX_SESSION_COUNT_RARE =
|
||||
3; // .125/hr
|
||||
private static final int DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME = 20;
|
||||
private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 0;
|
||||
private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
|
||||
private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
|
||||
|
||||
/** How much time each app will have to run jobs within their standby bucket window. */
|
||||
public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
|
||||
@@ -2079,11 +2113,14 @@ public final class QuotaController extends StateController {
|
||||
*/
|
||||
public int MAX_JOB_COUNT_RARE = DEFAULT_MAX_JOB_COUNT_RARE;
|
||||
|
||||
/** The period of time used to rate limit recently run jobs. */
|
||||
public long RATE_LIMITING_WINDOW_MS = DEFAULT_RATE_LIMITING_WINDOW_MS;
|
||||
|
||||
/**
|
||||
* The maximum number of jobs that can run within the past
|
||||
* {@link #ALLOWED_TIME_PER_PERIOD_MS}.
|
||||
* The maximum number of jobs that can run within the past {@link #RATE_LIMITING_WINDOW_MS}.
|
||||
*/
|
||||
public int MAX_JOB_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME;
|
||||
public int MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
|
||||
DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
|
||||
|
||||
/**
|
||||
* The maximum number of {@link TimingSession}s an app can run within this particular
|
||||
@@ -2113,7 +2150,8 @@ public final class QuotaController extends StateController {
|
||||
* The maximum number of {@link TimingSession}s that can run within the past
|
||||
* {@link #ALLOWED_TIME_PER_PERIOD_MS}.
|
||||
*/
|
||||
public int MAX_SESSION_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME;
|
||||
public int MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
|
||||
DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
|
||||
|
||||
/**
|
||||
* Treat two distinct {@link TimingSession}s as the same if they start and end within this
|
||||
@@ -2122,6 +2160,29 @@ public final class QuotaController extends StateController {
|
||||
public long TIMING_SESSION_COALESCING_DURATION_MS =
|
||||
DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
|
||||
|
||||
// Safeguards
|
||||
|
||||
/** The minimum number of jobs that any bucket will be allowed to run within its window. */
|
||||
private static final int MIN_BUCKET_JOB_COUNT = 10;
|
||||
|
||||
/**
|
||||
* The minimum number of {@link TimingSession}s that any bucket will be allowed to run
|
||||
* within its window.
|
||||
*/
|
||||
private static final int MIN_BUCKET_SESSION_COUNT = 1;
|
||||
|
||||
/** The minimum value that {@link #MAX_EXECUTION_TIME_MS} can have. */
|
||||
private static final long MIN_MAX_EXECUTION_TIME_MS = 60 * MINUTE_IN_MILLIS;
|
||||
|
||||
/** The minimum value that {@link #MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW} can have. */
|
||||
private static final int MIN_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 10;
|
||||
|
||||
/** The minimum value that {@link #MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW} can have. */
|
||||
private static final int MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 10;
|
||||
|
||||
/** 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);
|
||||
}
|
||||
@@ -2167,8 +2228,11 @@ public final class QuotaController extends StateController {
|
||||
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_PER_ALLOWED_TIME = mParser.getInt(
|
||||
KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME);
|
||||
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(
|
||||
@@ -2177,9 +2241,9 @@ public final class QuotaController extends StateController {
|
||||
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_PER_ALLOWED_TIME = mParser.getInt(
|
||||
KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME,
|
||||
DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME);
|
||||
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);
|
||||
@@ -2192,7 +2256,14 @@ public final class QuotaController extends StateController {
|
||||
synchronized (mLock) {
|
||||
boolean changed = false;
|
||||
|
||||
long newAllowedTimeMs = Math.min(MAX_PERIOD_MS,
|
||||
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;
|
||||
@@ -2231,47 +2302,44 @@ public final class QuotaController extends StateController {
|
||||
mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
|
||||
changed = true;
|
||||
}
|
||||
long newMaxExecutionTimeMs = Math.max(60 * MINUTE_IN_MILLIS,
|
||||
Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
|
||||
if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
|
||||
mMaxExecutionTimeMs = newMaxExecutionTimeMs;
|
||||
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
|
||||
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 newMaxCountPerAllowedPeriod = Math.max(10,
|
||||
MAX_JOB_COUNT_PER_ALLOWED_TIME);
|
||||
if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) {
|
||||
mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod;
|
||||
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(mMaxJobCountPerAllowedTime,
|
||||
Math.max(MIN_BUCKET_JOB_COUNT, 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;
|
||||
changed = true;
|
||||
}
|
||||
int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
|
||||
Math.max(MIN_BUCKET_JOB_COUNT, 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;
|
||||
changed = true;
|
||||
}
|
||||
int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
|
||||
Math.max(MIN_BUCKET_JOB_COUNT, 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;
|
||||
changed = true;
|
||||
}
|
||||
int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
|
||||
Math.max(MIN_BUCKET_JOB_COUNT, 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;
|
||||
changed = true;
|
||||
}
|
||||
int newMaxSessionCountPerAllowedPeriod = Math.max(10,
|
||||
MAX_SESSION_COUNT_PER_ALLOWED_TIME);
|
||||
if (mMaxSessionCountPerAllowedTime != newMaxSessionCountPerAllowedPeriod) {
|
||||
mMaxSessionCountPerAllowedTime = newMaxSessionCountPerAllowedPeriod;
|
||||
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 =
|
||||
@@ -2332,14 +2400,15 @@ public final class QuotaController extends StateController {
|
||||
pw.printPair(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println();
|
||||
pw.printPair(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println();
|
||||
pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println();
|
||||
pw.printPair(KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, MAX_JOB_COUNT_PER_ALLOWED_TIME)
|
||||
.println();
|
||||
pw.printPair(KEY_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS).println();
|
||||
pw.printPair(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
|
||||
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW).println();
|
||||
pw.printPair(KEY_MAX_SESSION_COUNT_ACTIVE, MAX_SESSION_COUNT_ACTIVE).println();
|
||||
pw.printPair(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println();
|
||||
pw.printPair(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println();
|
||||
pw.printPair(KEY_MAX_SESSION_COUNT_RARE, MAX_SESSION_COUNT_RARE).println();
|
||||
pw.printPair(KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME, MAX_SESSION_COUNT_PER_ALLOWED_TIME)
|
||||
.println();
|
||||
pw.printPair(KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
|
||||
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW).println();
|
||||
pw.printPair(KEY_TIMING_SESSION_COALESCING_DURATION_MS,
|
||||
TIMING_SESSION_COALESCING_DURATION_MS).println();
|
||||
pw.decreaseIndent();
|
||||
@@ -2365,8 +2434,10 @@ public final class QuotaController extends StateController {
|
||||
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
|
||||
MAX_JOB_COUNT_FREQUENT);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
|
||||
MAX_JOB_COUNT_PER_ALLOWED_TIME);
|
||||
proto.write(ConstantsProto.QuotaController.RATE_LIMITING_WINDOW_MS,
|
||||
RATE_LIMITING_WINDOW_MS);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
|
||||
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_ACTIVE,
|
||||
MAX_SESSION_COUNT_ACTIVE);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_WORKING,
|
||||
@@ -2375,8 +2446,8 @@ public final class QuotaController extends StateController {
|
||||
MAX_SESSION_COUNT_FREQUENT);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RARE,
|
||||
MAX_SESSION_COUNT_RARE);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_ALLOWED_TIME,
|
||||
MAX_SESSION_COUNT_PER_ALLOWED_TIME);
|
||||
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
|
||||
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
|
||||
proto.write(ConstantsProto.QuotaController.TIMING_SESSION_COALESCING_DURATION_MS,
|
||||
TIMING_SESSION_COALESCING_DURATION_MS);
|
||||
proto.end(qcToken);
|
||||
@@ -2431,8 +2502,18 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int getMaxJobCountPerAllowedTime() {
|
||||
return mMaxJobCountPerAllowedTime;
|
||||
int getMaxJobCountPerRateLimitingWindow() {
|
||||
return mMaxJobCountPerRateLimitingWindow;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int getMaxSessionCountPerRateLimitingWindow() {
|
||||
return mMaxSessionCountPerRateLimitingWindow;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
long getRateLimitingWindowMs() {
|
||||
return mRateLimitingWindowMs;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -2440,11 +2521,6 @@ public final class QuotaController extends StateController {
|
||||
return mTimingSessionCoalescingDurationMs;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int getMaxSessionCountPerAllowedTime() {
|
||||
return mMaxSessionCountPerAllowedTime;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
List<TimingSession> getTimingSessions(int userId, String packageName) {
|
||||
@@ -2658,6 +2734,12 @@ public final class QuotaController extends StateController {
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.WINDOW_SIZE_MS,
|
||||
es.windowSizeMs);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_LIMIT,
|
||||
es.jobCountLimit);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_LIMIT,
|
||||
es.sessionCountLimit);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.EXECUTION_TIME_IN_WINDOW_MS,
|
||||
es.executionTimeInWindowMs);
|
||||
@@ -2674,20 +2756,20 @@ public final class QuotaController extends StateController {
|
||||
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_WINDOW,
|
||||
es.sessionCountInWindow);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.QUOTA_CUTOFF_TIME_ELAPSED,
|
||||
es.quotaCutoffTimeElapsed);
|
||||
StateControllerProto.QuotaController.ExecutionStats.IN_QUOTA_TIME_ELAPSED,
|
||||
es.inQuotaTimeElapsed);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_EXPIRATION_TIME_ELAPSED,
|
||||
es.jobCountExpirationTimeElapsed);
|
||||
es.jobRateLimitExpirationTimeElapsed);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_ALLOWED_TIME,
|
||||
es.jobCountInAllowedTime);
|
||||
StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_RATE_LIMITING_WINDOW,
|
||||
es.jobCountInRateLimitingWindow);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_EXPIRATION_TIME_ELAPSED,
|
||||
es.sessionCountExpirationTimeElapsed);
|
||||
es.sessionRateLimitExpirationTimeElapsed);
|
||||
proto.write(
|
||||
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_ALLOWED_TIME,
|
||||
es.sessionCountInAllowedTime);
|
||||
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_RATE_LIMITING_WINDOW,
|
||||
es.sessionCountInRateLimitingWindow);
|
||||
proto.end(esToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,6 +386,8 @@ public class QuotaControllerTest {
|
||||
ExecutionStats expectedStats = new ExecutionStats();
|
||||
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
|
||||
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
|
||||
|
||||
final int uid = 10001;
|
||||
mQuotaController.onAppRemovedLocked("com.android.test.remove", uid);
|
||||
@@ -424,6 +426,8 @@ public class QuotaControllerTest {
|
||||
ExecutionStats expectedStats = new ExecutionStats();
|
||||
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
|
||||
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
|
||||
|
||||
mQuotaController.onUserRemovedLocked(0);
|
||||
assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
|
||||
@@ -456,6 +460,8 @@ public class QuotaControllerTest {
|
||||
ExecutionStats inputStats = new ExecutionStats();
|
||||
|
||||
inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
|
||||
inputStats.jobCountLimit = expectedStats.jobCountLimit = 100;
|
||||
inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100;
|
||||
// Invalid time is now +24 hours since there are no sessions at all for the app.
|
||||
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
|
||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
|
||||
@@ -520,6 +526,7 @@ public class QuotaControllerTest {
|
||||
assertEquals(expectedStats, inputStats);
|
||||
|
||||
inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
|
||||
inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 2;
|
||||
// Invalid time is now since the start of the session is at the very edge of the window
|
||||
// cutoff time.
|
||||
expectedStats.expirationTimeElapsed = now;
|
||||
@@ -528,10 +535,13 @@ public class QuotaControllerTest {
|
||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||
expectedStats.sessionCountInWindow = 3;
|
||||
expectedStats.inQuotaTimeElapsed = now + 11 * MINUTE_IN_MILLIS;
|
||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||
assertEquals(expectedStats, inputStats);
|
||||
|
||||
inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
|
||||
inputStats.jobCountLimit = expectedStats.jobCountLimit = 6;
|
||||
inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100;
|
||||
// Invalid time is now since the session straddles the window cutoff time.
|
||||
expectedStats.expirationTimeElapsed = now;
|
||||
expectedStats.executionTimeInWindowMs = 11 * MINUTE_IN_MILLIS;
|
||||
@@ -539,8 +549,7 @@ public class QuotaControllerTest {
|
||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||
expectedStats.sessionCountInWindow = 4;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
expectedStats.inQuotaTimeElapsed = now + 5 * MINUTE_IN_MILLIS;
|
||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||
assertEquals(expectedStats, inputStats);
|
||||
|
||||
@@ -553,8 +562,9 @@ public class QuotaControllerTest {
|
||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||
expectedStats.sessionCountInWindow = 4;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
// App goes under job execution time limit in ~61 minutes, but will be under job count limit
|
||||
// in 65 minutes.
|
||||
expectedStats.inQuotaTimeElapsed = now + 65 * MINUTE_IN_MILLIS;
|
||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||
assertEquals(expectedStats, inputStats);
|
||||
|
||||
@@ -567,8 +577,7 @@ public class QuotaControllerTest {
|
||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||
expectedStats.sessionCountInWindow = 5;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS + 5 * MINUTE_IN_MILLIS;
|
||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||
assertEquals(expectedStats, inputStats);
|
||||
|
||||
@@ -577,6 +586,7 @@ public class QuotaControllerTest {
|
||||
.add(0,
|
||||
createTimingSession(now - (23 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 3));
|
||||
inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
|
||||
inputStats.jobCountLimit = expectedStats.jobCountLimit = 100;
|
||||
// Invalid time is now +1 hour since the earliest session in the max period is 1 hour
|
||||
// before the end of the max period cutoff time.
|
||||
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
|
||||
@@ -585,7 +595,7 @@ public class QuotaControllerTest {
|
||||
expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 18;
|
||||
expectedStats.sessionCountInWindow = 5;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||
expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||
assertEquals(expectedStats, inputStats);
|
||||
@@ -602,7 +612,7 @@ public class QuotaControllerTest {
|
||||
expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||
expectedStats.sessionCountInWindow = 5;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||
expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||
assertEquals(expectedStats, inputStats);
|
||||
@@ -627,6 +637,8 @@ public class QuotaControllerTest {
|
||||
|
||||
// Active
|
||||
expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
|
||||
expectedStats.expirationTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
|
||||
expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInWindow = 5;
|
||||
@@ -638,44 +650,102 @@ public class QuotaControllerTest {
|
||||
|
||||
// Working
|
||||
expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
|
||||
expectedStats.expirationTimeElapsed = now;
|
||||
expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInWindow = 10;
|
||||
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||
expectedStats.sessionCountInWindow = 2;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
||||
expectedStats.inQuotaTimeElapsed = now + 3 * MINUTE_IN_MILLIS
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
assertEquals(expectedStats,
|
||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
|
||||
|
||||
// Frequent
|
||||
expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
|
||||
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
|
||||
expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInWindow = 15;
|
||||
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||
expectedStats.sessionCountInWindow = 3;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
||||
expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
assertEquals(expectedStats,
|
||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
|
||||
|
||||
// Rare
|
||||
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
|
||||
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
|
||||
expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInWindow = 20;
|
||||
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||
expectedStats.sessionCountInWindow = 4;
|
||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
||||
expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
|
||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||
assertEquals(expectedStats,
|
||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
|
||||
*/
|
||||
@Test
|
||||
public void testGetExecutionStatsLocked_Values_BeginningOfTime() {
|
||||
// Set time to 3 minutes after boot.
|
||||
advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
|
||||
advanceElapsedClock(3 * MINUTE_IN_MILLIS);
|
||||
|
||||
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||
createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2));
|
||||
|
||||
ExecutionStats expectedStats = new ExecutionStats();
|
||||
|
||||
// Active
|
||||
expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
|
||||
expectedStats.expirationTimeElapsed = 11 * MINUTE_IN_MILLIS;
|
||||
expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInWindow = 2;
|
||||
expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
|
||||
expectedStats.bgJobCountInMaxPeriod = 2;
|
||||
expectedStats.sessionCountInWindow = 1;
|
||||
assertEquals(expectedStats,
|
||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
|
||||
|
||||
// Working
|
||||
expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
|
||||
expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
|
||||
assertEquals(expectedStats,
|
||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
|
||||
|
||||
// Frequent
|
||||
expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
|
||||
expectedStats.expirationTimeElapsed = 8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
|
||||
assertEquals(expectedStats,
|
||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
|
||||
|
||||
// Rare
|
||||
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
|
||||
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
|
||||
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
|
||||
expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
|
||||
assertEquals(expectedStats,
|
||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
|
||||
*/
|
||||
@@ -850,13 +920,15 @@ public class QuotaControllerTest {
|
||||
|
||||
ExecutionStats expectedStats = new ExecutionStats();
|
||||
expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
|
||||
expectedStats.jobCountLimit = originalStatsActive.jobCountLimit;
|
||||
expectedStats.sessionCountLimit = originalStatsActive.sessionCountLimit;
|
||||
expectedStats.expirationTimeElapsed = originalStatsActive.expirationTimeElapsed;
|
||||
expectedStats.executionTimeInWindowMs = originalStatsActive.executionTimeInWindowMs;
|
||||
expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
|
||||
expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
|
||||
expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod;
|
||||
expectedStats.sessionCountInWindow = originalStatsActive.sessionCountInWindow;
|
||||
expectedStats.quotaCutoffTimeElapsed = originalStatsActive.quotaCutoffTimeElapsed;
|
||||
expectedStats.inQuotaTimeElapsed = originalStatsActive.inQuotaTimeElapsed;
|
||||
final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0,
|
||||
"com.android.test", ACTIVE_INDEX);
|
||||
// Stats for the same bucket should use the same object.
|
||||
@@ -864,33 +936,39 @@ public class QuotaControllerTest {
|
||||
assertEquals(expectedStats, newStatsActive);
|
||||
|
||||
expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs;
|
||||
expectedStats.jobCountLimit = originalStatsWorking.jobCountLimit;
|
||||
expectedStats.sessionCountLimit = originalStatsWorking.sessionCountLimit;
|
||||
expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed;
|
||||
expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
|
||||
expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
|
||||
expectedStats.sessionCountInWindow = originalStatsWorking.sessionCountInWindow;
|
||||
expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
|
||||
expectedStats.inQuotaTimeElapsed = originalStatsWorking.inQuotaTimeElapsed;
|
||||
final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0,
|
||||
"com.android.test", WORKING_INDEX);
|
||||
assertTrue(originalStatsWorking == newStatsWorking);
|
||||
assertNotEquals(expectedStats, newStatsWorking);
|
||||
|
||||
expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs;
|
||||
expectedStats.jobCountLimit = originalStatsFrequent.jobCountLimit;
|
||||
expectedStats.sessionCountLimit = originalStatsFrequent.sessionCountLimit;
|
||||
expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed;
|
||||
expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
|
||||
expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
|
||||
expectedStats.sessionCountInWindow = originalStatsFrequent.sessionCountInWindow;
|
||||
expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
|
||||
expectedStats.inQuotaTimeElapsed = originalStatsFrequent.inQuotaTimeElapsed;
|
||||
final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
|
||||
"com.android.test", FREQUENT_INDEX);
|
||||
assertTrue(originalStatsFrequent == newStatsFrequent);
|
||||
assertNotEquals(expectedStats, newStatsFrequent);
|
||||
|
||||
expectedStats.windowSizeMs = originalStatsRare.windowSizeMs;
|
||||
expectedStats.jobCountLimit = originalStatsRare.jobCountLimit;
|
||||
expectedStats.sessionCountLimit = originalStatsRare.sessionCountLimit;
|
||||
expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed;
|
||||
expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
|
||||
expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
|
||||
expectedStats.sessionCountInWindow = originalStatsRare.sessionCountInWindow;
|
||||
expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
|
||||
expectedStats.inQuotaTimeElapsed = originalStatsRare.inQuotaTimeElapsed;
|
||||
final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0,
|
||||
"com.android.test", RARE_INDEX);
|
||||
assertTrue(originalStatsRare == newStatsRare);
|
||||
@@ -1065,7 +1143,7 @@ public class QuotaControllerTest {
|
||||
public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() {
|
||||
setDischarging();
|
||||
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME;
|
||||
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
|
||||
mQuotaController.saveTimingSession(0, "com.android.test.spam",
|
||||
createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
|
||||
mQuotaController.saveTimingSession(0, "com.android.test.spam",
|
||||
@@ -1100,7 +1178,7 @@ public class QuotaControllerTest {
|
||||
public void testIsWithinQuotaLocked_OverDuration_OverJobCount() {
|
||||
setDischarging();
|
||||
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME;
|
||||
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
|
||||
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||
createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
|
||||
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||
@@ -1141,7 +1219,7 @@ public class QuotaControllerTest {
|
||||
advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS);
|
||||
|
||||
assertEquals(2, mQuotaController.getExecutionStatsLocked(
|
||||
SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInAllowedTime);
|
||||
SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInRateLimitingWindow);
|
||||
assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus));
|
||||
}
|
||||
|
||||
@@ -1212,9 +1290,9 @@ public class QuotaControllerTest {
|
||||
mQuotaController.getTimingSessions(SOURCE_USER_ID, fgChangerPkgName).size());
|
||||
for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) {
|
||||
assertEquals(42, mQuotaController.getExecutionStatsLocked(
|
||||
SOURCE_USER_ID, fgChangerPkgName, i).jobCountInAllowedTime);
|
||||
SOURCE_USER_ID, fgChangerPkgName, i).jobCountInRateLimitingWindow);
|
||||
assertEquals(1, mQuotaController.getExecutionStatsLocked(
|
||||
SOURCE_USER_ID, unaffectedPkgName, i).jobCountInAllowedTime);
|
||||
SOURCE_USER_ID, unaffectedPkgName, i).jobCountInRateLimitingWindow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1555,26 +1633,29 @@ public class QuotaControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaybeScheduleStartAlarmLocked_JobCount_AllowedTime() {
|
||||
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();
|
||||
|
||||
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
final int standbyBucket = WORKING_INDEX;
|
||||
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
|
||||
SOURCE_PACKAGE, standbyBucket);
|
||||
stats.jobCountInAllowedTime =
|
||||
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME + 2;
|
||||
stats.jobCountInRateLimitingWindow =
|
||||
mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW + 2;
|
||||
|
||||
// Invalid time in the past, so the count shouldn't be used.
|
||||
stats.jobCountExpirationTimeElapsed =
|
||||
now - mQuotaController.getAllowedTimePerPeriodMs() / 2;
|
||||
stats.jobRateLimitExpirationTimeElapsed = now - 5 * MINUTE_IN_MILLIS / 2;
|
||||
mQuotaController.maybeScheduleStartAlarmLocked(
|
||||
SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
|
||||
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
|
||||
|
||||
// Invalid time in the future, so the count should be used.
|
||||
stats.jobCountExpirationTimeElapsed =
|
||||
now + mQuotaController.getAllowedTimePerPeriodMs() / 2;
|
||||
final long expectedWorkingAlarmTime =
|
||||
stats.jobCountExpirationTimeElapsed + mQuotaController.getAllowedTimePerPeriodMs();
|
||||
// Valid time in the future, so the count should be used.
|
||||
stats.jobRateLimitExpirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS / 2;
|
||||
final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed;
|
||||
mQuotaController.maybeScheduleStartAlarmLocked(
|
||||
SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
|
||||
verify(mAlarmManager, times(1))
|
||||
@@ -1732,12 +1813,13 @@ public class QuotaControllerTest {
|
||||
mQcConstants.MAX_JOB_COUNT_WORKING = 4000;
|
||||
mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
|
||||
mQcConstants.MAX_JOB_COUNT_RARE = 2000;
|
||||
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
|
||||
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_ALLOWED_TIME = 50;
|
||||
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 50;
|
||||
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 10 * SECOND_IN_MILLIS;
|
||||
|
||||
mQcConstants.updateConstants();
|
||||
@@ -1750,12 +1832,13 @@ public class QuotaControllerTest {
|
||||
mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
|
||||
assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
|
||||
assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
|
||||
assertEquals(500, mQuotaController.getMaxJobCountPerAllowedTime());
|
||||
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
|
||||
assertEquals(500, mQuotaController.getMaxJobCountPerRateLimitingWindow());
|
||||
assertEquals(5000, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
|
||||
assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
|
||||
assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
|
||||
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
|
||||
assertEquals(50, mQuotaController.getMaxSessionCountPerAllowedTime());
|
||||
assertEquals(50, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
|
||||
assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
|
||||
assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
|
||||
assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
|
||||
@@ -1778,12 +1861,13 @@ public class QuotaControllerTest {
|
||||
mQcConstants.MAX_JOB_COUNT_WORKING = 1;
|
||||
mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
|
||||
mQcConstants.MAX_JOB_COUNT_RARE = 1;
|
||||
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
|
||||
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 = 1;
|
||||
mQcConstants.MAX_SESSION_COUNT_FREQUENT = 2;
|
||||
mQcConstants.MAX_SESSION_COUNT_RARE = 1;
|
||||
mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = 0;
|
||||
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();
|
||||
@@ -1795,16 +1879,17 @@ public class QuotaControllerTest {
|
||||
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
|
||||
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
|
||||
assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
|
||||
assertEquals(10, mQuotaController.getMaxJobCountPerAllowedTime());
|
||||
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
|
||||
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
|
||||
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
|
||||
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
|
||||
assertEquals(10, mQuotaController.getMaxSessionCountPerAllowedTime());
|
||||
assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
|
||||
assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
|
||||
assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
|
||||
assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
|
||||
assertEquals(30 * SECOND_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
|
||||
assertEquals(10, mQuotaController.getMaxJobCountPerRateLimitingWindow());
|
||||
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
|
||||
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
|
||||
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
|
||||
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RARE_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.getTimingSessionCoalescingDurationMs());
|
||||
|
||||
// Test larger than a day. Controller should cap at one day.
|
||||
@@ -1815,6 +1900,7 @@ public class QuotaControllerTest {
|
||||
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();
|
||||
@@ -1826,6 +1912,7 @@ public class QuotaControllerTest {
|
||||
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
|
||||
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
|
||||
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
|
||||
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
|
||||
assertEquals(15 * MINUTE_IN_MILLIS,
|
||||
mQuotaController.getTimingSessionCoalescingDurationMs());
|
||||
}
|
||||
@@ -2126,7 +2213,7 @@ public class QuotaControllerTest {
|
||||
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
|
||||
SOURCE_PACKAGE, standbyBucket);
|
||||
assertEquals(0, stats.jobCountInAllowedTime);
|
||||
assertEquals(0, stats.jobCountInRateLimitingWindow);
|
||||
|
||||
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
|
||||
mQuotaController.prepareForExecutionLocked(jobFg1);
|
||||
@@ -2138,7 +2225,7 @@ public class QuotaControllerTest {
|
||||
mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false);
|
||||
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
|
||||
assertEquals(0, stats.jobCountInAllowedTime);
|
||||
assertEquals(0, stats.jobCountInRateLimitingWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2154,7 +2241,7 @@ public class QuotaControllerTest {
|
||||
|
||||
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
|
||||
SOURCE_PACKAGE, standbyBucket);
|
||||
assertEquals(0, stats.jobCountInAllowedTime);
|
||||
assertEquals(0, stats.jobCountInRateLimitingWindow);
|
||||
|
||||
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
|
||||
mQuotaController.prepareForExecutionLocked(jobBg1);
|
||||
@@ -2165,7 +2252,7 @@ public class QuotaControllerTest {
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
|
||||
|
||||
assertEquals(2, stats.jobCountInAllowedTime);
|
||||
assertEquals(2, stats.jobCountInRateLimitingWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2421,10 +2508,10 @@ public class QuotaControllerTest {
|
||||
|
||||
/**
|
||||
* Tests that the start alarm is properly scheduled when a job has been throttled due to the job
|
||||
* count quota.
|
||||
* count rate limiting.
|
||||
*/
|
||||
@Test
|
||||
public void testStartAlarmScheduled_JobCount_AllowedTime() {
|
||||
public void testStartAlarmScheduled_JobCount_RateLimitingWindow() {
|
||||
// saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
|
||||
// because it schedules an alarm too. Prevent it from doing so.
|
||||
spyOn(mQuotaController);
|
||||
@@ -2432,7 +2519,7 @@ public class QuotaControllerTest {
|
||||
|
||||
// Essentially disable session throttling.
|
||||
mQcConstants.MAX_SESSION_COUNT_WORKING =
|
||||
mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = Integer.MAX_VALUE;
|
||||
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = Integer.MAX_VALUE;
|
||||
mQcConstants.updateConstants();
|
||||
|
||||
final int standbyBucket = WORKING_INDEX;
|
||||
@@ -2444,7 +2531,7 @@ public class QuotaControllerTest {
|
||||
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
|
||||
|
||||
// Ran jobs up to the job limit. All of them should be allowed to run.
|
||||
for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) {
|
||||
for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; ++i) {
|
||||
JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
|
||||
setStandbyBucket(WORKING_INDEX, job);
|
||||
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||
@@ -2466,18 +2553,17 @@ public class QuotaControllerTest {
|
||||
|
||||
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
|
||||
SOURCE_PACKAGE, standbyBucket);
|
||||
final long expectedWorkingAlarmTime =
|
||||
stats.jobCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS;
|
||||
final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed;
|
||||
verify(mAlarmManager, times(1))
|
||||
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the start alarm is properly scheduled when a job has been throttled due to the job
|
||||
* count quota.
|
||||
* Tests that the start alarm is properly scheduled when a job has been throttled due to the
|
||||
* session count rate limiting.
|
||||
*/
|
||||
@Test
|
||||
public void testStartAlarmScheduled_TimingSessionCount_AllowedTime() {
|
||||
public void testStartAlarmScheduled_TimingSessionCount_RateLimitingWindow() {
|
||||
// saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
|
||||
// because it schedules an alarm too. Prevent it from doing so.
|
||||
spyOn(mQuotaController);
|
||||
@@ -2485,10 +2571,10 @@ public class QuotaControllerTest {
|
||||
|
||||
// Essentially disable job count throttling.
|
||||
mQcConstants.MAX_JOB_COUNT_FREQUENT =
|
||||
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = Integer.MAX_VALUE;
|
||||
// Make sure throttling is because of COUNT_PER_ALLOWED_TIME.
|
||||
mQcConstants.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_ALLOWED_TIME + 1;
|
||||
mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW + 1;
|
||||
mQcConstants.updateConstants();
|
||||
|
||||
final int standbyBucket = FREQUENT_INDEX;
|
||||
@@ -2500,7 +2586,7 @@ public class QuotaControllerTest {
|
||||
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
|
||||
|
||||
// Ran jobs up to the job limit. All of them should be allowed to run.
|
||||
for (int i = 0; i < mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME; ++i) {
|
||||
for (int i = 0; i < mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW; ++i) {
|
||||
JobStatus job = createJobStatus(
|
||||
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", i);
|
||||
setStandbyBucket(FREQUENT_INDEX, job);
|
||||
@@ -2515,7 +2601,7 @@ public class QuotaControllerTest {
|
||||
// Start alarm shouldn't have been scheduled since the app was in quota up until this point.
|
||||
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
|
||||
|
||||
// The app is now out of job count quota
|
||||
// The app is now out of session count quota
|
||||
JobStatus throttledJob = createJobStatus(
|
||||
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42);
|
||||
mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
|
||||
@@ -2523,8 +2609,7 @@ public class QuotaControllerTest {
|
||||
|
||||
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
|
||||
SOURCE_PACKAGE, standbyBucket);
|
||||
final long expectedWorkingAlarmTime =
|
||||
stats.sessionCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS;
|
||||
final long expectedWorkingAlarmTime = stats.sessionRateLimitExpirationTimeElapsed;
|
||||
verify(mAlarmManager, times(1))
|
||||
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user