Merge "Add throttling by job run session." into qt-dev
This commit is contained in:
@@ -276,6 +276,24 @@ message ConstantsProto {
|
|||||||
// The maximum number of jobs that should be allowed to run in the past
|
// The maximum number of jobs that should be allowed to run in the past
|
||||||
// {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
|
// {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
|
||||||
optional int32 max_job_count_per_allowed_time = 12;
|
optional int32 max_job_count_per_allowed_time = 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;
|
||||||
|
// The maximum number of timing sessions an app can run within this particular standby
|
||||||
|
// bucket's window size.
|
||||||
|
optional int32 max_session_count_working = 14;
|
||||||
|
// The maximum number of timing sessions an app can run within this particular standby
|
||||||
|
// bucket's window size.
|
||||||
|
optional int32 max_session_count_frequent = 15;
|
||||||
|
// The maximum number of timing sessions an app can run within this particular standby
|
||||||
|
// 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;
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
optional QuotaController quota_controller = 24;
|
optional QuotaController quota_controller = 24;
|
||||||
|
|
||||||
@@ -510,6 +528,12 @@ message StateControllerProto {
|
|||||||
optional int64 execution_time_in_max_period_ms = 6;
|
optional int64 execution_time_in_max_period_ms = 6;
|
||||||
optional int32 bg_job_count_in_max_period = 7;
|
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.
|
||||||
|
*/
|
||||||
|
optional int32 session_count_in_window = 11;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time after which the sum of all the app's sessions plus
|
* 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
|
* ConstantsProto.QuotaController.in_quota_buffer_ms equals the quota. This is only
|
||||||
@@ -535,6 +559,21 @@ message StateControllerProto {
|
|||||||
* ConstantsProto.QuotaController.allowed_time_per_period_ms.
|
* ConstantsProto.QuotaController.allowed_time_per_period_ms.
|
||||||
*/
|
*/
|
||||||
optional int32 job_count_in_allowed_time = 10;
|
optional int32 job_count_in_allowed_time = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Package {
|
message Package {
|
||||||
|
|||||||
@@ -253,6 +253,12 @@ public final class QuotaController extends StateController {
|
|||||||
public long executionTimeInMaxPeriodMs;
|
public long executionTimeInMaxPeriodMs;
|
||||||
public int bgJobCountInMaxPeriod;
|
public int bgJobCountInMaxPeriod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public int sessionCountInWindow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time after which the sum of all the app's sessions plus {@link #mQuotaBufferMs}
|
* The time after which the sum of all the app's sessions plus {@link #mQuotaBufferMs}
|
||||||
* equals the quota. This is only valid if
|
* equals the quota. This is only valid if
|
||||||
@@ -274,21 +280,34 @@ public final class QuotaController extends StateController {
|
|||||||
*/
|
*/
|
||||||
public int jobCountInAllowedTime;
|
public int jobCountInAllowedTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time after which {@link #sessionCountInAllowedTime} should be considered
|
||||||
|
* invalid, in the elapsed realtime timebase.
|
||||||
|
*/
|
||||||
|
public long sessionCountExpirationTimeElapsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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}.
|
||||||
|
*/
|
||||||
|
public int sessionCountInAllowedTime;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder()
|
return "expirationTime=" + expirationTimeElapsed + ", "
|
||||||
.append("expirationTime=").append(expirationTimeElapsed).append(", ")
|
+ "windowSize=" + windowSizeMs + ", "
|
||||||
.append("windowSize=").append(windowSizeMs).append(", ")
|
+ "executionTimeInWindow=" + executionTimeInWindowMs + ", "
|
||||||
.append("executionTimeInWindow=").append(executionTimeInWindowMs).append(", ")
|
+ "bgJobCountInWindow=" + bgJobCountInWindow + ", "
|
||||||
.append("bgJobCountInWindow=").append(bgJobCountInWindow).append(", ")
|
+ "executionTimeInMaxPeriod=" + executionTimeInMaxPeriodMs + ", "
|
||||||
.append("executionTimeInMaxPeriod=").append(executionTimeInMaxPeriodMs)
|
+ "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
|
||||||
.append(", ")
|
+ "sessionCountInWindow=" + sessionCountInWindow + ", "
|
||||||
.append("bgJobCountInMaxPeriod=").append(bgJobCountInMaxPeriod).append(", ")
|
+ "quotaCutoffTime=" + quotaCutoffTimeElapsed + ", "
|
||||||
.append("quotaCutoffTime=").append(quotaCutoffTimeElapsed).append(", ")
|
+ "jobCountExpirationTime=" + jobCountExpirationTimeElapsed + ", "
|
||||||
.append("jobCountExpirationTime=").append(jobCountExpirationTimeElapsed)
|
+ "jobCountInAllowedTime=" + jobCountInAllowedTime + ", "
|
||||||
.append(", ")
|
+ "sessionCountExpirationTime=" + sessionCountExpirationTimeElapsed + ", "
|
||||||
.append("jobCountInAllowedTime=").append(jobCountInAllowedTime)
|
+ "sessionCountInAllowedTime=" + sessionCountInAllowedTime;
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -300,10 +319,15 @@ public final class QuotaController extends StateController {
|
|||||||
&& this.executionTimeInWindowMs == other.executionTimeInWindowMs
|
&& this.executionTimeInWindowMs == other.executionTimeInWindowMs
|
||||||
&& this.bgJobCountInWindow == other.bgJobCountInWindow
|
&& this.bgJobCountInWindow == other.bgJobCountInWindow
|
||||||
&& this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
|
&& this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
|
||||||
|
&& this.sessionCountInWindow == other.sessionCountInWindow
|
||||||
&& this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
|
&& this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
|
||||||
&& this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed
|
&& this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed
|
||||||
&& this.jobCountExpirationTimeElapsed == other.jobCountExpirationTimeElapsed
|
&& this.jobCountExpirationTimeElapsed == other.jobCountExpirationTimeElapsed
|
||||||
&& this.jobCountInAllowedTime == other.jobCountInAllowedTime;
|
&& this.jobCountInAllowedTime == other.jobCountInAllowedTime
|
||||||
|
&& this.sessionCountExpirationTimeElapsed
|
||||||
|
== other.sessionCountExpirationTimeElapsed
|
||||||
|
&& this.sessionCountInAllowedTime
|
||||||
|
== other.sessionCountInAllowedTime;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -318,9 +342,12 @@ public final class QuotaController extends StateController {
|
|||||||
result = 31 * result + bgJobCountInWindow;
|
result = 31 * result + bgJobCountInWindow;
|
||||||
result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
|
result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
|
||||||
result = 31 * result + bgJobCountInMaxPeriod;
|
result = 31 * result + bgJobCountInMaxPeriod;
|
||||||
|
result = 31 * result + sessionCountInWindow;
|
||||||
result = 31 * result + hashLong(quotaCutoffTimeElapsed);
|
result = 31 * result + hashLong(quotaCutoffTimeElapsed);
|
||||||
result = 31 * result + hashLong(jobCountExpirationTimeElapsed);
|
result = 31 * result + hashLong(jobCountExpirationTimeElapsed);
|
||||||
result = 31 * result + jobCountInAllowedTime;
|
result = 31 * result + jobCountInAllowedTime;
|
||||||
|
result = 31 * result + hashLong(sessionCountExpirationTimeElapsed);
|
||||||
|
result = 31 * result + sessionCountInAllowedTime;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,6 +428,12 @@ public final class QuotaController extends StateController {
|
|||||||
/** The maximum number of jobs that can run within the past {@link #mAllowedTimePerPeriodMs}. */
|
/** The maximum number of jobs that can run within the past {@link #mAllowedTimePerPeriodMs}. */
|
||||||
private int mMaxJobCountPerAllowedTime = 20;
|
private int mMaxJobCountPerAllowedTime = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of {@link TimingSession}s that can run within the past {@link
|
||||||
|
* #mAllowedTimePerPeriodMs}.
|
||||||
|
*/
|
||||||
|
private int mMaxSessionCountPerAllowedTime = 20;
|
||||||
|
|
||||||
private long mNextCleanupTimeElapsed = 0;
|
private long mNextCleanupTimeElapsed = 0;
|
||||||
private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
|
private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
|
||||||
new AlarmManager.OnAlarmListener() {
|
new AlarmManager.OnAlarmListener() {
|
||||||
@@ -480,6 +513,29 @@ public final class QuotaController extends StateController {
|
|||||||
/** The minimum number of jobs that any bucket will be allowed to run. */
|
/** The minimum number of jobs that any bucket will be allowed to run. */
|
||||||
private static final int MIN_BUCKET_JOB_COUNT = 100;
|
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
|
||||||
|
* {@link TimingSession}s within the latest time interval of its rolling window size.
|
||||||
|
*
|
||||||
|
* @see #mBucketPeriodsMs
|
||||||
|
*/
|
||||||
|
private final int[] mMaxBucketSessionCounts = new int[]{
|
||||||
|
QcConstants.DEFAULT_MAX_SESSION_COUNT_ACTIVE,
|
||||||
|
QcConstants.DEFAULT_MAX_SESSION_COUNT_WORKING,
|
||||||
|
QcConstants.DEFAULT_MAX_SESSION_COUNT_FREQUENT,
|
||||||
|
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;
|
||||||
|
|
||||||
/** An app has reached its quota. The message should contain a {@link Package} object. */
|
/** An app has reached its quota. The message should contain a {@link Package} object. */
|
||||||
private static final int MSG_REACHED_QUOTA = 0;
|
private static final int MSG_REACHED_QUOTA = 0;
|
||||||
/** Drop any old timing sessions. */
|
/** Drop any old timing sessions. */
|
||||||
@@ -695,14 +751,10 @@ public final class QuotaController extends StateController {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0
|
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
|
||||||
&& isUnderJobCountQuotaLocked(userId, packageName, standbyBucket);
|
return getRemainingExecutionTimeLocked(stats) > 0
|
||||||
}
|
&& isUnderJobCountQuotaLocked(stats, standbyBucket)
|
||||||
|
&& isUnderSessionCountQuotaLocked(stats, standbyBucket);
|
||||||
private boolean isUnderJobCountQuotaLocked(final int userId, @NonNull final String packageName,
|
|
||||||
final int standbyBucket) {
|
|
||||||
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket, false);
|
|
||||||
return isUnderJobCountQuotaLocked(stats, standbyBucket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats,
|
private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats,
|
||||||
@@ -715,6 +767,17 @@ public final class QuotaController extends StateController {
|
|||||||
&& (stats.bgJobCountInWindow < mMaxBucketJobCounts[standbyBucket]);
|
&& (stats.bgJobCountInWindow < mMaxBucketJobCounts[standbyBucket]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats,
|
||||||
|
final int standbyBucket) {
|
||||||
|
final long now = sElapsedRealtimeClock.millis();
|
||||||
|
final boolean isUnderAllowedTimeQuota =
|
||||||
|
(stats.sessionCountExpirationTimeElapsed <= now
|
||||||
|
|| stats.sessionCountInAllowedTime
|
||||||
|
< mMaxSessionCountPerAllowedTime);
|
||||||
|
return isUnderAllowedTimeQuota
|
||||||
|
&& stats.sessionCountInWindow < mMaxBucketSessionCounts[standbyBucket];
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
long getRemainingExecutionTimeLocked(@NonNull final JobStatus jobStatus) {
|
long getRemainingExecutionTimeLocked(@NonNull final JobStatus jobStatus) {
|
||||||
return getRemainingExecutionTimeLocked(jobStatus.getSourceUserId(),
|
return getRemainingExecutionTimeLocked(jobStatus.getSourceUserId(),
|
||||||
@@ -739,7 +802,11 @@ public final class QuotaController extends StateController {
|
|||||||
if (standbyBucket == NEVER_INDEX) {
|
if (standbyBucket == NEVER_INDEX) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
|
return getRemainingExecutionTimeLocked(
|
||||||
|
getExecutionStatsLocked(userId, packageName, standbyBucket));
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getRemainingExecutionTimeLocked(@NonNull ExecutionStats stats) {
|
||||||
return Math.min(mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs,
|
return Math.min(mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs,
|
||||||
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs);
|
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs);
|
||||||
}
|
}
|
||||||
@@ -877,6 +944,7 @@ public final class QuotaController extends StateController {
|
|||||||
stats.bgJobCountInWindow = 0;
|
stats.bgJobCountInWindow = 0;
|
||||||
stats.executionTimeInMaxPeriodMs = 0;
|
stats.executionTimeInMaxPeriodMs = 0;
|
||||||
stats.bgJobCountInMaxPeriod = 0;
|
stats.bgJobCountInMaxPeriod = 0;
|
||||||
|
stats.sessionCountInWindow = 0;
|
||||||
stats.quotaCutoffTimeElapsed = 0;
|
stats.quotaCutoffTimeElapsed = 0;
|
||||||
|
|
||||||
Timer timer = mPkgTimers.get(userId, packageName);
|
Timer timer = mPkgTimers.get(userId, packageName);
|
||||||
@@ -906,12 +974,14 @@ public final class QuotaController extends StateController {
|
|||||||
|
|
||||||
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
|
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
|
||||||
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
|
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
|
||||||
|
int sessionCountInWindow = 0;
|
||||||
// The minimum time between the start time and the beginning of the sessions that were
|
// The minimum time between the start time and the beginning of the sessions that were
|
||||||
// looked at --> how much time the stats will be valid for.
|
// looked at --> how much time the stats will be valid for.
|
||||||
long emptyTimeMs = Long.MAX_VALUE;
|
long emptyTimeMs = Long.MAX_VALUE;
|
||||||
// Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
|
// Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
|
||||||
// the most recent ones.
|
// the most recent ones.
|
||||||
for (int i = sessions.size() - 1; i >= 0; --i) {
|
final int loopStart = sessions.size() - 1;
|
||||||
|
for (int i = loopStart; i >= 0; --i) {
|
||||||
TimingSession session = sessions.get(i);
|
TimingSession session = sessions.get(i);
|
||||||
|
|
||||||
// Window management.
|
// Window management.
|
||||||
@@ -924,6 +994,12 @@ public final class QuotaController extends StateController {
|
|||||||
session.startTimeElapsed + stats.executionTimeInWindowMs
|
session.startTimeElapsed + stats.executionTimeInWindowMs
|
||||||
- mAllowedTimeIntoQuotaMs);
|
- mAllowedTimeIntoQuotaMs);
|
||||||
}
|
}
|
||||||
|
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) {
|
} else if (startWindowElapsed < session.endTimeElapsed) {
|
||||||
// The session started before the window but ended within the window. Only include
|
// The session started before the window but ended within the window. Only include
|
||||||
// the portion that was within the window.
|
// the portion that was within the window.
|
||||||
@@ -935,6 +1011,11 @@ public final class QuotaController extends StateController {
|
|||||||
startWindowElapsed + stats.executionTimeInWindowMs
|
startWindowElapsed + stats.executionTimeInWindowMs
|
||||||
- mAllowedTimeIntoQuotaMs);
|
- mAllowedTimeIntoQuotaMs);
|
||||||
}
|
}
|
||||||
|
if (i == loopStart
|
||||||
|
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
|
||||||
|
> mTimingSessionCoalescingDurationMs) {
|
||||||
|
sessionCountInWindow++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Max period check.
|
// Max period check.
|
||||||
@@ -965,9 +1046,27 @@ public final class QuotaController extends StateController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stats.expirationTimeElapsed = nowElapsed + emptyTimeMs;
|
stats.expirationTimeElapsed = nowElapsed + emptyTimeMs;
|
||||||
|
stats.sessionCountInWindow = sessionCountInWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invalidateAllExecutionStatsLocked(final int userId,
|
/** Invalidate ExecutionStats for all apps. */
|
||||||
|
@VisibleForTesting
|
||||||
|
void invalidateAllExecutionStatsLocked() {
|
||||||
|
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||||
|
mExecutionStatsCache.forEach((appStats) -> {
|
||||||
|
if (appStats != null) {
|
||||||
|
for (int i = 0; i < appStats.length; ++i) {
|
||||||
|
ExecutionStats stats = appStats[i];
|
||||||
|
if (stats != null) {
|
||||||
|
stats.expirationTimeElapsed = nowElapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void invalidateAllExecutionStatsLocked(final int userId,
|
||||||
@NonNull final String packageName) {
|
@NonNull final String packageName) {
|
||||||
ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
|
ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
|
||||||
if (appStats != null) {
|
if (appStats != null) {
|
||||||
@@ -1003,6 +1102,27 @@ public final class QuotaController extends StateController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void incrementTimingSessionCount(final int userId, @NonNull final String packageName) {
|
||||||
|
final long now = sElapsedRealtimeClock.millis();
|
||||||
|
ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
|
||||||
|
if (appStats == null) {
|
||||||
|
appStats = new ExecutionStats[mBucketPeriodsMs.length];
|
||||||
|
mExecutionStatsCache.add(userId, packageName, appStats);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < appStats.length; ++i) {
|
||||||
|
ExecutionStats stats = appStats[i];
|
||||||
|
if (stats == null) {
|
||||||
|
stats = new ExecutionStats();
|
||||||
|
appStats[i] = stats;
|
||||||
|
}
|
||||||
|
if (stats.sessionCountExpirationTimeElapsed <= now) {
|
||||||
|
stats.sessionCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
|
||||||
|
stats.sessionCountInAllowedTime = 0;
|
||||||
|
}
|
||||||
|
stats.sessionCountInAllowedTime++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void saveTimingSession(final int userId, @NonNull final String packageName,
|
void saveTimingSession(final int userId, @NonNull final String packageName,
|
||||||
@NonNull final TimingSession session) {
|
@NonNull final TimingSession session) {
|
||||||
@@ -1216,11 +1336,14 @@ public final class QuotaController extends StateController {
|
|||||||
final String pkgString = string(userId, packageName);
|
final String pkgString = string(userId, packageName);
|
||||||
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
|
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
|
||||||
final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
|
final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
|
||||||
|
final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats,
|
||||||
|
standbyBucket);
|
||||||
|
|
||||||
QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
|
QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
|
||||||
if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
|
if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
|
||||||
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
|
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
|
||||||
&& isUnderJobCountQuota) {
|
&& isUnderJobCountQuota
|
||||||
|
&& isUnderTimingSessionCountQuota) {
|
||||||
// Already in quota. Why was this method called?
|
// Already in quota. Why was this method called?
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
|
Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
|
||||||
@@ -1253,6 +1376,10 @@ public final class QuotaController extends StateController {
|
|||||||
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
|
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
|
||||||
stats.jobCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
|
stats.jobCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
|
||||||
}
|
}
|
||||||
|
if (!isUnderTimingSessionCountQuota) {
|
||||||
|
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
|
||||||
|
stats.sessionCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
|
||||||
|
}
|
||||||
// Only schedule the alarm if:
|
// Only schedule the alarm if:
|
||||||
// 1. There isn't one currently scheduled
|
// 1. There isn't one currently scheduled
|
||||||
// 2. The new alarm is significantly earlier than the previous alarm (which could be the
|
// 2. The new alarm is significantly earlier than the previous alarm (which could be the
|
||||||
@@ -1483,6 +1610,7 @@ public final class QuotaController extends StateController {
|
|||||||
// of jobs.
|
// of jobs.
|
||||||
// However, cancel the currently scheduled cutoff since it's not currently useful.
|
// However, cancel the currently scheduled cutoff since it's not currently useful.
|
||||||
cancelCutoff();
|
cancelCutoff();
|
||||||
|
incrementTimingSessionCount(mPkg.userId, mPkg.packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1842,6 +1970,14 @@ public final class QuotaController extends StateController {
|
|||||||
private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
|
private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
|
||||||
private static final String KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME =
|
private static final String KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME =
|
||||||
"max_count_per_allowed_time";
|
"max_count_per_allowed_time";
|
||||||
|
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_TIMING_SESSION_COALESCING_DURATION_MS =
|
||||||
|
"timing_session_coalescing_duration_ms";
|
||||||
|
|
||||||
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
|
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
|
||||||
10 * 60 * 1000L; // 10 minutes
|
10 * 60 * 1000L; // 10 minutes
|
||||||
@@ -1866,6 +2002,16 @@ public final class QuotaController extends StateController {
|
|||||||
private static final int DEFAULT_MAX_JOB_COUNT_RARE =
|
private static final int DEFAULT_MAX_JOB_COUNT_RARE =
|
||||||
2400; // 100/hr
|
2400; // 100/hr
|
||||||
private static final int DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
|
private static final int DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
|
||||||
|
private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
|
||||||
|
20; // 120/hr
|
||||||
|
private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
|
||||||
|
10; // 5/hr
|
||||||
|
private static final int DEFAULT_MAX_SESSION_COUNT_FREQUENT =
|
||||||
|
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;
|
||||||
|
|
||||||
/** How much time each app will have to run jobs within their standby bucket window. */
|
/** 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;
|
public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
|
||||||
@@ -1939,6 +2085,43 @@ public final class QuotaController extends StateController {
|
|||||||
*/
|
*/
|
||||||
public int MAX_JOB_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME;
|
public int MAX_JOB_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of {@link TimingSession}s an app can run within this particular
|
||||||
|
* standby bucket's window size.
|
||||||
|
*/
|
||||||
|
public int MAX_SESSION_COUNT_ACTIVE = DEFAULT_MAX_SESSION_COUNT_ACTIVE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of {@link TimingSession}s an app can run within this particular
|
||||||
|
* standby bucket's window size.
|
||||||
|
*/
|
||||||
|
public int MAX_SESSION_COUNT_WORKING = DEFAULT_MAX_SESSION_COUNT_WORKING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of {@link TimingSession}s an app can run within this particular
|
||||||
|
* standby bucket's window size.
|
||||||
|
*/
|
||||||
|
public int MAX_SESSION_COUNT_FREQUENT = DEFAULT_MAX_SESSION_COUNT_FREQUENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of {@link TimingSession}s an app can run within this particular
|
||||||
|
* standby bucket's window size.
|
||||||
|
*/
|
||||||
|
public int MAX_SESSION_COUNT_RARE = DEFAULT_MAX_SESSION_COUNT_RARE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Treat two distinct {@link TimingSession}s as the same if they start and end within this
|
||||||
|
* amount of time of each other.
|
||||||
|
*/
|
||||||
|
public long TIMING_SESSION_COALESCING_DURATION_MS =
|
||||||
|
DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
|
||||||
|
|
||||||
QcConstants(Handler handler) {
|
QcConstants(Handler handler) {
|
||||||
super(handler);
|
super(handler);
|
||||||
}
|
}
|
||||||
@@ -1986,6 +2169,20 @@ public final class QuotaController extends StateController {
|
|||||||
KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
|
KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
|
||||||
MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
|
MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
|
||||||
KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME);
|
KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME);
|
||||||
|
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_PER_ALLOWED_TIME = mParser.getInt(
|
||||||
|
KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME,
|
||||||
|
DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME);
|
||||||
|
TIMING_SESSION_COALESCING_DURATION_MS = mParser.getLong(
|
||||||
|
KEY_TIMING_SESSION_COALESCING_DURATION_MS,
|
||||||
|
DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS);
|
||||||
|
|
||||||
updateConstants();
|
updateConstants();
|
||||||
}
|
}
|
||||||
@@ -2071,11 +2268,48 @@ public final class QuotaController extends StateController {
|
|||||||
mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
|
mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
int newMaxSessionCountPerAllowedPeriod = Math.max(10,
|
||||||
|
MAX_SESSION_COUNT_PER_ALLOWED_TIME);
|
||||||
|
if (mMaxSessionCountPerAllowedTime != newMaxSessionCountPerAllowedPeriod) {
|
||||||
|
mMaxSessionCountPerAllowedTime = newMaxSessionCountPerAllowedPeriod;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
|
||||||
|
Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
|
||||||
|
if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
|
||||||
|
mTimingSessionCoalescingDurationMs = newSessionCoalescingDurationMs;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (changed && mShouldThrottle) {
|
if (changed && mShouldThrottle) {
|
||||||
// Update job bookkeeping out of band.
|
// Update job bookkeeping out of band.
|
||||||
BackgroundThread.getHandler().post(() -> {
|
BackgroundThread.getHandler().post(() -> {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
invalidateAllExecutionStatsLocked();
|
||||||
maybeUpdateAllConstraintsLocked();
|
maybeUpdateAllConstraintsLocked();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2100,6 +2334,14 @@ public final class QuotaController extends StateController {
|
|||||||
pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).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)
|
pw.printPair(KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, MAX_JOB_COUNT_PER_ALLOWED_TIME)
|
||||||
.println();
|
.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_TIMING_SESSION_COALESCING_DURATION_MS,
|
||||||
|
TIMING_SESSION_COALESCING_DURATION_MS).println();
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2125,6 +2367,18 @@ public final class QuotaController extends StateController {
|
|||||||
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
|
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
|
||||||
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
|
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
|
||||||
MAX_JOB_COUNT_PER_ALLOWED_TIME);
|
MAX_JOB_COUNT_PER_ALLOWED_TIME);
|
||||||
|
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_ACTIVE,
|
||||||
|
MAX_SESSION_COUNT_ACTIVE);
|
||||||
|
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_WORKING,
|
||||||
|
MAX_SESSION_COUNT_WORKING);
|
||||||
|
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_FREQUENT,
|
||||||
|
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.TIMING_SESSION_COALESCING_DURATION_MS,
|
||||||
|
TIMING_SESSION_COALESCING_DURATION_MS);
|
||||||
proto.end(qcToken);
|
proto.end(qcToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2142,6 +2396,12 @@ public final class QuotaController extends StateController {
|
|||||||
return mMaxBucketJobCounts;
|
return mMaxBucketJobCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@NonNull
|
||||||
|
int[] getBucketMaxSessionCounts() {
|
||||||
|
return mMaxBucketSessionCounts;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@NonNull
|
@NonNull
|
||||||
long[] getBucketWindowSizes() {
|
long[] getBucketWindowSizes() {
|
||||||
@@ -2175,6 +2435,16 @@ public final class QuotaController extends StateController {
|
|||||||
return mMaxJobCountPerAllowedTime;
|
return mMaxJobCountPerAllowedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
long getTimingSessionCoalescingDurationMs() {
|
||||||
|
return mTimingSessionCoalescingDurationMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
int getMaxSessionCountPerAllowedTime() {
|
||||||
|
return mMaxSessionCountPerAllowedTime;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
List<TimingSession> getTimingSessions(int userId, String packageName) {
|
List<TimingSession> getTimingSessions(int userId, String packageName) {
|
||||||
@@ -2400,6 +2670,9 @@ public final class QuotaController extends StateController {
|
|||||||
proto.write(
|
proto.write(
|
||||||
StateControllerProto.QuotaController.ExecutionStats.BG_JOB_COUNT_IN_MAX_PERIOD,
|
StateControllerProto.QuotaController.ExecutionStats.BG_JOB_COUNT_IN_MAX_PERIOD,
|
||||||
es.bgJobCountInMaxPeriod);
|
es.bgJobCountInMaxPeriod);
|
||||||
|
proto.write(
|
||||||
|
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_WINDOW,
|
||||||
|
es.sessionCountInWindow);
|
||||||
proto.write(
|
proto.write(
|
||||||
StateControllerProto.QuotaController.ExecutionStats.QUOTA_CUTOFF_TIME_ELAPSED,
|
StateControllerProto.QuotaController.ExecutionStats.QUOTA_CUTOFF_TIME_ELAPSED,
|
||||||
es.quotaCutoffTimeElapsed);
|
es.quotaCutoffTimeElapsed);
|
||||||
@@ -2409,6 +2682,12 @@ public final class QuotaController extends StateController {
|
|||||||
proto.write(
|
proto.write(
|
||||||
StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_ALLOWED_TIME,
|
StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_ALLOWED_TIME,
|
||||||
es.jobCountInAllowedTime);
|
es.jobCountInAllowedTime);
|
||||||
|
proto.write(
|
||||||
|
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_EXPIRATION_TIME_ELAPSED,
|
||||||
|
es.sessionCountExpirationTimeElapsed);
|
||||||
|
proto.write(
|
||||||
|
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_ALLOWED_TIME,
|
||||||
|
es.sessionCountInAllowedTime);
|
||||||
proto.end(esToken);
|
proto.end(esToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -469,6 +469,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 0;
|
expectedStats.bgJobCountInWindow = 0;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 0;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
assertEquals(expectedStats, inputStats);
|
assertEquals(expectedStats, inputStats);
|
||||||
|
|
||||||
@@ -479,6 +480,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 3;
|
expectedStats.bgJobCountInWindow = 3;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 1;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
assertEquals(expectedStats, inputStats);
|
assertEquals(expectedStats, inputStats);
|
||||||
|
|
||||||
@@ -490,6 +492,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 3;
|
expectedStats.bgJobCountInWindow = 3;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 1;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
assertEquals(expectedStats, inputStats);
|
assertEquals(expectedStats, inputStats);
|
||||||
|
|
||||||
@@ -501,6 +504,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 3;
|
expectedStats.bgJobCountInWindow = 3;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 1;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
assertEquals(expectedStats, inputStats);
|
assertEquals(expectedStats, inputStats);
|
||||||
|
|
||||||
@@ -511,6 +515,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 4;
|
expectedStats.bgJobCountInWindow = 4;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 2;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
assertEquals(expectedStats, inputStats);
|
assertEquals(expectedStats, inputStats);
|
||||||
|
|
||||||
@@ -522,6 +527,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 5;
|
expectedStats.bgJobCountInWindow = 5;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 3;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
assertEquals(expectedStats, inputStats);
|
assertEquals(expectedStats, inputStats);
|
||||||
|
|
||||||
@@ -532,6 +538,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 10;
|
expectedStats.bgJobCountInWindow = 10;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 4;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
@@ -545,6 +552,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 10;
|
expectedStats.bgJobCountInWindow = 10;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 4;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
@@ -558,6 +566,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 15;
|
expectedStats.bgJobCountInWindow = 15;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 15;
|
expectedStats.bgJobCountInMaxPeriod = 15;
|
||||||
|
expectedStats.sessionCountInWindow = 5;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
@@ -575,6 +584,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 15;
|
expectedStats.bgJobCountInWindow = 15;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 18;
|
expectedStats.bgJobCountInMaxPeriod = 18;
|
||||||
|
expectedStats.sessionCountInWindow = 5;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
@@ -585,12 +595,13 @@ public class QuotaControllerTest {
|
|||||||
createTimingSession(now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
|
createTimingSession(now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
|
||||||
2 * MINUTE_IN_MILLIS, 2));
|
2 * MINUTE_IN_MILLIS, 2));
|
||||||
inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
|
inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
|
||||||
// Invalid time is now since the earlist session straddles the max period cutoff time.
|
// Invalid time is now since the earliest session straddles the max period cutoff time.
|
||||||
expectedStats.expirationTimeElapsed = now;
|
expectedStats.expirationTimeElapsed = now;
|
||||||
expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInWindow = 15;
|
expectedStats.bgJobCountInWindow = 15;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||||
|
expectedStats.sessionCountInWindow = 5;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
|
||||||
@@ -621,6 +632,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 5;
|
expectedStats.bgJobCountInWindow = 5;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||||
|
expectedStats.sessionCountInWindow = 1;
|
||||||
assertEquals(expectedStats,
|
assertEquals(expectedStats,
|
||||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
|
mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
|
||||||
|
|
||||||
@@ -631,6 +643,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 10;
|
expectedStats.bgJobCountInWindow = 10;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||||
|
expectedStats.sessionCountInWindow = 2;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
assertEquals(expectedStats,
|
assertEquals(expectedStats,
|
||||||
@@ -643,6 +656,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 15;
|
expectedStats.bgJobCountInWindow = 15;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||||
|
expectedStats.sessionCountInWindow = 3;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
assertEquals(expectedStats,
|
assertEquals(expectedStats,
|
||||||
@@ -655,17 +669,161 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = 20;
|
expectedStats.bgJobCountInWindow = 20;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
|
||||||
expectedStats.bgJobCountInMaxPeriod = 20;
|
expectedStats.bgJobCountInMaxPeriod = 20;
|
||||||
|
expectedStats.sessionCountInWindow = 4;
|
||||||
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
|
||||||
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
+ mQcConstants.IN_QUOTA_BUFFER_MS;
|
||||||
assertEquals(expectedStats,
|
assertEquals(expectedStats,
|
||||||
mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
|
mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetExecutionStatsLocked_CoalescingSessions() {
|
||||||
|
for (int i = 0; i < 10; ++i) {
|
||||||
|
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||||
|
createTimingSession(
|
||||||
|
JobSchedulerService.sElapsedRealtimeClock.millis(),
|
||||||
|
5 * MINUTE_IN_MILLIS, 5));
|
||||||
|
advanceElapsedClock(5 * MINUTE_IN_MILLIS);
|
||||||
|
advanceElapsedClock(5 * MINUTE_IN_MILLIS);
|
||||||
|
for (int j = 0; j < 5; ++j) {
|
||||||
|
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||||
|
createTimingSession(
|
||||||
|
JobSchedulerService.sElapsedRealtimeClock.millis(),
|
||||||
|
MINUTE_IN_MILLIS, 2));
|
||||||
|
advanceElapsedClock(MINUTE_IN_MILLIS);
|
||||||
|
advanceElapsedClock(54 * SECOND_IN_MILLIS);
|
||||||
|
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||||
|
createTimingSession(
|
||||||
|
JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1));
|
||||||
|
advanceElapsedClock(500);
|
||||||
|
advanceElapsedClock(400);
|
||||||
|
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||||
|
createTimingSession(
|
||||||
|
JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1));
|
||||||
|
advanceElapsedClock(100);
|
||||||
|
advanceElapsedClock(5 * SECOND_IN_MILLIS);
|
||||||
|
}
|
||||||
|
advanceElapsedClock(40 * MINUTE_IN_MILLIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 0;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(32, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(128, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(160, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 500;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(22, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(88, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(110, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 1000;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(22, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(88, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(110, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * SECOND_IN_MILLIS;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(14, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(56, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(70, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = MINUTE_IN_MILLIS;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(4, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(16, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(20, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * MINUTE_IN_MILLIS;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(2, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(8, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(10, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 15 * MINUTE_IN_MILLIS;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(2, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(8, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(10, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
mQuotaController.invalidateAllExecutionStatsLocked();
|
||||||
|
assertEquals(0, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(2, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(8, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
|
||||||
|
assertEquals(10, mQuotaController.getExecutionStatsLocked(
|
||||||
|
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
|
* Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetExecutionStatsLocked_Caching() {
|
public void testGetExecutionStatsLocked_Caching() {
|
||||||
|
spyOn(mQuotaController);
|
||||||
|
doNothing().when(mQuotaController).invalidateAllExecutionStatsLocked();
|
||||||
|
|
||||||
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
|
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||||
mQuotaController.saveTimingSession(0, "com.android.test",
|
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||||
createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
|
createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
|
||||||
@@ -697,6 +855,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
|
expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
|
||||||
expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
|
expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
|
||||||
expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod;
|
expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod;
|
||||||
|
expectedStats.sessionCountInWindow = originalStatsActive.sessionCountInWindow;
|
||||||
expectedStats.quotaCutoffTimeElapsed = originalStatsActive.quotaCutoffTimeElapsed;
|
expectedStats.quotaCutoffTimeElapsed = originalStatsActive.quotaCutoffTimeElapsed;
|
||||||
final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0,
|
final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0,
|
||||||
"com.android.test", ACTIVE_INDEX);
|
"com.android.test", ACTIVE_INDEX);
|
||||||
@@ -708,6 +867,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed;
|
expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed;
|
||||||
expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
|
expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
|
||||||
expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
|
expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
|
||||||
|
expectedStats.sessionCountInWindow = originalStatsWorking.sessionCountInWindow;
|
||||||
expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
|
expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
|
||||||
final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0,
|
final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0,
|
||||||
"com.android.test", WORKING_INDEX);
|
"com.android.test", WORKING_INDEX);
|
||||||
@@ -718,6 +878,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed;
|
expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed;
|
||||||
expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
|
expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
|
||||||
expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
|
expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
|
||||||
|
expectedStats.sessionCountInWindow = originalStatsFrequent.sessionCountInWindow;
|
||||||
expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
|
expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
|
||||||
final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
|
final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
|
||||||
"com.android.test", FREQUENT_INDEX);
|
"com.android.test", FREQUENT_INDEX);
|
||||||
@@ -728,6 +889,7 @@ public class QuotaControllerTest {
|
|||||||
expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed;
|
expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed;
|
||||||
expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
|
expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
|
||||||
expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
|
expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
|
||||||
|
expectedStats.sessionCountInWindow = originalStatsRare.sessionCountInWindow;
|
||||||
expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
|
expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
|
||||||
final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0,
|
final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0,
|
||||||
"com.android.test", RARE_INDEX);
|
"com.android.test", RARE_INDEX);
|
||||||
@@ -1056,6 +1218,37 @@ public class QuotaControllerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
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();
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; ++i) {
|
||||||
|
mQuotaController.saveTimingSession(0, "com.android.test",
|
||||||
|
createTimingSession(now - ((10 - i) * MINUTE_IN_MILLIS), 30 * SECOND_IN_MILLIS,
|
||||||
|
2));
|
||||||
|
mQuotaController.incrementJobCount(0, "com.android.test", 2);
|
||||||
|
|
||||||
|
assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
|
||||||
|
i < 2,
|
||||||
|
mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
|
||||||
|
assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
|
||||||
|
i < 3,
|
||||||
|
mQuotaController.isWithinQuotaLocked(0, "com.android.test", FREQUENT_INDEX));
|
||||||
|
assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
|
||||||
|
i < 4,
|
||||||
|
mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
|
||||||
|
assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
|
||||||
|
i < 5,
|
||||||
|
mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMaybeScheduleCleanupAlarmLocked() {
|
public void testMaybeScheduleCleanupAlarmLocked() {
|
||||||
// No sessions saved yet.
|
// No sessions saved yet.
|
||||||
@@ -1244,6 +1437,10 @@ public class QuotaControllerTest {
|
|||||||
// Rare window size is 24 hours.
|
// Rare window size is 24 hours.
|
||||||
final int standbyBucket = RARE_INDEX;
|
final int standbyBucket = RARE_INDEX;
|
||||||
|
|
||||||
|
// Prevent timing session throttling from affecting the test.
|
||||||
|
mQcConstants.MAX_SESSION_COUNT_RARE = 50;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
// No sessions saved yet.
|
// No sessions saved yet.
|
||||||
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
|
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
|
||||||
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
|
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
|
||||||
@@ -1536,6 +1733,12 @@ public class QuotaControllerTest {
|
|||||||
mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
|
mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
|
||||||
mQcConstants.MAX_JOB_COUNT_RARE = 2000;
|
mQcConstants.MAX_JOB_COUNT_RARE = 2000;
|
||||||
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
|
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 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.TIMING_SESSION_COALESCING_DURATION_MS = 10 * SECOND_IN_MILLIS;
|
||||||
|
|
||||||
mQcConstants.updateConstants();
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
@@ -1552,6 +1755,13 @@ public class QuotaControllerTest {
|
|||||||
assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
|
assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
|
||||||
assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
|
assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
|
||||||
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
|
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
|
||||||
|
assertEquals(50, mQuotaController.getMaxSessionCountPerAllowedTime());
|
||||||
|
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(10 * SECOND_IN_MILLIS,
|
||||||
|
mQuotaController.getTimingSessionCoalescingDurationMs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1569,6 +1779,12 @@ public class QuotaControllerTest {
|
|||||||
mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
|
mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
|
||||||
mQcConstants.MAX_JOB_COUNT_RARE = 1;
|
mQcConstants.MAX_JOB_COUNT_RARE = 1;
|
||||||
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
|
mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 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.TIMING_SESSION_COALESCING_DURATION_MS = -1;
|
||||||
|
|
||||||
mQcConstants.updateConstants();
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
@@ -1584,6 +1800,12 @@ public class QuotaControllerTest {
|
|||||||
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
|
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
|
||||||
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
|
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
|
||||||
assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_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(0, mQuotaController.getTimingSessionCoalescingDurationMs());
|
||||||
|
|
||||||
// Test larger than a day. Controller should cap at one day.
|
// Test larger than a day. Controller should cap at one day.
|
||||||
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
|
mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
|
||||||
@@ -1593,6 +1815,7 @@ public class QuotaControllerTest {
|
|||||||
mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
|
mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
|
||||||
mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
|
mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
|
||||||
mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
|
mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
|
||||||
|
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 25 * HOUR_IN_MILLIS;
|
||||||
|
|
||||||
mQcConstants.updateConstants();
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
@@ -1603,6 +1826,8 @@ public class QuotaControllerTest {
|
|||||||
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
|
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
|
||||||
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
|
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
|
||||||
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
|
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
|
||||||
|
assertEquals(15 * MINUTE_IN_MILLIS,
|
||||||
|
mQuotaController.getTimingSessionCoalescingDurationMs());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tests that TimingSessions aren't saved when the device is charging. */
|
/** Tests that TimingSessions aren't saved when the device is charging. */
|
||||||
@@ -2205,7 +2430,11 @@ public class QuotaControllerTest {
|
|||||||
spyOn(mQuotaController);
|
spyOn(mQuotaController);
|
||||||
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
|
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
|
||||||
|
|
||||||
final long start = JobSchedulerService.sElapsedRealtimeClock.millis();
|
// Essentially disable session throttling.
|
||||||
|
mQcConstants.MAX_SESSION_COUNT_WORKING =
|
||||||
|
mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = Integer.MAX_VALUE;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
final int standbyBucket = WORKING_INDEX;
|
final int standbyBucket = WORKING_INDEX;
|
||||||
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
|
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
|
||||||
|
|
||||||
@@ -2217,6 +2446,7 @@ public class QuotaControllerTest {
|
|||||||
// Ran jobs up to the job limit. All of them should be allowed to run.
|
// 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_ALLOWED_TIME; ++i) {
|
||||||
JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
|
JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
|
||||||
|
setStandbyBucket(WORKING_INDEX, job);
|
||||||
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||||
assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
|
assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
|
||||||
mQuotaController.prepareForExecutionLocked(job);
|
mQuotaController.prepareForExecutionLocked(job);
|
||||||
@@ -2230,6 +2460,7 @@ public class QuotaControllerTest {
|
|||||||
// The app is now out of job count quota
|
// The app is now out of job count quota
|
||||||
JobStatus throttledJob = createJobStatus(
|
JobStatus throttledJob = createJobStatus(
|
||||||
"testStartAlarmScheduled_JobCount_AllowedTime", 42);
|
"testStartAlarmScheduled_JobCount_AllowedTime", 42);
|
||||||
|
setStandbyBucket(WORKING_INDEX, throttledJob);
|
||||||
mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
|
mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
|
||||||
assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
|
assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
|
||||||
|
|
||||||
@@ -2240,4 +2471,61 @@ public class QuotaControllerTest {
|
|||||||
verify(mAlarmManager, times(1))
|
verify(mAlarmManager, times(1))
|
||||||
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
|
.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.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStartAlarmScheduled_TimingSessionCount_AllowedTime() {
|
||||||
|
// saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
|
||||||
|
// because it schedules an alarm too. Prevent it from doing so.
|
||||||
|
spyOn(mQuotaController);
|
||||||
|
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
|
||||||
|
|
||||||
|
// 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_SESSION_COUNT_FREQUENT =
|
||||||
|
mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME + 1;
|
||||||
|
mQcConstants.updateConstants();
|
||||||
|
|
||||||
|
final int standbyBucket = FREQUENT_INDEX;
|
||||||
|
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
|
||||||
|
|
||||||
|
// No sessions saved yet.
|
||||||
|
mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
|
||||||
|
standbyBucket);
|
||||||
|
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) {
|
||||||
|
JobStatus job = createJobStatus(
|
||||||
|
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", i);
|
||||||
|
setStandbyBucket(FREQUENT_INDEX, job);
|
||||||
|
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||||
|
assertTrue("Constraint not satisfied for job #" + (i + 1),
|
||||||
|
job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
|
||||||
|
mQuotaController.prepareForExecutionLocked(job);
|
||||||
|
advanceElapsedClock(SECOND_IN_MILLIS);
|
||||||
|
mQuotaController.maybeStopTrackingJobLocked(job, null, false);
|
||||||
|
advanceElapsedClock(SECOND_IN_MILLIS);
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
JobStatus throttledJob = createJobStatus(
|
||||||
|
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42);
|
||||||
|
mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
|
||||||
|
assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
|
||||||
|
|
||||||
|
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
|
||||||
|
SOURCE_PACKAGE, standbyBucket);
|
||||||
|
final long expectedWorkingAlarmTime =
|
||||||
|
stats.sessionCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS;
|
||||||
|
verify(mAlarmManager, times(1))
|
||||||
|
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user