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

Commit b86c4a61 authored by Xin Guan's avatar Xin Guan Committed by Android (Google) Code Review
Browse files

Merge "Offer additional quota to system installers." into main

parents 9bbb3032 bbdaf006
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -116,3 +116,13 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "additional_quota_for_system_installer"
    namespace: "backstage_power"
    description: "Offer additional quota for system installer"
    bug: "398264531"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+59 −8
Original line number Diff line number Diff line
@@ -494,6 +494,9 @@ public final class QuotaController extends StateController {

    private long mEjLimitAdditionSpecialMs = QcConstants.DEFAULT_EJ_LIMIT_ADDITION_SPECIAL_MS;

    private long mAllowedTimePeriodAdditionaInstallerMs =
            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;

    /**
     * The period of time used to calculate expedited job sessions. Apps can only have expedited job
     * sessions totalling {@link #mEJLimitsMs}[bucket within this period of time (without factoring
@@ -1095,6 +1098,18 @@ public final class QuotaController extends StateController {
        return baseLimitMs;
    }

    private long getAllowedTimePerPeriodMsLocked(final int userId, @NonNull final String pkgName,
            final int standbyBucket) {
        final long baseLimitMs = mAllowedTimePerPeriodMs[standbyBucket];
        if (Flags.adjustQuotaDefaultConstants()
                && Flags.additionalQuotaForSystemInstaller()
                && standbyBucket == EXEMPTED_INDEX
                && mSystemInstallers.contains(userId, pkgName)) {
            return baseLimitMs + mAllowedTimePeriodAdditionaInstallerMs;
        }
        return baseLimitMs;
    }

    /**
     * Returns the amount of time, in milliseconds, until the package would have reached its
     * duration quota, assuming it has a job counting towards its quota the entire time. This takes
@@ -1112,25 +1127,26 @@ public final class QuotaController extends StateController {

        List<TimedEvent> events = mTimingEvents.get(userId, packageName);
        final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
        final long allowedTimePerPeriodMs =
                getAllowedTimePerPeriodMsLocked(userId, packageName, standbyBucket);
        if (events == null || events.size() == 0) {
            // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
            // essentially run until they reach the maximum limit.
            if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
            if (stats.windowSizeMs == allowedTimePerPeriodMs) {
                return mMaxExecutionTimeMs;
            }
            return mAllowedTimePerPeriodMs[standbyBucket];
            return allowedTimePerPeriodMs;
        }

        final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
        final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
        final long allowedTimePerPeriodMs = mAllowedTimePerPeriodMs[standbyBucket];
        final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
        final long maxExecutionTimeRemainingMs =
                mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;

        // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
        // essentially run until they reach the maximum limit.
        if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
        if (stats.windowSizeMs == allowedTimePerPeriodMs) {
            return calculateTimeUntilQuotaConsumedLocked(
                    events, startMaxElapsed, maxExecutionTimeRemainingMs);
        }
@@ -1270,7 +1286,8 @@ public final class QuotaController extends StateController {
            appStats[standbyBucket] = stats;
        }
        if (refreshStatsIfOld) {
            final long bucketAllowedTimeMs = mAllowedTimePerPeriodMs[standbyBucket];
            final long bucketAllowedTimeMs =
                    getAllowedTimePerPeriodMsLocked(userId, packageName, standbyBucket);
            final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
            final int jobCountLimit = mMaxBucketJobCounts[standbyBucket];
            final int sessionCountLimit = mMaxBucketSessionCounts[standbyBucket];
@@ -1845,9 +1862,10 @@ public final class QuotaController extends StateController {
        final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats);
        final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats);
        final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName);

        final long allowedTimePerPeriosMs =
                getAllowedTimePerPeriodMsLocked(userId, packageName, standbyBucket);
        final boolean inRegularQuota =
                stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs[standbyBucket]
                stats.executionTimeInWindowMs < allowedTimePerPeriosMs
                        && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
                        && isUnderJobCountQuota
                        && isUnderTimingSessionCountQuota;
@@ -3037,6 +3055,9 @@ public final class QuotaController extends StateController {
        static final String KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
                QC_CONSTANT_PREFIX + "allowed_time_per_period_restricted_ms";
        @VisibleForTesting
        static final String KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                QC_CONSTANT_PREFIX + "allowed_time_per_period_addition_installer_ms";
        @VisibleForTesting
        static final String KEY_IN_QUOTA_BUFFER_MS =
                QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
        @VisibleForTesting
@@ -3169,6 +3190,8 @@ public final class QuotaController extends StateController {
                10 * 60 * 1000L; // 10 minutes
        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
                10 * 60 * 1000L; // 10 minutes
        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                10 * 60 * 1000L; // 10 minutes
        private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
                30 * 1000L; // 30 seconds
        // Legacy default window size for EXEMPTED bucket
@@ -3509,6 +3532,9 @@ public final class QuotaController extends StateController {
         */
        public long EJ_LIMIT_ADDITION_INSTALLER_MS = DEFAULT_EJ_LIMIT_ADDITION_INSTALLER_MS;

        public long ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;

        /**
         * The period of time used to calculate expedited job sessions. Apps can only have expedited
         * job sessions totalling EJ_LIMIT_<bucket>_MS within this period of time (without factoring
@@ -3603,6 +3629,7 @@ public final class QuotaController extends StateController {
                case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS:
                case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS:
                case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS:
                case KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS:
                case KEY_IN_QUOTA_BUFFER_MS:
                case KEY_MAX_EXECUTION_TIME_MS:
                case KEY_WINDOW_SIZE_ACTIVE_MS:
@@ -3847,7 +3874,7 @@ public final class QuotaController extends StateController {
                    KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
                    KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
                    KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
                    KEY_IN_QUOTA_BUFFER_MS,
                    KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS, KEY_IN_QUOTA_BUFFER_MS,
                    KEY_MAX_EXECUTION_TIME_MS,
                    KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
                    KEY_WINDOW_SIZE_WORKING_MS,
@@ -3871,6 +3898,9 @@ public final class QuotaController extends StateController {
            ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
                            DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
            ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
                            DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
            IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
                    DEFAULT_IN_QUOTA_BUFFER_MS);
            MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
@@ -3995,6 +4025,18 @@ public final class QuotaController extends StateController {
                mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
                mShouldReevaluateConstraints = true;
            }

            if (Flags.additionalQuotaForSystemInstaller()) {
                // The additions must be in the range
                // [0 minutes, exempted window size - active limit].
                long newAdditionInstallerMs = Math.max(0,
                        Math.min(mBucketPeriodsMs[EXEMPTED_INDEX] - newAllowedTimeExemptedMs,
                                ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS));
                if (mAllowedTimePeriodAdditionaInstallerMs != newAdditionInstallerMs) {
                    mAllowedTimePeriodAdditionaInstallerMs = newAdditionInstallerMs;
                    mShouldReevaluateConstraints = true;
                }
            }
        }

        private void updateRateLimitingConstantsLocked() {
@@ -4159,6 +4201,8 @@ public final class QuotaController extends StateController {
                    .println();
            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
                    ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println();
            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
                    ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS).println();
            pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
            pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println();
            pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
@@ -4334,6 +4378,11 @@ public final class QuotaController extends StateController {
        return mEjLimitAdditionInstallerMs;
    }

    @VisibleForTesting
    long getAllowedTimePeriodAdditionInstallerMs() {
        return mAllowedTimePeriodAdditionaInstallerMs;
    }

    @VisibleForTesting
    long getEjLimitAdditionSpecialMs() {
        return mEjLimitAdditionSpecialMs;
@@ -4435,6 +4484,8 @@ public final class QuotaController extends StateController {
                + ": " + Flags.enforceQuotaPolicyToFgsJobs());
        pw.println("    " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS
                + ": " + Flags.enforceQuotaPolicyToTopStartedJobs());
        pw.println("    " + Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER
                + ": " + Flags.additionalQuotaForSystemInstaller());
        pw.println();

        pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
+127 −1
Original line number Diff line number Diff line
@@ -2388,6 +2388,104 @@ public class QuotaControllerTest {
        }
    }

    @Test
    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
            Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER})
    public void testGetTimeUntilQuotaConsumedLocked_Installer() {
        PackageInfo pi = new PackageInfo();
        pi.packageName = SOURCE_PACKAGE;
        pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES};
        pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED};
        pi.applicationInfo = new ApplicationInfo();
        pi.applicationInfo.uid = mSourceUid;
        doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
        doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission(
                eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid));
        mQuotaController.onSystemServicesReady();

        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
        // Close to RARE boundary.
        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                createTimingSession(
                        now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS),
                        90 * SECOND_IN_MILLIS, 5), false);
        // Far away from FREQUENT boundary.
        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                createTimingSession(
                        now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS -  HOUR_IN_MILLIS),
                        2 * MINUTE_IN_MILLIS, 5), false);
        // Overlap WORKING_SET boundary.
        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                createTimingSession(
                        now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS),
                        2 * MINUTE_IN_MILLIS, 5), false);
        // Close to ACTIVE boundary.
        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                createTimingSession(
                        now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS -  MINUTE_IN_MILLIS),
                        2 * MINUTE_IN_MILLIS, 5), false);
        // Close to EXEMPTED boundary.
        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                createTimingSession(
                        now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS -  MINUTE_IN_MILLIS),
                        2 * MINUTE_IN_MILLIS, 5), false);

        // No additional quota for the system installer when the app is in RARE, FREQUENT,
        // WORKING_SET or ACTIVE bucket.
        setStandbyBucket(RARE_INDEX);
        synchronized (mQuotaController.mLock) {
            assertEquals(30 * SECOND_IN_MILLIS,
                    mQuotaController.getRemainingExecutionTimeLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
            assertEquals(2 * MINUTE_IN_MILLIS,
                    mQuotaController.getTimeUntilQuotaConsumedLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
        }

        setStandbyBucket(FREQUENT_INDEX);
        synchronized (mQuotaController.mLock) {
            assertEquals(2 * MINUTE_IN_MILLIS,
                    mQuotaController.getRemainingExecutionTimeLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
            assertEquals(2 * MINUTE_IN_MILLIS,
                    mQuotaController.getTimeUntilQuotaConsumedLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
        }

        setStandbyBucket(WORKING_INDEX);
        synchronized (mQuotaController.mLock) {
            assertEquals(5 * MINUTE_IN_MILLIS,
                    mQuotaController.getRemainingExecutionTimeLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
            assertEquals(6 * MINUTE_IN_MILLIS,
                    mQuotaController.getTimeUntilQuotaConsumedLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
        }

        // ACTIVE window != allowed time.
        setStandbyBucket(ACTIVE_INDEX);
        synchronized (mQuotaController.mLock) {
            assertEquals(6 * MINUTE_IN_MILLIS,
                    mQuotaController.getRemainingExecutionTimeLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
            assertEquals(8 * MINUTE_IN_MILLIS,
                    mQuotaController.getTimeUntilQuotaConsumedLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
        }

        // Additional quota for the system installer when the app is in EXEMPTED bucket.
        // EXEMPTED window == allowed time.
        setStandbyBucket(EXEMPTED_INDEX);
        synchronized (mQuotaController.mLock) {
            assertEquals(18 * MINUTE_IN_MILLIS,
                    mQuotaController.getRemainingExecutionTimeLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 8 * MINUTE_IN_MILLIS,
                    mQuotaController.getTimeUntilQuotaConsumedLocked(
                            SOURCE_USER_ID, SOURCE_PACKAGE));
        }
    }

    /**
     * Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
     */
@@ -4119,6 +4217,12 @@ public class QuotaControllerTest {
        assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
        assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
        assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());

        mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER);
        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
                6 * MINUTE_IN_MILLIS);
        assertEquals(6 * MINUTE_IN_MILLIS,
                mQuotaController.getAllowedTimePeriodAdditionInstallerMs());
    }

    @Test
@@ -4222,6 +4326,13 @@ public class QuotaControllerTest {
        assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs());
        assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs());

        mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER);
        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
                -MINUTE_IN_MILLIS);
        assertEquals(0,
                mQuotaController.getAllowedTimePeriodAdditionInstallerMs());
        mSetFlagsRule.disableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER);

        // Invalid configurations.
        // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
@@ -4237,9 +4348,18 @@ public class QuotaControllerTest {
                10 * MINUTE_IN_MILLIS);
        setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 5 * MINUTE_IN_MILLIS);

        assertTrue(mQuotaController.getInQuotaBufferMs()
        assertTrue(mQuotaController.getAllowedTimePeriodAdditionInstallerMs()
                <= mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);

        mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER);
        // ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER should never be greater than
        // ALLOWED_TIME_PER_PERIOD.
        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
                 15 * MINUTE_IN_MILLIS);
        assertTrue(mQuotaController.getInQuotaBufferMs()
                <= mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
        mSetFlagsRule.disableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER);

        // Test larger than a day. Controller should cap at one day.
        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
                25 * HOUR_IN_MILLIS);
@@ -4318,6 +4438,12 @@ public class QuotaControllerTest {
        assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
        assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
        assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());

        mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER);
        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
                25 * HOUR_IN_MILLIS);
        assertEquals(0, mQuotaController.getAllowedTimePeriodAdditionInstallerMs());
        mSetFlagsRule.disableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER);
    }

    /** Tests that TimingSessions aren't saved when the device is charging. */