Loading apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +60 −8 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.Handler; Loading @@ -64,6 +65,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.ConstantsProto; Loading Loading @@ -507,6 +509,8 @@ public final class QuotaController extends StateController { QcConstants.DEFAULT_EJ_LIMIT_RESTRICTED_MS }; private long mEjLimitSpecialAdditionMs = QcConstants.DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_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 @@ -517,22 +521,26 @@ public final class QuotaController extends StateController { /** * Length of time used to split an app's top time into chunks. */ public long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; private long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; /** * How much EJ quota to give back to an app based on the number of top app time chunks it had. */ public long mEJRewardTopAppMs = QcConstants.DEFAULT_EJ_REWARD_TOP_APP_MS; private long mEJRewardTopAppMs = QcConstants.DEFAULT_EJ_REWARD_TOP_APP_MS; /** * How much EJ quota to give back to an app based on each non-top user interaction. */ public long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS; private long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS; /** * How much EJ quota to give back to an app based on each notification seen event. */ public long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; private long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; /** The package verifier app. */ @Nullable private String mPackageVerifier; /** An app has reached its quota. The message should contain a {@link Package} object. */ @VisibleForTesting Loading Loading @@ -587,6 +595,16 @@ public final class QuotaController extends StateController { } } @Override public void onSystemServicesReady() { String[] pkgNames = LocalServices.getService(PackageManagerInternal.class) .getKnownPackageNames( PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM); synchronized (mLock) { mPackageVerifier = ArrayUtils.firstOrNull(pkgNames); } } @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { final int userId = jobStatus.getSourceUserId(); Loading Loading @@ -875,7 +893,7 @@ public final class QuotaController extends StateController { if (quota.getStandbyBucketLocked() == NEVER_INDEX) { return 0; } final long limitMs = mEJLimitsMs[quota.getStandbyBucketLocked()]; final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); long remainingMs = limitMs - quota.getTallyLocked(); // Stale sessions may still be factored into tally. Make sure they're removed. Loading Loading @@ -912,6 +930,14 @@ public final class QuotaController extends StateController { return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis()); } private long getEJLimitMsLocked(@NonNull final String packageName, final int standbyBucket) { final long baseLimitMs = mEJLimitsMs[standbyBucket]; if (packageName.equals(mPackageVerifier)) { return baseLimitMs + mEjLimitSpecialAdditionMs; } 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 Loading @@ -1014,7 +1040,7 @@ public final class QuotaController extends StateController { final long nowElapsed = sElapsedRealtimeClock.millis(); ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); final long limitMs = mEJLimitsMs[quota.getStandbyBucketLocked()]; final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); final long startWindowElapsed = Math.max(0, nowElapsed - mEJLimitWindowSizeMs); long remainingDeadSpaceMs = remainingExecutionTimeMs; // Total time looked at where a session wouldn't be phasing out. Loading Loading @@ -1606,7 +1632,7 @@ public final class QuotaController extends StateController { inRegularQuotaTimeElapsed = inQuotaTimeElapsed; } if (remainingEJQuota <= 0) { final long limitMs = mEJLimitsMs[standbyBucket] - mQuotaBufferMs; final long limitMs = getEJLimitMsLocked(packageName, standbyBucket) - mQuotaBufferMs; long sumMs = 0; final Timer ejTimer = mEJPkgTimers.get(userId, packageName); if (ejTimer != null && ejTimer.isActive()) { Loading Loading @@ -2741,6 +2767,9 @@ public final class QuotaController extends StateController { static final String KEY_EJ_LIMIT_RESTRICTED_MS = QC_CONSTANT_PREFIX + "ej_limit_restricted_ms"; @VisibleForTesting static final String KEY_EJ_LIMIT_SPECIAL_ADDITION_MS = QC_CONSTANT_PREFIX + "ej_limit_special_addition_ms"; @VisibleForTesting static final String KEY_EJ_WINDOW_SIZE_MS = QC_CONSTANT_PREFIX + "ej_window_size_ms"; @VisibleForTesting Loading Loading @@ -2801,6 +2830,7 @@ public final class QuotaController extends StateController { private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_RARE_MS = DEFAULT_EJ_LIMIT_FREQUENT_MS; private static final long DEFAULT_EJ_LIMIT_RESTRICTED_MS = 5 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_MS = 30 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_WINDOW_SIZE_MS = 24 * HOUR_IN_MILLIS; private static final long DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS = 30 * SECOND_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS; Loading Loading @@ -3000,6 +3030,11 @@ public final class QuotaController extends StateController { */ public long EJ_LIMIT_RESTRICTED_MS = DEFAULT_EJ_LIMIT_RESTRICTED_MS; /** * How much additional EJ quota special, critical apps should get. */ public long EJ_LIMIT_SPECIAL_ADDITION_MS = DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_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 @@ -3053,6 +3088,7 @@ public final class QuotaController extends StateController { case KEY_EJ_LIMIT_FREQUENT_MS: case KEY_EJ_LIMIT_RARE_MS: case KEY_EJ_LIMIT_RESTRICTED_MS: case KEY_EJ_LIMIT_SPECIAL_ADDITION_MS: case KEY_EJ_WINDOW_SIZE_MS: updateEJLimitConstantsLocked(); break; Loading Loading @@ -3376,7 +3412,8 @@ public final class QuotaController extends StateController { DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_EJ_LIMIT_ACTIVE_MS, KEY_EJ_LIMIT_WORKING_MS, KEY_EJ_LIMIT_FREQUENT_MS, KEY_EJ_LIMIT_RARE_MS, KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_WINDOW_SIZE_MS); KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, KEY_EJ_WINDOW_SIZE_MS); EJ_LIMIT_ACTIVE_MS = properties.getLong( KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS); EJ_LIMIT_WORKING_MS = properties.getLong( Loading @@ -3387,6 +3424,8 @@ public final class QuotaController extends StateController { KEY_EJ_LIMIT_RARE_MS, DEFAULT_EJ_LIMIT_RARE_MS); EJ_LIMIT_RESTRICTED_MS = properties.getLong( KEY_EJ_LIMIT_RESTRICTED_MS, DEFAULT_EJ_LIMIT_RESTRICTED_MS); EJ_LIMIT_SPECIAL_ADDITION_MS = properties.getLong( KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_MS); EJ_WINDOW_SIZE_MS = properties.getLong( KEY_EJ_WINDOW_SIZE_MS, DEFAULT_EJ_WINDOW_SIZE_MS); Loading Loading @@ -3432,6 +3471,13 @@ public final class QuotaController extends StateController { mEJLimitsMs[RESTRICTED_INDEX] = newRestrictedLimitMs; mShouldReevaluateConstraints = true; } // The addition must be in the range [0 minutes, window size - active limit]. long newSpecialAdditionMs = Math.max(0, Math.min(newWindowSizeMs - newActiveLimitMs, EJ_LIMIT_SPECIAL_ADDITION_MS)); if (mEjLimitSpecialAdditionMs != newSpecialAdditionMs) { mEjLimitSpecialAdditionMs = newSpecialAdditionMs; mShouldReevaluateConstraints = true; } } private void dump(IndentingPrintWriter pw) { Loading Loading @@ -3470,6 +3516,7 @@ public final class QuotaController extends StateController { pw.print(KEY_EJ_LIMIT_FREQUENT_MS, EJ_LIMIT_FREQUENT_MS).println(); pw.print(KEY_EJ_LIMIT_RARE_MS, EJ_LIMIT_RARE_MS).println(); pw.print(KEY_EJ_LIMIT_RESTRICTED_MS, EJ_LIMIT_RESTRICTED_MS).println(); pw.print(KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, EJ_LIMIT_SPECIAL_ADDITION_MS).println(); pw.print(KEY_EJ_WINDOW_SIZE_MS, EJ_WINDOW_SIZE_MS).println(); pw.print(KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, EJ_TOP_APP_TIME_CHUNK_SIZE_MS).println(); pw.print(KEY_EJ_REWARD_TOP_APP_MS, EJ_REWARD_TOP_APP_MS).println(); Loading Loading @@ -3592,6 +3639,11 @@ public final class QuotaController extends StateController { return mEJLimitsMs; } @VisibleForTesting long getEjLimitSpecialAdditionMs() { return mEjLimitSpecialAdditionMs; } @VisibleForTesting @NonNull long getEJLimitWindowSizeMs() { Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +40 −1 Original line number Diff line number Diff line Loading @@ -136,6 +136,8 @@ public class QuotaControllerTest { @Mock private JobSchedulerService mJobSchedulerService; @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private UsageStatsManagerInternal mUsageStatsManager; private JobStore mJobStore; Loading Loading @@ -172,7 +174,7 @@ public class QuotaControllerTest { doReturn(mUsageStatsManager) .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); // Used in JobStatus. doReturn(mock(PackageManagerInternal.class)) doReturn(mPackageManagerInternal) .when(() -> LocalServices.getService(PackageManagerInternal.class)); // Used in QuotaController.Handler. mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir()); Loading Loading @@ -2377,6 +2379,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 30 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 27 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, 10 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 12 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 10 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS); Loading Loading @@ -2414,6 +2417,7 @@ public class QuotaControllerTest { assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(27 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); assertEquals(10 * HOUR_IN_MILLIS, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(12 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); Loading Loading @@ -2452,6 +2456,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, -1); Loading Loading @@ -2486,6 +2491,7 @@ public class QuotaControllerTest { assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(0, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(1, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); Loading Loading @@ -2518,6 +2524,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); Loading @@ -2542,6 +2549,7 @@ public class QuotaControllerTest { assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); Loading Loading @@ -3497,6 +3505,37 @@ public class QuotaControllerTest { } } @Test public void testGetRemainingEJExecutionTimeLocked_SpecialApp() { doReturn(new String[]{SOURCE_PACKAGE}).when(mPackageManagerInternal) .getKnownPackageNames(eq(PackageManagerInternal.PACKAGE_VERIFIER), anyInt()); mQuotaController.onSystemServicesReady(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - mQcConstants.EJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); final long[] limits = mQuotaController.getEJLimitsMs(); for (int i = 0; i < limits.length; ++i) { setStandbyBucket(i); assertEquals("Got wrong remaining EJ execution time for bucket #" + i, i == NEVER_INDEX ? 0 : (limits[i] + mQuotaController.getEjLimitSpecialAdditionMs() - 5 * MINUTE_IN_MILLIS), mQuotaController.getRemainingEJExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } } @Test public void testGetRemainingEJExecutionTimeLocked_OneSessionStraddlesEdge() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +60 −8 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.Handler; Loading @@ -64,6 +65,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.ConstantsProto; Loading Loading @@ -507,6 +509,8 @@ public final class QuotaController extends StateController { QcConstants.DEFAULT_EJ_LIMIT_RESTRICTED_MS }; private long mEjLimitSpecialAdditionMs = QcConstants.DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_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 @@ -517,22 +521,26 @@ public final class QuotaController extends StateController { /** * Length of time used to split an app's top time into chunks. */ public long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; private long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; /** * How much EJ quota to give back to an app based on the number of top app time chunks it had. */ public long mEJRewardTopAppMs = QcConstants.DEFAULT_EJ_REWARD_TOP_APP_MS; private long mEJRewardTopAppMs = QcConstants.DEFAULT_EJ_REWARD_TOP_APP_MS; /** * How much EJ quota to give back to an app based on each non-top user interaction. */ public long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS; private long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS; /** * How much EJ quota to give back to an app based on each notification seen event. */ public long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; private long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; /** The package verifier app. */ @Nullable private String mPackageVerifier; /** An app has reached its quota. The message should contain a {@link Package} object. */ @VisibleForTesting Loading Loading @@ -587,6 +595,16 @@ public final class QuotaController extends StateController { } } @Override public void onSystemServicesReady() { String[] pkgNames = LocalServices.getService(PackageManagerInternal.class) .getKnownPackageNames( PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM); synchronized (mLock) { mPackageVerifier = ArrayUtils.firstOrNull(pkgNames); } } @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { final int userId = jobStatus.getSourceUserId(); Loading Loading @@ -875,7 +893,7 @@ public final class QuotaController extends StateController { if (quota.getStandbyBucketLocked() == NEVER_INDEX) { return 0; } final long limitMs = mEJLimitsMs[quota.getStandbyBucketLocked()]; final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); long remainingMs = limitMs - quota.getTallyLocked(); // Stale sessions may still be factored into tally. Make sure they're removed. Loading Loading @@ -912,6 +930,14 @@ public final class QuotaController extends StateController { return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis()); } private long getEJLimitMsLocked(@NonNull final String packageName, final int standbyBucket) { final long baseLimitMs = mEJLimitsMs[standbyBucket]; if (packageName.equals(mPackageVerifier)) { return baseLimitMs + mEjLimitSpecialAdditionMs; } 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 Loading @@ -1014,7 +1040,7 @@ public final class QuotaController extends StateController { final long nowElapsed = sElapsedRealtimeClock.millis(); ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); final long limitMs = mEJLimitsMs[quota.getStandbyBucketLocked()]; final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); final long startWindowElapsed = Math.max(0, nowElapsed - mEJLimitWindowSizeMs); long remainingDeadSpaceMs = remainingExecutionTimeMs; // Total time looked at where a session wouldn't be phasing out. Loading Loading @@ -1606,7 +1632,7 @@ public final class QuotaController extends StateController { inRegularQuotaTimeElapsed = inQuotaTimeElapsed; } if (remainingEJQuota <= 0) { final long limitMs = mEJLimitsMs[standbyBucket] - mQuotaBufferMs; final long limitMs = getEJLimitMsLocked(packageName, standbyBucket) - mQuotaBufferMs; long sumMs = 0; final Timer ejTimer = mEJPkgTimers.get(userId, packageName); if (ejTimer != null && ejTimer.isActive()) { Loading Loading @@ -2741,6 +2767,9 @@ public final class QuotaController extends StateController { static final String KEY_EJ_LIMIT_RESTRICTED_MS = QC_CONSTANT_PREFIX + "ej_limit_restricted_ms"; @VisibleForTesting static final String KEY_EJ_LIMIT_SPECIAL_ADDITION_MS = QC_CONSTANT_PREFIX + "ej_limit_special_addition_ms"; @VisibleForTesting static final String KEY_EJ_WINDOW_SIZE_MS = QC_CONSTANT_PREFIX + "ej_window_size_ms"; @VisibleForTesting Loading Loading @@ -2801,6 +2830,7 @@ public final class QuotaController extends StateController { private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_RARE_MS = DEFAULT_EJ_LIMIT_FREQUENT_MS; private static final long DEFAULT_EJ_LIMIT_RESTRICTED_MS = 5 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_MS = 30 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_WINDOW_SIZE_MS = 24 * HOUR_IN_MILLIS; private static final long DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS = 30 * SECOND_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS; Loading Loading @@ -3000,6 +3030,11 @@ public final class QuotaController extends StateController { */ public long EJ_LIMIT_RESTRICTED_MS = DEFAULT_EJ_LIMIT_RESTRICTED_MS; /** * How much additional EJ quota special, critical apps should get. */ public long EJ_LIMIT_SPECIAL_ADDITION_MS = DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_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 @@ -3053,6 +3088,7 @@ public final class QuotaController extends StateController { case KEY_EJ_LIMIT_FREQUENT_MS: case KEY_EJ_LIMIT_RARE_MS: case KEY_EJ_LIMIT_RESTRICTED_MS: case KEY_EJ_LIMIT_SPECIAL_ADDITION_MS: case KEY_EJ_WINDOW_SIZE_MS: updateEJLimitConstantsLocked(); break; Loading Loading @@ -3376,7 +3412,8 @@ public final class QuotaController extends StateController { DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_EJ_LIMIT_ACTIVE_MS, KEY_EJ_LIMIT_WORKING_MS, KEY_EJ_LIMIT_FREQUENT_MS, KEY_EJ_LIMIT_RARE_MS, KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_WINDOW_SIZE_MS); KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, KEY_EJ_WINDOW_SIZE_MS); EJ_LIMIT_ACTIVE_MS = properties.getLong( KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS); EJ_LIMIT_WORKING_MS = properties.getLong( Loading @@ -3387,6 +3424,8 @@ public final class QuotaController extends StateController { KEY_EJ_LIMIT_RARE_MS, DEFAULT_EJ_LIMIT_RARE_MS); EJ_LIMIT_RESTRICTED_MS = properties.getLong( KEY_EJ_LIMIT_RESTRICTED_MS, DEFAULT_EJ_LIMIT_RESTRICTED_MS); EJ_LIMIT_SPECIAL_ADDITION_MS = properties.getLong( KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_MS); EJ_WINDOW_SIZE_MS = properties.getLong( KEY_EJ_WINDOW_SIZE_MS, DEFAULT_EJ_WINDOW_SIZE_MS); Loading Loading @@ -3432,6 +3471,13 @@ public final class QuotaController extends StateController { mEJLimitsMs[RESTRICTED_INDEX] = newRestrictedLimitMs; mShouldReevaluateConstraints = true; } // The addition must be in the range [0 minutes, window size - active limit]. long newSpecialAdditionMs = Math.max(0, Math.min(newWindowSizeMs - newActiveLimitMs, EJ_LIMIT_SPECIAL_ADDITION_MS)); if (mEjLimitSpecialAdditionMs != newSpecialAdditionMs) { mEjLimitSpecialAdditionMs = newSpecialAdditionMs; mShouldReevaluateConstraints = true; } } private void dump(IndentingPrintWriter pw) { Loading Loading @@ -3470,6 +3516,7 @@ public final class QuotaController extends StateController { pw.print(KEY_EJ_LIMIT_FREQUENT_MS, EJ_LIMIT_FREQUENT_MS).println(); pw.print(KEY_EJ_LIMIT_RARE_MS, EJ_LIMIT_RARE_MS).println(); pw.print(KEY_EJ_LIMIT_RESTRICTED_MS, EJ_LIMIT_RESTRICTED_MS).println(); pw.print(KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, EJ_LIMIT_SPECIAL_ADDITION_MS).println(); pw.print(KEY_EJ_WINDOW_SIZE_MS, EJ_WINDOW_SIZE_MS).println(); pw.print(KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, EJ_TOP_APP_TIME_CHUNK_SIZE_MS).println(); pw.print(KEY_EJ_REWARD_TOP_APP_MS, EJ_REWARD_TOP_APP_MS).println(); Loading Loading @@ -3592,6 +3639,11 @@ public final class QuotaController extends StateController { return mEJLimitsMs; } @VisibleForTesting long getEjLimitSpecialAdditionMs() { return mEjLimitSpecialAdditionMs; } @VisibleForTesting @NonNull long getEJLimitWindowSizeMs() { Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +40 −1 Original line number Diff line number Diff line Loading @@ -136,6 +136,8 @@ public class QuotaControllerTest { @Mock private JobSchedulerService mJobSchedulerService; @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private UsageStatsManagerInternal mUsageStatsManager; private JobStore mJobStore; Loading Loading @@ -172,7 +174,7 @@ public class QuotaControllerTest { doReturn(mUsageStatsManager) .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); // Used in JobStatus. doReturn(mock(PackageManagerInternal.class)) doReturn(mPackageManagerInternal) .when(() -> LocalServices.getService(PackageManagerInternal.class)); // Used in QuotaController.Handler. mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir()); Loading Loading @@ -2377,6 +2379,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 30 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 27 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, 10 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 12 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 10 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS); Loading Loading @@ -2414,6 +2417,7 @@ public class QuotaControllerTest { assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(27 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); assertEquals(10 * HOUR_IN_MILLIS, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(12 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); Loading Loading @@ -2452,6 +2456,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, -1); Loading Loading @@ -2486,6 +2491,7 @@ public class QuotaControllerTest { assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(0, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(1, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); Loading Loading @@ -2518,6 +2524,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); Loading @@ -2542,6 +2549,7 @@ public class QuotaControllerTest { assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); Loading Loading @@ -3497,6 +3505,37 @@ public class QuotaControllerTest { } } @Test public void testGetRemainingEJExecutionTimeLocked_SpecialApp() { doReturn(new String[]{SOURCE_PACKAGE}).when(mPackageManagerInternal) .getKnownPackageNames(eq(PackageManagerInternal.PACKAGE_VERIFIER), anyInt()); mQuotaController.onSystemServicesReady(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - mQcConstants.EJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); final long[] limits = mQuotaController.getEJLimitsMs(); for (int i = 0; i < limits.length; ++i) { setStandbyBucket(i); assertEquals("Got wrong remaining EJ execution time for bucket #" + i, i == NEVER_INDEX ? 0 : (limits[i] + mQuotaController.getEjLimitSpecialAdditionMs() - 5 * MINUTE_IN_MILLIS), mQuotaController.getRemainingEJExecutionTimeLocked( SOURCE_USER_ID, SOURCE_PACKAGE)); } } @Test public void testGetRemainingEJExecutionTimeLocked_OneSessionStraddlesEdge() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); Loading