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();
|
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).
|
* realtime timebase).
|
||||||
*/
|
*/
|
||||||
private final SparseLongArray mTempAllowlistGraceCache = new SparseLongArray();
|
private final SparseLongArray mTempAllowlistGraceCache = new SparseLongArray();
|
||||||
@@ -815,6 +815,19 @@ public final class QuotaController extends StateController {
|
|||||||
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
|
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. */
|
/** @return true if the job is within expedited job quota. */
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) {
|
public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) {
|
||||||
@@ -833,11 +846,8 @@ public final class QuotaController extends StateController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final long nowElapsed = sElapsedRealtimeClock.millis();
|
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||||
final long tempAllowlistGracePeriodEndElapsed =
|
if (hasTempAllowlistExemptionLocked(jobStatus.getSourceUid(),
|
||||||
mTempAllowlistGraceCache.get(jobStatus.getSourceUid());
|
jobStatus.getEffectiveStandbyBucket(), nowElapsed)) {
|
||||||
final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid())
|
|
||||||
|| nowElapsed < tempAllowlistGracePeriodEndElapsed;
|
|
||||||
if (hasTempAllowlistExemption) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2127,10 +2137,8 @@ public final class QuotaController extends StateController {
|
|||||||
final long nowElapsed = sElapsedRealtimeClock.millis();
|
final long nowElapsed = sElapsedRealtimeClock.millis();
|
||||||
final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName,
|
final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName,
|
||||||
mPkg.userId, nowElapsed);
|
mPkg.userId, nowElapsed);
|
||||||
final long tempAllowlistGracePeriodEndElapsed = mTempAllowlistGraceCache.get(mUid);
|
|
||||||
final boolean hasTempAllowlistExemption = !mRegularJobTimer
|
final boolean hasTempAllowlistExemption = !mRegularJobTimer
|
||||||
&& (mTempAllowlistCache.get(mUid)
|
&& hasTempAllowlistExemptionLocked(mUid, standbyBucket, nowElapsed);
|
||||||
|| nowElapsed < tempAllowlistGracePeriodEndElapsed);
|
|
||||||
final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid);
|
final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid);
|
||||||
final boolean hasTopAppExemption = !mRegularJobTimer
|
final boolean hasTopAppExemption = !mRegularJobTimer
|
||||||
&& (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed);
|
&& (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.
|
* 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));
|
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.
|
* Tests that Timers properly track sessions when TOP state and temp allowlisting overlaps.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user