Merge "Ensure apps can schedule/start EJs when temp allowlisted." into sc-dev

This commit is contained in:
Kweku Adams
2021-03-15 22:54:10 +00:00
committed by Android (Google) Code Review
2 changed files with 197 additions and 1 deletions

View File

@@ -813,12 +813,21 @@ public final class QuotaController extends StateController {
// 1. it's already running (already executing expedited jobs should be allowed to finish)
// 2. the app is currently in the foreground
// 3. the app overall is within its quota
// 4. It's on the temp allowlist (or within the grace period)
if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) {
return true;
}
final long tempAllowlistGracePeriodEndElapsed =
mTempAllowlistGraceCache.get(jobStatus.getSourceUid());
final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid())
|| sElapsedRealtimeClock.millis() < tempAllowlistGracePeriodEndElapsed;
if (hasTempAllowlistExemption) {
return true;
}
Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
// Any already executing expedited jbos should be allowed to finish.
// Any already executing expedited jobs should be allowed to finish.
if (ejTimer != null && ejTimer.isRunning(jobStatus)) {
return true;
}

View File

@@ -1879,6 +1879,144 @@ public class QuotaControllerTest {
}
}
@Test
public void testIsWithinEJQuotaLocked_NeverApp() {
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
setStandbyBucket(NEVER_INDEX, js);
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
}
}
@Test
public void testIsWithinEJQuotaLocked_Charging() {
setCharging();
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_Charging", 1);
synchronized (mQuotaController.mLock) {
assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
}
}
@Test
public void testIsWithinEJQuotaLocked_UnderDuration() {
setDischarging();
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_UnderDuration", 1);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
synchronized (mQuotaController.mLock) {
assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
}
}
@Test
public void testIsWithinEJQuotaLocked_OverDuration() {
setDischarging();
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_OverDuration", 1);
setStandbyBucket(FREQUENT_INDEX, js);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
}
}
@Test
public void testIsWithinEJQuotaLocked_TimingSession() {
setDischarging();
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 8 * MINUTE_IN_MILLIS);
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TimingSession", 1);
for (int i = 0; i < 25; ++i) {
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
2), true);
synchronized (mQuotaController.mLock) {
setStandbyBucket(ACTIVE_INDEX, js);
assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
i < 19, mQuotaController.isWithinEJQuotaLocked(js));
setStandbyBucket(WORKING_INDEX, js);
assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
i < 14, mQuotaController.isWithinEJQuotaLocked(js));
setStandbyBucket(FREQUENT_INDEX, js);
assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
i < 12, mQuotaController.isWithinEJQuotaLocked(js));
setStandbyBucket(RARE_INDEX, js);
assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
i < 9, mQuotaController.isWithinEJQuotaLocked(js));
setStandbyBucket(RESTRICTED_INDEX, js);
assertEquals("Restricted has incorrect quota status with " + (i + 1) + " sessions",
i < 7, mQuotaController.isWithinEJQuotaLocked(js));
}
}
}
/**
* Tests that Timers properly track sessions when an app is added and removed from the temp
* allowlist.
*/
@Test
public void testIsWithinEJQuotaLocked_TempAllowlisting() {
setDischarging();
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting", 1);
setStandbyBucket(FREQUENT_INDEX, js);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
}
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs);
Handler handler = mQuotaController.getHandler();
spyOn(handler);
// Apps on the temp allowlist should be able to schedule & start EJs, even if they're out
// of quota (as long as they are in the temp allowlist grace period).
mTempAllowlistListener.onAppAdded(mSourceUid);
synchronized (mQuotaController.mLock) {
assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
}
advanceElapsedClock(10 * SECOND_IN_MILLIS);
mTempAllowlistListener.onAppRemoved(mSourceUid);
advanceElapsedClock(10 * SECOND_IN_MILLIS);
// Still in grace period
synchronized (mQuotaController.mLock) {
assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
}
advanceElapsedClock(6 * SECOND_IN_MILLIS);
// Out of grace period.
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
}
}
@Test
public void testMaybeScheduleCleanupAlarmLocked() {
// No sessions saved yet.
@@ -3880,6 +4018,55 @@ public class QuotaControllerTest {
}
}
@Test
public void testGetRemainingEJExecutionTimeLocked_IncrementalTimingSessions() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS);
for (int i = 1; i <= 25; ++i) {
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
2), true);
synchronized (mQuotaController.mLock) {
setStandbyBucket(ACTIVE_INDEX);
assertEquals("Active has incorrect remaining EJ time with " + i + " sessions",
(20 - i) * MINUTE_IN_MILLIS,
mQuotaController.getRemainingEJExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
setStandbyBucket(WORKING_INDEX);
assertEquals("Working has incorrect remaining EJ time with " + i + " sessions",
(15 - i) * MINUTE_IN_MILLIS,
mQuotaController.getRemainingEJExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
setStandbyBucket(FREQUENT_INDEX);
assertEquals("Frequent has incorrect remaining EJ time with " + i + " sessions",
(13 - i) * MINUTE_IN_MILLIS,
mQuotaController.getRemainingEJExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
setStandbyBucket(RARE_INDEX);
assertEquals("Rare has incorrect remaining EJ time with " + i + " sessions",
(10 - i) * MINUTE_IN_MILLIS,
mQuotaController.getRemainingEJExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
setStandbyBucket(RESTRICTED_INDEX);
assertEquals("Restricted has incorrect remaining EJ time with " + i + " sessions",
(5 - i) * MINUTE_IN_MILLIS,
mQuotaController.getRemainingEJExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
}
@Test
public void testGetTimeUntilEJQuotaConsumedLocked_NoHistory() {
final long[] limits = mQuotaController.getEJLimitsMs();