Merge "Don't give RESTRICTED jobs temp allowlist exemption." into tm-dev
This commit is contained in:
@@ -307,7 +307,7 @@ public final class QuotaController extends StateController {
|
||||
private final SparseBooleanArray mTempAllowlistCache = new SparseBooleanArray();
|
||||
|
||||
/**
|
||||
* Mapping of UIDs to the when their temp allowlist grace period ends (in the elapsed
|
||||
* Mapping of UIDs to when their temp allowlist grace period ends (in the elapsed
|
||||
* realtime timebase).
|
||||
*/
|
||||
private final SparseLongArray mTempAllowlistGraceCache = new SparseLongArray();
|
||||
@@ -815,6 +815,19 @@ public final class QuotaController extends StateController {
|
||||
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
|
||||
}
|
||||
|
||||
private boolean hasTempAllowlistExemptionLocked(int sourceUid, int standbyBucket,
|
||||
long nowElapsed) {
|
||||
if (standbyBucket == RESTRICTED_INDEX || standbyBucket == NEVER_INDEX) {
|
||||
// Don't let RESTRICTED apps get free quota from the temp allowlist.
|
||||
// TODO: consider granting the exemption to RESTRICTED apps if the temp allowlist allows
|
||||
// them to start FGS
|
||||
return false;
|
||||
}
|
||||
final long tempAllowlistGracePeriodEndElapsed = mTempAllowlistGraceCache.get(sourceUid);
|
||||
return mTempAllowlistCache.get(sourceUid)
|
||||
|| nowElapsed < tempAllowlistGracePeriodEndElapsed;
|
||||
}
|
||||
|
||||
/** @return true if the job is within expedited job quota. */
|
||||
@GuardedBy("mLock")
|
||||
public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) {
|
||||
@@ -833,11 +846,8 @@ public final class QuotaController extends StateController {
|
||||
}
|
||||
|
||||
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||
final long tempAllowlistGracePeriodEndElapsed =
|
||||
mTempAllowlistGraceCache.get(jobStatus.getSourceUid());
|
||||
final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid())
|
||||
|| nowElapsed < tempAllowlistGracePeriodEndElapsed;
|
||||
if (hasTempAllowlistExemption) {
|
||||
if (hasTempAllowlistExemptionLocked(jobStatus.getSourceUid(),
|
||||
jobStatus.getEffectiveStandbyBucket(), nowElapsed)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2127,10 +2137,8 @@ public final class QuotaController extends StateController {
|
||||
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||
final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName,
|
||||
mPkg.userId, nowElapsed);
|
||||
final long tempAllowlistGracePeriodEndElapsed = mTempAllowlistGraceCache.get(mUid);
|
||||
final boolean hasTempAllowlistExemption = !mRegularJobTimer
|
||||
&& (mTempAllowlistCache.get(mUid)
|
||||
|| nowElapsed < tempAllowlistGracePeriodEndElapsed);
|
||||
&& hasTempAllowlistExemptionLocked(mUid, standbyBucket, nowElapsed);
|
||||
final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid);
|
||||
final boolean hasTopAppExemption = !mRegularJobTimer
|
||||
&& (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed);
|
||||
|
||||
@@ -2164,6 +2164,49 @@ public class QuotaControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsWithinEJQuotaLocked_TempAllowlisting_Restricted() {
|
||||
setDischarging();
|
||||
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting_Restricted", 1);
|
||||
setStandbyBucket(RESTRICTED_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_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs);
|
||||
Handler handler = mQuotaController.getHandler();
|
||||
spyOn(handler);
|
||||
|
||||
// The temp allowlist should not enable RESTRICTED apps' to schedule & start EJs if they're
|
||||
// out of quota.
|
||||
mTempAllowlistListener.onAppAdded(mSourceUid);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
|
||||
}
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
mTempAllowlistListener.onAppRemoved(mSourceUid);
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
// Still in grace period
|
||||
synchronized (mQuotaController.mLock) {
|
||||
assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
|
||||
}
|
||||
advanceElapsedClock(6 * SECOND_IN_MILLIS);
|
||||
// Out of grace period.
|
||||
synchronized (mQuotaController.mLock) {
|
||||
assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that Timers properly track sessions when an app becomes top and is closed.
|
||||
*/
|
||||
@@ -5559,6 +5602,111 @@ public class QuotaControllerTest {
|
||||
mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEJTimerTracking_TempAllowlisting_Restricted() {
|
||||
setDischarging();
|
||||
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
|
||||
final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
|
||||
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs);
|
||||
Handler handler = mQuotaController.getHandler();
|
||||
spyOn(handler);
|
||||
|
||||
JobStatus job = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting_Restricted", 1);
|
||||
setStandbyBucket(RESTRICTED_INDEX, job);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||
}
|
||||
assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
List<TimingSession> expected = new ArrayList<>();
|
||||
|
||||
long start = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.prepareForExecutionLocked(job);
|
||||
}
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStopTrackingJobLocked(job, job, true);
|
||||
}
|
||||
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
|
||||
assertEquals(expected,
|
||||
mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
|
||||
advanceElapsedClock(SECOND_IN_MILLIS);
|
||||
|
||||
// Job starts after app is added to temp allowlist and stops before removal.
|
||||
start = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
mTempAllowlistListener.onAppAdded(mSourceUid);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||
mQuotaController.prepareForExecutionLocked(job);
|
||||
}
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStopTrackingJobLocked(job, null, false);
|
||||
}
|
||||
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
|
||||
assertEquals(expected,
|
||||
mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
|
||||
// Job starts after app is added to temp allowlist and stops after removal,
|
||||
// before grace period ends.
|
||||
start = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
mTempAllowlistListener.onAppAdded(mSourceUid);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||
mQuotaController.prepareForExecutionLocked(job);
|
||||
}
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
mTempAllowlistListener.onAppRemoved(mSourceUid);
|
||||
long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS;
|
||||
advanceElapsedClock(elapsedGracePeriodMs);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStopTrackingJobLocked(job, null, false);
|
||||
}
|
||||
expected.add(createTimingSession(start, 12 * SECOND_IN_MILLIS, 1));
|
||||
assertEquals(expected,
|
||||
mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
|
||||
advanceElapsedClock(SECOND_IN_MILLIS);
|
||||
elapsedGracePeriodMs += SECOND_IN_MILLIS;
|
||||
|
||||
// Job starts during grace period and ends after grace period ends
|
||||
start = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||
mQuotaController.prepareForExecutionLocked(job);
|
||||
}
|
||||
final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs;
|
||||
advanceElapsedClock(remainingGracePeriod);
|
||||
// Wait for handler to update Timer
|
||||
// Can't directly evaluate the message because for some reason, the captured message returns
|
||||
// the wrong 'what' even though the correct message goes to the handler and the correct
|
||||
// path executes.
|
||||
verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any());
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS + remainingGracePeriod, 1));
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStopTrackingJobLocked(job, job, true);
|
||||
}
|
||||
assertEquals(expected,
|
||||
mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
|
||||
// Job starts and runs completely after temp allowlist grace period.
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
start = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStartTrackingJobLocked(job, null);
|
||||
mQuotaController.prepareForExecutionLocked(job);
|
||||
}
|
||||
advanceElapsedClock(10 * SECOND_IN_MILLIS);
|
||||
synchronized (mQuotaController.mLock) {
|
||||
mQuotaController.maybeStopTrackingJobLocked(job, job, true);
|
||||
}
|
||||
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
|
||||
assertEquals(expected,
|
||||
mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that Timers properly track sessions when TOP state and temp allowlisting overlaps.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user