Merge "Partial revert "Deferring low and min priority jobs when quota is low."" into tm-dev

This commit is contained in:
Kweku Adams
2022-02-26 14:48:39 +00:00
committed by Android (Google) Code Review
2 changed files with 76 additions and 594 deletions

View File

@@ -37,7 +37,6 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.IUidObserver; import android.app.IUidObserver;
import android.app.job.JobInfo;
import android.app.usage.UsageEvents; import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener; import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -165,28 +164,6 @@ public final class QuotaController extends StateController {
*/ */
public long inQuotaTimeElapsed; public long inQuotaTimeElapsed;
/**
* The time after which the app will be under the bucket quota and can start running
* low priority jobs again. This is only valid if
* {@link #executionTimeInWindowMs} >=
* {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityLow}),
* {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
* {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
* {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
*/
public long inQuotaTimeLowElapsed;
/**
* The time after which the app will be under the bucket quota and can start running
* min priority jobs again. This is only valid if
* {@link #executionTimeInWindowMs} >=
* {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityMin}),
* {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
* {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
* {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
*/
public long inQuotaTimeMinElapsed;
/** /**
* The time after which {@link #jobCountInRateLimitingWindow} should be considered invalid, * The time after which {@link #jobCountInRateLimitingWindow} should be considered invalid,
* in the elapsed realtime timebase. * in the elapsed realtime timebase.
@@ -227,8 +204,6 @@ public final class QuotaController extends StateController {
+ "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", " + "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
+ "sessionCountInWindow=" + sessionCountInWindow + ", " + "sessionCountInWindow=" + sessionCountInWindow + ", "
+ "inQuotaTime=" + inQuotaTimeElapsed + ", " + "inQuotaTime=" + inQuotaTimeElapsed + ", "
+ "inQuotaTimeLow=" + inQuotaTimeLowElapsed + ", "
+ "inQuotaTimeMin=" + inQuotaTimeMinElapsed + ", "
+ "rateLimitJobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", " + "rateLimitJobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
+ "rateLimitJobCountWindow=" + jobCountInRateLimitingWindow + ", " + "rateLimitJobCountWindow=" + jobCountInRateLimitingWindow + ", "
+ "rateLimitSessionCountExpirationTime=" + "rateLimitSessionCountExpirationTime="
@@ -385,24 +360,6 @@ public final class QuotaController extends StateController {
*/ */
private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs; private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
/**
* The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
* {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
* surplus of this amount of remaining allowed time before we start running low priority
* jobs.
*/
private float mAllowedTimeSurplusPriorityLow =
QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
/**
* The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
* {@link JobInfo#PRIORITY_MIN min priority} jobs. In other words, there must be a minimum
* surplus of this amount of remaining allowed time before we start running low priority
* jobs.
*/
private float mAllowedTimeSurplusPriorityMin =
QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
/** The period of time used to rate limit recently run jobs. */ /** The period of time used to rate limit recently run jobs. */
private long mRateLimitingWindowMs = QcConstants.DEFAULT_RATE_LIMITING_WINDOW_MS; private long mRateLimitingWindowMs = QcConstants.DEFAULT_RATE_LIMITING_WINDOW_MS;
@@ -828,8 +785,7 @@ public final class QuotaController extends StateController {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
} }
return getTimeUntilQuotaConsumedLocked( return getTimeUntilQuotaConsumedLocked(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
jobStatus.getEffectivePriority());
} }
// Expedited job. // Expedited job.
@@ -919,8 +875,7 @@ public final class QuotaController extends StateController {
return isTopStartedJobLocked(jobStatus) return isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid()) || isUidInForeground(jobStatus.getSourceUid())
|| isWithinQuotaLocked( || isWithinQuotaLocked(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket, jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
jobStatus.getEffectivePriority());
} }
@GuardedBy("mLock") @GuardedBy("mLock")
@@ -937,7 +892,7 @@ public final class QuotaController extends StateController {
@VisibleForTesting @VisibleForTesting
@GuardedBy("mLock") @GuardedBy("mLock")
boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName, boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
final int standbyBucket, final int priority) { final int standbyBucket) {
if (!mIsEnabled) { if (!mIsEnabled) {
return true; return true;
} }
@@ -945,18 +900,9 @@ public final class QuotaController extends StateController {
if (isQuotaFreeLocked(standbyBucket)) return true; if (isQuotaFreeLocked(standbyBucket)) return true;
final long minSurplus;
if (priority <= JobInfo.PRIORITY_MIN) {
minSurplus = (long)
(mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityMin);
} else if (priority <= JobInfo.PRIORITY_LOW) {
minSurplus = (long)
(mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityLow);
} else {
minSurplus = 0;
}
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
return getRemainingExecutionTimeLocked(stats) > minSurplus // TODO: use a higher minimum remaining time for jobs with MINIMUM priority
return getRemainingExecutionTimeLocked(stats) > 0
&& isUnderJobCountQuotaLocked(stats, standbyBucket) && isUnderJobCountQuotaLocked(stats, standbyBucket)
&& isUnderSessionCountQuotaLocked(stats, standbyBucket); && isUnderSessionCountQuotaLocked(stats, standbyBucket);
} }
@@ -1074,8 +1020,7 @@ public final class QuotaController extends StateController {
* job is running. * job is running.
*/ */
@VisibleForTesting @VisibleForTesting
long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName, long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName) {
@JobInfo.Priority int jobPriority) {
final long nowElapsed = sElapsedRealtimeClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis();
final int standbyBucket = JobSchedulerService.standbyBucketForPackage( final int standbyBucket = JobSchedulerService.standbyBucketForPackage(
packageName, userId, nowElapsed); packageName, userId, nowElapsed);
@@ -1096,15 +1041,11 @@ 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;
final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(standbyBucket, jobPriority); final long allowedTimePerPeriodMs = mAllowedTimePerPeriodMs[standbyBucket];
final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs; final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
final long maxExecutionTimeRemainingMs = final long maxExecutionTimeRemainingMs =
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs; mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
if (maxExecutionTimeRemainingMs < 0) {
return 0;
}
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit. // essentially run until they reach the maximum limit.
if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) { if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
@@ -1112,10 +1053,6 @@ public final class QuotaController extends StateController {
sessions, startMaxElapsed, maxExecutionTimeRemainingMs); sessions, startMaxElapsed, maxExecutionTimeRemainingMs);
} }
if (allowedTimeRemainingMs < 0) {
return 0;
}
// Need to check both max time and period time in case one is less than the other. // Need to check both max time and period time in case one is less than the other.
// For example, max time remaining could be less than bucket time remaining, but sessions // For example, max time remaining could be less than bucket time remaining, but sessions
// contributing to the max time remaining could phase out enough that we'd want to use the // contributing to the max time remaining could phase out enough that we'd want to use the
@@ -1127,21 +1064,6 @@ public final class QuotaController extends StateController {
sessions, startWindowElapsed, allowedTimeRemainingMs)); sessions, startWindowElapsed, allowedTimeRemainingMs));
} }
private long getAllowedTimePerPeriodMs(int standbyBucket, @JobInfo.Priority int jobPriority) {
return getAllowedTimePerPeriodMs(mAllowedTimePerPeriodMs[standbyBucket], jobPriority);
}
private long getAllowedTimePerPeriodMs(long initialAllowedTime,
@JobInfo.Priority int jobPriority) {
if (jobPriority <= JobInfo.PRIORITY_MIN) {
return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityMin));
}
if (jobPriority <= JobInfo.PRIORITY_LOW) {
return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityLow));
}
return initialAllowedTime;
}
/** /**
* Calculates how much time it will take, in milliseconds, until the quota is fully consumed. * Calculates how much time it will take, in milliseconds, until the quota is fully consumed.
* *
@@ -1299,15 +1221,10 @@ public final class QuotaController extends StateController {
stats.sessionCountInWindow = 0; stats.sessionCountInWindow = 0;
if (stats.jobCountLimit == 0 || stats.sessionCountLimit == 0) { if (stats.jobCountLimit == 0 || stats.sessionCountLimit == 0) {
// App won't be in quota until configuration changes. // App won't be in quota until configuration changes.
stats.inQuotaTimeElapsed = stats.inQuotaTimeLowElapsed = stats.inQuotaTimeMinElapsed = stats.inQuotaTimeElapsed = Long.MAX_VALUE;
Long.MAX_VALUE;
} else { } else {
stats.inQuotaTimeElapsed = 0; stats.inQuotaTimeElapsed = 0;
} }
final long allowedTimeMinMs =
getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_MIN);
final long allowedTimeLowMs =
getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_LOW);
final long allowedTimeIntoQuotaMs = stats.allowedTimePerPeriodMs - mQuotaBufferMs; final long allowedTimeIntoQuotaMs = stats.allowedTimePerPeriodMs - mQuotaBufferMs;
Timer timer = mPkgTimers.get(userId, packageName); Timer timer = mPkgTimers.get(userId, packageName);
@@ -1326,25 +1243,13 @@ public final class QuotaController extends StateController {
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
nowElapsed - allowedTimeIntoQuotaMs + stats.windowSizeMs); nowElapsed - allowedTimeIntoQuotaMs + stats.windowSizeMs);
} }
if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
nowElapsed - allowedTimeLowMs + stats.windowSizeMs);
}
if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
nowElapsed - allowedTimeMinMs + stats.windowSizeMs);
}
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) { if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
final long inQuotaTime = nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS; final long inQuotaTime = nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime); stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
} }
if (stats.bgJobCountInWindow >= stats.jobCountLimit) { if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
final long inQuotaTime = nowElapsed + stats.windowSizeMs; final long inQuotaTime = nowElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime); stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
} }
} }
@@ -1386,23 +1291,9 @@ public final class QuotaController extends StateController {
start + stats.executionTimeInWindowMs - allowedTimeIntoQuotaMs start + stats.executionTimeInWindowMs - allowedTimeIntoQuotaMs
+ stats.windowSizeMs); + stats.windowSizeMs);
} }
if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
start + stats.executionTimeInWindowMs - allowedTimeLowMs
+ stats.windowSizeMs);
}
if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
start + stats.executionTimeInWindowMs - allowedTimeMinMs
+ stats.windowSizeMs);
}
if (stats.bgJobCountInWindow >= stats.jobCountLimit) { if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs; final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime); stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
inQuotaTime);
stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
inQuotaTime);
} }
if (i == loopStart if (i == loopStart
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed) || (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
@@ -1413,10 +1304,6 @@ public final class QuotaController extends StateController {
if (sessionCountInWindow >= stats.sessionCountLimit) { if (sessionCountInWindow >= stats.sessionCountLimit) {
final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs; final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime); stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
inQuotaTime);
stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
inQuotaTime);
} }
} }
} }
@@ -1706,7 +1593,7 @@ public final class QuotaController extends StateController {
/** /**
* Update the CONSTRAINT_WITHIN_QUOTA bit for all of the Jobs for a given package. * Update the CONSTRAINT_WITHIN_QUOTA bit for all of the Jobs for a given package.
* *
* @return true if at least one job had its bit changed * @return the set of jobs whose status changed
*/ */
@NonNull @NonNull
private ArraySet<JobStatus> maybeUpdateConstraintForPkgLocked(final long nowElapsed, private ArraySet<JobStatus> maybeUpdateConstraintForPkgLocked(final long nowElapsed,
@@ -1719,8 +1606,7 @@ public final class QuotaController extends StateController {
// Quota is the same for all jobs within a package. // Quota is the same for all jobs within a package.
final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket(); final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket();
final boolean realInQuota = isWithinQuotaLocked( final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket);
userId, packageName, realStandbyBucket, JobInfo.PRIORITY_DEFAULT);
boolean outOfEJQuota = false; boolean outOfEJQuota = false;
for (int i = jobs.size() - 1; i >= 0; --i) { for (int i = jobs.size() - 1; i >= 0; --i) {
final JobStatus js = jobs.valueAt(i); final JobStatus js = jobs.valueAt(i);
@@ -1733,8 +1619,7 @@ public final class QuotaController extends StateController {
changedJobs.add(js); changedJobs.add(js);
} }
} else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX } else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX
&& realStandbyBucket == js.getEffectiveStandbyBucket() && realStandbyBucket == js.getEffectiveStandbyBucket()) {
&& js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
// An app in the ACTIVE bucket may be out of quota while the job could be in quota // An app in the ACTIVE bucket may be out of quota while the job could be in quota
// for some reason. Therefore, avoid setting the real value here and check each job // for some reason. Therefore, avoid setting the real value here and check each job
// individually. // individually.
@@ -1798,8 +1683,9 @@ public final class QuotaController extends StateController {
final String packageName = jobStatus.getSourcePackageName(); final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket(); final int realStandbyBucket = jobStatus.getStandbyBucket();
if (isWithinEJQuota if (isWithinEJQuota
&& isWithinQuotaLocked(userId, packageName, realStandbyBucket, && isWithinQuotaLocked(userId, packageName, realStandbyBucket)) {
JobInfo.PRIORITY_MIN)) { // TODO(141645789): we probably shouldn't cancel the alarm until we've verified
// that all jobs for the userId-package are within quota.
mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, packageName)); mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, packageName));
} else { } else {
mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket); mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
@@ -1860,25 +1746,8 @@ public final class QuotaController extends StateController {
standbyBucket); standbyBucket);
final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName); final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName);
int minPriority = JobInfo.PRIORITY_MAX;
boolean hasDefPlus = false, hasLow = false, hasMin = false;
for (int i = jobs.size() - 1; i >= 0; --i) {
final int priority = jobs.valueAt(i).getEffectivePriority();
minPriority = Math.min(minPriority, priority);
if (priority <= JobInfo.PRIORITY_MIN) {
hasMin = true;
} else if (priority <= JobInfo.PRIORITY_LOW) {
hasLow = true;
} else {
hasDefPlus = true;
}
if (hasMin && hasLow && hasDefPlus) {
break;
}
}
final boolean inRegularQuota = final boolean inRegularQuota =
stats.executionTimeInWindowMs stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs[standbyBucket]
< getAllowedTimePerPeriodMs(standbyBucket, minPriority)
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
&& isUnderJobCountQuota && isUnderJobCountQuota
&& isUnderTimingSessionCountQuota; && isUnderTimingSessionCountQuota;
@@ -1900,24 +1769,7 @@ public final class QuotaController extends StateController {
long inEJQuotaTimeElapsed = Long.MAX_VALUE; long inEJQuotaTimeElapsed = Long.MAX_VALUE;
if (!inRegularQuota) { if (!inRegularQuota) {
// The time this app will have quota again. // The time this app will have quota again.
long executionInQuotaTime = Long.MAX_VALUE; long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
boolean hasExecutionInQuotaTime = false;
if (hasMin && stats.inQuotaTimeMinElapsed > 0) {
executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeMinElapsed);
hasExecutionInQuotaTime = true;
}
if (hasLow && stats.inQuotaTimeLowElapsed > 0) {
executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeLowElapsed);
hasExecutionInQuotaTime = true;
}
if (hasDefPlus && stats.inQuotaTimeElapsed > 0) {
executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeElapsed);
hasExecutionInQuotaTime = true;
}
long inQuotaTimeElapsed = 0;
if (hasExecutionInQuotaTime) {
inQuotaTimeElapsed = executionInQuotaTime;
}
if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) { if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
// App hit the rate limit. // App hit the rate limit.
inQuotaTimeElapsed = inQuotaTimeElapsed =
@@ -2130,7 +1982,6 @@ public final class QuotaController extends StateController {
private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>(); private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>();
private long mStartTimeElapsed; private long mStartTimeElapsed;
private int mBgJobCount; private int mBgJobCount;
private int mLowestPriority = JobInfo.PRIORITY_MAX;
private long mDebitAdjustment; private long mDebitAdjustment;
Timer(int uid, int userId, String packageName, boolean regularJobTimer) { Timer(int uid, int userId, String packageName, boolean regularJobTimer) {
@@ -2153,8 +2004,6 @@ public final class QuotaController extends StateController {
Slog.v(TAG, "Starting to track " + jobStatus.toShortString()); Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
} }
// Always maintain list of running jobs, even when quota is free. // Always maintain list of running jobs, even when quota is free.
final boolean priorityLowered = mLowestPriority > jobStatus.getEffectivePriority();
mLowestPriority = Math.min(mLowestPriority, jobStatus.getEffectivePriority());
if (mRunningBgJobs.add(jobStatus) && shouldTrackLocked()) { if (mRunningBgJobs.add(jobStatus) && shouldTrackLocked()) {
mBgJobCount++; mBgJobCount++;
if (mRegularJobTimer) { if (mRegularJobTimer) {
@@ -2170,8 +2019,6 @@ public final class QuotaController extends StateController {
invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
} }
scheduleCutoff(); scheduleCutoff();
} else if (mRegularJobTimer && priorityLowered) {
rescheduleCutoff();
} }
} }
} }
@@ -2196,19 +2043,6 @@ public final class QuotaController extends StateController {
&& !isQuotaFreeLocked(standbyBucket)) { && !isQuotaFreeLocked(standbyBucket)) {
emitSessionLocked(nowElapsed); emitSessionLocked(nowElapsed);
cancelCutoff(); cancelCutoff();
mLowestPriority = JobInfo.PRIORITY_MAX;
} else if (mRegularJobTimer
&& mLowestPriority == jobStatus.getEffectivePriority()) {
// Lowest priority doesn't matter for EJ timers.
final int oldPriority = mLowestPriority;
mLowestPriority = JobInfo.PRIORITY_MAX;
for (int i = mRunningBgJobs.size() - 1; i >= 0; --i) {
mLowestPriority = Math.min(mLowestPriority,
mRunningBgJobs.valueAt(i).getEffectivePriority());
}
if (mLowestPriority != oldPriority) {
rescheduleCutoff();
}
} }
} }
} }
@@ -2335,14 +2169,9 @@ public final class QuotaController extends StateController {
} }
Message msg = mHandler.obtainMessage( Message msg = mHandler.obtainMessage(
mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg); mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg);
final long timeRemainingMs; final long timeRemainingMs = mRegularJobTimer
if (mRegularJobTimer) { ? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName)
timeRemainingMs = getTimeUntilQuotaConsumedLocked( : getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
mPkg.userId, mPkg.packageName, mLowestPriority);
} else {
timeRemainingMs =
getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
}
if (DEBUG) { if (DEBUG) {
Slog.i(TAG, Slog.i(TAG,
(mRegularJobTimer ? "Regular job" : "EJ") + " for " + mPkg + " has " (mRegularJobTimer ? "Regular job" : "EJ") + " for " + mPkg + " has "
@@ -2695,19 +2524,26 @@ public final class QuotaController extends StateController {
Slog.d(TAG, "Checking if " + pkg + " has reached its quota."); Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
} }
final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked( long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId,
sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName); pkg.packageName);
if (changedJobs.size() > 0) { if (timeRemainingMs <= 50) {
// Less than 50 milliseconds left. Start process of shutting down jobs.
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota."); if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
mStateChangedListener.onControllerStateChanged(changedJobs); mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
pkg.userId, pkg.packageName));
} else { } else {
// This could potentially happen if an old session phases out while a // This could potentially happen if an old session phases out while a
// job is currently running. // job is currently running.
// Reschedule message // Reschedule message
Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg);
timeRemainingMs = getTimeUntilQuotaConsumedLocked(pkg.userId,
pkg.packageName);
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, pkg + " had early REACHED_QUOTA message"); Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left.");
} }
mPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff(); sendMessageDelayed(rescheduleMsg, timeRemainingMs);
} }
break; break;
} }
@@ -2717,19 +2553,25 @@ public final class QuotaController extends StateController {
Slog.d(TAG, "Checking if " + pkg + " has reached its EJ quota."); Slog.d(TAG, "Checking if " + pkg + " has reached its EJ quota.");
} }
final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked( long timeRemainingMs = getRemainingEJExecutionTimeLocked(
sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName); pkg.userId, pkg.packageName);
if (changedJobs.size() > 0) { if (timeRemainingMs <= 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota."); if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
mStateChangedListener.onControllerStateChanged(changedJobs); mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
pkg.userId, pkg.packageName));
} else { } else {
// This could potentially happen if an old session phases out while a // This could potentially happen if an old session phases out while a
// job is currently running. // job is currently running.
// Reschedule message // Reschedule message
Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_QUOTA, pkg);
timeRemainingMs = getTimeUntilEJQuotaConsumedLocked(
pkg.userId, pkg.packageName);
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, pkg + " had early REACHED_EJ_QUOTA message"); Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left for EJ");
} }
mEJPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff(); sendMessageDelayed(rescheduleMsg, timeRemainingMs);
} }
break; break;
} }
@@ -2993,12 +2835,6 @@ public final class QuotaController extends StateController {
static final String KEY_IN_QUOTA_BUFFER_MS = static final String KEY_IN_QUOTA_BUFFER_MS =
QC_CONSTANT_PREFIX + "in_quota_buffer_ms"; QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@VisibleForTesting @VisibleForTesting
static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_low";
@VisibleForTesting
static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_min";
@VisibleForTesting
static final String KEY_WINDOW_SIZE_EXEMPTED_MS = static final String KEY_WINDOW_SIZE_EXEMPTED_MS =
QC_CONSTANT_PREFIX + "window_size_exempted_ms"; QC_CONSTANT_PREFIX + "window_size_exempted_ms";
@VisibleForTesting @VisibleForTesting
@@ -3130,8 +2966,6 @@ public final class QuotaController extends StateController {
10 * 60 * 1000L; // 10 minutes 10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_IN_QUOTA_BUFFER_MS = private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds 30 * 1000L; // 30 seconds
private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW = .25f;
private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN = .5f;
private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS = private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS =
DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS = private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
@@ -3229,22 +3063,6 @@ public final class QuotaController extends StateController {
*/ */
public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS; public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS;
/**
* The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
* {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
* surplus of this amount of remaining allowed time before we start running low priority
* jobs.
*/
public float ALLOWED_TIME_SURPLUS_PRIORITY_LOW = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
/**
* The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
* {@link JobInfo#PRIORITY_MIN low priority} jobs. In other words, there must be a minimum
* surplus of this amount of remaining allowed time before we start running min priority
* jobs.
*/
public float ALLOWED_TIME_SURPLUS_PRIORITY_MIN = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
/** /**
* The quota window size of the particular standby bucket. Apps in this standby bucket are * The quota window size of the particular standby bucket. Apps in this standby bucket are
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past
@@ -3514,8 +3332,6 @@ public final class QuotaController extends StateController {
case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS: case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS: case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS: case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS:
case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW:
case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN:
case KEY_IN_QUOTA_BUFFER_MS: case KEY_IN_QUOTA_BUFFER_MS:
case KEY_MAX_EXECUTION_TIME_MS: case KEY_MAX_EXECUTION_TIME_MS:
case KEY_WINDOW_SIZE_ACTIVE_MS: case KEY_WINDOW_SIZE_ACTIVE_MS:
@@ -3757,7 +3573,6 @@ public final class QuotaController extends StateController {
KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
KEY_IN_QUOTA_BUFFER_MS, KEY_IN_QUOTA_BUFFER_MS,
KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
KEY_MAX_EXECUTION_TIME_MS, KEY_MAX_EXECUTION_TIME_MS,
KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS, KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
KEY_WINDOW_SIZE_WORKING_MS, KEY_WINDOW_SIZE_WORKING_MS,
@@ -3781,12 +3596,6 @@ public final class QuotaController extends StateController {
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS = ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS); DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW,
DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW);
ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS); DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
@@ -3865,23 +3674,6 @@ public final class QuotaController extends StateController {
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs; mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true; mShouldReevaluateConstraints = true;
} }
// Low priority surplus should be in the range [0, .9]. A value of 1 would essentially
// mean never run low priority jobs.
float newAllowedTimeSurplusPriorityLow =
Math.max(0f, Math.min(.9f, ALLOWED_TIME_SURPLUS_PRIORITY_LOW));
if (Float.compare(
mAllowedTimeSurplusPriorityLow, newAllowedTimeSurplusPriorityLow) != 0) {
mAllowedTimeSurplusPriorityLow = newAllowedTimeSurplusPriorityLow;
mShouldReevaluateConstraints = true;
}
// Min priority surplus should be in the range [0, mAllowedTimeSurplusPriorityLow].
float newAllowedTimeSurplusPriorityMin = Math.max(0f,
Math.min(mAllowedTimeSurplusPriorityLow, ALLOWED_TIME_SURPLUS_PRIORITY_MIN));
if (Float.compare(
mAllowedTimeSurplusPriorityMin, newAllowedTimeSurplusPriorityMin) != 0) {
mAllowedTimeSurplusPriorityMin = newAllowedTimeSurplusPriorityMin;
mShouldReevaluateConstraints = true;
}
long newExemptedPeriodMs = Math.max(mAllowedTimePerPeriodMs[EXEMPTED_INDEX], long newExemptedPeriodMs = Math.max(mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS)); Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
if (mBucketPeriodsMs[EXEMPTED_INDEX] != newExemptedPeriodMs) { if (mBucketPeriodsMs[EXEMPTED_INDEX] != newExemptedPeriodMs) {
@@ -4081,10 +3873,6 @@ public final class QuotaController extends StateController {
.println(); .println();
pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println(); ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println();
pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, ALLOWED_TIME_SURPLUS_PRIORITY_LOW)
.println();
pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, ALLOWED_TIME_SURPLUS_PRIORITY_MIN)
.println();
pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println(); pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println(); pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println();
pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println(); pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
@@ -4209,16 +3997,6 @@ public final class QuotaController extends StateController {
return mAllowedTimePerPeriodMs; return mAllowedTimePerPeriodMs;
} }
@VisibleForTesting
float getAllowedTimeSurplusPriorityLow() {
return mAllowedTimeSurplusPriorityLow;
}
@VisibleForTesting
float getAllowedTimeSurplusPriorityMin() {
return mAllowedTimeSurplusPriorityMin;
}
@VisibleForTesting @VisibleForTesting
@NonNull @NonNull
int[] getBucketMaxJobCounts() { int[] getBucketMaxJobCounts() {

View File

@@ -16,11 +16,6 @@
package com.android.server.job.controllers; package com.android.server.job.controllers;
import static android.app.job.JobInfo.PRIORITY_DEFAULT;
import static android.app.job.JobInfo.PRIORITY_HIGH;
import static android.app.job.JobInfo.PRIORITY_LOW;
import static android.app.job.JobInfo.PRIORITY_MIN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -275,14 +270,14 @@ public class QuotaControllerTest {
} }
private void setCharging() { private void setCharging() {
when(mJobSchedulerService.isBatteryCharging()).thenReturn(true); doReturn(true).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked(); mQuotaController.onBatteryStateChangedLocked();
} }
} }
private void setDischarging() { private void setDischarging() {
when(mJobSchedulerService.isBatteryCharging()).thenReturn(false); doReturn(false).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked(); mQuotaController.onBatteryStateChangedLocked();
} }
@@ -415,14 +410,6 @@ public class QuotaControllerTest {
} }
} }
private void setDeviceConfigFloat(String key, float val) {
mDeviceConfigPropertiesBuilder.setFloat(key, val);
synchronized (mQuotaController.mLock) {
mQuotaController.prepareForUpdatedConstantsLocked();
mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
}
}
private void waitForNonDelayedMessagesProcessed() { private void waitForNonDelayedMessagesProcessed() {
mQuotaController.getHandler().runWithScissors(() -> {}, 15_000); mQuotaController.getHandler().runWithScissors(() -> {}, 15_000);
} }
@@ -861,7 +848,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
assertEquals(expectedStats, inputStats); assertEquals(expectedStats, inputStats);
assertTrue(mQuotaController.isWithinQuotaLocked( assertTrue(mQuotaController.isWithinQuotaLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
} }
assertTrue("Job not ready: " + jobStatus, jobStatus.isReady()); assertTrue("Job not ready: " + jobStatus, jobStatus.isReady());
} }
@@ -885,7 +872,7 @@ public class QuotaControllerTest {
assertEquals(expectedStats, inputStats); assertEquals(expectedStats, inputStats);
assertFalse( assertFalse(
mQuotaController.isWithinQuotaLocked( mQuotaController.isWithinQuotaLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
} }
// Quota should be exceeded due to activity in active timer. // Quota should be exceeded due to activity in active timer.
@@ -910,7 +897,7 @@ public class QuotaControllerTest {
assertEquals(expectedStats, inputStats); assertEquals(expectedStats, inputStats);
assertFalse( assertFalse(
mQuotaController.isWithinQuotaLocked( mQuotaController.isWithinQuotaLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady()); assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady());
} }
} }
@@ -1508,7 +1495,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS, assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
} }
@@ -1541,7 +1528,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS, assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
setStandbyBucket(FREQUENT_INDEX); setStandbyBucket(FREQUENT_INDEX);
@@ -1551,7 +1538,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS, assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
setStandbyBucket(WORKING_INDEX); setStandbyBucket(WORKING_INDEX);
@@ -1561,7 +1548,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(7 * MINUTE_IN_MILLIS, assertEquals(7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
// ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
@@ -1573,7 +1560,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
} }
@@ -1597,7 +1584,7 @@ public class QuotaControllerTest {
// Max time will phase out, so should use bucket limit. // Max time will phase out, so should use bucket limit.
assertEquals(10 * MINUTE_IN_MILLIS, assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1613,7 +1600,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS, assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1630,7 +1617,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(3 * MINUTE_IN_MILLIS, assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
} }
@@ -1663,7 +1650,7 @@ public class QuotaControllerTest {
// window time. // window time.
assertEquals(10 * MINUTE_IN_MILLIS, assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1690,107 +1677,7 @@ public class QuotaControllerTest {
// Max time only has one minute phase out. Bucket time has 2 minute phase out. // Max time only has one minute phase out. Bucket time has 2 minute phase out.
assertEquals(9 * MINUTE_IN_MILLIS, assertEquals(9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
}
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based on the job's priority.
*/
@Test
public void testGetTimeUntilQuotaConsumedLocked_Priority() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Close to RARE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
150 * SECOND_IN_MILLIS, 5), false);
// Far away from FREQUENT boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (7 * HOUR_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5), false);
// Overlap WORKING_SET boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// Close to ACTIVE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
setStandbyBucket(RARE_INDEX);
synchronized (mQuotaController.mLock) {
assertEquals(30 * SECOND_IN_MILLIS,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
assertEquals(0,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
assertEquals(0,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
}
setStandbyBucket(FREQUENT_INDEX);
synchronized (mQuotaController.mLock) {
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
assertEquals(30 * SECOND_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
assertEquals(0,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
}
setStandbyBucket(WORKING_INDEX);
synchronized (mQuotaController.mLock) {
assertEquals(6 * MINUTE_IN_MILLIS,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
assertEquals(7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
assertEquals(4 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
assertEquals(2 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
}
// ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
// max execution time.
setStandbyBucket(ACTIVE_INDEX);
synchronized (mQuotaController.mLock) {
assertEquals(7 * MINUTE_IN_MILLIS,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
} }
} }
@@ -1820,7 +1707,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS, assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
} }
@@ -1842,7 +1729,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS, assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1854,7 +1741,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS, assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1867,7 +1754,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS, assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1882,15 +1769,15 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE)); SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS, assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked( mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT)); SOURCE_USER_ID, SOURCE_PACKAGE));
} }
} }
@Test @Test
public void testIsWithinQuotaLocked_NeverApp() { public void testIsWithinQuotaLocked_NeverApp() {
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinQuotaLocked( assertFalse(
0, "com.android.test.never", NEVER_INDEX, PRIORITY_DEFAULT)); mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
} }
} }
@@ -1898,8 +1785,7 @@ public class QuotaControllerTest {
public void testIsWithinQuotaLocked_Charging() { public void testIsWithinQuotaLocked_Charging() {
setCharging(); setCharging();
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
assertTrue(mQuotaController.isWithinQuotaLocked( assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
} }
} }
@@ -1913,8 +1799,7 @@ public class QuotaControllerTest {
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5); mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
assertTrue(mQuotaController.isWithinQuotaLocked( assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
} }
} }
@@ -1931,7 +1816,7 @@ public class QuotaControllerTest {
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount); mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount);
assertFalse(mQuotaController.isWithinQuotaLocked( assertFalse(mQuotaController.isWithinQuotaLocked(
0, "com.android.test.spam", WORKING_INDEX, PRIORITY_DEFAULT)); 0, "com.android.test.spam", WORKING_INDEX));
} }
mQuotaController.saveTimingSession(0, "com.android.test.frequent", mQuotaController.saveTimingSession(0, "com.android.test.frequent",
@@ -1941,7 +1826,7 @@ public class QuotaControllerTest {
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false); createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false);
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinQuotaLocked( assertFalse(mQuotaController.isWithinQuotaLocked(
0, "com.android.test.frequent", FREQUENT_INDEX, PRIORITY_DEFAULT)); 0, "com.android.test.frequent", FREQUENT_INDEX));
} }
} }
@@ -1957,8 +1842,7 @@ public class QuotaControllerTest {
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false); createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5); mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
assertFalse(mQuotaController.isWithinQuotaLocked( assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
} }
} }
@@ -1974,8 +1858,7 @@ public class QuotaControllerTest {
false); false);
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount); mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount);
assertFalse(mQuotaController.isWithinQuotaLocked( assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
} }
} }
@@ -2128,65 +2011,21 @@ public class QuotaControllerTest {
assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions", assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
i < 2, i < 2,
mQuotaController.isWithinQuotaLocked( mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions", assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
i < 3, i < 3,
mQuotaController.isWithinQuotaLocked( mQuotaController.isWithinQuotaLocked(
0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT)); 0, "com.android.test", FREQUENT_INDEX));
assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions", assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
i < 4, i < 4,
mQuotaController.isWithinQuotaLocked( mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions", assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
i < 5, i < 5,
mQuotaController.isWithinQuotaLocked( mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
} }
} }
} }
@Test
public void testIsWithinQuotaLocked_Priority() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", FREQUENT_INDEX, PRIORITY_HIGH));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT));
assertFalse(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", FREQUENT_INDEX, PRIORITY_LOW));
assertFalse(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", FREQUENT_INDEX, PRIORITY_MIN));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", WORKING_INDEX, PRIORITY_HIGH));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", WORKING_INDEX, PRIORITY_LOW));
assertFalse(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", WORKING_INDEX, PRIORITY_MIN));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", ACTIVE_INDEX, PRIORITY_HIGH));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", ACTIVE_INDEX, PRIORITY_LOW));
assertTrue(mQuotaController.isWithinQuotaLocked(
0, "com.android.test", ACTIVE_INDEX, PRIORITY_MIN));
}
}
@Test @Test
public void testIsWithinEJQuotaLocked_NeverApp() { public void testIsWithinEJQuotaLocked_NeverApp() {
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1); JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
@@ -2737,8 +2576,7 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) { synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked( mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
} }
verify(mAlarmManager, timeout(1000).times(0)).setWindow( verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2790,128 +2628,6 @@ public class QuotaControllerTest {
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
} }
@Test
public void testMaybeScheduleStartAlarmLocked_Priority() {
// 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();
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 5);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (24 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
InOrder inOrder = inOrder(mAlarmManager);
JobStatus jobDef = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
SOURCE_PACKAGE, CALLING_UID,
new JobInfo.Builder(1, new ComponentName(mContext, "TestQuotaJobService"))
.setPriority(PRIORITY_DEFAULT)
.build());
JobStatus jobLow = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
SOURCE_PACKAGE, CALLING_UID,
new JobInfo.Builder(2, new ComponentName(mContext, "TestQuotaJobService"))
.setPriority(PRIORITY_LOW)
.build());
JobStatus jobMin = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
SOURCE_PACKAGE, CALLING_UID,
new JobInfo.Builder(3, new ComponentName(mContext, "TestQuotaJobService"))
.setPriority(PRIORITY_MIN)
.build());
setStandbyBucket(RARE_INDEX, jobDef, jobLow, jobMin);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
// Min job requires 5 mins of surplus.
long expectedAlarmTime = now + 23 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
// Low job requires 2.5 mins of surplus.
expectedAlarmTime = now + 17 * HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
// Default+ jobs require IN_QUOTA_BUFFER_MS.
expectedAlarmTime = now + mQcConstants.IN_QUOTA_BUFFER_MS;
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
setStandbyBucket(FREQUENT_INDEX, jobDef, jobLow, jobMin);
mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
// Min job requires 5 mins of surplus.
expectedAlarmTime = now + 7 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
// Low job requires 2.5 mins of surplus.
expectedAlarmTime = now + HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
// Default+ jobs already have enough quota.
inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
setStandbyBucket(WORKING_INDEX, jobDef, jobLow, jobMin);
mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
// Min job requires 5 mins of surplus.
expectedAlarmTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
// Low job has enough surplus.
inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
// Default+ jobs already have enough quota.
inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
}
}
/** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */ /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
@Test @Test
public void testMaybeScheduleStartAlarmLocked_BucketChange() { public void testMaybeScheduleStartAlarmLocked_BucketChange() {
@@ -3212,8 +2928,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
11 * MINUTE_IN_MILLIS); 11 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, .7f);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .2f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 99 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 99 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
@@ -3269,8 +2983,6 @@ public class QuotaControllerTest {
assertEquals(11 * MINUTE_IN_MILLIS, assertEquals(11 * MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]); mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
assertEquals(.7f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
assertEquals(.2f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(99 * MINUTE_IN_MILLIS, assertEquals(99 * MINUTE_IN_MILLIS,
mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]); mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
@@ -3327,8 +3039,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
-MINUTE_IN_MILLIS); -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, -.1f);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, -.01f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, -MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
@@ -3379,8 +3089,6 @@ public class QuotaControllerTest {
assertEquals(MINUTE_IN_MILLIS, assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]); mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getInQuotaBufferMs()); assertEquals(0, mQuotaController.getInQuotaBufferMs());
assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
@@ -3451,8 +3159,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
25 * HOUR_IN_MILLIS); 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, 1f);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .95f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
@@ -3492,8 +3198,6 @@ public class QuotaControllerTest {
assertEquals(24 * HOUR_IN_MILLIS, assertEquals(24 * HOUR_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]); mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);