Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0974a50 authored by Kweku Adams's avatar Kweku Adams Committed by Automerger Merge Worker
Browse files

Merge "Don't give RESTRICTED jobs temp allowlist exemption." into tm-dev am: 58dce496

parents e5ba791d 58dce496
Loading
Loading
Loading
Loading
+17 −9
Original line number Original line Diff line number Diff line
@@ -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);
+148 −0
Original line number Original line Diff line number Diff line
@@ -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.
     */
     */