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

Commit 7a97fd74 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Fix QC alarm spamming." into rvc-dev

parents 272cae6e b99b582f
Loading
Loading
Loading
Loading
+28 −8
Original line number Diff line number Diff line
@@ -241,10 +241,11 @@ public final class QuotaController extends StateController {
                    + "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
                    + "sessionCountInWindow=" + sessionCountInWindow + ", "
                    + "inQuotaTime=" + inQuotaTimeElapsed + ", "
                    + "jobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
                    + "jobCountInRateLimitingWindow=" + jobCountInRateLimitingWindow + ", "
                    + "sessionCountExpirationTime=" + sessionRateLimitExpirationTimeElapsed + ", "
                    + "sessionCountInRateLimitingWindow=" + sessionCountInRateLimitingWindow;
                    + "rateLimitJobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
                    + "rateLimitJobCountWindow=" + jobCountInRateLimitingWindow + ", "
                    + "rateLimitSessionCountExpirationTime="
                    + sessionRateLimitExpirationTimeElapsed + ", "
                    + "rateLimitSessionCountWindow=" + sessionCountInRateLimitingWindow;
        }

        @Override
@@ -863,12 +864,19 @@ public final class QuotaController extends StateController {
        stats.executionTimeInMaxPeriodMs = 0;
        stats.bgJobCountInMaxPeriod = 0;
        stats.sessionCountInWindow = 0;
        if (stats.jobCountLimit == 0 || stats.sessionCountLimit == 0) {
            // App won't be in quota until configuration changes.
            stats.inQuotaTimeElapsed = Long.MAX_VALUE;
        } else {
            stats.inQuotaTimeElapsed = 0;
        }

        Timer timer = mPkgTimers.get(userId, packageName);
        final long nowElapsed = sElapsedRealtimeClock.millis();
        stats.expirationTimeElapsed = nowElapsed + MAX_PERIOD_MS;
        if (timer != null && timer.isActive()) {
            // Exclude active sessions from the session count so that new jobs aren't prevented
            // from starting due to an app hitting the session limit.
            stats.executionTimeInWindowMs =
                    stats.executionTimeInMaxPeriodMs = timer.getCurrentDuration(nowElapsed);
            stats.bgJobCountInWindow = stats.bgJobCountInMaxPeriod = timer.getBgJobCount();
@@ -883,6 +891,10 @@ public final class QuotaController extends StateController {
                stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
                        nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
            }
            if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
                stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
                        nowElapsed + stats.windowSizeMs);
            }
        }

        List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
@@ -1303,6 +1315,13 @@ public final class QuotaController extends StateController {
            inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
                    stats.sessionRateLimitExpirationTimeElapsed);
        }
        if (inQuotaTimeElapsed <= sElapsedRealtimeClock.millis()) {
            final long nowElapsed = sElapsedRealtimeClock.millis();
            Slog.wtf(TAG,
                    "In quota time is " + (nowElapsed - inQuotaTimeElapsed) + "ms old. Now="
                            + nowElapsed + ", inQuotaTime=" + inQuotaTimeElapsed + ": " + stats);
            inQuotaTimeElapsed = nowElapsed + 5 * MINUTE_IN_MILLIS;
        }
        mInQuotaAlarmListener.addAlarmLocked(userId, packageName, inQuotaTimeElapsed);
    }

@@ -1916,8 +1935,8 @@ public final class QuotaController extends StateController {
        @GuardedBy("mLock")
        private void setNextAlarmLocked(long earliestTriggerElapsed) {
            if (mAlarmQueue.size() > 0) {
                final long nextTriggerTimeElapsed = Math.max(earliestTriggerElapsed,
                        mAlarmQueue.peek().second);
                final Pair<Package, Long> alarm = mAlarmQueue.peek();
                final long nextTriggerTimeElapsed = Math.max(earliestTriggerElapsed, alarm.second);
                // Only schedule the alarm if one of the following is true:
                // 1. There isn't one currently scheduled
                // 2. The new alarm is significantly earlier than the previous alarm. If it's
@@ -1928,7 +1947,8 @@ public final class QuotaController extends StateController {
                        || nextTriggerTimeElapsed < mTriggerTimeElapsed - 3 * MINUTE_IN_MILLIS
                        || mTriggerTimeElapsed < nextTriggerTimeElapsed) {
                    if (DEBUG) {
                        Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed);
                        Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed
                                + " for app " + alarm.first);
                    }
                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextTriggerTimeElapsed,
                            ALARM_TAG_QUOTA_CHECK, this, mHandler);
+6 −1
Original line number Diff line number Diff line
@@ -408,7 +408,12 @@ public class CountQuotaTracker extends QuotaTracker {
    void updateExecutionStatsLocked(final int userId, @NonNull final String packageName,
            @Nullable final String tag, @NonNull ExecutionStats stats) {
        stats.countInWindow = 0;
        if (stats.countLimit == 0) {
            // UPTC won't be in quota until configuration changes.
            stats.inQuotaTimeElapsed = Long.MAX_VALUE;
        } else {
            stats.inQuotaTimeElapsed = 0;
        }

        // This can be used to determine when an app will have enough quota to transition from
        // out-of-quota to in-quota.
+71 −0
Original line number Diff line number Diff line
@@ -620,6 +620,77 @@ public class QuotaControllerTest {
        assertEquals(expectedStats, inputStats);
    }

    @Test
    public void testUpdateExecutionStatsLocked_WithTimer() {
        final long now = sElapsedRealtimeClock.millis();
        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);

        ExecutionStats expectedStats = new ExecutionStats();
        ExecutionStats inputStats = new ExecutionStats();
        inputStats.windowSizeMs = expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
        inputStats.jobCountLimit = expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
        inputStats.sessionCountLimit = expectedStats.sessionCountLimit =
                mQcConstants.MAX_SESSION_COUNT_RARE;
        // Active timer isn't counted as session yet.
        expectedStats.sessionCountInWindow = 0;
        // Timer only, under quota.
        for (int i = 1; i < mQcConstants.MAX_JOB_COUNT_RARE; ++i) {
            JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", i);
            setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window
            mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
            mQuotaController.prepareForExecutionLocked(jobStatus);
            advanceElapsedClock(7000);

            expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis();
            expectedStats.executionTimeInWindowMs = expectedStats.executionTimeInMaxPeriodMs =
                    7000 * i;
            expectedStats.bgJobCountInWindow = expectedStats.bgJobCountInMaxPeriod = i;
            mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
            assertEquals(expectedStats, inputStats);
            assertTrue(mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
                    RARE_INDEX));
        }

        // Add old session. Make sure values are combined correctly.
        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS),
                        10 * MINUTE_IN_MILLIS, 5));
        expectedStats.sessionCountInWindow = 1;

        expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS;
        expectedStats.executionTimeInWindowMs += 10 * MINUTE_IN_MILLIS;
        expectedStats.executionTimeInMaxPeriodMs += 10 * MINUTE_IN_MILLIS;
        expectedStats.bgJobCountInWindow += 5;
        expectedStats.bgJobCountInMaxPeriod += 5;
        // Active timer is under quota, so out of quota due to old session.
        expectedStats.inQuotaTimeElapsed =
                sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS;
        mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
        assertEquals(expectedStats, inputStats);
        assertFalse(
                mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));

        // Quota should be exceeded due to activity in active timer.
        JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", 0);
        setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window
        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
        mQuotaController.prepareForExecutionLocked(jobStatus);
        advanceElapsedClock(10000);

        expectedStats.executionTimeInWindowMs += 10000;
        expectedStats.executionTimeInMaxPeriodMs += 10000;
        expectedStats.bgJobCountInWindow++;
        expectedStats.bgJobCountInMaxPeriod++;
        // Out of quota due to activity in active timer, so in quota time should be when enough
        // time has passed since active timer.
        expectedStats.inQuotaTimeElapsed =
                sElapsedRealtimeClock.millis() + expectedStats.windowSizeMs;
        mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
        assertEquals(expectedStats, inputStats);
        assertFalse(
                mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
    }

    /**
     * Tests that getExecutionStatsLocked returns the correct stats.
     */