Loading apex/jobscheduler/service/aconfig/job.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -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 } } apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +59 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -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); } Loading Loading @@ -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]; Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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: Loading Loading @@ -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, Loading @@ -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, Loading Loading @@ -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() { Loading Loading @@ -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(); Loading Loading @@ -4334,6 +4378,11 @@ public final class QuotaController extends StateController { return mEjLimitAdditionInstallerMs; } @VisibleForTesting long getAllowedTimePeriodAdditionInstallerMs() { return mAllowedTimePeriodAdditionaInstallerMs; } @VisibleForTesting long getEjLimitAdditionSpecialMs() { return mEjLimitAdditionSpecialMs; Loading Loading @@ -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()); Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +127 −1 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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 Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -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. */ Loading Loading
apex/jobscheduler/service/aconfig/job.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -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 } }
apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +59 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -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); } Loading Loading @@ -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]; Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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: Loading Loading @@ -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, Loading @@ -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, Loading Loading @@ -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() { Loading Loading @@ -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(); Loading Loading @@ -4334,6 +4378,11 @@ public final class QuotaController extends StateController { return mEjLimitAdditionInstallerMs; } @VisibleForTesting long getAllowedTimePeriodAdditionInstallerMs() { return mAllowedTimePeriodAdditionaInstallerMs; } @VisibleForTesting long getEjLimitAdditionSpecialMs() { return mEjLimitAdditionSpecialMs; Loading Loading @@ -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()); Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +127 −1 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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 Loading Loading @@ -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, Loading @@ -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); Loading Loading @@ -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. */ Loading