1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.job.controllers; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 27 import static com.android.server.job.Flags.FLAG_COUNT_QUOTA_FIX; 28 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; 29 import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; 30 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; 31 import static com.android.server.job.JobSchedulerService.NEVER_INDEX; 32 import static com.android.server.job.JobSchedulerService.RARE_INDEX; 33 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; 34 import static com.android.server.job.JobSchedulerService.WORKING_INDEX; 35 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 36 import static com.android.server.job.JobSchedulerService.sSystemClock; 37 38 import static org.junit.Assert.assertEquals; 39 import static org.junit.Assert.assertFalse; 40 import static org.junit.Assert.assertNotEquals; 41 import static org.junit.Assert.assertNotNull; 42 import static org.junit.Assert.assertNull; 43 import static org.junit.Assert.assertTrue; 44 import static org.junit.Assert.fail; 45 import static org.mockito.ArgumentMatchers.any; 46 import static org.mockito.ArgumentMatchers.anyInt; 47 import static org.mockito.ArgumentMatchers.anyLong; 48 import static org.mockito.ArgumentMatchers.anyString; 49 import static org.mockito.ArgumentMatchers.argThat; 50 import static org.mockito.Mockito.atLeast; 51 import static org.mockito.Mockito.eq; 52 import static org.mockito.Mockito.never; 53 import static org.mockito.Mockito.timeout; 54 import static org.mockito.Mockito.times; 55 import static org.mockito.Mockito.verify; 56 57 import android.Manifest; 58 import android.app.ActivityManager; 59 import android.app.ActivityManagerInternal; 60 import android.app.AlarmManager; 61 import android.app.AppGlobals; 62 import android.app.IActivityManager; 63 import android.app.IUidObserver; 64 import android.app.job.JobInfo; 65 import android.app.usage.UsageEvents; 66 import android.app.usage.UsageStatsManager; 67 import android.app.usage.UsageStatsManagerInternal; 68 import android.compat.testing.PlatformCompatChangeRule; 69 import android.content.ComponentName; 70 import android.content.Context; 71 import android.content.pm.ApplicationInfo; 72 import android.content.pm.PackageInfo; 73 import android.content.pm.PackageManager; 74 import android.content.pm.PackageManagerInternal; 75 import android.os.BatteryManagerInternal; 76 import android.os.Handler; 77 import android.os.Looper; 78 import android.os.RemoteException; 79 import android.os.ServiceManager; 80 import android.os.SystemClock; 81 import android.platform.test.annotations.DisableFlags; 82 import android.platform.test.annotations.EnableFlags; 83 import android.platform.test.annotations.RequiresFlagsEnabled; 84 import android.platform.test.flag.junit.CheckFlagsRule; 85 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 86 import android.platform.test.flag.junit.SetFlagsRule; 87 import android.provider.DeviceConfig; 88 import android.util.ArraySet; 89 import android.util.SparseBooleanArray; 90 91 import androidx.test.filters.LargeTest; 92 import androidx.test.runner.AndroidJUnit4; 93 94 import com.android.internal.util.ArrayUtils; 95 import com.android.server.LocalServices; 96 import com.android.server.PowerAllowlistInternal; 97 import com.android.server.compat.PlatformCompat; 98 import com.android.server.job.Flags; 99 import com.android.server.job.JobSchedulerInternal; 100 import com.android.server.job.JobSchedulerService; 101 import com.android.server.job.JobStore; 102 import com.android.server.job.controllers.QuotaController.ExecutionStats; 103 import com.android.server.job.controllers.QuotaController.QcConstants; 104 import com.android.server.job.controllers.QuotaController.ShrinkableDebits; 105 import com.android.server.job.controllers.QuotaController.TimedEvent; 106 import com.android.server.job.controllers.QuotaController.TimingSession; 107 import com.android.server.usage.AppStandbyInternal; 108 109 import org.junit.After; 110 import org.junit.Before; 111 import org.junit.Rule; 112 import org.junit.Test; 113 import org.junit.rules.TestRule; 114 import org.junit.runner.RunWith; 115 import org.mockito.ArgumentCaptor; 116 import org.mockito.ArgumentMatchers; 117 import org.mockito.InOrder; 118 import org.mockito.Mock; 119 import org.mockito.MockitoSession; 120 import org.mockito.quality.Strictness; 121 import org.mockito.stubbing.Answer; 122 123 import java.time.Clock; 124 import java.time.Duration; 125 import java.time.ZoneOffset; 126 import java.util.ArrayList; 127 import java.util.List; 128 import java.util.concurrent.Executor; 129 130 @RunWith(AndroidJUnit4.class) 131 public class QuotaControllerTest { 132 private static final long SECOND_IN_MILLIS = 1000L; 133 private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; 134 private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; 135 private static final String TAG_CLEANUP = "*job.cleanup*"; 136 private static final String TAG_QUOTA_CHECK = "*job.quota_check*"; 137 private static final int CALLING_UID = 1000; 138 private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; 139 private static final int SOURCE_USER_ID = 0; 140 141 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 142 143 @Rule 144 public TestRule compatChangeRule = new PlatformCompatChangeRule(); 145 private QuotaController mQuotaController; 146 private QuotaController.QcConstants mQcConstants; 147 private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants(); 148 private int mSourceUid; 149 private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener; 150 private IUidObserver mUidObserver; 151 private UsageStatsManagerInternal.UsageEventListener mUsageEventListener; 152 DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; 153 154 private MockitoSession mMockingSession; 155 @Mock 156 private ActivityManagerInternal mActivityMangerInternal; 157 @Mock 158 private AlarmManager mAlarmManager; 159 @Mock 160 private Context mContext; 161 @Mock 162 private JobSchedulerService mJobSchedulerService; 163 @Mock 164 private PackageManager mPackageManager; 165 @Mock 166 private PackageManagerInternal mPackageManagerInternal; 167 @Mock 168 private PowerAllowlistInternal mPowerAllowlistInternal; 169 @Mock 170 private UsageStatsManagerInternal mUsageStatsManager; 171 @Mock 172 private PlatformCompat mPlatformCompat; 173 174 @Rule 175 public final CheckFlagsRule mCheckFlagsRule = 176 DeviceFlagsValueProvider.createCheckFlagsRule(); 177 178 private JobStore mJobStore; 179 180 @Before setUp()181 public void setUp() { 182 mMockingSession = mockitoSession() 183 .initMocks(this) 184 .strictness(Strictness.LENIENT) 185 .spyStatic(DeviceConfig.class) 186 .mockStatic(LocalServices.class) 187 .mockStatic(ServiceManager.class) 188 .startMocking(); 189 190 // Called in StateController constructor. 191 when(mJobSchedulerService.getTestableContext()).thenReturn(mContext); 192 when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); 193 when(mJobSchedulerService.getConstants()).thenReturn(mConstants); 194 // Called in QuotaController constructor. 195 IActivityManager activityManager = ActivityManager.getService(); 196 spyOn(activityManager); 197 try { 198 doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); 199 } catch (RemoteException e) { 200 fail("registerUidObserver threw exception: " + e.getMessage()); 201 } 202 when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); 203 when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); 204 205 doReturn(mActivityMangerInternal) 206 .when(() -> LocalServices.getService(ActivityManagerInternal.class)); 207 doReturn(mock(AppStandbyInternal.class)) 208 .when(() -> LocalServices.getService(AppStandbyInternal.class)); 209 doReturn(mock(BatteryManagerInternal.class)) 210 .when(() -> LocalServices.getService(BatteryManagerInternal.class)); 211 doReturn(mUsageStatsManager) 212 .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); 213 JobSchedulerService.sUsageStatsManagerInternal = mUsageStatsManager; 214 doReturn(mPowerAllowlistInternal) 215 .when(() -> LocalServices.getService(PowerAllowlistInternal.class)); 216 // Used in JobStatus. 217 doReturn(mock(JobSchedulerInternal.class)) 218 .when(() -> LocalServices.getService(JobSchedulerInternal.class)); 219 doReturn(mPackageManagerInternal) 220 .when(() -> LocalServices.getService(PackageManagerInternal.class)); 221 // Used in QuotaController.Handler. 222 mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir()); 223 when(mJobSchedulerService.getJobStore()).thenReturn(mJobStore); 224 // Used in QuotaController.QcConstants 225 doAnswer((Answer<Void>) invocationOnMock -> null) 226 .when(() -> DeviceConfig.addOnPropertiesChangedListener( 227 anyString(), any(Executor.class), 228 any(DeviceConfig.OnPropertiesChangedListener.class))); 229 mDeviceConfigPropertiesBuilder = 230 new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); 231 doAnswer( 232 (Answer<DeviceConfig.Properties>) invocationOnMock 233 -> mDeviceConfigPropertiesBuilder.build()) 234 .when(() -> DeviceConfig.getProperties( 235 eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.<String>any())); 236 // Used in QuotaController.onSystemServicesReady 237 when(mContext.getPackageManager()).thenReturn(mPackageManager); 238 239 // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions 240 // in the past, and QuotaController sometimes floors values at 0, so if the test time 241 // causes sessions with negative timestamps, they will fail. 242 JobSchedulerService.sSystemClock = 243 getAdvancedClock(Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC), 244 24 * HOUR_IN_MILLIS); 245 JobSchedulerService.sUptimeMillisClock = getAdvancedClock( 246 Clock.fixed(SystemClock.uptimeClock().instant(), ZoneOffset.UTC), 247 24 * HOUR_IN_MILLIS); 248 JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock( 249 Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC), 250 24 * HOUR_IN_MILLIS); 251 252 // Initialize real objects. 253 // Capture the listeners. 254 ArgumentCaptor<IUidObserver> uidObserverCaptor = 255 ArgumentCaptor.forClass(IUidObserver.class); 256 ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = 257 ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class); 258 ArgumentCaptor<UsageStatsManagerInternal.UsageEventListener> ueListenerCaptor = 259 ArgumentCaptor.forClass(UsageStatsManagerInternal.UsageEventListener.class); 260 doReturn(mPlatformCompat) 261 .when(() -> ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); 262 mQuotaController = new QuotaController(mJobSchedulerService, 263 mock(BackgroundJobsController.class), mock(ConnectivityController.class)); 264 265 verify(mPowerAllowlistInternal) 266 .registerTempAllowlistChangeListener(taChangeCaptor.capture()); 267 mTempAllowlistListener = taChangeCaptor.getValue(); 268 verify(mUsageStatsManager).registerListener(ueListenerCaptor.capture()); 269 mUsageEventListener = ueListenerCaptor.getValue(); 270 try { 271 verify(activityManager).registerUidObserver( 272 uidObserverCaptor.capture(), 273 eq(ActivityManager.UID_OBSERVER_PROCSTATE), 274 eq(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE), 275 any()); 276 mUidObserver = uidObserverCaptor.getValue(); 277 mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0); 278 // Need to do this since we're using a mock JS and not a real object. 279 doReturn(new ArraySet<>(new String[]{SOURCE_PACKAGE})) 280 .when(mJobSchedulerService).getPackagesForUidLocked(mSourceUid); 281 } catch (RemoteException e) { 282 fail(e.getMessage()); 283 } 284 mQcConstants = mQuotaController.getQcConstants(); 285 } 286 287 @After tearDown()288 public void tearDown() { 289 if (mMockingSession != null) { 290 mMockingSession.finishMocking(); 291 } 292 } 293 getAdvancedClock(Clock clock, long incrementMs)294 private Clock getAdvancedClock(Clock clock, long incrementMs) { 295 return Clock.offset(clock, Duration.ofMillis(incrementMs)); 296 } 297 advanceElapsedClock(long incrementMs)298 private void advanceElapsedClock(long incrementMs) { 299 JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock( 300 JobSchedulerService.sElapsedRealtimeClock, incrementMs); 301 } 302 setCharging()303 private void setCharging() { 304 synchronized (mQuotaController.mLock) { 305 doReturn(true).when(mJobSchedulerService).isBatteryCharging(); 306 mQuotaController.onBatteryStateChangedLocked(); 307 } 308 } 309 setDischarging()310 private void setDischarging() { 311 synchronized (mQuotaController.mLock) { 312 doReturn(false).when(mJobSchedulerService).isBatteryCharging(); 313 mQuotaController.onBatteryStateChangedLocked(); 314 } 315 } 316 getProcessStateQuotaFreeThreshold()317 private int getProcessStateQuotaFreeThreshold() { 318 synchronized (mQuotaController.mLock) { 319 return mQuotaController.getProcessStateQuotaFreeThreshold(mSourceUid); 320 } 321 } 322 setProcessState(int procState)323 private void setProcessState(int procState) { 324 setProcessState(procState, mSourceUid); 325 } 326 setProcessState(int procState, int uid)327 private void setProcessState(int procState, int uid) { 328 try { 329 doReturn(procState).when(mActivityMangerInternal).getUidProcessState(uid); 330 SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids(); 331 spyOn(foregroundUids); 332 final boolean contained = foregroundUids.get(uid); 333 mUidObserver.onUidStateChanged(uid, procState, 0, 334 ActivityManager.PROCESS_CAPABILITY_NONE); 335 if (procState <= getProcessStateQuotaFreeThreshold()) { 336 if (!contained) { 337 verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)) 338 .put(eq(uid), eq(true)); 339 } 340 assertTrue(foregroundUids.get(uid)); 341 } else { 342 if (contained) { 343 verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)) 344 .delete(eq(uid)); 345 } 346 assertFalse(foregroundUids.get(uid)); 347 } 348 waitForNonDelayedMessagesProcessed(); 349 } catch (Exception e) { 350 fail("exception encountered: " + e.getMessage()); 351 } 352 } 353 bucketIndexToUsageStatsBucket(int bucketIndex)354 private int bucketIndexToUsageStatsBucket(int bucketIndex) { 355 switch (bucketIndex) { 356 case EXEMPTED_INDEX: 357 return UsageStatsManager.STANDBY_BUCKET_EXEMPTED; 358 case ACTIVE_INDEX: 359 return UsageStatsManager.STANDBY_BUCKET_ACTIVE; 360 case WORKING_INDEX: 361 return UsageStatsManager.STANDBY_BUCKET_WORKING_SET; 362 case FREQUENT_INDEX: 363 return UsageStatsManager.STANDBY_BUCKET_FREQUENT; 364 case RARE_INDEX: 365 return UsageStatsManager.STANDBY_BUCKET_RARE; 366 case RESTRICTED_INDEX: 367 return UsageStatsManager.STANDBY_BUCKET_RESTRICTED; 368 default: 369 return UsageStatsManager.STANDBY_BUCKET_NEVER; 370 } 371 } 372 setStandbyBucket(int bucketIndex)373 private void setStandbyBucket(int bucketIndex) { 374 when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID), 375 anyLong())).thenReturn(bucketIndexToUsageStatsBucket(bucketIndex)); 376 mQuotaController.updateStandbyBucket(SOURCE_USER_ID, SOURCE_PACKAGE, bucketIndex); 377 } 378 setStandbyBucket(int bucketIndex, JobStatus... jobs)379 private void setStandbyBucket(int bucketIndex, JobStatus... jobs) { 380 setStandbyBucket(bucketIndex); 381 for (JobStatus job : jobs) { 382 job.setStandbyBucket(bucketIndex); 383 when(mUsageStatsManager.getAppStandbyBucket( 384 eq(job.getSourcePackageName()), eq(job.getSourceUserId()), anyLong())) 385 .thenReturn(bucketIndexToUsageStatsBucket(bucketIndex)); 386 } 387 } 388 trackJobs(JobStatus... jobs)389 private void trackJobs(JobStatus... jobs) { 390 for (JobStatus job : jobs) { 391 mJobStore.add(job); 392 synchronized (mQuotaController.mLock) { 393 mQuotaController.maybeStartTrackingJobLocked(job, null); 394 } 395 } 396 } 397 createJobInfoBuilder(int jobId)398 private JobInfo.Builder createJobInfoBuilder(int jobId) { 399 return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestQuotaJobService")); 400 } 401 createJobStatus(String testTag, int jobId)402 private JobStatus createJobStatus(String testTag, int jobId) { 403 return createJobStatus(testTag, createJobInfoBuilder(jobId).build()); 404 } 405 createJobStatus(String testTag, JobInfo jobInfo)406 private JobStatus createJobStatus(String testTag, JobInfo jobInfo) { 407 return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); 408 } 409 createExpeditedJobStatus(String testTag, int jobId)410 private JobStatus createExpeditedJobStatus(String testTag, int jobId) { 411 JobInfo jobInfo = new JobInfo.Builder(jobId, 412 new ComponentName(mContext, "TestQuotaExpeditedJobService")) 413 .setExpedited(true) 414 .build(); 415 return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); 416 } 417 createJobStatus(String testTag, String packageName, int callingUid, JobInfo jobInfo)418 private JobStatus createJobStatus(String testTag, String packageName, int callingUid, 419 JobInfo jobInfo) { 420 JobStatus js = JobStatus.createFromJobInfo( 421 jobInfo, callingUid, packageName, SOURCE_USER_ID, "QCTest", testTag); 422 js.serviceProcessName = "testProcess"; 423 // Make sure tests aren't passing just because the default bucket is likely ACTIVE. 424 js.setStandbyBucket(FREQUENT_INDEX); 425 // Make sure Doze and background-not-restricted don't affect tests. 426 js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(), 427 /* state */ true, /* allowlisted */false); 428 js.setBackgroundNotRestrictedConstraintSatisfied( 429 sElapsedRealtimeClock.millis(), true, false); 430 js.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); 431 return js; 432 } 433 createTimingSession(long start, long duration, int count)434 private TimingSession createTimingSession(long start, long duration, int count) { 435 return new TimingSession(start, start + duration, count); 436 } 437 setDeviceConfigLong(String key, long val)438 private void setDeviceConfigLong(String key, long val) { 439 mDeviceConfigPropertiesBuilder.setLong(key, val); 440 synchronized (mQuotaController.mLock) { 441 mQuotaController.prepareForUpdatedConstantsLocked(); 442 mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); 443 } 444 } 445 setDeviceConfigInt(String key, int val)446 private void setDeviceConfigInt(String key, int val) { 447 mDeviceConfigPropertiesBuilder.setInt(key, val); 448 synchronized (mQuotaController.mLock) { 449 mQuotaController.prepareForUpdatedConstantsLocked(); 450 mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); 451 } 452 } 453 waitForNonDelayedMessagesProcessed()454 private void waitForNonDelayedMessagesProcessed() { 455 mQuotaController.getHandler().runWithScissors(() -> {}, 15_000); 456 } 457 458 @Test testSaveTimingSession()459 public void testSaveTimingSession() { 460 assertNull(mQuotaController.getTimingSessions(0, "com.android.test")); 461 462 List<TimingSession> expectedRegular = new ArrayList<>(); 463 List<TimingSession> expectedEJ = new ArrayList<>(); 464 TimingSession one = new TimingSession(1, 10, 1); 465 TimingSession two = new TimingSession(11, 20, 2); 466 TimingSession thr = new TimingSession(21, 30, 3); 467 TimingSession fou = new TimingSession(31, 40, 4); 468 469 mQuotaController.saveTimingSession(0, "com.android.test", one, false); 470 expectedRegular.add(one); 471 assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); 472 assertTrue( 473 ArrayUtils.isEmpty(mQuotaController.getEJTimingSessions(0, "com.android.test"))); 474 475 mQuotaController.saveTimingSession(0, "com.android.test", two, false); 476 expectedRegular.add(two); 477 assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); 478 assertTrue( 479 ArrayUtils.isEmpty(mQuotaController.getEJTimingSessions(0, "com.android.test"))); 480 481 mQuotaController.saveTimingSession(0, "com.android.test", thr, true); 482 expectedEJ.add(thr); 483 assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); 484 assertEquals(expectedEJ, mQuotaController.getEJTimingSessions(0, "com.android.test")); 485 486 mQuotaController.saveTimingSession(0, "com.android.test", fou, false); 487 mQuotaController.saveTimingSession(0, "com.android.test", fou, true); 488 expectedRegular.add(fou); 489 expectedEJ.add(fou); 490 assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); 491 assertEquals(expectedEJ, mQuotaController.getEJTimingSessions(0, "com.android.test")); 492 } 493 494 @Test testDeleteObsoleteSessionsLocked()495 public void testDeleteObsoleteSessionsLocked() { 496 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 497 TimingSession one = createTimingSession( 498 now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); 499 TimingSession two = createTimingSession( 500 now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); 501 TimingSession thr = createTimingSession( 502 now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); 503 // Overlaps 24 hour boundary. 504 TimingSession fou = createTimingSession( 505 now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1); 506 // Way past the 24 hour boundary. 507 TimingSession fiv = createTimingSession( 508 now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4); 509 List<TimedEvent> expectedRegular = new ArrayList<>(); 510 List<TimedEvent> expectedEJ = new ArrayList<>(); 511 // Added in correct (chronological) order. 512 expectedRegular.add(fou); 513 expectedRegular.add(thr); 514 expectedRegular.add(two); 515 expectedRegular.add(one); 516 expectedEJ.add(fou); 517 expectedEJ.add(one); 518 mQuotaController.saveTimingSession(0, "com.android.test", fiv, false); 519 mQuotaController.saveTimingSession(0, "com.android.test", fou, false); 520 mQuotaController.saveTimingSession(0, "com.android.test", thr, false); 521 mQuotaController.saveTimingSession(0, "com.android.test", two, false); 522 mQuotaController.saveTimingSession(0, "com.android.test", one, false); 523 mQuotaController.saveTimingSession(0, "com.android.test", fiv, true); 524 mQuotaController.saveTimingSession(0, "com.android.test", fou, true); 525 mQuotaController.saveTimingSession(0, "com.android.test", one, true); 526 527 synchronized (mQuotaController.mLock) { 528 mQuotaController.deleteObsoleteSessionsLocked(); 529 } 530 531 assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test")); 532 assertEquals(expectedEJ, mQuotaController.getEJTimingSessions(0, "com.android.test")); 533 } 534 535 @Test testOnAppRemovedLocked()536 public void testOnAppRemovedLocked() { 537 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 538 mQuotaController.saveTimingSession(0, "com.android.test.remove", 539 createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 540 mQuotaController.saveTimingSession(0, "com.android.test.remove", 541 createTimingSession( 542 now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5), 543 false); 544 mQuotaController.saveTimingSession(0, "com.android.test.remove", 545 createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); 546 mQuotaController.saveTimingSession(0, "com.android.test.remove", 547 createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); 548 mQuotaController.saveTimingSession(0, "com.android.test.remove", 549 createTimingSession(now - (15 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); 550 // Test that another app isn't affected. 551 TimingSession one = createTimingSession( 552 now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); 553 TimingSession two = createTimingSession( 554 now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); 555 List<TimingSession> expected = new ArrayList<>(); 556 // Added in correct (chronological) order. 557 expected.add(two); 558 expected.add(one); 559 mQuotaController.saveTimingSession(0, "com.android.test.stay", two, false); 560 mQuotaController.saveTimingSession(0, "com.android.test.stay", one, false); 561 mQuotaController.saveTimingSession(0, "com.android.test.stay", one, true); 562 563 assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test.remove")); 564 assertNotNull(mQuotaController.getEJTimingSessions(0, "com.android.test.remove")); 565 assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test.stay")); 566 assertNotNull(mQuotaController.getEJTimingSessions(0, "com.android.test.stay")); 567 568 ExecutionStats expectedStats = new ExecutionStats(); 569 expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS; 570 expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS; 571 expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; 572 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; 573 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; 574 575 final int uid = 10001; 576 synchronized (mQuotaController.mLock) { 577 mQuotaController.onAppRemovedLocked("com.android.test.remove", uid); 578 } 579 assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove")); 580 assertNull(mQuotaController.getEJTimingSessions(0, "com.android.test.remove")); 581 assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay")); 582 synchronized (mQuotaController.mLock) { 583 assertEquals(expectedStats, 584 mQuotaController.getExecutionStatsLocked( 585 0, "com.android.test.remove", RARE_INDEX)); 586 assertNotEquals(expectedStats, 587 mQuotaController.getExecutionStatsLocked( 588 0, "com.android.test.stay", RARE_INDEX)); 589 590 assertFalse(mQuotaController.getForegroundUids().get(uid)); 591 } 592 } 593 594 @Test testOnUserRemovedLocked()595 public void testOnUserRemovedLocked() { 596 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 597 mQuotaController.saveTimingSession(0, "com.android.test", 598 createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 599 mQuotaController.saveTimingSession(0, "com.android.test", 600 createTimingSession( 601 now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5), 602 false); 603 mQuotaController.saveTimingSession(0, "com.android.test", 604 createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); 605 mQuotaController.saveTimingSession(0, "com.android.test", 606 createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); 607 mQuotaController.saveTimingSession(0, "com.android.test", 608 createTimingSession(now - (15 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true); 609 // Test that another user isn't affected. 610 TimingSession one = createTimingSession( 611 now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); 612 TimingSession two = createTimingSession( 613 now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); 614 List<TimingSession> expectedRegular = new ArrayList<>(); 615 List<TimingSession> expectedEJ = new ArrayList<>(); 616 // Added in correct (chronological) order. 617 expectedRegular.add(two); 618 expectedRegular.add(one); 619 expectedEJ.add(one); 620 mQuotaController.saveTimingSession(10, "com.android.test", two, false); 621 mQuotaController.saveTimingSession(10, "com.android.test", one, false); 622 mQuotaController.saveTimingSession(10, "com.android.test", one, true); 623 624 assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test")); 625 assertNotNull(mQuotaController.getEJTimingSessions(0, "com.android.test")); 626 assertNotNull(mQuotaController.getTimingSessions(10, "com.android.test")); 627 assertNotNull(mQuotaController.getEJTimingSessions(10, "com.android.test")); 628 629 ExecutionStats expectedStats = new ExecutionStats(); 630 expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS; 631 expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS; 632 expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; 633 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; 634 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; 635 636 synchronized (mQuotaController.mLock) { 637 mQuotaController.onUserRemovedLocked(0); 638 assertNull(mQuotaController.getTimingSessions(0, "com.android.test")); 639 assertNull(mQuotaController.getEJTimingSessions(0, "com.android.test")); 640 assertEquals(expectedRegular, 641 mQuotaController.getTimingSessions(10, "com.android.test")); 642 assertEquals(expectedEJ, 643 mQuotaController.getEJTimingSessions(10, "com.android.test")); 644 assertEquals(expectedStats, 645 mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); 646 assertNotEquals(expectedStats, 647 mQuotaController.getExecutionStatsLocked(10, "com.android.test", RARE_INDEX)); 648 } 649 } 650 651 @Test testUpdateExecutionStatsLocked_NoTimer()652 public void testUpdateExecutionStatsLocked_NoTimer() { 653 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 654 // Added in chronological order. 655 mQuotaController.saveTimingSession(0, "com.android.test", 656 createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 657 mQuotaController.saveTimingSession(0, "com.android.test", 658 createTimingSession( 659 now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5), 660 false); 661 mQuotaController.saveTimingSession(0, "com.android.test", 662 createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); 663 mQuotaController.saveTimingSession(0, "com.android.test", 664 createTimingSession( 665 now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), 666 false); 667 mQuotaController.saveTimingSession(0, "com.android.test", 668 createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3), false); 669 670 // Test an app that hasn't had any activity. 671 ExecutionStats expectedStats = new ExecutionStats(); 672 ExecutionStats inputStats = new ExecutionStats(); 673 674 inputStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS; 675 inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS; 676 inputStats.jobCountLimit = expectedStats.jobCountLimit = 100; 677 inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100; 678 // Invalid time is now +24 hours since there are no sessions at all for the app. 679 expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS; 680 expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS; 681 synchronized (mQuotaController.mLock) { 682 mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats); 683 } 684 assertEquals(expectedStats, inputStats); 685 686 inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS; 687 // Invalid time is now +18 hours since there are no sessions in the window but the earliest 688 // session is 6 hours ago. 689 expectedStats.expirationTimeElapsed = now + 18 * HOUR_IN_MILLIS; 690 expectedStats.executionTimeInWindowMs = 0; 691 expectedStats.bgJobCountInWindow = 0; 692 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 693 expectedStats.bgJobCountInMaxPeriod = 15; 694 expectedStats.sessionCountInWindow = 0; 695 synchronized (mQuotaController.mLock) { 696 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 697 } 698 assertEquals(expectedStats, inputStats); 699 700 inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS; 701 // Invalid time is now since the session straddles the window cutoff time. 702 expectedStats.expirationTimeElapsed = now; 703 expectedStats.executionTimeInWindowMs = 2 * MINUTE_IN_MILLIS; 704 expectedStats.bgJobCountInWindow = 3; 705 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 706 expectedStats.bgJobCountInMaxPeriod = 15; 707 expectedStats.sessionCountInWindow = 1; 708 synchronized (mQuotaController.mLock) { 709 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 710 } 711 assertEquals(expectedStats, inputStats); 712 713 inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * MINUTE_IN_MILLIS; 714 // Invalid time is now since the start of the session is at the very edge of the window 715 // cutoff time. 716 expectedStats.expirationTimeElapsed = now; 717 expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS; 718 expectedStats.bgJobCountInWindow = 3; 719 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 720 expectedStats.bgJobCountInMaxPeriod = 15; 721 expectedStats.sessionCountInWindow = 1; 722 synchronized (mQuotaController.mLock) { 723 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 724 } 725 assertEquals(expectedStats, inputStats); 726 727 inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS; 728 // Invalid time is now +44 minutes since the earliest session in the window is now-5 729 // minutes. 730 expectedStats.expirationTimeElapsed = now + 44 * MINUTE_IN_MILLIS; 731 expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS; 732 expectedStats.bgJobCountInWindow = 3; 733 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 734 expectedStats.bgJobCountInMaxPeriod = 15; 735 expectedStats.sessionCountInWindow = 1; 736 synchronized (mQuotaController.mLock) { 737 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 738 } 739 assertEquals(expectedStats, inputStats); 740 741 inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS; 742 // Invalid time is now since the session is at the very edge of the window cutoff time. 743 expectedStats.expirationTimeElapsed = now; 744 expectedStats.executionTimeInWindowMs = 5 * MINUTE_IN_MILLIS; 745 expectedStats.bgJobCountInWindow = 4; 746 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 747 expectedStats.bgJobCountInMaxPeriod = 15; 748 expectedStats.sessionCountInWindow = 2; 749 synchronized (mQuotaController.mLock) { 750 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 751 } 752 assertEquals(expectedStats, inputStats); 753 754 inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS; 755 inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 2; 756 // Invalid time is now since the start of the session is at the very edge of the window 757 // cutoff time. 758 expectedStats.expirationTimeElapsed = now; 759 expectedStats.executionTimeInWindowMs = 6 * MINUTE_IN_MILLIS; 760 expectedStats.bgJobCountInWindow = 5; 761 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 762 expectedStats.bgJobCountInMaxPeriod = 15; 763 expectedStats.sessionCountInWindow = 3; 764 expectedStats.inQuotaTimeElapsed = now + 11 * MINUTE_IN_MILLIS; 765 synchronized (mQuotaController.mLock) { 766 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 767 } 768 assertEquals(expectedStats, inputStats); 769 770 inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS; 771 inputStats.jobCountLimit = expectedStats.jobCountLimit = 6; 772 inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100; 773 // Invalid time is now since the session straddles the window cutoff time. 774 expectedStats.expirationTimeElapsed = now; 775 expectedStats.executionTimeInWindowMs = 11 * MINUTE_IN_MILLIS; 776 expectedStats.bgJobCountInWindow = 10; 777 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 778 expectedStats.bgJobCountInMaxPeriod = 15; 779 expectedStats.sessionCountInWindow = 4; 780 expectedStats.inQuotaTimeElapsed = now + 5 * MINUTE_IN_MILLIS; 781 synchronized (mQuotaController.mLock) { 782 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 783 } 784 assertEquals(expectedStats, inputStats); 785 786 inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * HOUR_IN_MILLIS; 787 // Invalid time is now +59 minutes since the earliest session in the window is now-121 788 // minutes. 789 expectedStats.expirationTimeElapsed = now + 59 * MINUTE_IN_MILLIS; 790 expectedStats.executionTimeInWindowMs = 12 * MINUTE_IN_MILLIS; 791 expectedStats.bgJobCountInWindow = 10; 792 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 793 expectedStats.bgJobCountInMaxPeriod = 15; 794 expectedStats.sessionCountInWindow = 4; 795 // App goes under job execution time limit in ~61 minutes, but will be under job count limit 796 // in 65 minutes. 797 expectedStats.inQuotaTimeElapsed = now + 65 * MINUTE_IN_MILLIS; 798 synchronized (mQuotaController.mLock) { 799 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 800 } 801 assertEquals(expectedStats, inputStats); 802 803 inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS; 804 // Invalid time is now since the start of the session is at the very edge of the window 805 // cutoff time. 806 expectedStats.expirationTimeElapsed = now; 807 expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS; 808 expectedStats.bgJobCountInWindow = 15; 809 expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; 810 expectedStats.bgJobCountInMaxPeriod = 15; 811 expectedStats.sessionCountInWindow = 5; 812 expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS + 5 * MINUTE_IN_MILLIS; 813 synchronized (mQuotaController.mLock) { 814 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 815 } 816 assertEquals(expectedStats, inputStats); 817 818 // Make sure expirationTimeElapsed is set correctly when it's dependent on the max period. 819 mQuotaController.getTimingSessions(0, "com.android.test") 820 .add(0, 821 createTimingSession(now - (23 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 3)); 822 inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS; 823 inputStats.jobCountLimit = expectedStats.jobCountLimit = 100; 824 // Invalid time is now +1 hour since the earliest session in the max period is 1 hour 825 // before the end of the max period cutoff time. 826 expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; 827 expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS; 828 expectedStats.bgJobCountInWindow = 15; 829 expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS; 830 expectedStats.bgJobCountInMaxPeriod = 18; 831 expectedStats.sessionCountInWindow = 5; 832 expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS 833 + mQcConstants.IN_QUOTA_BUFFER_MS; 834 synchronized (mQuotaController.mLock) { 835 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 836 } 837 assertEquals(expectedStats, inputStats); 838 839 mQuotaController.getTimingSessions(0, "com.android.test") 840 .add(0, 841 createTimingSession(now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 842 2 * MINUTE_IN_MILLIS, 2)); 843 inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS; 844 // Invalid time is now since the earliest session straddles the max period cutoff time. 845 expectedStats.expirationTimeElapsed = now; 846 expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS; 847 expectedStats.bgJobCountInWindow = 15; 848 expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS; 849 expectedStats.bgJobCountInMaxPeriod = 20; 850 expectedStats.sessionCountInWindow = 5; 851 expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS 852 + mQcConstants.IN_QUOTA_BUFFER_MS; 853 synchronized (mQuotaController.mLock) { 854 mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); 855 } 856 assertEquals(expectedStats, inputStats); 857 } 858 859 @Test testUpdateExecutionStatsLocked_WithTimer()860 public void testUpdateExecutionStatsLocked_WithTimer() { 861 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 862 863 ExecutionStats expectedStats = new ExecutionStats(); 864 ExecutionStats inputStats = new ExecutionStats(); 865 inputStats.allowedTimePerPeriodMs = expectedStats.allowedTimePerPeriodMs = 866 10 * MINUTE_IN_MILLIS; 867 inputStats.windowSizeMs = expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; 868 inputStats.jobCountLimit = expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; 869 inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 870 mQcConstants.MAX_SESSION_COUNT_RARE; 871 // Active timer isn't counted as session yet. 872 expectedStats.sessionCountInWindow = 0; 873 // Timer only, under quota. 874 for (int i = 1; i < mQcConstants.MAX_JOB_COUNT_RARE; ++i) { 875 JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", i); 876 setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window 877 synchronized (mQuotaController.mLock) { 878 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 879 mQuotaController.prepareForExecutionLocked(jobStatus); 880 } 881 advanceElapsedClock(7000); 882 883 expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis(); 884 expectedStats.executionTimeInWindowMs = expectedStats.executionTimeInMaxPeriodMs = 885 7000 * i; 886 expectedStats.bgJobCountInWindow = expectedStats.bgJobCountInMaxPeriod = i; 887 synchronized (mQuotaController.mLock) { 888 mQuotaController.updateExecutionStatsLocked( 889 SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); 890 assertEquals(expectedStats, inputStats); 891 assertTrue(mQuotaController.isWithinQuotaLocked( 892 SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); 893 } 894 assertTrue("Job not ready: " + jobStatus, jobStatus.isReady()); 895 } 896 897 // Add old session. Make sure values are combined correctly. 898 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 899 createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS), 900 10 * MINUTE_IN_MILLIS, 5), false); 901 expectedStats.sessionCountInWindow = 1; 902 903 expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS; 904 expectedStats.executionTimeInWindowMs += 10 * MINUTE_IN_MILLIS; 905 expectedStats.executionTimeInMaxPeriodMs += 10 * MINUTE_IN_MILLIS; 906 expectedStats.bgJobCountInWindow += 5; 907 expectedStats.bgJobCountInMaxPeriod += 5; 908 // Active timer is under quota, so out of quota due to old session. 909 expectedStats.inQuotaTimeElapsed = 910 sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS; 911 synchronized (mQuotaController.mLock) { 912 mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); 913 assertEquals(expectedStats, inputStats); 914 assertFalse( 915 mQuotaController.isWithinQuotaLocked( 916 SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); 917 } 918 919 // Quota should be exceeded due to activity in active timer. 920 JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", 0); 921 setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window 922 synchronized (mQuotaController.mLock) { 923 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 924 mQuotaController.prepareForExecutionLocked(jobStatus); 925 } 926 advanceElapsedClock(10000); 927 928 expectedStats.executionTimeInWindowMs += 10000; 929 expectedStats.executionTimeInMaxPeriodMs += 10000; 930 expectedStats.bgJobCountInWindow++; 931 expectedStats.bgJobCountInMaxPeriod++; 932 // Out of quota due to activity in active timer, so in quota time should be when enough 933 // time has passed since active timer. 934 expectedStats.inQuotaTimeElapsed = 935 sElapsedRealtimeClock.millis() + expectedStats.windowSizeMs; 936 synchronized (mQuotaController.mLock) { 937 mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); 938 assertEquals(expectedStats, inputStats); 939 assertFalse( 940 mQuotaController.isWithinQuotaLocked( 941 SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); 942 assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady()); 943 } 944 } 945 946 /** 947 * Tests that getExecutionStatsLocked returns the correct stats. 948 */ 949 @Test 950 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetExecutionStatsLocked_Values_LegacyDefaultBucketWindowSizes()951 public void testGetExecutionStatsLocked_Values_LegacyDefaultBucketWindowSizes() { 952 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 953 mQuotaController.saveTimingSession(0, "com.android.test", 954 createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - HOUR_IN_MILLIS), 955 10 * MINUTE_IN_MILLIS, 5), false); 956 mQuotaController.saveTimingSession(0, "com.android.test", 957 createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS), 958 10 * MINUTE_IN_MILLIS, 5), false); 959 mQuotaController.saveTimingSession(0, "com.android.test", 960 createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, 961 10 * MINUTE_IN_MILLIS, 5), false); 962 mQuotaController.saveTimingSession(0, "com.android.test", 963 createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 964 965 ExecutionStats expectedStats = new ExecutionStats(); 966 967 // Active 968 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; 969 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; 970 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; 971 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; 972 expectedStats.expirationTimeElapsed = now + 4 * MINUTE_IN_MILLIS; 973 expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS; 974 expectedStats.bgJobCountInWindow = 5; 975 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 976 expectedStats.bgJobCountInMaxPeriod = 20; 977 expectedStats.sessionCountInWindow = 1; 978 synchronized (mQuotaController.mLock) { 979 assertEquals(expectedStats, 980 mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX)); 981 } 982 983 // Working 984 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS; 985 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; 986 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; 987 expectedStats.expirationTimeElapsed = now; 988 expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS; 989 expectedStats.bgJobCountInWindow = 10; 990 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 991 expectedStats.bgJobCountInMaxPeriod = 20; 992 expectedStats.sessionCountInWindow = 2; 993 expectedStats.inQuotaTimeElapsed = now + 3 * MINUTE_IN_MILLIS 994 + mQcConstants.IN_QUOTA_BUFFER_MS; 995 synchronized (mQuotaController.mLock) { 996 assertEquals(expectedStats, 997 mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX)); 998 } 999 1000 // Frequent 1001 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS; 1002 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; 1003 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; 1004 expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; 1005 expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS; 1006 expectedStats.bgJobCountInWindow = 15; 1007 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 1008 expectedStats.bgJobCountInMaxPeriod = 20; 1009 expectedStats.sessionCountInWindow = 3; 1010 expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS 1011 + mQcConstants.IN_QUOTA_BUFFER_MS; 1012 synchronized (mQuotaController.mLock) { 1013 assertEquals(expectedStats, 1014 mQuotaController.getExecutionStatsLocked( 1015 0, "com.android.test", FREQUENT_INDEX)); 1016 } 1017 1018 // Rare 1019 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS; 1020 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; 1021 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; 1022 expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; 1023 expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS; 1024 expectedStats.bgJobCountInWindow = 20; 1025 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 1026 expectedStats.bgJobCountInMaxPeriod = 20; 1027 expectedStats.sessionCountInWindow = 4; 1028 expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS 1029 + mQcConstants.IN_QUOTA_BUFFER_MS; 1030 synchronized (mQuotaController.mLock) { 1031 assertEquals(expectedStats, 1032 mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); 1033 } 1034 } 1035 1036 @Test 1037 @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) 1038 @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS) testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes()1039 public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() { 1040 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 1041 mQuotaController.saveTimingSession(0, "com.android.test", 1042 createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1043 mQuotaController.saveTimingSession(0, "com.android.test", 1044 createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1045 mQuotaController.saveTimingSession(0, "com.android.test", 1046 createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1047 mQuotaController.saveTimingSession(0, "com.android.test", 1048 createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 1049 1050 ExecutionStats expectedStats = new ExecutionStats(); 1051 1052 // Exempted 1053 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; 1054 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS; 1055 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED; 1056 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED; 1057 expectedStats.expirationTimeElapsed = now + 14 * MINUTE_IN_MILLIS; 1058 expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS; 1059 expectedStats.bgJobCountInWindow = 5; 1060 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 1061 expectedStats.bgJobCountInMaxPeriod = 20; 1062 expectedStats.sessionCountInWindow = 1; 1063 synchronized (mQuotaController.mLock) { 1064 assertEquals(expectedStats, 1065 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1066 EXEMPTED_INDEX)); 1067 } 1068 1069 // Active 1070 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; 1071 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; 1072 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; 1073 // There is only one session in the past active bucket window, the empty time for this 1074 // window is the bucket window size - duration of the session. 1075 expectedStats.expirationTimeElapsed = now + 24 * MINUTE_IN_MILLIS; 1076 synchronized (mQuotaController.mLock) { 1077 assertEquals(expectedStats, 1078 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1079 ACTIVE_INDEX)); 1080 } 1081 1082 // Working 1083 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS; 1084 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; 1085 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; 1086 expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; 1087 expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS; 1088 expectedStats.bgJobCountInWindow = 10; 1089 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 1090 expectedStats.bgJobCountInMaxPeriod = 20; 1091 expectedStats.sessionCountInWindow = 2; 1092 expectedStats.inQuotaTimeElapsed = now + 2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS 1093 + mQcConstants.IN_QUOTA_BUFFER_MS; 1094 synchronized (mQuotaController.mLock) { 1095 assertEquals(expectedStats, 1096 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1097 WORKING_INDEX)); 1098 } 1099 1100 // Frequent 1101 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS; 1102 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; 1103 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; 1104 expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; 1105 expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS; 1106 expectedStats.bgJobCountInWindow = 15; 1107 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 1108 expectedStats.bgJobCountInMaxPeriod = 20; 1109 expectedStats.sessionCountInWindow = 3; 1110 expectedStats.inQuotaTimeElapsed = now + 10 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS 1111 + mQcConstants.IN_QUOTA_BUFFER_MS; 1112 synchronized (mQuotaController.mLock) { 1113 assertEquals(expectedStats, 1114 mQuotaController.getExecutionStatsLocked( 1115 0, "com.android.test", FREQUENT_INDEX)); 1116 } 1117 1118 // Rare 1119 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS; 1120 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; 1121 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; 1122 expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; 1123 expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS; 1124 expectedStats.bgJobCountInWindow = 20; 1125 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 1126 expectedStats.bgJobCountInMaxPeriod = 20; 1127 expectedStats.sessionCountInWindow = 4; 1128 expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS 1129 + mQcConstants.IN_QUOTA_BUFFER_MS; 1130 synchronized (mQuotaController.mLock) { 1131 assertEquals(expectedStats, 1132 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1133 RARE_INDEX)); 1134 } 1135 } 1136 1137 @Test 1138 @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS, 1139 Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS}) testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes_Tuning()1140 public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes_Tuning() { 1141 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 1142 mQuotaController.saveTimingSession(0, "com.android.test", 1143 createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1144 mQuotaController.saveTimingSession(0, "com.android.test", 1145 createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1146 mQuotaController.saveTimingSession(0, "com.android.test", 1147 createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1148 mQuotaController.saveTimingSession(0, "com.android.test", 1149 createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 1150 1151 ExecutionStats expectedStats = new ExecutionStats(); 1152 1153 // Exempted 1154 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; 1155 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS; 1156 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED; 1157 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED; 1158 expectedStats.expirationTimeElapsed = now + 34 * MINUTE_IN_MILLIS; 1159 expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS; 1160 expectedStats.bgJobCountInWindow = 5; 1161 expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; 1162 expectedStats.bgJobCountInMaxPeriod = 20; 1163 expectedStats.sessionCountInWindow = 1; 1164 synchronized (mQuotaController.mLock) { 1165 assertEquals(expectedStats, 1166 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1167 EXEMPTED_INDEX)); 1168 } 1169 1170 // Active 1171 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; 1172 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; 1173 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; 1174 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; 1175 // There is only one session in the past active bucket window, the empty time for this 1176 // window is the bucket window size - duration of the session. 1177 expectedStats.expirationTimeElapsed = now + 54 * MINUTE_IN_MILLIS; 1178 synchronized (mQuotaController.mLock) { 1179 assertEquals(expectedStats, 1180 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1181 ACTIVE_INDEX)); 1182 } 1183 } 1184 1185 /** 1186 * Tests that getExecutionStatsLocked returns the correct stats soon after device startup. 1187 */ 1188 @Test 1189 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetExecutionStatsLocked_Values_BeginningOfTime_LegacyDefaultBucketWindowSizes()1190 public void testGetExecutionStatsLocked_Values_BeginningOfTime_LegacyDefaultBucketWindowSizes() { 1191 // Set time to 3 minutes after boot. 1192 advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); 1193 advanceElapsedClock(3 * MINUTE_IN_MILLIS); 1194 1195 mQuotaController.saveTimingSession(0, "com.android.test", 1196 createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false); 1197 1198 ExecutionStats expectedStats = new ExecutionStats(); 1199 1200 // Active 1201 expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS; 1202 expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS; 1203 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; 1204 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; 1205 expectedStats.expirationTimeElapsed = 11 * MINUTE_IN_MILLIS; 1206 expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS; 1207 expectedStats.bgJobCountInWindow = 2; 1208 expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS; 1209 expectedStats.bgJobCountInMaxPeriod = 2; 1210 expectedStats.sessionCountInWindow = 1; 1211 synchronized (mQuotaController.mLock) { 1212 assertEquals(expectedStats, 1213 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1214 ACTIVE_INDEX)); 1215 } 1216 1217 // Working 1218 expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS; 1219 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; 1220 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; 1221 expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1222 synchronized (mQuotaController.mLock) { 1223 assertEquals(expectedStats, 1224 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1225 WORKING_INDEX)); 1226 } 1227 1228 // Frequent 1229 expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS; 1230 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; 1231 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; 1232 expectedStats.expirationTimeElapsed = 8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1233 synchronized (mQuotaController.mLock) { 1234 assertEquals(expectedStats, 1235 mQuotaController.getExecutionStatsLocked( 1236 0, "com.android.test", FREQUENT_INDEX)); 1237 } 1238 1239 // Rare 1240 expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; 1241 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; 1242 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; 1243 expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1244 synchronized (mQuotaController.mLock) { 1245 assertEquals(expectedStats, 1246 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1247 RARE_INDEX)); 1248 } 1249 } 1250 1251 @Test 1252 @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) 1253 @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS) testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes()1254 public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() { 1255 // Set time to 3 minutes after boot. 1256 advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); 1257 advanceElapsedClock(3 * MINUTE_IN_MILLIS); 1258 1259 mQuotaController.saveTimingSession(0, "com.android.test", 1260 createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false); 1261 1262 ExecutionStats expectedStats = new ExecutionStats(); 1263 1264 // Exempted 1265 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; 1266 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS; 1267 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED; 1268 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED; 1269 expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS; 1270 expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS; 1271 expectedStats.bgJobCountInWindow = 2; 1272 expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS; 1273 expectedStats.bgJobCountInMaxPeriod = 2; 1274 expectedStats.sessionCountInWindow = 1; 1275 synchronized (mQuotaController.mLock) { 1276 assertEquals(expectedStats, 1277 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1278 EXEMPTED_INDEX)); 1279 } 1280 1281 // Active 1282 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; 1283 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; 1284 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; 1285 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; 1286 expectedStats.expirationTimeElapsed = 20 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS; 1287 synchronized (mQuotaController.mLock) { 1288 assertEquals(expectedStats, 1289 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1290 ACTIVE_INDEX)); 1291 } 1292 1293 // Working 1294 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS; 1295 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; 1296 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; 1297 expectedStats.expirationTimeElapsed = 4 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1298 synchronized (mQuotaController.mLock) { 1299 assertEquals(expectedStats, 1300 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1301 WORKING_INDEX)); 1302 } 1303 1304 // Frequent 1305 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS; 1306 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; 1307 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; 1308 expectedStats.expirationTimeElapsed = 12 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1309 synchronized (mQuotaController.mLock) { 1310 assertEquals(expectedStats, 1311 mQuotaController.getExecutionStatsLocked( 1312 0, "com.android.test", FREQUENT_INDEX)); 1313 } 1314 1315 // Rare 1316 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS; 1317 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; 1318 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; 1319 expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1320 synchronized (mQuotaController.mLock) { 1321 assertEquals(expectedStats, 1322 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1323 RARE_INDEX)); 1324 } 1325 } 1326 1327 @Test 1328 @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS, 1329 Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS}) testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes_Tunning()1330 public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes_Tunning() { 1331 // Set time to 3 minutes after boot. 1332 advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); 1333 advanceElapsedClock(3 * MINUTE_IN_MILLIS); 1334 1335 mQuotaController.saveTimingSession(0, "com.android.test", 1336 createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false); 1337 1338 ExecutionStats expectedStats = new ExecutionStats(); 1339 1340 // Exempted 1341 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; 1342 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS; 1343 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED; 1344 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED; 1345 expectedStats.expirationTimeElapsed = 30 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS; 1346 expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS; 1347 expectedStats.bgJobCountInWindow = 2; 1348 expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS; 1349 expectedStats.bgJobCountInMaxPeriod = 2; 1350 expectedStats.sessionCountInWindow = 1; 1351 synchronized (mQuotaController.mLock) { 1352 assertEquals(expectedStats, 1353 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1354 EXEMPTED_INDEX)); 1355 } 1356 1357 // Active 1358 expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; 1359 expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; 1360 expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; 1361 expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; 1362 expectedStats.expirationTimeElapsed = 50 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS; 1363 synchronized (mQuotaController.mLock) { 1364 assertEquals(expectedStats, 1365 mQuotaController.getExecutionStatsLocked(0, "com.android.test", 1366 ACTIVE_INDEX)); 1367 } 1368 } 1369 1370 /** 1371 * Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing. 1372 */ 1373 @Test 1374 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetExecutionStatsLocked_CoalescingSessions_LegacyDefaultBucketWindowSizes()1375 public void testGetExecutionStatsLocked_CoalescingSessions_LegacyDefaultBucketWindowSizes() { 1376 for (int i = 0; i < 10; ++i) { 1377 mQuotaController.saveTimingSession(0, "com.android.test", 1378 createTimingSession( 1379 JobSchedulerService.sElapsedRealtimeClock.millis(), 1380 5 * MINUTE_IN_MILLIS, 5), false); 1381 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 1382 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 1383 for (int j = 0; j < 5; ++j) { 1384 mQuotaController.saveTimingSession(0, "com.android.test", 1385 createTimingSession( 1386 JobSchedulerService.sElapsedRealtimeClock.millis(), 1387 MINUTE_IN_MILLIS, 2), false); 1388 advanceElapsedClock(MINUTE_IN_MILLIS); 1389 advanceElapsedClock(54 * SECOND_IN_MILLIS); 1390 mQuotaController.saveTimingSession(0, "com.android.test", 1391 createTimingSession( 1392 JobSchedulerService.sElapsedRealtimeClock.millis(), 1393 500, 1), false); 1394 advanceElapsedClock(500); 1395 advanceElapsedClock(400); 1396 mQuotaController.saveTimingSession(0, "com.android.test", 1397 createTimingSession( 1398 JobSchedulerService.sElapsedRealtimeClock.millis(), 1399 100, 1), false); 1400 advanceElapsedClock(100); 1401 advanceElapsedClock(5 * SECOND_IN_MILLIS); 1402 } 1403 advanceElapsedClock(40 * MINUTE_IN_MILLIS); 1404 } 1405 1406 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0); 1407 1408 synchronized (mQuotaController.mLock) { 1409 mQuotaController.invalidateAllExecutionStatsLocked(); 1410 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1411 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1412 assertEquals(32, mQuotaController.getExecutionStatsLocked( 1413 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1414 assertEquals(128, mQuotaController.getExecutionStatsLocked( 1415 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1416 assertEquals(160, mQuotaController.getExecutionStatsLocked( 1417 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1418 } 1419 1420 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500); 1421 1422 synchronized (mQuotaController.mLock) { 1423 mQuotaController.invalidateAllExecutionStatsLocked(); 1424 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1425 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1426 assertEquals(22, mQuotaController.getExecutionStatsLocked( 1427 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1428 assertEquals(88, mQuotaController.getExecutionStatsLocked( 1429 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1430 assertEquals(110, mQuotaController.getExecutionStatsLocked( 1431 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1432 } 1433 1434 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000); 1435 1436 synchronized (mQuotaController.mLock) { 1437 mQuotaController.invalidateAllExecutionStatsLocked(); 1438 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1439 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1440 assertEquals(22, mQuotaController.getExecutionStatsLocked( 1441 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1442 assertEquals(88, mQuotaController.getExecutionStatsLocked( 1443 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1444 assertEquals(110, mQuotaController.getExecutionStatsLocked( 1445 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1446 } 1447 1448 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1449 5 * SECOND_IN_MILLIS); 1450 1451 synchronized (mQuotaController.mLock) { 1452 mQuotaController.invalidateAllExecutionStatsLocked(); 1453 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1454 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1455 assertEquals(14, mQuotaController.getExecutionStatsLocked( 1456 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1457 assertEquals(56, mQuotaController.getExecutionStatsLocked( 1458 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1459 assertEquals(70, mQuotaController.getExecutionStatsLocked( 1460 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1461 } 1462 1463 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1464 MINUTE_IN_MILLIS); 1465 1466 synchronized (mQuotaController.mLock) { 1467 mQuotaController.invalidateAllExecutionStatsLocked(); 1468 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1469 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1470 assertEquals(4, mQuotaController.getExecutionStatsLocked( 1471 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1472 assertEquals(16, mQuotaController.getExecutionStatsLocked( 1473 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1474 assertEquals(20, mQuotaController.getExecutionStatsLocked( 1475 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1476 } 1477 1478 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1479 5 * MINUTE_IN_MILLIS); 1480 1481 synchronized (mQuotaController.mLock) { 1482 mQuotaController.invalidateAllExecutionStatsLocked(); 1483 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1484 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1485 assertEquals(2, mQuotaController.getExecutionStatsLocked( 1486 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1487 assertEquals(8, mQuotaController.getExecutionStatsLocked( 1488 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1489 assertEquals(10, mQuotaController.getExecutionStatsLocked( 1490 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1491 } 1492 1493 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1494 15 * MINUTE_IN_MILLIS); 1495 1496 synchronized (mQuotaController.mLock) { 1497 mQuotaController.invalidateAllExecutionStatsLocked(); 1498 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1499 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1500 assertEquals(2, mQuotaController.getExecutionStatsLocked( 1501 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1502 assertEquals(8, mQuotaController.getExecutionStatsLocked( 1503 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1504 assertEquals(10, mQuotaController.getExecutionStatsLocked( 1505 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1506 } 1507 1508 // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference 1509 // between an hour and 15 minutes. 1510 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS); 1511 1512 synchronized (mQuotaController.mLock) { 1513 mQuotaController.invalidateAllExecutionStatsLocked(); 1514 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1515 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1516 assertEquals(2, mQuotaController.getExecutionStatsLocked( 1517 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1518 assertEquals(8, mQuotaController.getExecutionStatsLocked( 1519 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1520 assertEquals(10, mQuotaController.getExecutionStatsLocked( 1521 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1522 } 1523 } 1524 1525 @Test 1526 @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) 1527 @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS) testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes()1528 public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() { 1529 for (int i = 0; i < 20; ++i) { 1530 mQuotaController.saveTimingSession(0, "com.android.test", 1531 createTimingSession( 1532 JobSchedulerService.sElapsedRealtimeClock.millis(), 1533 5 * MINUTE_IN_MILLIS, 5), false); 1534 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 1535 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 1536 for (int j = 0; j < 5; ++j) { 1537 mQuotaController.saveTimingSession(0, "com.android.test", 1538 createTimingSession( 1539 JobSchedulerService.sElapsedRealtimeClock.millis(), 1540 MINUTE_IN_MILLIS, 2), false); 1541 advanceElapsedClock(MINUTE_IN_MILLIS); 1542 advanceElapsedClock(54 * SECOND_IN_MILLIS); 1543 mQuotaController.saveTimingSession(0, "com.android.test", 1544 createTimingSession( 1545 JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false); 1546 advanceElapsedClock(500); 1547 advanceElapsedClock(400); 1548 mQuotaController.saveTimingSession(0, "com.android.test", 1549 createTimingSession( 1550 JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false); 1551 advanceElapsedClock(100); 1552 advanceElapsedClock(5 * SECOND_IN_MILLIS); 1553 } 1554 advanceElapsedClock(40 * MINUTE_IN_MILLIS); 1555 } 1556 1557 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0); 1558 1559 synchronized (mQuotaController.mLock) { 1560 mQuotaController.invalidateAllExecutionStatsLocked(); 1561 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1562 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1563 assertEquals(64, mQuotaController.getExecutionStatsLocked( 1564 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1565 assertEquals(192, mQuotaController.getExecutionStatsLocked( 1566 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1567 assertEquals(320, mQuotaController.getExecutionStatsLocked( 1568 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1569 } 1570 1571 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500); 1572 1573 synchronized (mQuotaController.mLock) { 1574 mQuotaController.invalidateAllExecutionStatsLocked(); 1575 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1576 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1577 // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced 1578 assertEquals(44, mQuotaController.getExecutionStatsLocked( 1579 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1580 // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced 1581 assertEquals(132, mQuotaController.getExecutionStatsLocked( 1582 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1583 // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced 1584 assertEquals(220, mQuotaController.getExecutionStatsLocked( 1585 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1586 } 1587 1588 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000); 1589 1590 synchronized (mQuotaController.mLock) { 1591 mQuotaController.invalidateAllExecutionStatsLocked(); 1592 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1593 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1594 assertEquals(44, mQuotaController.getExecutionStatsLocked( 1595 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1596 assertEquals(132, mQuotaController.getExecutionStatsLocked( 1597 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1598 assertEquals(220, mQuotaController.getExecutionStatsLocked( 1599 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1600 } 1601 1602 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1603 5 * SECOND_IN_MILLIS); 1604 1605 synchronized (mQuotaController.mLock) { 1606 mQuotaController.invalidateAllExecutionStatsLocked(); 1607 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1608 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1609 // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced 1610 assertEquals(28, mQuotaController.getExecutionStatsLocked( 1611 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1612 // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced 1613 assertEquals(84, mQuotaController.getExecutionStatsLocked( 1614 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1615 // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced 1616 assertEquals(140, mQuotaController.getExecutionStatsLocked( 1617 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1618 } 1619 1620 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1621 MINUTE_IN_MILLIS); 1622 1623 // Only two TimingSessions there for every hour. 1624 synchronized (mQuotaController.mLock) { 1625 mQuotaController.invalidateAllExecutionStatsLocked(); 1626 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1627 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1628 assertEquals(8, mQuotaController.getExecutionStatsLocked( 1629 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1630 assertEquals(24, mQuotaController.getExecutionStatsLocked( 1631 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1632 assertEquals(40, mQuotaController.getExecutionStatsLocked( 1633 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1634 } 1635 1636 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1637 5 * MINUTE_IN_MILLIS); 1638 1639 // Only one TimingSessions there for every hour 1640 synchronized (mQuotaController.mLock) { 1641 mQuotaController.invalidateAllExecutionStatsLocked(); 1642 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1643 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1644 assertEquals(4, mQuotaController.getExecutionStatsLocked( 1645 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1646 assertEquals(12, mQuotaController.getExecutionStatsLocked( 1647 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1648 assertEquals(20, mQuotaController.getExecutionStatsLocked( 1649 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1650 } 1651 1652 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1653 15 * MINUTE_IN_MILLIS); 1654 1655 synchronized (mQuotaController.mLock) { 1656 mQuotaController.invalidateAllExecutionStatsLocked(); 1657 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1658 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1659 assertEquals(4, mQuotaController.getExecutionStatsLocked( 1660 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1661 assertEquals(12, mQuotaController.getExecutionStatsLocked( 1662 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1663 assertEquals(20, mQuotaController.getExecutionStatsLocked( 1664 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1665 } 1666 1667 // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference 1668 // between an hour and 15 minutes. 1669 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS); 1670 1671 synchronized (mQuotaController.mLock) { 1672 mQuotaController.invalidateAllExecutionStatsLocked(); 1673 assertEquals(0, mQuotaController.getExecutionStatsLocked( 1674 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1675 assertEquals(4, mQuotaController.getExecutionStatsLocked( 1676 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1677 assertEquals(12, mQuotaController.getExecutionStatsLocked( 1678 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1679 assertEquals(20, mQuotaController.getExecutionStatsLocked( 1680 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1681 } 1682 } 1683 1684 @Test 1685 @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS, 1686 Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS}) testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes_Tuning()1687 public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes_Tuning() { 1688 for (int i = 0; i < 20; ++i) { 1689 mQuotaController.saveTimingSession(0, "com.android.test", 1690 createTimingSession( 1691 JobSchedulerService.sElapsedRealtimeClock.millis(), 1692 5 * MINUTE_IN_MILLIS, 5), false); 1693 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 1694 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 1695 for (int j = 0; j < 5; ++j) { 1696 mQuotaController.saveTimingSession(0, "com.android.test", 1697 createTimingSession( 1698 JobSchedulerService.sElapsedRealtimeClock.millis(), 1699 MINUTE_IN_MILLIS, 2), false); 1700 advanceElapsedClock(MINUTE_IN_MILLIS); 1701 advanceElapsedClock(54 * SECOND_IN_MILLIS); 1702 mQuotaController.saveTimingSession(0, "com.android.test", 1703 createTimingSession( 1704 JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false); 1705 advanceElapsedClock(500); 1706 advanceElapsedClock(400); 1707 mQuotaController.saveTimingSession(0, "com.android.test", 1708 createTimingSession( 1709 JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false); 1710 advanceElapsedClock(100); 1711 advanceElapsedClock(5 * SECOND_IN_MILLIS); 1712 } 1713 advanceElapsedClock(40 * MINUTE_IN_MILLIS); 1714 } 1715 1716 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0); 1717 1718 synchronized (mQuotaController.mLock) { 1719 mQuotaController.invalidateAllExecutionStatsLocked(); 1720 assertEquals(16, mQuotaController.getExecutionStatsLocked( 1721 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1722 assertEquals(64, mQuotaController.getExecutionStatsLocked( 1723 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1724 assertEquals(192, mQuotaController.getExecutionStatsLocked( 1725 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1726 assertEquals(320, mQuotaController.getExecutionStatsLocked( 1727 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1728 } 1729 1730 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500); 1731 1732 synchronized (mQuotaController.mLock) { 1733 mQuotaController.invalidateAllExecutionStatsLocked(); 1734 assertEquals(11, mQuotaController.getExecutionStatsLocked( 1735 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1736 // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced 1737 assertEquals(44, mQuotaController.getExecutionStatsLocked( 1738 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1739 // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced 1740 assertEquals(132, mQuotaController.getExecutionStatsLocked( 1741 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1742 // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced 1743 assertEquals(220, mQuotaController.getExecutionStatsLocked( 1744 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1745 } 1746 1747 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000); 1748 1749 synchronized (mQuotaController.mLock) { 1750 mQuotaController.invalidateAllExecutionStatsLocked(); 1751 assertEquals(11, mQuotaController.getExecutionStatsLocked( 1752 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1753 assertEquals(44, mQuotaController.getExecutionStatsLocked( 1754 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1755 assertEquals(132, mQuotaController.getExecutionStatsLocked( 1756 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1757 assertEquals(220, mQuotaController.getExecutionStatsLocked( 1758 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1759 } 1760 1761 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1762 5 * SECOND_IN_MILLIS); 1763 1764 synchronized (mQuotaController.mLock) { 1765 mQuotaController.invalidateAllExecutionStatsLocked(); 1766 assertEquals(7, mQuotaController.getExecutionStatsLocked( 1767 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1768 // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced 1769 assertEquals(28, mQuotaController.getExecutionStatsLocked( 1770 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1771 // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced 1772 assertEquals(84, mQuotaController.getExecutionStatsLocked( 1773 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1774 // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced 1775 assertEquals(140, mQuotaController.getExecutionStatsLocked( 1776 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1777 } 1778 1779 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1780 MINUTE_IN_MILLIS); 1781 1782 // Only two TimingSessions there for every hour. 1783 synchronized (mQuotaController.mLock) { 1784 mQuotaController.invalidateAllExecutionStatsLocked(); 1785 assertEquals(2, mQuotaController.getExecutionStatsLocked( 1786 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1787 assertEquals(8, mQuotaController.getExecutionStatsLocked( 1788 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1789 assertEquals(24, mQuotaController.getExecutionStatsLocked( 1790 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1791 assertEquals(40, mQuotaController.getExecutionStatsLocked( 1792 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1793 } 1794 1795 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1796 5 * MINUTE_IN_MILLIS); 1797 1798 // Only one TimingSessions there for every hour 1799 synchronized (mQuotaController.mLock) { 1800 mQuotaController.invalidateAllExecutionStatsLocked(); 1801 assertEquals(1, mQuotaController.getExecutionStatsLocked( 1802 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1803 assertEquals(4, mQuotaController.getExecutionStatsLocked( 1804 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1805 assertEquals(12, mQuotaController.getExecutionStatsLocked( 1806 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1807 assertEquals(20, mQuotaController.getExecutionStatsLocked( 1808 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1809 } 1810 1811 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1812 15 * MINUTE_IN_MILLIS); 1813 1814 synchronized (mQuotaController.mLock) { 1815 mQuotaController.invalidateAllExecutionStatsLocked(); 1816 assertEquals(1, mQuotaController.getExecutionStatsLocked( 1817 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1818 assertEquals(4, mQuotaController.getExecutionStatsLocked( 1819 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1820 assertEquals(12, mQuotaController.getExecutionStatsLocked( 1821 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1822 assertEquals(20, mQuotaController.getExecutionStatsLocked( 1823 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1824 } 1825 1826 // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference 1827 // between an hour and 15 minutes. 1828 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS); 1829 1830 synchronized (mQuotaController.mLock) { 1831 mQuotaController.invalidateAllExecutionStatsLocked(); 1832 assertEquals(1, mQuotaController.getExecutionStatsLocked( 1833 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow); 1834 assertEquals(4, mQuotaController.getExecutionStatsLocked( 1835 0, "com.android.test", WORKING_INDEX).sessionCountInWindow); 1836 assertEquals(12, mQuotaController.getExecutionStatsLocked( 1837 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow); 1838 assertEquals(20, mQuotaController.getExecutionStatsLocked( 1839 0, "com.android.test", RARE_INDEX).sessionCountInWindow); 1840 } 1841 } 1842 1843 /** 1844 * Tests that getExecutionStatsLocked properly caches the stats and returns the cached object. 1845 */ 1846 @Test testGetExecutionStatsLocked_Caching()1847 public void testGetExecutionStatsLocked_Caching() { 1848 spyOn(mQuotaController); 1849 doNothing().when(mQuotaController).invalidateAllExecutionStatsLocked(); 1850 1851 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 1852 mQuotaController.saveTimingSession(0, "com.android.test", 1853 createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1854 mQuotaController.saveTimingSession(0, "com.android.test", 1855 createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); 1856 mQuotaController.saveTimingSession(0, "com.android.test", 1857 createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, 1858 10 * MINUTE_IN_MILLIS, 5), false); 1859 mQuotaController.saveTimingSession(0, "com.android.test", 1860 createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 1861 final ExecutionStats originalStatsActive; 1862 final ExecutionStats originalStatsWorking; 1863 final ExecutionStats originalStatsFrequent; 1864 final ExecutionStats originalStatsRare; 1865 synchronized (mQuotaController.mLock) { 1866 originalStatsActive = mQuotaController.getExecutionStatsLocked( 1867 0, "com.android.test", ACTIVE_INDEX); 1868 originalStatsWorking = mQuotaController.getExecutionStatsLocked( 1869 0, "com.android.test", WORKING_INDEX); 1870 originalStatsFrequent = mQuotaController.getExecutionStatsLocked( 1871 0, "com.android.test", FREQUENT_INDEX); 1872 originalStatsRare = mQuotaController.getExecutionStatsLocked( 1873 0, "com.android.test", RARE_INDEX); 1874 } 1875 1876 // Advance clock so that the working stats shouldn't be the same. 1877 advanceElapsedClock(MINUTE_IN_MILLIS); 1878 // Change frequent bucket size so that the stats need to be recalculated. 1879 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 6 * HOUR_IN_MILLIS); 1880 1881 ExecutionStats expectedStats = new ExecutionStats(); 1882 expectedStats.allowedTimePerPeriodMs = originalStatsActive.allowedTimePerPeriodMs; 1883 expectedStats.windowSizeMs = originalStatsActive.windowSizeMs; 1884 expectedStats.jobCountLimit = originalStatsActive.jobCountLimit; 1885 expectedStats.sessionCountLimit = originalStatsActive.sessionCountLimit; 1886 expectedStats.expirationTimeElapsed = originalStatsActive.expirationTimeElapsed; 1887 expectedStats.executionTimeInWindowMs = originalStatsActive.executionTimeInWindowMs; 1888 expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow; 1889 expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs; 1890 expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod; 1891 expectedStats.sessionCountInWindow = originalStatsActive.sessionCountInWindow; 1892 expectedStats.inQuotaTimeElapsed = originalStatsActive.inQuotaTimeElapsed; 1893 final ExecutionStats newStatsActive; 1894 synchronized (mQuotaController.mLock) { 1895 newStatsActive = mQuotaController.getExecutionStatsLocked( 1896 0, "com.android.test", ACTIVE_INDEX); 1897 } 1898 // Stats for the same bucket should use the same object. 1899 assertTrue(originalStatsActive == newStatsActive); 1900 assertEquals(expectedStats, newStatsActive); 1901 1902 expectedStats.allowedTimePerPeriodMs = originalStatsWorking.allowedTimePerPeriodMs; 1903 expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs; 1904 expectedStats.jobCountLimit = originalStatsWorking.jobCountLimit; 1905 expectedStats.sessionCountLimit = originalStatsWorking.sessionCountLimit; 1906 expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed; 1907 expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs; 1908 expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow; 1909 expectedStats.sessionCountInWindow = originalStatsWorking.sessionCountInWindow; 1910 expectedStats.inQuotaTimeElapsed = originalStatsWorking.inQuotaTimeElapsed; 1911 final ExecutionStats newStatsWorking; 1912 synchronized (mQuotaController.mLock) { 1913 newStatsWorking = mQuotaController.getExecutionStatsLocked( 1914 0, "com.android.test", WORKING_INDEX); 1915 } 1916 assertTrue(originalStatsWorking == newStatsWorking); 1917 assertNotEquals(expectedStats, newStatsWorking); 1918 1919 expectedStats.allowedTimePerPeriodMs = originalStatsFrequent.allowedTimePerPeriodMs; 1920 expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs; 1921 expectedStats.jobCountLimit = originalStatsFrequent.jobCountLimit; 1922 expectedStats.sessionCountLimit = originalStatsFrequent.sessionCountLimit; 1923 expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed; 1924 expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs; 1925 expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow; 1926 expectedStats.sessionCountInWindow = originalStatsFrequent.sessionCountInWindow; 1927 expectedStats.inQuotaTimeElapsed = originalStatsFrequent.inQuotaTimeElapsed; 1928 final ExecutionStats newStatsFrequent; 1929 synchronized (mQuotaController.mLock) { 1930 newStatsFrequent = mQuotaController.getExecutionStatsLocked( 1931 0, "com.android.test", FREQUENT_INDEX); 1932 } 1933 assertTrue(originalStatsFrequent == newStatsFrequent); 1934 assertNotEquals(expectedStats, newStatsFrequent); 1935 1936 expectedStats.allowedTimePerPeriodMs = originalStatsRare.allowedTimePerPeriodMs; 1937 expectedStats.windowSizeMs = originalStatsRare.windowSizeMs; 1938 expectedStats.jobCountLimit = originalStatsRare.jobCountLimit; 1939 expectedStats.sessionCountLimit = originalStatsRare.sessionCountLimit; 1940 expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed; 1941 expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs; 1942 expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow; 1943 expectedStats.sessionCountInWindow = originalStatsRare.sessionCountInWindow; 1944 expectedStats.inQuotaTimeElapsed = originalStatsRare.inQuotaTimeElapsed; 1945 final ExecutionStats newStatsRare; 1946 synchronized (mQuotaController.mLock) { 1947 newStatsRare = mQuotaController.getExecutionStatsLocked( 1948 0, "com.android.test", RARE_INDEX); 1949 } 1950 assertTrue(originalStatsRare == newStatsRare); 1951 assertEquals(expectedStats, newStatsRare); 1952 } 1953 1954 @Test testGetMaxJobExecutionTimeLocked_Regular()1955 public void testGetMaxJobExecutionTimeLocked_Regular() { 1956 mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, 1957 createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 1958 3 * MINUTE_IN_MILLIS, 5), false); 1959 final long timeUntilQuotaConsumedMs = 7 * MINUTE_IN_MILLIS; 1960 JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); 1961 JobStatus jobHigh = createJobStatus("testGetMaxJobExecutionTimeLocked", 1962 createJobInfoBuilder(2).setPriority(JobInfo.PRIORITY_HIGH).build()); 1963 setStandbyBucket(RARE_INDEX, job); 1964 setStandbyBucket(RARE_INDEX, jobHigh); 1965 1966 setCharging(); 1967 synchronized (mQuotaController.mLock) { 1968 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 1969 mQuotaController.getMaxJobExecutionTimeMsLocked((job))); 1970 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 1971 mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); 1972 } 1973 1974 setDischarging(); 1975 setProcessState(getProcessStateQuotaFreeThreshold()); 1976 synchronized (mQuotaController.mLock) { 1977 assertEquals(timeUntilQuotaConsumedMs, 1978 mQuotaController.getMaxJobExecutionTimeMsLocked((job))); 1979 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 1980 mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); 1981 } 1982 1983 // Top-started job 1984 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 1985 // Top-stared jobs are out of quota enforcement. 1986 setProcessState(ActivityManager.PROCESS_STATE_TOP); 1987 synchronized (mQuotaController.mLock) { 1988 trackJobs(job, jobHigh); 1989 mQuotaController.prepareForExecutionLocked(job); 1990 mQuotaController.prepareForExecutionLocked(jobHigh); 1991 } 1992 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 1993 synchronized (mQuotaController.mLock) { 1994 assertEquals(timeUntilQuotaConsumedMs, 1995 mQuotaController.getMaxJobExecutionTimeMsLocked((job))); 1996 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 1997 mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); 1998 mQuotaController.maybeStopTrackingJobLocked(job, null); 1999 mQuotaController.maybeStopTrackingJobLocked(jobHigh, null); 2000 } 2001 2002 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2003 synchronized (mQuotaController.mLock) { 2004 assertEquals(timeUntilQuotaConsumedMs, 2005 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2006 assertEquals(timeUntilQuotaConsumedMs, 2007 mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh)); 2008 } 2009 2010 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2011 // Quota is enforced for top-started job after the process leaves TOP/BTOP state. 2012 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2013 synchronized (mQuotaController.mLock) { 2014 trackJobs(job, jobHigh); 2015 mQuotaController.prepareForExecutionLocked(job); 2016 mQuotaController.prepareForExecutionLocked(jobHigh); 2017 } 2018 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2019 synchronized (mQuotaController.mLock) { 2020 assertEquals(timeUntilQuotaConsumedMs, 2021 mQuotaController.getMaxJobExecutionTimeMsLocked((job))); 2022 assertEquals(timeUntilQuotaConsumedMs, 2023 mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); 2024 mQuotaController.maybeStopTrackingJobLocked(job, null); 2025 mQuotaController.maybeStopTrackingJobLocked(jobHigh, null); 2026 } 2027 2028 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2029 synchronized (mQuotaController.mLock) { 2030 assertEquals(timeUntilQuotaConsumedMs, 2031 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2032 assertEquals(timeUntilQuotaConsumedMs, 2033 mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh)); 2034 } 2035 } 2036 2037 @Test testGetMaxJobExecutionTimeLocked_Regular_ImportantWhileForeground()2038 public void testGetMaxJobExecutionTimeLocked_Regular_ImportantWhileForeground() { 2039 mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, 2040 createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 2041 3 * MINUTE_IN_MILLIS, 5), false); 2042 final long timeUntilQuotaConsumedMs = 7 * MINUTE_IN_MILLIS; 2043 JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); 2044 //noinspection deprecation 2045 JobStatus jobDefIWF; 2046 mSetFlagsRule.disableFlags(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND); 2047 jobDefIWF = createJobStatus("testGetMaxJobExecutionTimeLocked_IWF", 2048 createJobInfoBuilder(1) 2049 .setImportantWhileForeground(true) 2050 .setPriority(JobInfo.PRIORITY_DEFAULT) 2051 .build()); 2052 2053 setStandbyBucket(RARE_INDEX, jobDefIWF); 2054 setCharging(); 2055 synchronized (mQuotaController.mLock) { 2056 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 2057 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2058 } 2059 2060 setDischarging(); 2061 setProcessState(getProcessStateQuotaFreeThreshold()); 2062 synchronized (mQuotaController.mLock) { 2063 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 2064 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2065 } 2066 2067 // Top-started job 2068 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2069 // Top-stared jobs are out of quota enforcement. 2070 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2071 synchronized (mQuotaController.mLock) { 2072 trackJobs(jobDefIWF); 2073 mQuotaController.prepareForExecutionLocked(jobDefIWF); 2074 } 2075 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2076 synchronized (mQuotaController.mLock) { 2077 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 2078 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2079 mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null); 2080 } 2081 2082 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2083 synchronized (mQuotaController.mLock) { 2084 assertEquals(timeUntilQuotaConsumedMs, 2085 mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF)); 2086 } 2087 2088 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2089 // Quota is enforced for top-started job after the process leaves TOP/BTOP state. 2090 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2091 synchronized (mQuotaController.mLock) { 2092 trackJobs(jobDefIWF); 2093 mQuotaController.prepareForExecutionLocked(jobDefIWF); 2094 } 2095 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2096 synchronized (mQuotaController.mLock) { 2097 assertEquals(timeUntilQuotaConsumedMs, 2098 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2099 mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null); 2100 } 2101 2102 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2103 synchronized (mQuotaController.mLock) { 2104 assertEquals(timeUntilQuotaConsumedMs, 2105 mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF)); 2106 } 2107 2108 mSetFlagsRule.enableFlags(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND); 2109 jobDefIWF = createJobStatus("testGetMaxJobExecutionTimeLocked_IWF", 2110 createJobInfoBuilder(1) 2111 .setImportantWhileForeground(true) 2112 .setPriority(JobInfo.PRIORITY_DEFAULT) 2113 .build()); 2114 2115 setStandbyBucket(RARE_INDEX, jobDefIWF); 2116 setCharging(); 2117 synchronized (mQuotaController.mLock) { 2118 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 2119 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2120 } 2121 2122 setDischarging(); 2123 setProcessState(getProcessStateQuotaFreeThreshold()); 2124 synchronized (mQuotaController.mLock) { 2125 assertEquals(timeUntilQuotaConsumedMs, 2126 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2127 } 2128 2129 // Top-started job 2130 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2131 // Top-stared jobs are out of quota enforcement. 2132 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2133 synchronized (mQuotaController.mLock) { 2134 trackJobs(jobDefIWF); 2135 mQuotaController.prepareForExecutionLocked(jobDefIWF); 2136 } 2137 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2138 synchronized (mQuotaController.mLock) { 2139 assertEquals(timeUntilQuotaConsumedMs, 2140 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2141 mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null); 2142 } 2143 2144 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2145 synchronized (mQuotaController.mLock) { 2146 assertEquals(timeUntilQuotaConsumedMs, 2147 mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF)); 2148 } 2149 2150 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2151 // Quota is enforced for top-started job after the process leaves TOP/BTOP state. 2152 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2153 synchronized (mQuotaController.mLock) { 2154 trackJobs(jobDefIWF); 2155 mQuotaController.prepareForExecutionLocked(jobDefIWF); 2156 } 2157 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2158 synchronized (mQuotaController.mLock) { 2159 assertEquals(timeUntilQuotaConsumedMs, 2160 mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); 2161 mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null); 2162 } 2163 2164 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2165 synchronized (mQuotaController.mLock) { 2166 assertEquals(timeUntilQuotaConsumedMs, 2167 mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF)); 2168 } 2169 } 2170 2171 @Test testGetMaxJobExecutionTimeLocked_Regular_Active()2172 public void testGetMaxJobExecutionTimeLocked_Regular_Active() { 2173 JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked_Regular_Active", 0); 2174 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 2175 10 * MINUTE_IN_MILLIS); 2176 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 10 * MINUTE_IN_MILLIS); 2177 setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 2 * HOUR_IN_MILLIS); 2178 setDischarging(); 2179 setStandbyBucket(ACTIVE_INDEX, job); 2180 setProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); 2181 2182 // ACTIVE apps (where allowed time = window size) should be capped at max execution limit. 2183 synchronized (mQuotaController.mLock) { 2184 assertEquals(2 * HOUR_IN_MILLIS, 2185 mQuotaController.getMaxJobExecutionTimeMsLocked((job))); 2186 } 2187 2188 // Make sure sessions are factored in properly. 2189 mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, 2190 createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS), 2191 30 * MINUTE_IN_MILLIS, 1), false); 2192 synchronized (mQuotaController.mLock) { 2193 assertEquals(90 * MINUTE_IN_MILLIS, 2194 mQuotaController.getMaxJobExecutionTimeMsLocked((job))); 2195 } 2196 2197 mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, 2198 createTimingSession(sElapsedRealtimeClock.millis() - (5 * HOUR_IN_MILLIS), 2199 30 * MINUTE_IN_MILLIS, 1), false); 2200 mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, 2201 createTimingSession(sElapsedRealtimeClock.millis() - (4 * HOUR_IN_MILLIS), 2202 30 * MINUTE_IN_MILLIS, 1), false); 2203 mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, 2204 createTimingSession(sElapsedRealtimeClock.millis() - (3 * HOUR_IN_MILLIS), 2205 25 * MINUTE_IN_MILLIS, 1), false); 2206 synchronized (mQuotaController.mLock) { 2207 assertEquals(5 * MINUTE_IN_MILLIS, 2208 mQuotaController.getMaxJobExecutionTimeMsLocked((job))); 2209 } 2210 } 2211 2212 @Test 2213 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetMaxJobExecutionTimeLocked_EJ_LegacyDefaultEJLimits()2214 public void testGetMaxJobExecutionTimeLocked_EJ_LegacyDefaultEJLimits() { 2215 final long timeUsedMs = 3 * MINUTE_IN_MILLIS; 2216 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2217 createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 2218 timeUsedMs, 5), true); 2219 JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0); 2220 setStandbyBucket(RARE_INDEX, job); 2221 synchronized (mQuotaController.mLock) { 2222 mQuotaController.maybeStartTrackingJobLocked(job, null); 2223 } 2224 2225 setCharging(); 2226 synchronized (mQuotaController.mLock) { 2227 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 2228 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2229 } 2230 2231 setDischarging(); 2232 setProcessState(getProcessStateQuotaFreeThreshold()); 2233 synchronized (mQuotaController.mLock) { 2234 assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2, 2235 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2236 } 2237 2238 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2239 // Top-started job 2240 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2241 synchronized (mQuotaController.mLock) { 2242 mQuotaController.prepareForExecutionLocked(job); 2243 } 2244 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2245 synchronized (mQuotaController.mLock) { 2246 // Top-started job is out of quota enforcement. 2247 assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, 2248 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2249 mQuotaController.maybeStopTrackingJobLocked(job, null); 2250 } 2251 2252 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2253 synchronized (mQuotaController.mLock) { 2254 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, 2255 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2256 } 2257 2258 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2259 // Top-started job 2260 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2261 synchronized (mQuotaController.mLock) { 2262 mQuotaController.prepareForExecutionLocked(job); 2263 } 2264 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2265 synchronized (mQuotaController.mLock) { 2266 // Top-started job is enforced by quota policy after the app leaves the TOP state. 2267 // The max execution time should be the total EJ session limit of the RARE bucket 2268 // minus the time has been used. 2269 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, 2270 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2271 mQuotaController.maybeStopTrackingJobLocked(job, null); 2272 } 2273 2274 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2275 synchronized (mQuotaController.mLock) { 2276 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, 2277 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2278 } 2279 2280 // Test used quota rolling out of window. 2281 synchronized (mQuotaController.mLock) { 2282 mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); 2283 } 2284 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2285 createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS, 2286 timeUsedMs, 5), true); 2287 2288 setProcessState(getProcessStateQuotaFreeThreshold()); 2289 synchronized (mQuotaController.mLock) { 2290 assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2, 2291 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2292 } 2293 2294 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2295 // Top-started job 2296 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2297 synchronized (mQuotaController.mLock) { 2298 mQuotaController.maybeStartTrackingJobLocked(job, null); 2299 mQuotaController.prepareForExecutionLocked(job); 2300 } 2301 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2302 synchronized (mQuotaController.mLock) { 2303 // Top-started job is out of quota enforcement. 2304 assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, 2305 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2306 mQuotaController.maybeStopTrackingJobLocked(job, null); 2307 } 2308 2309 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2310 synchronized (mQuotaController.mLock) { 2311 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, 2312 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2313 } 2314 2315 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2316 // Top-started job 2317 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2318 synchronized (mQuotaController.mLock) { 2319 mQuotaController.maybeStartTrackingJobLocked(job, null); 2320 mQuotaController.prepareForExecutionLocked(job); 2321 } 2322 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2323 synchronized (mQuotaController.mLock) { 2324 // Top-started job is enforced by quota policy after the app leaves the TOP state. 2325 // The max execution time should be the total EJ session limit of the RARE bucket. 2326 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, 2327 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2328 mQuotaController.maybeStopTrackingJobLocked(job, null); 2329 } 2330 2331 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2332 synchronized (mQuotaController.mLock) { 2333 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, 2334 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2335 } 2336 } 2337 2338 @Test 2339 @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetMaxJobExecutionTimeLocked_EJ_NewDefaultEJLimits()2340 public void testGetMaxJobExecutionTimeLocked_EJ_NewDefaultEJLimits() { 2341 final long timeUsedMs = 3 * MINUTE_IN_MILLIS; 2342 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2343 createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 2344 timeUsedMs, 5), true); 2345 JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0); 2346 setStandbyBucket(RARE_INDEX, job); 2347 synchronized (mQuotaController.mLock) { 2348 mQuotaController.maybeStartTrackingJobLocked(job, null); 2349 } 2350 2351 setCharging(); 2352 synchronized (mQuotaController.mLock) { 2353 assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 2354 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2355 } 2356 2357 setDischarging(); 2358 setProcessState(getProcessStateQuotaFreeThreshold()); 2359 synchronized (mQuotaController.mLock) { 2360 assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2, 2361 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2362 } 2363 2364 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2365 // Top-started job 2366 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2367 synchronized (mQuotaController.mLock) { 2368 mQuotaController.prepareForExecutionLocked(job); 2369 } 2370 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2371 synchronized (mQuotaController.mLock) { 2372 // Top-started job is out of quota enforcement. 2373 assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, 2374 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2375 mQuotaController.maybeStopTrackingJobLocked(job, null); 2376 } 2377 2378 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2379 synchronized (mQuotaController.mLock) { 2380 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, 2381 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2382 } 2383 2384 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2385 // Top-started job 2386 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2387 synchronized (mQuotaController.mLock) { 2388 mQuotaController.prepareForExecutionLocked(job); 2389 } 2390 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2391 synchronized (mQuotaController.mLock) { 2392 // Top-started job is enforced by quota policy after the app leaves the TOP state. 2393 // The max execution time should be the total EJ session limit of the RARE bucket. 2394 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, 2395 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2396 mQuotaController.maybeStopTrackingJobLocked(job, null); 2397 } 2398 2399 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2400 synchronized (mQuotaController.mLock) { 2401 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, 2402 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2403 } 2404 2405 // Test used quota rolling out of window. 2406 synchronized (mQuotaController.mLock) { 2407 mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); 2408 } 2409 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2410 createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS, 2411 timeUsedMs, 5), true); 2412 2413 setProcessState(getProcessStateQuotaFreeThreshold()); 2414 synchronized (mQuotaController.mLock) { 2415 // max of 50% WORKING limit and remaining quota 2416 assertEquals(10 * MINUTE_IN_MILLIS, 2417 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2418 } 2419 2420 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2421 // Top-started job 2422 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2423 synchronized (mQuotaController.mLock) { 2424 mQuotaController.maybeStartTrackingJobLocked(job, null); 2425 mQuotaController.prepareForExecutionLocked(job); 2426 } 2427 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2428 synchronized (mQuotaController.mLock) { 2429 // Top-started job is out of quota enforcement. 2430 assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, 2431 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2432 mQuotaController.maybeStopTrackingJobLocked(job, null); 2433 } 2434 2435 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2436 synchronized (mQuotaController.mLock) { 2437 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, 2438 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2439 } 2440 2441 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 2442 // Top-started job 2443 setProcessState(ActivityManager.PROCESS_STATE_TOP); 2444 synchronized (mQuotaController.mLock) { 2445 mQuotaController.maybeStartTrackingJobLocked(job, null); 2446 mQuotaController.prepareForExecutionLocked(job); 2447 } 2448 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 2449 synchronized (mQuotaController.mLock) { 2450 // Top-started job is enforced by quota policy after the app leaves the TOP state. 2451 // The max execution time should be the total EJ session limit of the RARE bucket. 2452 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, 2453 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2454 mQuotaController.maybeStopTrackingJobLocked(job, null); 2455 } 2456 2457 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 2458 synchronized (mQuotaController.mLock) { 2459 assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, 2460 mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 2461 } 2462 } 2463 2464 /** 2465 * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size. 2466 */ 2467 @Test 2468 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes()2469 public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() { 2470 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2471 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2472 createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false); 2473 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2474 createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), 2475 false); 2476 2477 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 2478 10 * MINUTE_IN_MILLIS); 2479 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 10 * MINUTE_IN_MILLIS); 2480 // window size = allowed time, so jobs can essentially run non-stop until they reach the 2481 // max execution time. 2482 setStandbyBucket(EXEMPTED_INDEX); 2483 synchronized (mQuotaController.mLock) { 2484 assertEquals(0, 2485 mQuotaController.getRemainingExecutionTimeLocked( 2486 SOURCE_USER_ID, SOURCE_PACKAGE)); 2487 assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS, 2488 mQuotaController.getTimeUntilQuotaConsumedLocked( 2489 SOURCE_USER_ID, SOURCE_PACKAGE)); 2490 } 2491 } 2492 2493 /** 2494 * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket 2495 * window. 2496 */ 2497 @Test 2498 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetTimeUntilQuotaConsumedLocked_BucketWindow_LegacyDefaultBucketWindowSizes()2499 public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_LegacyDefaultBucketWindowSizes() { 2500 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2501 // Close to RARE boundary. 2502 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2503 createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS), 2504 30 * SECOND_IN_MILLIS, 5), false); 2505 // Far away from FREQUENT boundary. 2506 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2507 createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS), 2508 3 * MINUTE_IN_MILLIS, 5), false); 2509 // Overlap WORKING_SET boundary. 2510 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2511 createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS), 2512 3 * MINUTE_IN_MILLIS, 5), false); 2513 // Close to ACTIVE boundary. 2514 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2515 createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS), 2516 3 * MINUTE_IN_MILLIS, 5), false); 2517 2518 setStandbyBucket(RARE_INDEX); 2519 synchronized (mQuotaController.mLock) { 2520 assertEquals(30 * SECOND_IN_MILLIS, 2521 mQuotaController.getRemainingExecutionTimeLocked( 2522 SOURCE_USER_ID, SOURCE_PACKAGE)); 2523 assertEquals(MINUTE_IN_MILLIS, 2524 mQuotaController.getTimeUntilQuotaConsumedLocked( 2525 SOURCE_USER_ID, SOURCE_PACKAGE)); 2526 } 2527 2528 setStandbyBucket(FREQUENT_INDEX); 2529 synchronized (mQuotaController.mLock) { 2530 assertEquals(MINUTE_IN_MILLIS, 2531 mQuotaController.getRemainingExecutionTimeLocked( 2532 SOURCE_USER_ID, SOURCE_PACKAGE)); 2533 assertEquals(MINUTE_IN_MILLIS, 2534 mQuotaController.getTimeUntilQuotaConsumedLocked( 2535 SOURCE_USER_ID, SOURCE_PACKAGE)); 2536 } 2537 2538 setStandbyBucket(WORKING_INDEX); 2539 synchronized (mQuotaController.mLock) { 2540 assertEquals(5 * MINUTE_IN_MILLIS, 2541 mQuotaController.getRemainingExecutionTimeLocked( 2542 SOURCE_USER_ID, SOURCE_PACKAGE)); 2543 assertEquals(7 * MINUTE_IN_MILLIS, 2544 mQuotaController.getTimeUntilQuotaConsumedLocked( 2545 SOURCE_USER_ID, SOURCE_PACKAGE)); 2546 } 2547 2548 // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the 2549 // max execution time. 2550 setStandbyBucket(ACTIVE_INDEX); 2551 synchronized (mQuotaController.mLock) { 2552 assertEquals(7 * MINUTE_IN_MILLIS, 2553 mQuotaController.getRemainingExecutionTimeLocked( 2554 SOURCE_USER_ID, SOURCE_PACKAGE)); 2555 assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, 2556 mQuotaController.getTimeUntilQuotaConsumedLocked( 2557 SOURCE_USER_ID, SOURCE_PACKAGE)); 2558 } 2559 } 2560 2561 @Test 2562 @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) 2563 @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS) testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes()2564 public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() { 2565 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2566 // Close to RARE boundary. 2567 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2568 createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS), 2569 30 * SECOND_IN_MILLIS, 5), false); 2570 // Far away from FREQUENT boundary. 2571 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2572 createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS), 2573 3 * MINUTE_IN_MILLIS, 5), false); 2574 // Overlap WORKING_SET boundary. 2575 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2576 createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS), 2577 3 * MINUTE_IN_MILLIS, 5), false); 2578 // Close to ACTIVE boundary. 2579 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2580 createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS), 2581 3 * MINUTE_IN_MILLIS, 5), false); 2582 2583 setStandbyBucket(RARE_INDEX); 2584 synchronized (mQuotaController.mLock) { 2585 assertEquals(30 * SECOND_IN_MILLIS, 2586 mQuotaController.getRemainingExecutionTimeLocked( 2587 SOURCE_USER_ID, SOURCE_PACKAGE)); 2588 assertEquals(MINUTE_IN_MILLIS, 2589 mQuotaController.getTimeUntilQuotaConsumedLocked( 2590 SOURCE_USER_ID, SOURCE_PACKAGE)); 2591 } 2592 2593 setStandbyBucket(FREQUENT_INDEX); 2594 synchronized (mQuotaController.mLock) { 2595 assertEquals(MINUTE_IN_MILLIS, 2596 mQuotaController.getRemainingExecutionTimeLocked( 2597 SOURCE_USER_ID, SOURCE_PACKAGE)); 2598 assertEquals(MINUTE_IN_MILLIS, 2599 mQuotaController.getTimeUntilQuotaConsumedLocked( 2600 SOURCE_USER_ID, SOURCE_PACKAGE)); 2601 } 2602 2603 setStandbyBucket(WORKING_INDEX); 2604 synchronized (mQuotaController.mLock) { 2605 assertEquals(5 * MINUTE_IN_MILLIS, 2606 mQuotaController.getRemainingExecutionTimeLocked( 2607 SOURCE_USER_ID, SOURCE_PACKAGE)); 2608 assertEquals(7 * MINUTE_IN_MILLIS, 2609 mQuotaController.getTimeUntilQuotaConsumedLocked( 2610 SOURCE_USER_ID, SOURCE_PACKAGE)); 2611 } 2612 2613 // ACTIVE window != allowed time. 2614 setStandbyBucket(ACTIVE_INDEX); 2615 synchronized (mQuotaController.mLock) { 2616 assertEquals(7 * MINUTE_IN_MILLIS, 2617 mQuotaController.getRemainingExecutionTimeLocked( 2618 SOURCE_USER_ID, SOURCE_PACKAGE)); 2619 assertEquals(10 * MINUTE_IN_MILLIS, 2620 mQuotaController.getTimeUntilQuotaConsumedLocked( 2621 SOURCE_USER_ID, SOURCE_PACKAGE)); 2622 } 2623 } 2624 2625 @Test 2626 @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS, 2627 Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS}) testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes_Tuning()2628 public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes_Tuning() { 2629 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2630 // Close to ACTIVE boundary. 2631 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2632 createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS), 2633 3 * MINUTE_IN_MILLIS, 5), false); 2634 2635 // ACTIVE window != allowed time. 2636 setStandbyBucket(ACTIVE_INDEX); 2637 synchronized (mQuotaController.mLock) { 2638 assertEquals(17 * MINUTE_IN_MILLIS, 2639 mQuotaController.getRemainingExecutionTimeLocked( 2640 SOURCE_USER_ID, SOURCE_PACKAGE)); 2641 assertEquals(20 * MINUTE_IN_MILLIS, 2642 mQuotaController.getTimeUntilQuotaConsumedLocked( 2643 SOURCE_USER_ID, SOURCE_PACKAGE)); 2644 } 2645 } 2646 2647 @Test 2648 @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS, 2649 Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER}) 2650 @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS) testGetTimeUntilQuotaConsumedLocked_Installer()2651 public void testGetTimeUntilQuotaConsumedLocked_Installer() { 2652 PackageInfo pi = new PackageInfo(); 2653 pi.packageName = SOURCE_PACKAGE; 2654 pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES}; 2655 pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED}; 2656 pi.applicationInfo = new ApplicationInfo(); 2657 pi.applicationInfo.uid = mSourceUid; 2658 doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt()); 2659 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission( 2660 eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid)); 2661 mQuotaController.onSystemServicesReady(); 2662 2663 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2664 // Close to RARE boundary. 2665 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2666 createTimingSession( 2667 now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS), 2668 90 * SECOND_IN_MILLIS, 5), false); 2669 // Far away from FREQUENT boundary. 2670 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2671 createTimingSession( 2672 now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS), 2673 2 * MINUTE_IN_MILLIS, 5), false); 2674 // Overlap WORKING_SET boundary. 2675 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2676 createTimingSession( 2677 now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS), 2678 2 * MINUTE_IN_MILLIS, 5), false); 2679 // Close to ACTIVE boundary. 2680 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2681 createTimingSession( 2682 now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS), 2683 2 * MINUTE_IN_MILLIS, 5), false); 2684 // Close to EXEMPTED boundary. 2685 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2686 createTimingSession( 2687 now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS), 2688 2 * MINUTE_IN_MILLIS, 5), false); 2689 2690 // No additional quota for the system installer when the app is in RARE, FREQUENT, 2691 // WORKING_SET or ACTIVE bucket. 2692 setStandbyBucket(RARE_INDEX); 2693 synchronized (mQuotaController.mLock) { 2694 assertEquals(30 * SECOND_IN_MILLIS, 2695 mQuotaController.getRemainingExecutionTimeLocked( 2696 SOURCE_USER_ID, SOURCE_PACKAGE)); 2697 assertEquals(2 * MINUTE_IN_MILLIS, 2698 mQuotaController.getTimeUntilQuotaConsumedLocked( 2699 SOURCE_USER_ID, SOURCE_PACKAGE)); 2700 } 2701 2702 setStandbyBucket(FREQUENT_INDEX); 2703 synchronized (mQuotaController.mLock) { 2704 assertEquals(2 * MINUTE_IN_MILLIS, 2705 mQuotaController.getRemainingExecutionTimeLocked( 2706 SOURCE_USER_ID, SOURCE_PACKAGE)); 2707 assertEquals(2 * MINUTE_IN_MILLIS, 2708 mQuotaController.getTimeUntilQuotaConsumedLocked( 2709 SOURCE_USER_ID, SOURCE_PACKAGE)); 2710 } 2711 2712 setStandbyBucket(WORKING_INDEX); 2713 synchronized (mQuotaController.mLock) { 2714 assertEquals(5 * MINUTE_IN_MILLIS, 2715 mQuotaController.getRemainingExecutionTimeLocked( 2716 SOURCE_USER_ID, SOURCE_PACKAGE)); 2717 assertEquals(6 * MINUTE_IN_MILLIS, 2718 mQuotaController.getTimeUntilQuotaConsumedLocked( 2719 SOURCE_USER_ID, SOURCE_PACKAGE)); 2720 } 2721 2722 // ACTIVE window != allowed time. 2723 setStandbyBucket(ACTIVE_INDEX); 2724 synchronized (mQuotaController.mLock) { 2725 assertEquals(6 * MINUTE_IN_MILLIS, 2726 mQuotaController.getRemainingExecutionTimeLocked( 2727 SOURCE_USER_ID, SOURCE_PACKAGE)); 2728 assertEquals(8 * MINUTE_IN_MILLIS, 2729 mQuotaController.getTimeUntilQuotaConsumedLocked( 2730 SOURCE_USER_ID, SOURCE_PACKAGE)); 2731 } 2732 2733 // Additional quota for the system installer when the app is in EXEMPTED bucket. 2734 // EXEMPTED window == allowed time. 2735 setStandbyBucket(EXEMPTED_INDEX); 2736 synchronized (mQuotaController.mLock) { 2737 assertEquals(18 * MINUTE_IN_MILLIS, 2738 mQuotaController.getRemainingExecutionTimeLocked( 2739 SOURCE_USER_ID, SOURCE_PACKAGE)); 2740 assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 8 * MINUTE_IN_MILLIS, 2741 mQuotaController.getTimeUntilQuotaConsumedLocked( 2742 SOURCE_USER_ID, SOURCE_PACKAGE)); 2743 } 2744 } 2745 2746 @Test 2747 @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS, 2748 Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER, 2749 Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS}) testGetTimeUntilQuotaConsumedLocked_Installer_Tuning()2750 public void testGetTimeUntilQuotaConsumedLocked_Installer_Tuning() { 2751 PackageInfo pi = new PackageInfo(); 2752 pi.packageName = SOURCE_PACKAGE; 2753 pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES}; 2754 pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED}; 2755 pi.applicationInfo = new ApplicationInfo(); 2756 pi.applicationInfo.uid = mSourceUid; 2757 doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt()); 2758 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission( 2759 eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid)); 2760 mQuotaController.onSystemServicesReady(); 2761 2762 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2763 // Close to EXEMPTED boundary. 2764 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2765 createTimingSession( 2766 now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS), 2767 2 * MINUTE_IN_MILLIS, 5), false); 2768 2769 // Additional quota for the system installer when the app is in EXEMPTED bucket. 2770 // EXEMPTED window == allowed time. 2771 setStandbyBucket(EXEMPTED_INDEX); 2772 synchronized (mQuotaController.mLock) { 2773 assertEquals(38 * MINUTE_IN_MILLIS, 2774 mQuotaController.getRemainingExecutionTimeLocked( 2775 SOURCE_USER_ID, SOURCE_PACKAGE)); 2776 assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 2 * MINUTE_IN_MILLIS, 2777 mQuotaController.getTimeUntilQuotaConsumedLocked( 2778 SOURCE_USER_ID, SOURCE_PACKAGE)); 2779 } 2780 } 2781 2782 /** 2783 * Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit. 2784 */ 2785 @Test testGetTimeUntilQuotaConsumedLocked_MaxExecution()2786 public void testGetTimeUntilQuotaConsumedLocked_MaxExecution() { 2787 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2788 // Overlap boundary. 2789 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2790 createTimingSession( 2791 now - (24 * HOUR_IN_MILLIS + 8 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, 5), 2792 false); 2793 2794 setStandbyBucket(WORKING_INDEX); 2795 synchronized (mQuotaController.mLock) { 2796 assertEquals(8 * MINUTE_IN_MILLIS, 2797 mQuotaController.getRemainingExecutionTimeLocked( 2798 SOURCE_USER_ID, SOURCE_PACKAGE)); 2799 // Max time will phase out, so should use bucket limit. 2800 assertEquals(10 * MINUTE_IN_MILLIS, 2801 mQuotaController.getTimeUntilQuotaConsumedLocked( 2802 SOURCE_USER_ID, SOURCE_PACKAGE)); 2803 } 2804 2805 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 2806 // Close to boundary. 2807 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2808 createTimingSession(now - (24 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS), 2809 mQcConstants.MAX_EXECUTION_TIME_MS - 5 * MINUTE_IN_MILLIS, 5), false); 2810 2811 setStandbyBucket(WORKING_INDEX); 2812 synchronized (mQuotaController.mLock) { 2813 assertEquals(5 * MINUTE_IN_MILLIS, 2814 mQuotaController.getRemainingExecutionTimeLocked( 2815 SOURCE_USER_ID, SOURCE_PACKAGE)); 2816 assertEquals(10 * MINUTE_IN_MILLIS, 2817 mQuotaController.getTimeUntilQuotaConsumedLocked( 2818 SOURCE_USER_ID, SOURCE_PACKAGE)); 2819 } 2820 2821 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 2822 // Far from boundary. 2823 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2824 createTimingSession( 2825 now - (20 * HOUR_IN_MILLIS), 2826 mQcConstants.MAX_EXECUTION_TIME_MS - 3 * MINUTE_IN_MILLIS, 5), 2827 false); 2828 2829 setStandbyBucket(WORKING_INDEX); 2830 synchronized (mQuotaController.mLock) { 2831 assertEquals(3 * MINUTE_IN_MILLIS, 2832 mQuotaController.getRemainingExecutionTimeLocked( 2833 SOURCE_USER_ID, SOURCE_PACKAGE)); 2834 assertEquals(3 * MINUTE_IN_MILLIS, 2835 mQuotaController.getTimeUntilQuotaConsumedLocked( 2836 SOURCE_USER_ID, SOURCE_PACKAGE)); 2837 } 2838 } 2839 2840 /** 2841 * Test getTimeUntilQuotaConsumedLocked when the max execution time and bucket window time 2842 * remaining are equal. 2843 */ 2844 @Test testGetTimeUntilQuotaConsumedLocked_EqualTimeRemaining()2845 public void testGetTimeUntilQuotaConsumedLocked_EqualTimeRemaining() { 2846 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2847 setStandbyBucket(FREQUENT_INDEX); 2848 2849 // Overlap boundary. 2850 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2851 createTimingSession( 2852 now - (24 * HOUR_IN_MILLIS + 11 * MINUTE_IN_MILLIS), 2853 mQcConstants.MAX_EXECUTION_TIME_MS, 2854 5), false); 2855 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2856 createTimingSession( 2857 now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS + MINUTE_IN_MILLIS), 2858 3 * MINUTE_IN_MILLIS, 5), 2859 false); 2860 2861 synchronized (mQuotaController.mLock) { 2862 // Both max and bucket time have 8 minutes left. 2863 assertEquals(8 * MINUTE_IN_MILLIS, 2864 mQuotaController.getRemainingExecutionTimeLocked( 2865 SOURCE_USER_ID, SOURCE_PACKAGE)); 2866 // Max time essentially free. Bucket time has 2 min phase out plus original 8 minute 2867 // window time. 2868 assertEquals(10 * MINUTE_IN_MILLIS, 2869 mQuotaController.getTimeUntilQuotaConsumedLocked( 2870 SOURCE_USER_ID, SOURCE_PACKAGE)); 2871 } 2872 2873 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 2874 // Overlap boundary. 2875 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2876 createTimingSession( 2877 now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5), 2878 false); 2879 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2880 createTimingSession( 2881 now - (20 * HOUR_IN_MILLIS), 2882 mQcConstants.MAX_EXECUTION_TIME_MS - 12 * MINUTE_IN_MILLIS, 2883 5), false); 2884 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2885 createTimingSession( 2886 now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS + MINUTE_IN_MILLIS), 2887 3 * MINUTE_IN_MILLIS, 5), 2888 false); 2889 2890 synchronized (mQuotaController.mLock) { 2891 // Both max and bucket time have 8 minutes left. 2892 assertEquals(8 * MINUTE_IN_MILLIS, 2893 mQuotaController.getRemainingExecutionTimeLocked( 2894 SOURCE_USER_ID, SOURCE_PACKAGE)); 2895 // Max time only has one minute phase out. Bucket time has 2 minute phase out. 2896 assertEquals(9 * MINUTE_IN_MILLIS, 2897 mQuotaController.getTimeUntilQuotaConsumedLocked( 2898 SOURCE_USER_ID, SOURCE_PACKAGE)); 2899 } 2900 } 2901 2902 /** 2903 * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size. 2904 */ 2905 @Test 2906 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes()2907 public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() { 2908 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2909 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2910 createTimingSession(now - (24 * HOUR_IN_MILLIS), 2911 mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS, 5), 2912 false); 2913 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2914 createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), 2915 false); 2916 2917 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 2918 10 * MINUTE_IN_MILLIS); 2919 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 10 * MINUTE_IN_MILLIS); 2920 // window size = allowed time, so jobs can essentially run non-stop until they reach the 2921 // max execution time. 2922 setStandbyBucket(EXEMPTED_INDEX); 2923 synchronized (mQuotaController.mLock) { 2924 assertEquals(0, 2925 mQuotaController.getRemainingExecutionTimeLocked( 2926 SOURCE_USER_ID, SOURCE_PACKAGE)); 2927 assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS, 2928 mQuotaController.getTimeUntilQuotaConsumedLocked( 2929 SOURCE_USER_ID, SOURCE_PACKAGE)); 2930 } 2931 } 2932 2933 /** 2934 * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket 2935 * window and the session is rolling out of the window. 2936 */ 2937 @Test 2938 @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_LegacyDefaultBucketWindowSizes()2939 public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_LegacyDefaultBucketWindowSizes() { 2940 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 2941 2942 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2943 createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS, 2944 mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false); 2945 setStandbyBucket(RARE_INDEX); 2946 synchronized (mQuotaController.mLock) { 2947 assertEquals(0, 2948 mQuotaController.getRemainingExecutionTimeLocked( 2949 SOURCE_USER_ID, SOURCE_PACKAGE)); 2950 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 2951 mQuotaController.getTimeUntilQuotaConsumedLocked( 2952 SOURCE_USER_ID, SOURCE_PACKAGE)); 2953 } 2954 2955 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2956 createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS, 2957 mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false); 2958 setStandbyBucket(FREQUENT_INDEX); 2959 synchronized (mQuotaController.mLock) { 2960 assertEquals(0, 2961 mQuotaController.getRemainingExecutionTimeLocked( 2962 SOURCE_USER_ID, SOURCE_PACKAGE)); 2963 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 2964 mQuotaController.getTimeUntilQuotaConsumedLocked( 2965 SOURCE_USER_ID, SOURCE_PACKAGE)); 2966 } 2967 2968 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2969 createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, 2970 mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false); 2971 setStandbyBucket(WORKING_INDEX); 2972 synchronized (mQuotaController.mLock) { 2973 assertEquals(0, 2974 mQuotaController.getRemainingExecutionTimeLocked( 2975 SOURCE_USER_ID, SOURCE_PACKAGE)); 2976 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 2977 mQuotaController.getTimeUntilQuotaConsumedLocked( 2978 SOURCE_USER_ID, SOURCE_PACKAGE)); 2979 } 2980 2981 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 2982 createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS, 2983 mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5), false); 2984 // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the 2985 // max execution time. 2986 setStandbyBucket(ACTIVE_INDEX); 2987 synchronized (mQuotaController.mLock) { 2988 assertEquals(0, 2989 mQuotaController.getRemainingExecutionTimeLocked( 2990 SOURCE_USER_ID, SOURCE_PACKAGE)); 2991 assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS 2992 - (mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS 2993 + mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS 2994 + mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS), 2995 mQuotaController.getTimeUntilQuotaConsumedLocked( 2996 SOURCE_USER_ID, SOURCE_PACKAGE)); 2997 } 2998 } 2999 3000 @Test 3001 @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS) testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_NewDefaultBucketWindowSizes()3002 public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_NewDefaultBucketWindowSizes() { 3003 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3004 3005 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3006 createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS, 3007 mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false); 3008 setStandbyBucket(RARE_INDEX); 3009 synchronized (mQuotaController.mLock) { 3010 assertEquals(0, 3011 mQuotaController.getRemainingExecutionTimeLocked( 3012 SOURCE_USER_ID, SOURCE_PACKAGE)); 3013 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 3014 mQuotaController.getTimeUntilQuotaConsumedLocked( 3015 SOURCE_USER_ID, SOURCE_PACKAGE)); 3016 } 3017 3018 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3019 createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS, 3020 mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false); 3021 setStandbyBucket(FREQUENT_INDEX); 3022 synchronized (mQuotaController.mLock) { 3023 assertEquals(0, 3024 mQuotaController.getRemainingExecutionTimeLocked( 3025 SOURCE_USER_ID, SOURCE_PACKAGE)); 3026 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 3027 mQuotaController.getTimeUntilQuotaConsumedLocked( 3028 SOURCE_USER_ID, SOURCE_PACKAGE)); 3029 } 3030 3031 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3032 createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, 3033 mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false); 3034 setStandbyBucket(WORKING_INDEX); 3035 synchronized (mQuotaController.mLock) { 3036 assertEquals(0, 3037 mQuotaController.getRemainingExecutionTimeLocked( 3038 SOURCE_USER_ID, SOURCE_PACKAGE)); 3039 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 3040 mQuotaController.getTimeUntilQuotaConsumedLocked( 3041 SOURCE_USER_ID, SOURCE_PACKAGE)); 3042 } 3043 3044 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3045 createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS, 3046 mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5), 3047 false); 3048 // ACTIVE window != allowed time. 3049 setStandbyBucket(ACTIVE_INDEX); 3050 synchronized (mQuotaController.mLock) { 3051 assertEquals(0, 3052 mQuotaController.getRemainingExecutionTimeLocked( 3053 SOURCE_USER_ID, SOURCE_PACKAGE)); 3054 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 3055 mQuotaController.getTimeUntilQuotaConsumedLocked( 3056 SOURCE_USER_ID, SOURCE_PACKAGE)); 3057 } 3058 3059 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3060 createTimingSession(now - mQcConstants.WINDOW_SIZE_EXEMPTED_MS, 3061 mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 5), 3062 false); 3063 // EXEMPTED window != allowed time 3064 setStandbyBucket(EXEMPTED_INDEX); 3065 synchronized (mQuotaController.mLock) { 3066 assertEquals(0, 3067 mQuotaController.getRemainingExecutionTimeLocked( 3068 SOURCE_USER_ID, SOURCE_PACKAGE)); 3069 assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 3070 mQuotaController.getTimeUntilQuotaConsumedLocked( 3071 SOURCE_USER_ID, SOURCE_PACKAGE)); 3072 } 3073 } 3074 3075 @Test testIsWithinQuotaLocked_NeverApp()3076 public void testIsWithinQuotaLocked_NeverApp() { 3077 synchronized (mQuotaController.mLock) { 3078 assertFalse( 3079 mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX)); 3080 } 3081 } 3082 3083 @Test testIsWithinQuotaLocked_Charging()3084 public void testIsWithinQuotaLocked_Charging() { 3085 setCharging(); 3086 synchronized (mQuotaController.mLock) { 3087 assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX)); 3088 } 3089 } 3090 3091 @Test testIsWithinQuotaLocked_UnderDuration_UnderJobCount()3092 public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount() { 3093 setDischarging(); 3094 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3095 mQuotaController.saveTimingSession(0, "com.android.test", 3096 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 3097 mQuotaController.saveTimingSession(0, "com.android.test", 3098 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 3099 synchronized (mQuotaController.mLock) { 3100 mQuotaController.incrementJobCountLocked(0, "com.android.test", 5); 3101 assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); 3102 } 3103 } 3104 3105 @Test testIsWithinQuotaLocked_UnderDuration_OverJobCountRateLimitWindow()3106 public void testIsWithinQuotaLocked_UnderDuration_OverJobCountRateLimitWindow() { 3107 setDischarging(); 3108 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3109 final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; 3110 mQuotaController.saveTimingSession(0, "com.android.test.spam", 3111 createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false); 3112 mQuotaController.saveTimingSession(0, "com.android.test.spam", 3113 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount), 3114 false); 3115 synchronized (mQuotaController.mLock) { 3116 mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount); 3117 assertFalse(mQuotaController.isWithinQuotaLocked( 3118 0, "com.android.test.spam", WORKING_INDEX)); 3119 } 3120 3121 mQuotaController.saveTimingSession(0, "com.android.test.frequent", 3122 createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000), 3123 false); 3124 mQuotaController.saveTimingSession(0, "com.android.test.frequent", 3125 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false); 3126 synchronized (mQuotaController.mLock) { 3127 assertFalse(mQuotaController.isWithinQuotaLocked( 3128 0, "com.android.test.frequent", FREQUENT_INDEX)); 3129 } 3130 } 3131 3132 @Test testIsWithinQuotaLocked_OverDuration_UnderJobCount()3133 public void testIsWithinQuotaLocked_OverDuration_UnderJobCount() { 3134 setDischarging(); 3135 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3136 mQuotaController.saveTimingSession(0, "com.android.test", 3137 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 3138 mQuotaController.saveTimingSession(0, "com.android.test", 3139 createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); 3140 mQuotaController.saveTimingSession(0, "com.android.test", 3141 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false); 3142 synchronized (mQuotaController.mLock) { 3143 mQuotaController.incrementJobCountLocked(0, "com.android.test", 5); 3144 assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); 3145 } 3146 } 3147 3148 @Test testIsWithinQuotaLocked_OverDuration_OverJobCountRateLimitWindow()3149 public void testIsWithinQuotaLocked_OverDuration_OverJobCountRateLimitWindow() { 3150 setDischarging(); 3151 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3152 final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; 3153 mQuotaController.saveTimingSession(0, "com.android.test", 3154 createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false); 3155 mQuotaController.saveTimingSession(0, "com.android.test", 3156 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount), 3157 false); 3158 synchronized (mQuotaController.mLock) { 3159 mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount); 3160 assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); 3161 } 3162 } 3163 3164 @Test testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_BelowFGS()3165 public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_BelowFGS() { 3166 setDischarging(); 3167 3168 JobStatus jobStatus = createJobStatus( 3169 "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_BelowFGS", 1); 3170 setStandbyBucket(ACTIVE_INDEX, jobStatus); 3171 setProcessState(ActivityManager.PROCESS_STATE_BACKUP); 3172 3173 synchronized (mQuotaController.mLock) { 3174 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 3175 mQuotaController.prepareForExecutionLocked(jobStatus); 3176 } 3177 for (int i = 0; i < 20; ++i) { 3178 advanceElapsedClock(SECOND_IN_MILLIS); 3179 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 3180 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 3181 } 3182 synchronized (mQuotaController.mLock) { 3183 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 3184 } 3185 3186 advanceElapsedClock(15 * SECOND_IN_MILLIS); 3187 3188 synchronized (mQuotaController.mLock) { 3189 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 3190 mQuotaController.prepareForExecutionLocked(jobStatus); 3191 } 3192 for (int i = 0; i < 20; ++i) { 3193 advanceElapsedClock(SECOND_IN_MILLIS); 3194 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 3195 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 3196 } 3197 synchronized (mQuotaController.mLock) { 3198 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 3199 } 3200 3201 advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS); 3202 3203 synchronized (mQuotaController.mLock) { 3204 assertEquals(2, mQuotaController.getExecutionStatsLocked( 3205 SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInRateLimitingWindow); 3206 assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus)); 3207 assertTrue(jobStatus.isReady()); 3208 } 3209 } 3210 3211 @Test testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps()3212 public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps() 3213 throws Exception { 3214 setDischarging(); 3215 3216 final String unaffectedPkgName = "com.android.unaffected"; 3217 final int unaffectedUid = 10987; 3218 JobInfo unaffectedJobInfo = new JobInfo.Builder(1, 3219 new ComponentName(unaffectedPkgName, "foo")) 3220 .build(); 3221 JobStatus unaffected = createJobStatus( 3222 "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps", 3223 unaffectedPkgName, unaffectedUid, unaffectedJobInfo); 3224 setStandbyBucket(FREQUENT_INDEX, unaffected); 3225 setProcessState(ActivityManager.PROCESS_STATE_SERVICE, unaffectedUid); 3226 3227 final String fgChangerPkgName = "com.android.foreground.changer"; 3228 final int fgChangerUid = 10234; 3229 JobInfo fgChangerJobInfo = new JobInfo.Builder(2, 3230 new ComponentName(fgChangerPkgName, "foo")) 3231 .build(); 3232 JobStatus fgStateChanger = createJobStatus( 3233 "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps", 3234 fgChangerPkgName, fgChangerUid, fgChangerJobInfo); 3235 setStandbyBucket(ACTIVE_INDEX, fgStateChanger); 3236 setProcessState(ActivityManager.PROCESS_STATE_BACKUP, fgChangerUid); 3237 3238 doReturn(new ArraySet<>(new String[]{unaffectedPkgName})) 3239 .when(mJobSchedulerService).getPackagesForUidLocked(unaffectedUid); 3240 doReturn(new ArraySet<>(new String[]{fgChangerPkgName})) 3241 .when(mJobSchedulerService).getPackagesForUidLocked(fgChangerUid); 3242 3243 synchronized (mQuotaController.mLock) { 3244 mQuotaController.maybeStartTrackingJobLocked(unaffected, null); 3245 mQuotaController.prepareForExecutionLocked(unaffected); 3246 3247 mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); 3248 mQuotaController.prepareForExecutionLocked(fgStateChanger); 3249 } 3250 for (int i = 0; i < 20; ++i) { 3251 advanceElapsedClock(SECOND_IN_MILLIS); 3252 setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid); 3253 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid); 3254 } 3255 synchronized (mQuotaController.mLock) { 3256 mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null); 3257 } 3258 3259 advanceElapsedClock(15 * SECOND_IN_MILLIS); 3260 3261 synchronized (mQuotaController.mLock) { 3262 mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); 3263 mQuotaController.prepareForExecutionLocked(fgStateChanger); 3264 } 3265 for (int i = 0; i < 20; ++i) { 3266 advanceElapsedClock(SECOND_IN_MILLIS); 3267 setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid); 3268 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid); 3269 } 3270 synchronized (mQuotaController.mLock) { 3271 mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null); 3272 3273 mQuotaController.maybeStopTrackingJobLocked(unaffected, null); 3274 3275 assertTrue(mQuotaController.isWithinQuotaLocked(unaffected)); 3276 assertTrue(unaffected.isReady()); 3277 assertFalse(mQuotaController.isWithinQuotaLocked(fgStateChanger)); 3278 assertFalse(fgStateChanger.isReady()); 3279 } 3280 assertEquals(1, 3281 mQuotaController.getTimingSessions(SOURCE_USER_ID, unaffectedPkgName).size()); 3282 assertEquals(42, 3283 mQuotaController.getTimingSessions(SOURCE_USER_ID, fgChangerPkgName).size()); 3284 synchronized (mQuotaController.mLock) { 3285 for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) { 3286 assertEquals(42, mQuotaController.getExecutionStatsLocked( 3287 SOURCE_USER_ID, fgChangerPkgName, i).jobCountInRateLimitingWindow); 3288 assertEquals(1, mQuotaController.getExecutionStatsLocked( 3289 SOURCE_USER_ID, unaffectedPkgName, i).jobCountInRateLimitingWindow); 3290 } 3291 } 3292 } 3293 3294 @Test 3295 @RequiresFlagsEnabled(FLAG_COUNT_QUOTA_FIX) testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow()3296 public void testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow() { 3297 setDischarging(); 3298 3299 JobStatus jobRunning = createJobStatus( 3300 "testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow", 1); 3301 JobStatus jobPending = createJobStatus( 3302 "testIsWithinQuotaLocked_UnderDuration_OverJobCountInWindow", 2); 3303 setStandbyBucket(WORKING_INDEX, jobRunning, jobPending); 3304 3305 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); 3306 3307 long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3308 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3309 createTimingSession(now - (HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 9), false); 3310 3311 final ExecutionStats stats; 3312 synchronized (mQuotaController.mLock) { 3313 stats = mQuotaController.getExecutionStatsLocked( 3314 SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); 3315 assertTrue(mQuotaController 3316 .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); 3317 assertEquals(10, stats.jobCountLimit); 3318 assertEquals(9, stats.bgJobCountInWindow); 3319 } 3320 3321 when(mJobSchedulerService.isCurrentlyRunningLocked(jobRunning)).thenReturn(true); 3322 when(mJobSchedulerService.isCurrentlyRunningLocked(jobPending)).thenReturn(false); 3323 3324 InOrder inOrder = inOrder(mJobSchedulerService); 3325 trackJobs(jobRunning, jobPending); 3326 // UID in the background. 3327 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 3328 // Start the job. 3329 synchronized (mQuotaController.mLock) { 3330 mQuotaController.prepareForExecutionLocked(jobRunning); 3331 } 3332 3333 advanceElapsedClock(MINUTE_IN_MILLIS); 3334 // Wait for some extra time to allow for job processing. 3335 ArraySet<JobStatus> expected = new ArraySet<>(); 3336 expected.add(jobPending); 3337 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 3338 .onControllerStateChanged(eq(expected)); 3339 3340 synchronized (mQuotaController.mLock) { 3341 assertTrue(mQuotaController.isWithinQuotaLocked(jobRunning)); 3342 assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 3343 assertTrue(jobRunning.isReady()); 3344 assertFalse(mQuotaController.isWithinQuotaLocked(jobPending)); 3345 assertFalse(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 3346 assertFalse(jobPending.isReady()); 3347 assertEquals(10, stats.bgJobCountInWindow); 3348 } 3349 3350 advanceElapsedClock(MINUTE_IN_MILLIS); 3351 synchronized (mQuotaController.mLock) { 3352 mQuotaController.maybeStopTrackingJobLocked(jobRunning, null); 3353 } 3354 3355 synchronized (mQuotaController.mLock) { 3356 assertFalse(mQuotaController 3357 .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); 3358 assertEquals(10, stats.bgJobCountInWindow); 3359 } 3360 } 3361 3362 @Test testIsWithinQuotaLocked_TimingSession()3363 public void testIsWithinQuotaLocked_TimingSession() { 3364 setDischarging(); 3365 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3366 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 3); 3367 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 4); 3368 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 5); 3369 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 6); 3370 3371 for (int i = 0; i < 7; ++i) { 3372 mQuotaController.saveTimingSession(0, "com.android.test", 3373 createTimingSession(now - ((10 - i) * MINUTE_IN_MILLIS), 30 * SECOND_IN_MILLIS, 3374 2), false); 3375 3376 synchronized (mQuotaController.mLock) { 3377 mQuotaController.incrementJobCountLocked(0, "com.android.test", 2); 3378 3379 assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions", 3380 i < 2, 3381 mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX)); 3382 assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions", 3383 i < 3, 3384 mQuotaController.isWithinQuotaLocked( 3385 0, "com.android.test", FREQUENT_INDEX)); 3386 assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions", 3387 i < 4, 3388 mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX)); 3389 assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions", 3390 i < 5, 3391 mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX)); 3392 } 3393 } 3394 } 3395 3396 @Test testIsWithinQuotaLocked_UserInitiated()3397 public void testIsWithinQuotaLocked_UserInitiated() { 3398 // Put app in a state where regular jobs are out of quota. 3399 setDischarging(); 3400 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3401 final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; 3402 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3403 createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false); 3404 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3405 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount), 3406 false); 3407 JobStatus job = createJobStatus("testIsWithinQuotaLocked_UserInitiated", 1); 3408 spyOn(job); 3409 synchronized (mQuotaController.mLock) { 3410 mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, jobCount); 3411 assertFalse(mQuotaController 3412 .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); 3413 doReturn(false).when(job).shouldTreatAsUserInitiatedJob(); 3414 assertFalse(mQuotaController.isWithinQuotaLocked(job)); 3415 // User-initiated job should still be allowed. 3416 doReturn(true).when(job).shouldTreatAsUserInitiatedJob(); 3417 assertTrue(mQuotaController.isWithinQuotaLocked(job)); 3418 } 3419 } 3420 3421 3422 @Test testIsWithinEJQuotaLocked_NeverApp()3423 public void testIsWithinEJQuotaLocked_NeverApp() { 3424 JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1); 3425 setStandbyBucket(NEVER_INDEX, js); 3426 synchronized (mQuotaController.mLock) { 3427 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3428 } 3429 } 3430 3431 @Test testIsWithinEJQuotaLocked_Charging()3432 public void testIsWithinEJQuotaLocked_Charging() { 3433 setCharging(); 3434 JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_Charging", 1); 3435 synchronized (mQuotaController.mLock) { 3436 assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); 3437 } 3438 } 3439 3440 @Test testIsWithinEJQuotaLocked_UnderDuration()3441 public void testIsWithinEJQuotaLocked_UnderDuration() { 3442 setDischarging(); 3443 JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_UnderDuration", 1); 3444 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3445 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3446 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3447 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3448 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3449 synchronized (mQuotaController.mLock) { 3450 assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); 3451 } 3452 } 3453 3454 @Test testIsWithinEJQuotaLocked_OverDuration()3455 public void testIsWithinEJQuotaLocked_OverDuration() { 3456 setDischarging(); 3457 JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_OverDuration", 1); 3458 setStandbyBucket(FREQUENT_INDEX, js); 3459 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); 3460 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3461 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3462 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3463 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3464 createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3465 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3466 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); 3467 synchronized (mQuotaController.mLock) { 3468 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3469 } 3470 } 3471 3472 @Test testIsWithinEJQuotaLocked_TimingSession()3473 public void testIsWithinEJQuotaLocked_TimingSession() { 3474 setDischarging(); 3475 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 3476 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3477 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS); 3478 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS); 3479 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS); 3480 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 3481 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 8 * MINUTE_IN_MILLIS); 3482 3483 JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TimingSession", 1); 3484 for (int i = 0; i < 25; ++i) { 3485 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3486 createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 3487 2), true); 3488 3489 synchronized (mQuotaController.mLock) { 3490 setStandbyBucket(ACTIVE_INDEX, js); 3491 assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions", 3492 i < 19, mQuotaController.isWithinEJQuotaLocked(js)); 3493 3494 setStandbyBucket(WORKING_INDEX, js); 3495 assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions", 3496 i < 14, mQuotaController.isWithinEJQuotaLocked(js)); 3497 3498 setStandbyBucket(FREQUENT_INDEX, js); 3499 assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions", 3500 i < 12, mQuotaController.isWithinEJQuotaLocked(js)); 3501 3502 setStandbyBucket(RARE_INDEX, js); 3503 assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions", 3504 i < 9, mQuotaController.isWithinEJQuotaLocked(js)); 3505 3506 setStandbyBucket(RESTRICTED_INDEX, js); 3507 assertEquals("Restricted has incorrect quota status with " + (i + 1) + " sessions", 3508 i < 7, mQuotaController.isWithinEJQuotaLocked(js)); 3509 } 3510 } 3511 } 3512 3513 /** 3514 * Tests that Timers properly track sessions when an app is added and removed from the temp 3515 * allowlist. 3516 */ 3517 @Test testIsWithinEJQuotaLocked_TempAllowlisting()3518 public void testIsWithinEJQuotaLocked_TempAllowlisting() { 3519 setDischarging(); 3520 JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting", 1); 3521 setStandbyBucket(FREQUENT_INDEX, js); 3522 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); 3523 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3524 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3525 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3526 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3527 createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3528 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3529 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); 3530 synchronized (mQuotaController.mLock) { 3531 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3532 } 3533 3534 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 3535 final long gracePeriodMs = 15 * SECOND_IN_MILLIS; 3536 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); 3537 Handler handler = mQuotaController.getHandler(); 3538 spyOn(handler); 3539 3540 // Apps on the temp allowlist should be able to schedule & start EJs, even if they're out 3541 // of quota (as long as they are in the temp allowlist grace period). 3542 mTempAllowlistListener.onAppAdded(mSourceUid); 3543 synchronized (mQuotaController.mLock) { 3544 assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); 3545 } 3546 advanceElapsedClock(10 * SECOND_IN_MILLIS); 3547 mTempAllowlistListener.onAppRemoved(mSourceUid); 3548 advanceElapsedClock(10 * SECOND_IN_MILLIS); 3549 // Still in grace period 3550 synchronized (mQuotaController.mLock) { 3551 assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); 3552 } 3553 advanceElapsedClock(6 * SECOND_IN_MILLIS); 3554 // Out of grace period. 3555 synchronized (mQuotaController.mLock) { 3556 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3557 } 3558 } 3559 3560 @Test testIsWithinEJQuotaLocked_TempAllowlisting_Restricted()3561 public void testIsWithinEJQuotaLocked_TempAllowlisting_Restricted() { 3562 setDischarging(); 3563 JobStatus js = 3564 createExpeditedJobStatus( 3565 "testIsWithinEJQuotaLocked_TempAllowlisting_Restricted", 1); 3566 setStandbyBucket(RESTRICTED_INDEX, js); 3567 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); 3568 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3569 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3570 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3571 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3572 createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3573 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3574 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); 3575 synchronized (mQuotaController.mLock) { 3576 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3577 } 3578 3579 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 3580 final long gracePeriodMs = 15 * SECOND_IN_MILLIS; 3581 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); 3582 Handler handler = mQuotaController.getHandler(); 3583 spyOn(handler); 3584 3585 // The temp allowlist should not enable RESTRICTED apps' to schedule & start EJs if they're 3586 // out of quota. 3587 mTempAllowlistListener.onAppAdded(mSourceUid); 3588 synchronized (mQuotaController.mLock) { 3589 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3590 } 3591 advanceElapsedClock(10 * SECOND_IN_MILLIS); 3592 mTempAllowlistListener.onAppRemoved(mSourceUid); 3593 advanceElapsedClock(10 * SECOND_IN_MILLIS); 3594 // Still in grace period 3595 synchronized (mQuotaController.mLock) { 3596 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3597 } 3598 advanceElapsedClock(6 * SECOND_IN_MILLIS); 3599 // Out of grace period. 3600 synchronized (mQuotaController.mLock) { 3601 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3602 } 3603 } 3604 3605 /** 3606 * Tests that Timers properly track sessions when an app becomes top and is closed. 3607 */ 3608 @Test testIsWithinEJQuotaLocked_TopApp()3609 public void testIsWithinEJQuotaLocked_TopApp() { 3610 setDischarging(); 3611 JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TopApp", 1); 3612 setStandbyBucket(FREQUENT_INDEX, js); 3613 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); 3614 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3615 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3616 createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3617 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3618 createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); 3619 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3620 createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); 3621 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 3622 synchronized (mQuotaController.mLock) { 3623 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3624 } 3625 3626 final long gracePeriodMs = 15 * SECOND_IN_MILLIS; 3627 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, gracePeriodMs); 3628 Handler handler = mQuotaController.getHandler(); 3629 spyOn(handler); 3630 3631 // Apps on top should be able to schedule & start EJs, even if they're out 3632 // of quota (as long as they are in the top grace period). 3633 setProcessState(ActivityManager.PROCESS_STATE_TOP); 3634 synchronized (mQuotaController.mLock) { 3635 assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); 3636 } 3637 advanceElapsedClock(10 * SECOND_IN_MILLIS); 3638 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 3639 advanceElapsedClock(10 * SECOND_IN_MILLIS); 3640 // Still in grace period 3641 synchronized (mQuotaController.mLock) { 3642 assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); 3643 } 3644 advanceElapsedClock(6 * SECOND_IN_MILLIS); 3645 // Out of grace period. 3646 synchronized (mQuotaController.mLock) { 3647 assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); 3648 } 3649 } 3650 3651 @Test testMaybeScheduleCleanupAlarmLocked()3652 public void testMaybeScheduleCleanupAlarmLocked() { 3653 // No sessions saved yet. 3654 synchronized (mQuotaController.mLock) { 3655 mQuotaController.maybeScheduleCleanupAlarmLocked(); 3656 } 3657 verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any()); 3658 3659 // Test with only one timing session saved. 3660 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3661 final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); 3662 mQuotaController.saveTimingSession(0, "com.android.test", 3663 new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1), false); 3664 synchronized (mQuotaController.mLock) { 3665 mQuotaController.maybeScheduleCleanupAlarmLocked(); 3666 } 3667 verify(mAlarmManager, times(1)) 3668 .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any()); 3669 3670 // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again. 3671 mQuotaController.saveTimingSession(0, "com.android.test", 3672 createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); 3673 mQuotaController.saveTimingSession(0, "com.android.test", 3674 createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false); 3675 synchronized (mQuotaController.mLock) { 3676 mQuotaController.maybeScheduleCleanupAlarmLocked(); 3677 } 3678 verify(mAlarmManager, times(1)) 3679 .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any()); 3680 } 3681 3682 @Test testMaybeScheduleStartAlarmLocked_Active()3683 public void testMaybeScheduleStartAlarmLocked_Active() { 3684 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 3685 // because it schedules an alarm too. Prevent it from doing so. 3686 spyOn(mQuotaController); 3687 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 3688 3689 // Active window size is 10 minutes. 3690 final int standbyBucket = ACTIVE_INDEX; 3691 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); 3692 3693 JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1); 3694 setStandbyBucket(standbyBucket, jobStatus); 3695 synchronized (mQuotaController.mLock) { 3696 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 3697 } 3698 3699 // No sessions saved yet. 3700 synchronized (mQuotaController.mLock) { 3701 mQuotaController.maybeScheduleStartAlarmLocked( 3702 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3703 } 3704 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3705 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3706 3707 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3708 // Test with timing sessions out of window but still under max execution limit. 3709 final long expectedAlarmTime = 3710 (now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; 3711 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3712 createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false); 3713 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3714 createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false); 3715 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3716 createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false); 3717 synchronized (mQuotaController.mLock) { 3718 mQuotaController.maybeScheduleStartAlarmLocked( 3719 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3720 } 3721 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3722 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3723 3724 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3725 createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1), false); 3726 synchronized (mQuotaController.mLock) { 3727 mQuotaController.maybeScheduleStartAlarmLocked( 3728 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3729 } 3730 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3731 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3732 3733 synchronized (mQuotaController.mLock) { 3734 mQuotaController.prepareForExecutionLocked(jobStatus); 3735 } 3736 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 3737 synchronized (mQuotaController.mLock) { 3738 // Timer has only been going for 5 minutes in the past 10 minutes, which is under the 3739 // window size limit, but the total execution time for the past 24 hours is 6 hours, so 3740 // the job no longer has quota. 3741 assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked(jobStatus)); 3742 mQuotaController.maybeScheduleStartAlarmLocked( 3743 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3744 } 3745 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 3746 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 3747 any(Handler.class)); 3748 } 3749 3750 @Test testMaybeScheduleStartAlarmLocked_WorkingSet()3751 public void testMaybeScheduleStartAlarmLocked_WorkingSet() { 3752 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 3753 // because it schedules an alarm too. Prevent it from doing so. 3754 spyOn(mQuotaController); 3755 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 3756 3757 // Working set window size is 2 hours. 3758 final int standbyBucket = WORKING_INDEX; 3759 3760 JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_WorkingSet", 1); 3761 setStandbyBucket(standbyBucket, jobStatus); 3762 synchronized (mQuotaController.mLock) { 3763 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 3764 // No sessions saved yet. 3765 mQuotaController.maybeScheduleStartAlarmLocked( 3766 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3767 } 3768 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3769 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3770 3771 // Test with timing sessions out of window. 3772 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3773 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3774 createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); 3775 synchronized (mQuotaController.mLock) { 3776 mQuotaController.maybeScheduleStartAlarmLocked( 3777 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3778 } 3779 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3780 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3781 3782 // Test with timing sessions in window but still in quota. 3783 final long end = now - (mQcConstants.WINDOW_SIZE_WORKING_MS - 5 * MINUTE_IN_MILLIS); 3784 // Counting backwards, the quota will come back one minute before the end. 3785 final long expectedAlarmTime = end - MINUTE_IN_MILLIS + mQcConstants.WINDOW_SIZE_WORKING_MS 3786 + mQcConstants.IN_QUOTA_BUFFER_MS; 3787 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3788 new TimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, end, 1), false); 3789 synchronized (mQuotaController.mLock) { 3790 mQuotaController.maybeScheduleStartAlarmLocked( 3791 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3792 } 3793 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3794 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3795 3796 // Add some more sessions, but still in quota. 3797 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3798 createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); 3799 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3800 createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false); 3801 synchronized (mQuotaController.mLock) { 3802 mQuotaController.maybeScheduleStartAlarmLocked( 3803 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3804 } 3805 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3806 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3807 3808 // Test when out of quota. 3809 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3810 createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); 3811 synchronized (mQuotaController.mLock) { 3812 mQuotaController.maybeScheduleStartAlarmLocked( 3813 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3814 } 3815 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 3816 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 3817 any(Handler.class)); 3818 3819 // Alarm already scheduled, so make sure it's not scheduled again. 3820 synchronized (mQuotaController.mLock) { 3821 mQuotaController.maybeScheduleStartAlarmLocked( 3822 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3823 } 3824 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 3825 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 3826 any(Handler.class)); 3827 } 3828 3829 @Test testMaybeScheduleStartAlarmLocked_Frequent()3830 public void testMaybeScheduleStartAlarmLocked_Frequent() { 3831 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 3832 // because it schedules an alarm too. Prevent it from doing so. 3833 spyOn(mQuotaController); 3834 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 3835 3836 synchronized (mQuotaController.mLock) { 3837 mQuotaController.maybeStartTrackingJobLocked( 3838 createJobStatus("testMaybeScheduleStartAlarmLocked_Frequent", 1), null); 3839 } 3840 3841 // Frequent window size is 8 hours. 3842 final int standbyBucket = FREQUENT_INDEX; 3843 3844 // No sessions saved yet. 3845 synchronized (mQuotaController.mLock) { 3846 mQuotaController.maybeScheduleStartAlarmLocked( 3847 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3848 } 3849 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3850 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3851 3852 // Test with timing sessions out of window. 3853 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3854 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3855 createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS 3856 - 2 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); 3857 synchronized (mQuotaController.mLock) { 3858 mQuotaController.maybeScheduleStartAlarmLocked( 3859 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3860 } 3861 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3862 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3863 3864 // Test with timing sessions in window but still in quota. 3865 final long start = now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - 2 * HOUR_IN_MILLIS); 3866 final long expectedAlarmTime = start + mQcConstants.WINDOW_SIZE_FREQUENT_MS 3867 + mQcConstants.IN_QUOTA_BUFFER_MS; 3868 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3869 createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false); 3870 synchronized (mQuotaController.mLock) { 3871 mQuotaController.maybeScheduleStartAlarmLocked( 3872 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3873 } 3874 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3875 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3876 3877 // Add some more sessions, but still in quota. 3878 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3879 createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); 3880 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3881 createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false); 3882 synchronized (mQuotaController.mLock) { 3883 mQuotaController.maybeScheduleStartAlarmLocked( 3884 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3885 } 3886 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3887 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3888 3889 // Test when out of quota. 3890 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3891 createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); 3892 synchronized (mQuotaController.mLock) { 3893 mQuotaController.maybeScheduleStartAlarmLocked( 3894 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3895 } 3896 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 3897 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 3898 any(Handler.class)); 3899 3900 // Alarm already scheduled, so make sure it's not scheduled again. 3901 synchronized (mQuotaController.mLock) { 3902 mQuotaController.maybeScheduleStartAlarmLocked( 3903 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 3904 } 3905 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 3906 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 3907 any(Handler.class)); 3908 } 3909 3910 /** 3911 * Test that QC handles invalid cases where an app is in the NEVER bucket but has still run 3912 * jobs. 3913 */ 3914 @Test testMaybeScheduleStartAlarmLocked_Never_EffectiveNotNever()3915 public void testMaybeScheduleStartAlarmLocked_Never_EffectiveNotNever() { 3916 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 3917 // because it schedules an alarm too. Prevent it from doing so. 3918 spyOn(mQuotaController); 3919 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 3920 3921 synchronized (mQuotaController.mLock) { 3922 mQuotaController.maybeStartTrackingJobLocked( 3923 createJobStatus("testMaybeScheduleStartAlarmLocked_Never", 1), null); 3924 } 3925 3926 // The app is really in the NEVER bucket but is elevated somehow (eg via uidActive). 3927 setStandbyBucket(NEVER_INDEX); 3928 final int effectiveStandbyBucket = FREQUENT_INDEX; 3929 3930 // No sessions saved yet. 3931 synchronized (mQuotaController.mLock) { 3932 mQuotaController.maybeScheduleStartAlarmLocked( 3933 SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket); 3934 } 3935 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3936 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3937 3938 // Test with timing sessions out of window. 3939 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 3940 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3941 createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS 3942 - 2 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); 3943 synchronized (mQuotaController.mLock) { 3944 mQuotaController.maybeScheduleStartAlarmLocked( 3945 SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket); 3946 } 3947 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3948 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3949 3950 // Test with timing sessions in window but still in quota. 3951 final long start = now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - 2 * HOUR_IN_MILLIS); 3952 final long expectedAlarmTime = start + mQcConstants.WINDOW_SIZE_FREQUENT_MS 3953 + mQcConstants.IN_QUOTA_BUFFER_MS; 3954 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3955 createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false); 3956 synchronized (mQuotaController.mLock) { 3957 mQuotaController.maybeScheduleStartAlarmLocked( 3958 SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket); 3959 } 3960 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3961 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3962 3963 // Add some more sessions, but still in quota. 3964 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3965 createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); 3966 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3967 createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false); 3968 synchronized (mQuotaController.mLock) { 3969 mQuotaController.maybeScheduleStartAlarmLocked( 3970 SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket); 3971 } 3972 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 3973 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 3974 3975 // Test when out of quota. 3976 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 3977 createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); 3978 synchronized (mQuotaController.mLock) { 3979 mQuotaController.maybeScheduleStartAlarmLocked( 3980 SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket); 3981 } 3982 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 3983 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 3984 any(Handler.class)); 3985 3986 // Alarm already scheduled, so make sure it's not scheduled again. 3987 synchronized (mQuotaController.mLock) { 3988 mQuotaController.maybeScheduleStartAlarmLocked( 3989 SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket); 3990 } 3991 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 3992 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 3993 any(Handler.class)); 3994 } 3995 3996 @Test testMaybeScheduleStartAlarmLocked_Rare()3997 public void testMaybeScheduleStartAlarmLocked_Rare() { 3998 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 3999 // because it schedules an alarm too. Prevent it from doing so. 4000 spyOn(mQuotaController); 4001 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 4002 4003 // Rare window size is 24 hours. 4004 final int standbyBucket = RARE_INDEX; 4005 4006 JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Rare", 1); 4007 setStandbyBucket(standbyBucket, jobStatus); 4008 synchronized (mQuotaController.mLock) { 4009 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4010 } 4011 4012 // Prevent timing session throttling from affecting the test. 4013 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 50); 4014 4015 // No sessions saved yet. 4016 synchronized (mQuotaController.mLock) { 4017 mQuotaController.maybeScheduleStartAlarmLocked( 4018 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4019 } 4020 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 4021 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4022 4023 // Test with timing sessions out of window. 4024 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 4025 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4026 createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); 4027 synchronized (mQuotaController.mLock) { 4028 mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); 4029 } 4030 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 4031 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4032 4033 // Test with timing sessions in window but still in quota. 4034 final long start = now - (6 * HOUR_IN_MILLIS); 4035 // Counting backwards, the first minute in the session is over the allowed time, so it 4036 // needs to be excluded. 4037 final long expectedAlarmTime = 4038 start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS 4039 + mQcConstants.IN_QUOTA_BUFFER_MS; 4040 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4041 createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false); 4042 synchronized (mQuotaController.mLock) { 4043 mQuotaController.maybeScheduleStartAlarmLocked( 4044 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4045 } 4046 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 4047 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4048 4049 // Add some more sessions, but still in quota. 4050 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4051 createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false); 4052 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4053 createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false); 4054 synchronized (mQuotaController.mLock) { 4055 mQuotaController.maybeScheduleStartAlarmLocked( 4056 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4057 } 4058 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 4059 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4060 4061 // Test when out of quota. 4062 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4063 createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1), false); 4064 synchronized (mQuotaController.mLock) { 4065 mQuotaController.maybeScheduleStartAlarmLocked( 4066 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4067 } 4068 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4069 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 4070 any(Handler.class)); 4071 4072 // Alarm already scheduled, so make sure it's not scheduled again. 4073 synchronized (mQuotaController.mLock) { 4074 mQuotaController.maybeScheduleStartAlarmLocked( 4075 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4076 } 4077 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4078 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 4079 any(Handler.class)); 4080 } 4081 4082 /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */ 4083 @Test testMaybeScheduleStartAlarmLocked_BucketChange()4084 public void testMaybeScheduleStartAlarmLocked_BucketChange() { 4085 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 4086 // because it schedules an alarm too. Prevent it from doing so. 4087 spyOn(mQuotaController); 4088 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 4089 4090 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 4091 4092 // Affects rare bucket 4093 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4094 createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3), false); 4095 // Affects frequent and rare buckets 4096 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4097 createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3), false); 4098 // Affects working, frequent, and rare buckets 4099 final long outOfQuotaTime = now - HOUR_IN_MILLIS; 4100 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4101 createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10), false); 4102 // Affects all buckets 4103 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4104 createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3), false); 4105 4106 InOrder inOrder = inOrder(mAlarmManager); 4107 4108 JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_BucketChange", 1); 4109 4110 // Start in ACTIVE bucket. 4111 setStandbyBucket(ACTIVE_INDEX, jobStatus); 4112 synchronized (mQuotaController.mLock) { 4113 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4114 mQuotaController.maybeScheduleStartAlarmLocked( 4115 SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX); 4116 } 4117 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 4118 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 4119 any(Handler.class)); 4120 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 4121 .cancel(any(AlarmManager.OnAlarmListener.class)); 4122 4123 // And down from there. 4124 final long expectedWorkingAlarmTime = 4125 outOfQuotaTime + mQcConstants.WINDOW_SIZE_WORKING_MS 4126 + mQcConstants.IN_QUOTA_BUFFER_MS; 4127 setStandbyBucket(WORKING_INDEX, jobStatus); 4128 synchronized (mQuotaController.mLock) { 4129 mQuotaController.maybeScheduleStartAlarmLocked( 4130 SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); 4131 } 4132 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4133 anyInt(), eq(expectedWorkingAlarmTime), anyLong(), 4134 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4135 4136 final long expectedFrequentAlarmTime = 4137 outOfQuotaTime + mQcConstants.WINDOW_SIZE_FREQUENT_MS 4138 + mQcConstants.IN_QUOTA_BUFFER_MS; 4139 setStandbyBucket(FREQUENT_INDEX, jobStatus); 4140 synchronized (mQuotaController.mLock) { 4141 mQuotaController.maybeScheduleStartAlarmLocked( 4142 SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX); 4143 } 4144 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4145 anyInt(), eq(expectedFrequentAlarmTime), anyLong(), 4146 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4147 4148 final long expectedRareAlarmTime = 4149 outOfQuotaTime + mQcConstants.WINDOW_SIZE_RARE_MS 4150 + mQcConstants.IN_QUOTA_BUFFER_MS; 4151 setStandbyBucket(RARE_INDEX, jobStatus); 4152 synchronized (mQuotaController.mLock) { 4153 mQuotaController.maybeScheduleStartAlarmLocked( 4154 SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX); 4155 } 4156 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4157 anyInt(), eq(expectedRareAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 4158 any(Handler.class)); 4159 4160 // And back up again. 4161 setStandbyBucket(FREQUENT_INDEX, jobStatus); 4162 synchronized (mQuotaController.mLock) { 4163 mQuotaController.maybeScheduleStartAlarmLocked( 4164 SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX); 4165 } 4166 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4167 anyInt(), eq(expectedFrequentAlarmTime), anyLong(), 4168 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4169 4170 setStandbyBucket(WORKING_INDEX, jobStatus); 4171 synchronized (mQuotaController.mLock) { 4172 mQuotaController.maybeScheduleStartAlarmLocked( 4173 SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); 4174 } 4175 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4176 anyInt(), eq(expectedWorkingAlarmTime), anyLong(), 4177 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4178 4179 setStandbyBucket(ACTIVE_INDEX, jobStatus); 4180 synchronized (mQuotaController.mLock) { 4181 mQuotaController.maybeScheduleStartAlarmLocked( 4182 SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX); 4183 } 4184 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 4185 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 4186 any(Handler.class)); 4187 inOrder.verify(mAlarmManager, timeout(1000).times(1)) 4188 .cancel(any(AlarmManager.OnAlarmListener.class)); 4189 } 4190 4191 @Test testMaybeScheduleStartAlarmLocked_JobCount_RateLimitingWindow()4192 public void testMaybeScheduleStartAlarmLocked_JobCount_RateLimitingWindow() { 4193 // Set rate limiting period different from allowed time to confirm code sets based on 4194 // the former. 4195 final int standbyBucket = WORKING_INDEX; 4196 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, 4197 10 * MINUTE_IN_MILLIS); 4198 setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 5 * MINUTE_IN_MILLIS); 4199 4200 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 4201 4202 JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked", 1); 4203 setStandbyBucket(standbyBucket, jobStatus); 4204 synchronized (mQuotaController.mLock) { 4205 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4206 } 4207 4208 ExecutionStats stats; 4209 synchronized (mQuotaController.mLock) { 4210 stats = mQuotaController.getExecutionStatsLocked( 4211 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4212 } 4213 stats.jobCountInRateLimitingWindow = 4214 mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW + 2; 4215 4216 // Invalid time in the past, so the count shouldn't be used. 4217 stats.jobRateLimitExpirationTimeElapsed = now - 5 * MINUTE_IN_MILLIS / 2; 4218 synchronized (mQuotaController.mLock) { 4219 mQuotaController.maybeScheduleStartAlarmLocked( 4220 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4221 } 4222 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 4223 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4224 4225 // Valid time in the future, so the count should be used. 4226 stats.jobRateLimitExpirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS / 2; 4227 final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed; 4228 synchronized (mQuotaController.mLock) { 4229 mQuotaController.maybeScheduleStartAlarmLocked( 4230 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4231 } 4232 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4233 anyInt(), eq(expectedWorkingAlarmTime), anyLong(), 4234 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 4235 } 4236 4237 /** 4238 * Tests that the start alarm is properly rescheduled if the earliest session that contributes 4239 * to the app being out of quota contributes less than the quota buffer time. 4240 */ 4241 @Test testMaybeScheduleStartAlarmLocked_SmallRollingQuota_DefaultValues()4242 public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_DefaultValues() { 4243 // Use the default values 4244 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); 4245 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 4246 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); 4247 } 4248 4249 @Test testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedBufferSize()4250 public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedBufferSize() { 4251 // Make sure any new value is used correctly. 4252 setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 4253 mQcConstants.IN_QUOTA_BUFFER_MS * 2); 4254 4255 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); 4256 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 4257 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); 4258 } 4259 4260 @Test testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime()4261 public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() { 4262 // Make sure any new value is used correctly. 4263 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, 4264 mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS / 2); 4265 4266 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); 4267 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 4268 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); 4269 } 4270 4271 @Test testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedMaxTime()4272 public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedMaxTime() { 4273 // Make sure any new value is used correctly. 4274 setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 4275 mQcConstants.MAX_EXECUTION_TIME_MS / 2); 4276 4277 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); 4278 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 4279 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); 4280 } 4281 4282 @Test testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedEverything()4283 public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedEverything() { 4284 // Make sure any new value is used correctly. 4285 setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 4286 mQcConstants.IN_QUOTA_BUFFER_MS * 2); 4287 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, 4288 mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS / 2); 4289 setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 4290 mQcConstants.MAX_EXECUTION_TIME_MS / 2); 4291 4292 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); 4293 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); 4294 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); 4295 } 4296 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck()4297 private void runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck() { 4298 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 4299 // because it schedules an alarm too. Prevent it from doing so. 4300 spyOn(mQuotaController); 4301 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 4302 4303 synchronized (mQuotaController.mLock) { 4304 mQuotaController.maybeStartTrackingJobLocked( 4305 createJobStatus("testMaybeScheduleStartAlarmLocked", 1), null); 4306 } 4307 4308 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 4309 // Working set window size is configured with QcConstants.WINDOW_SIZE_WORKING_MS. 4310 final int standbyBucket = WORKING_INDEX; 4311 final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2; 4312 final long remainingTimeMs = 4313 mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS - contributionMs; 4314 4315 // Session straddles edge of bucket window. Only the contribution should be counted towards 4316 // the quota. 4317 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4318 createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS 4319 - 3 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS + contributionMs, 4320 3), false); 4321 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4322 createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2), false); 4323 // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which 4324 // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. 4325 final long expectedAlarmTime = now - HOUR_IN_MILLIS + mQcConstants.WINDOW_SIZE_WORKING_MS 4326 + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); 4327 synchronized (mQuotaController.mLock) { 4328 mQuotaController.maybeScheduleStartAlarmLocked( 4329 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4330 } 4331 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4332 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 4333 any(Handler.class)); 4334 } 4335 runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck()4336 private void runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck() { 4337 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 4338 // because it schedules an alarm too. Prevent it from doing so. 4339 spyOn(mQuotaController); 4340 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 4341 4342 synchronized (mQuotaController.mLock) { 4343 mQuotaController.maybeStartTrackingJobLocked( 4344 createJobStatus("testMaybeScheduleStartAlarmLocked", 1), null); 4345 } 4346 4347 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 4348 // Working set window size is 2 hours. 4349 final int standbyBucket = WORKING_INDEX; 4350 final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2; 4351 final long remainingTimeMs = mQcConstants.MAX_EXECUTION_TIME_MS - contributionMs; 4352 4353 // Session straddles edge of 24 hour window. Only the contribution should be counted towards 4354 // the quota. 4355 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4356 createTimingSession(now - (24 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS), 4357 3 * MINUTE_IN_MILLIS + contributionMs, 3), false); 4358 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 4359 createTimingSession(now - 20 * HOUR_IN_MILLIS, remainingTimeMs, 300), false); 4360 // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which 4361 // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. 4362 final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS 4363 + 24 * HOUR_IN_MILLIS 4364 + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); 4365 synchronized (mQuotaController.mLock) { 4366 mQuotaController.maybeScheduleStartAlarmLocked( 4367 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 4368 } 4369 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 4370 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 4371 any(Handler.class)); 4372 } 4373 4374 @Test testConstantsUpdating_ValidValues()4375 public void testConstantsUpdating_ValidValues() { 4376 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 4377 8 * MINUTE_IN_MILLIS); 4378 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 4379 5 * MINUTE_IN_MILLIS); 4380 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, 4381 7 * MINUTE_IN_MILLIS); 4382 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 4383 2 * MINUTE_IN_MILLIS); 4384 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 4 * MINUTE_IN_MILLIS); 4385 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, 4386 11 * MINUTE_IN_MILLIS); 4387 setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS); 4388 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 99 * MINUTE_IN_MILLIS); 4389 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS); 4390 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS); 4391 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 45 * MINUTE_IN_MILLIS); 4392 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 60 * MINUTE_IN_MILLIS); 4393 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 120 * MINUTE_IN_MILLIS); 4394 setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 3 * HOUR_IN_MILLIS); 4395 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_EXEMPTED, 6000); 4396 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, 5000); 4397 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 4000); 4398 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 3000); 4399 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RARE, 2000); 4400 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, 2000); 4401 setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * MINUTE_IN_MILLIS); 4402 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 500); 4403 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_EXEMPTED, 600); 4404 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 500); 4405 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 400); 4406 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 300); 4407 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 200); 4408 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RESTRICTED, 100); 4409 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 50); 4410 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 4411 10 * SECOND_IN_MILLIS); 4412 setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 7 * MINUTE_IN_MILLIS); 4413 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, 3 * HOUR_IN_MILLIS); 4414 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 2 * HOUR_IN_MILLIS); 4415 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 90 * MINUTE_IN_MILLIS); 4416 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS); 4417 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 30 * MINUTE_IN_MILLIS); 4418 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 27 * MINUTE_IN_MILLIS); 4419 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ADDITION_INSTALLER_MS, 7 * HOUR_IN_MILLIS); 4420 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ADDITION_SPECIAL_MS, 10 * HOUR_IN_MILLIS); 4421 setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 12 * HOUR_IN_MILLIS); 4422 setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 10 * MINUTE_IN_MILLIS); 4423 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS); 4424 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 86 * SECOND_IN_MILLIS); 4425 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 85 * SECOND_IN_MILLIS); 4426 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 4427 84 * SECOND_IN_MILLIS); 4428 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS); 4429 4430 assertEquals(8 * MINUTE_IN_MILLIS, 4431 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); 4432 assertEquals(5 * MINUTE_IN_MILLIS, 4433 mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]); 4434 assertEquals(7 * MINUTE_IN_MILLIS, 4435 mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]); 4436 assertEquals(2 * MINUTE_IN_MILLIS, 4437 mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]); 4438 assertEquals(4 * MINUTE_IN_MILLIS, 4439 mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]); 4440 assertEquals(11 * MINUTE_IN_MILLIS, 4441 mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]); 4442 assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); 4443 assertEquals(99 * MINUTE_IN_MILLIS, 4444 mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]); 4445 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); 4446 assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); 4447 assertEquals(45 * MINUTE_IN_MILLIS, 4448 mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]); 4449 assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]); 4450 assertEquals(120 * MINUTE_IN_MILLIS, 4451 mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]); 4452 assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs()); 4453 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getRateLimitingWindowMs()); 4454 assertEquals(500, mQuotaController.getMaxJobCountPerRateLimitingWindow()); 4455 assertEquals(6000, mQuotaController.getBucketMaxJobCounts()[EXEMPTED_INDEX]); 4456 assertEquals(5000, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]); 4457 assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]); 4458 assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]); 4459 assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]); 4460 assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]); 4461 assertEquals(50, mQuotaController.getMaxSessionCountPerRateLimitingWindow()); 4462 assertEquals(600, mQuotaController.getBucketMaxSessionCounts()[EXEMPTED_INDEX]); 4463 assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]); 4464 assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]); 4465 assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]); 4466 assertEquals(200, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]); 4467 assertEquals(100, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]); 4468 assertEquals(10 * SECOND_IN_MILLIS, 4469 mQuotaController.getTimingSessionCoalescingDurationMs()); 4470 assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs()); 4471 assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]); 4472 assertEquals(2 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]); 4473 assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]); 4474 assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); 4475 assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); 4476 assertEquals(27 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); 4477 assertEquals(7 * HOUR_IN_MILLIS, mQuotaController.getEjLimitAdditionInstallerMs()); 4478 assertEquals(10 * HOUR_IN_MILLIS, mQuotaController.getEjLimitAdditionSpecialMs()); 4479 assertEquals(12 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); 4480 assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); 4481 assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); 4482 assertEquals(86 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); 4483 assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); 4484 assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); 4485 assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); 4486 4487 mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER); 4488 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS, 4489 6 * MINUTE_IN_MILLIS); 4490 assertEquals(6 * MINUTE_IN_MILLIS, 4491 mQuotaController.getAllowedTimePeriodAdditionInstallerMs()); 4492 } 4493 4494 @Test testConstantsUpdating_InvalidValues()4495 public void testConstantsUpdating_InvalidValues() { 4496 // Test negatives/too low. 4497 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, -MINUTE_IN_MILLIS); 4498 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, -MINUTE_IN_MILLIS); 4499 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, -MINUTE_IN_MILLIS); 4500 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, -MINUTE_IN_MILLIS); 4501 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, -MINUTE_IN_MILLIS); 4502 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, 4503 -MINUTE_IN_MILLIS); 4504 setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS); 4505 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, -MINUTE_IN_MILLIS); 4506 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS); 4507 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS); 4508 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, -MINUTE_IN_MILLIS); 4509 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, -MINUTE_IN_MILLIS); 4510 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, -MINUTE_IN_MILLIS); 4511 setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, -MINUTE_IN_MILLIS); 4512 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_EXEMPTED, -1); 4513 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, -1); 4514 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 1); 4515 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 1); 4516 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RARE, 1); 4517 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, -1); 4518 setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * SECOND_IN_MILLIS); 4519 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 0); 4520 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_EXEMPTED, -1); 4521 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, -1); 4522 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 0); 4523 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, -3); 4524 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 0); 4525 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RESTRICTED, -5); 4526 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 0); 4527 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, -1); 4528 setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, -1); 4529 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, -1); 4530 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, -1); 4531 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, -1); 4532 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, -1); 4533 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, -1); 4534 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, -1); 4535 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ADDITION_INSTALLER_MS, -1); 4536 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ADDITION_SPECIAL_MS, -1); 4537 setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, -1); 4538 setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, -1); 4539 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, -1); 4540 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, -1); 4541 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1); 4542 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1); 4543 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1); 4544 4545 assertEquals(MINUTE_IN_MILLIS, 4546 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); 4547 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]); 4548 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]); 4549 assertEquals(MINUTE_IN_MILLIS, 4550 mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]); 4551 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]); 4552 assertEquals(MINUTE_IN_MILLIS, 4553 mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]); 4554 assertEquals(0, mQuotaController.getInQuotaBufferMs()); 4555 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]); 4556 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); 4557 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); 4558 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]); 4559 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]); 4560 assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]); 4561 assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs()); 4562 assertEquals(30 * SECOND_IN_MILLIS, mQuotaController.getRateLimitingWindowMs()); 4563 assertEquals(10, mQuotaController.getMaxJobCountPerRateLimitingWindow()); 4564 assertEquals(10, mQuotaController.getBucketMaxJobCounts()[EXEMPTED_INDEX]); 4565 assertEquals(10, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]); 4566 assertEquals(10, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]); 4567 assertEquals(10, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]); 4568 assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]); 4569 assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]); 4570 assertEquals(10, mQuotaController.getMaxSessionCountPerRateLimitingWindow()); 4571 assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[EXEMPTED_INDEX]); 4572 assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]); 4573 assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]); 4574 assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]); 4575 assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]); 4576 assertEquals(0, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]); 4577 assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs()); 4578 assertEquals(0, mQuotaController.getMinQuotaCheckDelayMs()); 4579 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]); 4580 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]); 4581 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]); 4582 assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); 4583 assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); 4584 assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); 4585 assertEquals(0, mQuotaController.getEjLimitAdditionInstallerMs()); 4586 assertEquals(0, mQuotaController.getEjLimitAdditionSpecialMs()); 4587 assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); 4588 assertEquals(1, mQuotaController.getEJTopAppTimeChunkSizeMs()); 4589 assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); 4590 assertEquals(5 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); 4591 assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs()); 4592 assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs()); 4593 assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs()); 4594 4595 mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER); 4596 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS, 4597 -MINUTE_IN_MILLIS); 4598 assertEquals(0, 4599 mQuotaController.getAllowedTimePeriodAdditionInstallerMs()); 4600 mSetFlagsRule.disableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER); 4601 4602 // Invalid configurations. 4603 // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD 4604 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 4605 10 * MINUTE_IN_MILLIS); 4606 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 4607 10 * MINUTE_IN_MILLIS); 4608 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, 4609 10 * MINUTE_IN_MILLIS); 4610 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 4611 2 * MINUTE_IN_MILLIS); 4612 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 10 * MINUTE_IN_MILLIS); 4613 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, 4614 10 * MINUTE_IN_MILLIS); 4615 setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 5 * MINUTE_IN_MILLIS); 4616 4617 assertTrue(mQuotaController.getAllowedTimePeriodAdditionInstallerMs() 4618 <= mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]); 4619 4620 mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER); 4621 // ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER should never be greater than 4622 // ALLOWED_TIME_PER_PERIOD. 4623 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS, 4624 15 * MINUTE_IN_MILLIS); 4625 assertTrue(mQuotaController.getInQuotaBufferMs() 4626 <= mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); 4627 mSetFlagsRule.disableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER); 4628 4629 // Test larger than a day. Controller should cap at one day. 4630 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 4631 25 * HOUR_IN_MILLIS); 4632 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 25 * HOUR_IN_MILLIS); 4633 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, 4634 25 * HOUR_IN_MILLIS); 4635 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 4636 25 * HOUR_IN_MILLIS); 4637 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 25 * HOUR_IN_MILLIS); 4638 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS, 4639 25 * HOUR_IN_MILLIS); 4640 setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS); 4641 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 25 * HOUR_IN_MILLIS); 4642 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS); 4643 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS); 4644 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 25 * HOUR_IN_MILLIS); 4645 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 25 * HOUR_IN_MILLIS); 4646 setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 30 * 24 * HOUR_IN_MILLIS); 4647 setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 25 * HOUR_IN_MILLIS); 4648 setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 25 * HOUR_IN_MILLIS); 4649 setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 4650 25 * HOUR_IN_MILLIS); 4651 setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 25 * HOUR_IN_MILLIS); 4652 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, 25 * HOUR_IN_MILLIS); 4653 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 25 * HOUR_IN_MILLIS); 4654 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 25 * HOUR_IN_MILLIS); 4655 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS); 4656 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 25 * HOUR_IN_MILLIS); 4657 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 25 * HOUR_IN_MILLIS); 4658 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ADDITION_INSTALLER_MS, 25 * HOUR_IN_MILLIS); 4659 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ADDITION_SPECIAL_MS, 25 * HOUR_IN_MILLIS); 4660 setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); 4661 setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 25 * HOUR_IN_MILLIS); 4662 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); 4663 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 25 * HOUR_IN_MILLIS); 4664 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS); 4665 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS); 4666 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); 4667 4668 assertEquals(24 * HOUR_IN_MILLIS, 4669 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); 4670 assertEquals(24 * HOUR_IN_MILLIS, 4671 mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]); 4672 assertEquals(24 * HOUR_IN_MILLIS, 4673 mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]); 4674 assertEquals(24 * HOUR_IN_MILLIS, 4675 mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]); 4676 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]); 4677 assertEquals(24 * HOUR_IN_MILLIS, 4678 mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]); 4679 assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); 4680 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]); 4681 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]); 4682 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]); 4683 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]); 4684 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]); 4685 assertEquals(7 * 24 * HOUR_IN_MILLIS, 4686 mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]); 4687 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs()); 4688 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getRateLimitingWindowMs()); 4689 assertEquals(15 * MINUTE_IN_MILLIS, 4690 mQuotaController.getTimingSessionCoalescingDurationMs()); 4691 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs()); 4692 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]); 4693 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]); 4694 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]); 4695 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); 4696 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); 4697 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); 4698 assertEquals(0, mQuotaController.getEjLimitAdditionInstallerMs()); 4699 assertEquals(0, mQuotaController.getEjLimitAdditionSpecialMs()); 4700 assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); 4701 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); 4702 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); 4703 assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); 4704 assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); 4705 assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); 4706 assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); 4707 4708 mSetFlagsRule.enableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER); 4709 setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS, 4710 25 * HOUR_IN_MILLIS); 4711 assertEquals(0, mQuotaController.getAllowedTimePeriodAdditionInstallerMs()); 4712 mSetFlagsRule.disableFlags(Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER); 4713 } 4714 4715 /** Tests that TimingSessions aren't saved when the device is charging. */ 4716 @Test testTimerTracking_Charging()4717 public void testTimerTracking_Charging() { 4718 setCharging(); 4719 4720 JobStatus jobStatus = createJobStatus("testTimerTracking_Charging", 1); 4721 synchronized (mQuotaController.mLock) { 4722 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4723 } 4724 4725 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4726 4727 synchronized (mQuotaController.mLock) { 4728 mQuotaController.prepareForExecutionLocked(jobStatus); 4729 } 4730 advanceElapsedClock(5 * SECOND_IN_MILLIS); 4731 synchronized (mQuotaController.mLock) { 4732 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 4733 } 4734 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4735 } 4736 4737 /** Tests that TimingSessions are saved properly when the device is discharging. */ 4738 @Test testTimerTracking_Discharging()4739 public void testTimerTracking_Discharging() { 4740 setDischarging(); 4741 setProcessState(ActivityManager.PROCESS_STATE_BACKUP); 4742 4743 JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1); 4744 synchronized (mQuotaController.mLock) { 4745 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4746 } 4747 4748 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4749 4750 List<TimingSession> expected = new ArrayList<>(); 4751 4752 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4753 synchronized (mQuotaController.mLock) { 4754 mQuotaController.prepareForExecutionLocked(jobStatus); 4755 } 4756 advanceElapsedClock(5 * SECOND_IN_MILLIS); 4757 synchronized (mQuotaController.mLock) { 4758 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 4759 } 4760 expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); 4761 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4762 4763 // Test overlapping jobs. 4764 JobStatus jobStatus2 = createJobStatus("testTimerTracking_Discharging", 2); 4765 synchronized (mQuotaController.mLock) { 4766 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 4767 } 4768 4769 JobStatus jobStatus3 = createJobStatus("testTimerTracking_Discharging", 3); 4770 synchronized (mQuotaController.mLock) { 4771 mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); 4772 } 4773 4774 advanceElapsedClock(SECOND_IN_MILLIS); 4775 4776 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4777 synchronized (mQuotaController.mLock) { 4778 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4779 mQuotaController.prepareForExecutionLocked(jobStatus); 4780 } 4781 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4782 synchronized (mQuotaController.mLock) { 4783 mQuotaController.prepareForExecutionLocked(jobStatus2); 4784 } 4785 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4786 synchronized (mQuotaController.mLock) { 4787 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 4788 } 4789 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4790 synchronized (mQuotaController.mLock) { 4791 mQuotaController.prepareForExecutionLocked(jobStatus3); 4792 } 4793 advanceElapsedClock(20 * SECOND_IN_MILLIS); 4794 synchronized (mQuotaController.mLock) { 4795 mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null); 4796 } 4797 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4798 synchronized (mQuotaController.mLock) { 4799 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 4800 } 4801 expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); 4802 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4803 } 4804 4805 /** 4806 * Tests that TimingSessions are saved properly when the device alternates between 4807 * charging and discharging. 4808 */ 4809 @Test testTimerTracking_ChargingAndDischarging()4810 public void testTimerTracking_ChargingAndDischarging() { 4811 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 4812 4813 JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1); 4814 synchronized (mQuotaController.mLock) { 4815 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4816 } 4817 JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2); 4818 synchronized (mQuotaController.mLock) { 4819 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 4820 } 4821 JobStatus jobStatus3 = createJobStatus("testTimerTracking_ChargingAndDischarging", 3); 4822 synchronized (mQuotaController.mLock) { 4823 mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); 4824 } 4825 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4826 List<TimingSession> expected = new ArrayList<>(); 4827 4828 // A job starting while charging. Only the portion that runs during the discharging period 4829 // should be counted. 4830 setCharging(); 4831 4832 synchronized (mQuotaController.mLock) { 4833 mQuotaController.prepareForExecutionLocked(jobStatus); 4834 } 4835 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4836 setDischarging(); 4837 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4838 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4839 synchronized (mQuotaController.mLock) { 4840 mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus); 4841 } 4842 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 4843 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4844 4845 advanceElapsedClock(SECOND_IN_MILLIS); 4846 4847 // One job starts while discharging, spans a charging session, and ends after the charging 4848 // session. Only the portions during the discharging periods should be counted. This should 4849 // result in two TimingSessions. A second job starts while discharging and ends within the 4850 // charging session. Only the portion during the first discharging portion should be 4851 // counted. A third job starts and ends within the charging session. The third job 4852 // shouldn't be included in either job count. 4853 setDischarging(); 4854 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4855 synchronized (mQuotaController.mLock) { 4856 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4857 mQuotaController.prepareForExecutionLocked(jobStatus); 4858 } 4859 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4860 synchronized (mQuotaController.mLock) { 4861 mQuotaController.prepareForExecutionLocked(jobStatus2); 4862 } 4863 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4864 setCharging(); 4865 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); 4866 synchronized (mQuotaController.mLock) { 4867 mQuotaController.prepareForExecutionLocked(jobStatus3); 4868 } 4869 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4870 synchronized (mQuotaController.mLock) { 4871 mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null); 4872 } 4873 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4874 synchronized (mQuotaController.mLock) { 4875 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 4876 } 4877 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4878 setDischarging(); 4879 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4880 advanceElapsedClock(20 * SECOND_IN_MILLIS); 4881 synchronized (mQuotaController.mLock) { 4882 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 4883 } 4884 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1)); 4885 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4886 4887 // A job starting while discharging and ending while charging. Only the portion that runs 4888 // during the discharging period should be counted. 4889 setDischarging(); 4890 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4891 synchronized (mQuotaController.mLock) { 4892 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 4893 mQuotaController.prepareForExecutionLocked(jobStatus2); 4894 } 4895 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4896 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 4897 setCharging(); 4898 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4899 synchronized (mQuotaController.mLock) { 4900 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 4901 } 4902 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4903 } 4904 4905 /** Tests that TimingSessions are saved properly when all the jobs are background jobs. */ 4906 @Test testTimerTracking_AllBackground()4907 public void testTimerTracking_AllBackground() { 4908 setDischarging(); 4909 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 4910 4911 JobStatus jobStatus = createJobStatus("testTimerTracking_AllBackground", 1); 4912 synchronized (mQuotaController.mLock) { 4913 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4914 } 4915 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4916 4917 List<TimingSession> expected = new ArrayList<>(); 4918 4919 // Test single job. 4920 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4921 synchronized (mQuotaController.mLock) { 4922 mQuotaController.prepareForExecutionLocked(jobStatus); 4923 } 4924 advanceElapsedClock(5 * SECOND_IN_MILLIS); 4925 synchronized (mQuotaController.mLock) { 4926 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 4927 } 4928 expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); 4929 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4930 4931 // Test overlapping jobs. 4932 JobStatus jobStatus2 = createJobStatus("testTimerTracking_AllBackground", 2); 4933 synchronized (mQuotaController.mLock) { 4934 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 4935 } 4936 4937 JobStatus jobStatus3 = createJobStatus("testTimerTracking_AllBackground", 3); 4938 synchronized (mQuotaController.mLock) { 4939 mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); 4940 } 4941 4942 advanceElapsedClock(SECOND_IN_MILLIS); 4943 4944 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 4945 synchronized (mQuotaController.mLock) { 4946 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4947 mQuotaController.prepareForExecutionLocked(jobStatus); 4948 } 4949 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4950 synchronized (mQuotaController.mLock) { 4951 mQuotaController.prepareForExecutionLocked(jobStatus2); 4952 } 4953 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4954 synchronized (mQuotaController.mLock) { 4955 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 4956 } 4957 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4958 synchronized (mQuotaController.mLock) { 4959 mQuotaController.prepareForExecutionLocked(jobStatus3); 4960 } 4961 advanceElapsedClock(20 * SECOND_IN_MILLIS); 4962 synchronized (mQuotaController.mLock) { 4963 mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null); 4964 } 4965 advanceElapsedClock(10 * SECOND_IN_MILLIS); 4966 synchronized (mQuotaController.mLock) { 4967 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 4968 } 4969 expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); 4970 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4971 } 4972 4973 /** Tests that Timers don't count foreground jobs. */ 4974 @Test testTimerTracking_AllForeground()4975 public void testTimerTracking_AllForeground() { 4976 setDischarging(); 4977 4978 JobStatus jobStatus = createJobStatus("testTimerTracking_AllForeground", 1); 4979 setProcessState(ActivityManager.PROCESS_STATE_TOP); 4980 synchronized (mQuotaController.mLock) { 4981 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 4982 } 4983 4984 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4985 4986 synchronized (mQuotaController.mLock) { 4987 mQuotaController.prepareForExecutionLocked(jobStatus); 4988 } 4989 advanceElapsedClock(5 * SECOND_IN_MILLIS); 4990 // Change to a state that should still be considered foreground. 4991 setProcessState(getProcessStateQuotaFreeThreshold()); 4992 advanceElapsedClock(5 * SECOND_IN_MILLIS); 4993 synchronized (mQuotaController.mLock) { 4994 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 4995 } 4996 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 4997 } 4998 4999 /** Tests that Timers count FOREGROUND_SERVICE jobs. */ 5000 @Test 5001 @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS) testTimerTracking_Fgs()5002 public void testTimerTracking_Fgs() { 5003 setDischarging(); 5004 5005 JobStatus jobStatus = createJobStatus("testTimerTracking_Fgs", 1); 5006 setProcessState(ActivityManager.PROCESS_STATE_BOUND_TOP); 5007 synchronized (mQuotaController.mLock) { 5008 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 5009 } 5010 5011 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5012 5013 synchronized (mQuotaController.mLock) { 5014 mQuotaController.prepareForExecutionLocked(jobStatus); 5015 } 5016 advanceElapsedClock(5 * SECOND_IN_MILLIS); 5017 // Change to FOREGROUND_SERVICE state that should count. 5018 setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); 5019 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5020 advanceElapsedClock(5 * SECOND_IN_MILLIS); 5021 synchronized (mQuotaController.mLock) { 5022 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 5023 } 5024 List<TimingSession> expected = new ArrayList<>(); 5025 expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); 5026 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5027 } 5028 5029 /** 5030 * Tests that Timers properly track sessions when switching between foreground and background 5031 * states. 5032 */ 5033 @Test testTimerTracking_ForegroundAndBackground()5034 public void testTimerTracking_ForegroundAndBackground() { 5035 setDischarging(); 5036 5037 JobStatus jobBg1 = createJobStatus("testTimerTracking_ForegroundAndBackground", 1); 5038 JobStatus jobBg2 = createJobStatus("testTimerTracking_ForegroundAndBackground", 2); 5039 JobStatus jobFg3 = createJobStatus("testTimerTracking_ForegroundAndBackground", 3); 5040 synchronized (mQuotaController.mLock) { 5041 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 5042 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5043 mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); 5044 } 5045 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5046 List<TimingSession> expected = new ArrayList<>(); 5047 5048 // UID starts out inactive. 5049 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 5050 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5051 synchronized (mQuotaController.mLock) { 5052 mQuotaController.prepareForExecutionLocked(jobBg1); 5053 } 5054 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5055 synchronized (mQuotaController.mLock) { 5056 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 5057 } 5058 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5059 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5060 5061 advanceElapsedClock(SECOND_IN_MILLIS); 5062 5063 // Bg job starts while inactive, spans an entire active session, and ends after the 5064 // active session. 5065 // App switching to foreground state then fg job starts. 5066 // App remains in foreground state after coming to foreground, so there should only be one 5067 // session. 5068 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5069 synchronized (mQuotaController.mLock) { 5070 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5071 mQuotaController.prepareForExecutionLocked(jobBg2); 5072 } 5073 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5074 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5075 setProcessState(getProcessStateQuotaFreeThreshold()); 5076 synchronized (mQuotaController.mLock) { 5077 mQuotaController.prepareForExecutionLocked(jobFg3); 5078 } 5079 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5080 synchronized (mQuotaController.mLock) { 5081 mQuotaController.maybeStopTrackingJobLocked(jobFg3, null); 5082 } 5083 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5084 synchronized (mQuotaController.mLock) { 5085 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 5086 } 5087 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5088 5089 advanceElapsedClock(SECOND_IN_MILLIS); 5090 5091 // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes 5092 // "inactive" and then bg job 2 starts. Then fg job ends. 5093 // This should result in two TimingSessions: 5094 // * The first should have a count of 1 5095 // * The second should have a count of 2 since it will include both jobs 5096 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5097 synchronized (mQuotaController.mLock) { 5098 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 5099 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5100 mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); 5101 } 5102 setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); 5103 synchronized (mQuotaController.mLock) { 5104 mQuotaController.prepareForExecutionLocked(jobBg1); 5105 } 5106 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5107 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5108 setProcessState(getProcessStateQuotaFreeThreshold()); 5109 synchronized (mQuotaController.mLock) { 5110 mQuotaController.prepareForExecutionLocked(jobFg3); 5111 } 5112 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5113 synchronized (mQuotaController.mLock) { 5114 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 5115 } 5116 advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now 5117 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5118 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 5119 synchronized (mQuotaController.mLock) { 5120 mQuotaController.prepareForExecutionLocked(jobBg2); 5121 } 5122 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5123 synchronized (mQuotaController.mLock) { 5124 mQuotaController.maybeStopTrackingJobLocked(jobFg3, null); 5125 } 5126 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5127 synchronized (mQuotaController.mLock) { 5128 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 5129 } 5130 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); 5131 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5132 } 5133 5134 /** 5135 * Tests that Timers don't track job counts while in the foreground. 5136 */ 5137 @Test testTimerTracking_JobCount_Foreground()5138 public void testTimerTracking_JobCount_Foreground() { 5139 setDischarging(); 5140 5141 final int standbyBucket = ACTIVE_INDEX; 5142 JobStatus jobFg1 = createJobStatus("testTimerTracking_JobCount_Foreground", 1); 5143 JobStatus jobFg2 = createJobStatus("testTimerTracking_JobCount_Foreground", 2); 5144 5145 synchronized (mQuotaController.mLock) { 5146 mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); 5147 mQuotaController.maybeStartTrackingJobLocked(jobFg2, null); 5148 } 5149 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5150 ExecutionStats stats; 5151 synchronized (mQuotaController.mLock) { 5152 stats = mQuotaController.getExecutionStatsLocked( 5153 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 5154 } 5155 assertEquals(0, stats.jobCountInRateLimitingWindow); 5156 5157 setProcessState(getProcessStateQuotaFreeThreshold()); 5158 synchronized (mQuotaController.mLock) { 5159 mQuotaController.prepareForExecutionLocked(jobFg1); 5160 } 5161 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5162 synchronized (mQuotaController.mLock) { 5163 mQuotaController.prepareForExecutionLocked(jobFg2); 5164 } 5165 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5166 synchronized (mQuotaController.mLock) { 5167 mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); 5168 } 5169 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5170 synchronized (mQuotaController.mLock) { 5171 mQuotaController.maybeStopTrackingJobLocked(jobFg2, null); 5172 } 5173 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5174 5175 assertEquals(0, stats.jobCountInRateLimitingWindow); 5176 } 5177 5178 /** 5179 * Tests that Timers properly track job counts while in the background. 5180 */ 5181 @Test testTimerTracking_JobCount_Background()5182 public void testTimerTracking_JobCount_Background() { 5183 final int standbyBucket = WORKING_INDEX; 5184 JobStatus jobBg1 = createJobStatus("testTimerTracking_JobCount_Background", 1); 5185 JobStatus jobBg2 = createJobStatus("testTimerTracking_JobCount_Background", 2); 5186 ExecutionStats stats; 5187 synchronized (mQuotaController.mLock) { 5188 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 5189 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5190 5191 stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, 5192 SOURCE_PACKAGE, standbyBucket); 5193 } 5194 assertEquals(0, stats.jobCountInRateLimitingWindow); 5195 5196 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 5197 synchronized (mQuotaController.mLock) { 5198 mQuotaController.prepareForExecutionLocked(jobBg1); 5199 } 5200 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5201 synchronized (mQuotaController.mLock) { 5202 mQuotaController.prepareForExecutionLocked(jobBg2); 5203 } 5204 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5205 synchronized (mQuotaController.mLock) { 5206 mQuotaController.maybeStopTrackingJobLocked(jobBg1, null); 5207 } 5208 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5209 synchronized (mQuotaController.mLock) { 5210 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 5211 } 5212 5213 assertEquals(2, stats.jobCountInRateLimitingWindow); 5214 } 5215 5216 /** 5217 * Tests that Timers properly track overlapping top and background jobs. 5218 */ 5219 @Test testTimerTracking_TopAndNonTop()5220 public void testTimerTracking_TopAndNonTop() { 5221 setDischarging(); 5222 5223 JobStatus jobBg1 = createJobStatus("testTimerTracking_TopAndNonTop", 1); 5224 JobStatus jobBg2 = createJobStatus("testTimerTracking_TopAndNonTop", 2); 5225 JobStatus jobFg1 = createJobStatus("testTimerTracking_TopAndNonTop", 3); 5226 JobStatus jobTop = createJobStatus("testTimerTracking_TopAndNonTop", 4); 5227 synchronized (mQuotaController.mLock) { 5228 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 5229 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5230 mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); 5231 mQuotaController.maybeStartTrackingJobLocked(jobTop, null); 5232 } 5233 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5234 List<TimingSession> expected = new ArrayList<>(); 5235 5236 // UID starts out inactive. 5237 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 5238 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5239 synchronized (mQuotaController.mLock) { 5240 mQuotaController.prepareForExecutionLocked(jobBg1); 5241 } 5242 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5243 synchronized (mQuotaController.mLock) { 5244 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 5245 } 5246 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5247 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5248 5249 advanceElapsedClock(SECOND_IN_MILLIS); 5250 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 5251 5252 // Bg job starts while inactive, spans an entire active session, and ends after the 5253 // active session. 5254 // App switching to top state then fg job starts. 5255 // App remains in top state after coming to top, so there should only be one 5256 // session. 5257 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5258 synchronized (mQuotaController.mLock) { 5259 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5260 mQuotaController.prepareForExecutionLocked(jobBg2); 5261 } 5262 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5263 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5264 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5265 synchronized (mQuotaController.mLock) { 5266 mQuotaController.prepareForExecutionLocked(jobTop); 5267 } 5268 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5269 synchronized (mQuotaController.mLock) { 5270 mQuotaController.maybeStopTrackingJobLocked(jobTop, null); 5271 } 5272 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5273 synchronized (mQuotaController.mLock) { 5274 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 5275 } 5276 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5277 5278 advanceElapsedClock(SECOND_IN_MILLIS); 5279 5280 // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to 5281 // foreground_service and a new job starts. Shortly after, uid goes 5282 // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. 5283 // This should result in two TimingSessions: 5284 // * The first should have a count of 1 5285 // * The second should have a count of 2, which accounts for the bg2 and fg, but not top 5286 // jobs. 5287 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5288 synchronized (mQuotaController.mLock) { 5289 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 5290 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5291 mQuotaController.maybeStartTrackingJobLocked(jobTop, null); 5292 } 5293 setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); 5294 synchronized (mQuotaController.mLock) { 5295 mQuotaController.prepareForExecutionLocked(jobBg1); 5296 } 5297 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5298 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5299 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5300 synchronized (mQuotaController.mLock) { 5301 mQuotaController.prepareForExecutionLocked(jobTop); 5302 } 5303 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5304 synchronized (mQuotaController.mLock) { 5305 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 5306 } 5307 advanceElapsedClock(5 * SECOND_IN_MILLIS); 5308 setProcessState(getProcessStateQuotaFreeThreshold()); 5309 synchronized (mQuotaController.mLock) { 5310 mQuotaController.prepareForExecutionLocked(jobFg1); 5311 } 5312 advanceElapsedClock(5 * SECOND_IN_MILLIS); 5313 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5314 advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now 5315 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5316 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 5317 synchronized (mQuotaController.mLock) { 5318 mQuotaController.prepareForExecutionLocked(jobBg2); 5319 } 5320 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5321 synchronized (mQuotaController.mLock) { 5322 mQuotaController.maybeStopTrackingJobLocked(jobTop, null); 5323 } 5324 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5325 synchronized (mQuotaController.mLock) { 5326 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 5327 mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); 5328 } 5329 // jobBg2 and jobFg1 are counted, jobTop is not counted. 5330 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); 5331 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5332 5333 advanceElapsedClock(SECOND_IN_MILLIS); 5334 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 5335 5336 // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to 5337 // foreground_service and a new job starts. Shortly after, uid goes 5338 // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. 5339 // This should result in two TimingSessions: 5340 // * The first should have a count of 1 5341 // * The second should have a count of 2, which accounts for the bg2 and fg and top jobs. 5342 // Top started jobs are not quota free any more if the process leaves TOP/BTOP state. 5343 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5344 synchronized (mQuotaController.mLock) { 5345 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 5346 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 5347 mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); 5348 mQuotaController.maybeStartTrackingJobLocked(jobTop, null); 5349 } 5350 setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); 5351 synchronized (mQuotaController.mLock) { 5352 mQuotaController.prepareForExecutionLocked(jobBg1); 5353 } 5354 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5355 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5356 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5357 synchronized (mQuotaController.mLock) { 5358 mQuotaController.prepareForExecutionLocked(jobTop); 5359 } 5360 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5361 synchronized (mQuotaController.mLock) { 5362 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 5363 } 5364 advanceElapsedClock(5 * SECOND_IN_MILLIS); 5365 setProcessState(getProcessStateQuotaFreeThreshold()); 5366 synchronized (mQuotaController.mLock) { 5367 mQuotaController.prepareForExecutionLocked(jobFg1); 5368 } 5369 advanceElapsedClock(5 * SECOND_IN_MILLIS); 5370 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5371 advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now 5372 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5373 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 5374 synchronized (mQuotaController.mLock) { 5375 mQuotaController.prepareForExecutionLocked(jobBg2); 5376 } 5377 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5378 synchronized (mQuotaController.mLock) { 5379 mQuotaController.maybeStopTrackingJobLocked(jobTop, null); 5380 } 5381 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5382 synchronized (mQuotaController.mLock) { 5383 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 5384 mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); 5385 } 5386 // jobBg2, jobFg1 and jobTop are counted. 5387 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 3)); 5388 assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5389 } 5390 5391 /** 5392 * Tests that Timers properly track regular sessions when an app is added and removed from the 5393 * temp allowlist. 5394 */ 5395 @Test testTimerTracking_TempAllowlisting()5396 public void testTimerTracking_TempAllowlisting() { 5397 // None of these should be affected purely by the temp allowlist changing. 5398 setDischarging(); 5399 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 5400 final long gracePeriodMs = 15 * SECOND_IN_MILLIS; 5401 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); 5402 Handler handler = mQuotaController.getHandler(); 5403 spyOn(handler); 5404 5405 JobStatus job1 = createJobStatus("testTimerTracking_TempAllowlisting", 1); 5406 JobStatus job2 = createJobStatus("testTimerTracking_TempAllowlisting", 2); 5407 JobStatus job3 = createJobStatus("testTimerTracking_TempAllowlisting", 3); 5408 JobStatus job4 = createJobStatus("testTimerTracking_TempAllowlisting", 4); 5409 JobStatus job5 = createJobStatus("testTimerTracking_TempAllowlisting", 5); 5410 synchronized (mQuotaController.mLock) { 5411 mQuotaController.maybeStartTrackingJobLocked(job1, null); 5412 } 5413 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5414 List<TimingSession> expected = new ArrayList<>(); 5415 5416 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5417 synchronized (mQuotaController.mLock) { 5418 mQuotaController.prepareForExecutionLocked(job1); 5419 } 5420 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5421 synchronized (mQuotaController.mLock) { 5422 mQuotaController.maybeStopTrackingJobLocked(job1, job1); 5423 } 5424 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5425 assertEquals(expected, 5426 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5427 5428 advanceElapsedClock(SECOND_IN_MILLIS); 5429 5430 // Job starts after app is added to temp allowlist and stops before removal. 5431 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5432 mTempAllowlistListener.onAppAdded(mSourceUid); 5433 synchronized (mQuotaController.mLock) { 5434 mQuotaController.maybeStartTrackingJobLocked(job2, null); 5435 mQuotaController.prepareForExecutionLocked(job2); 5436 } 5437 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5438 synchronized (mQuotaController.mLock) { 5439 mQuotaController.maybeStopTrackingJobLocked(job2, null); 5440 } 5441 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5442 assertEquals(expected, 5443 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5444 5445 // Job starts after app is added to temp allowlist and stops after removal, 5446 // before grace period ends. 5447 mTempAllowlistListener.onAppAdded(mSourceUid); 5448 synchronized (mQuotaController.mLock) { 5449 mQuotaController.maybeStartTrackingJobLocked(job3, null); 5450 mQuotaController.prepareForExecutionLocked(job3); 5451 } 5452 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5453 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5454 mTempAllowlistListener.onAppRemoved(mSourceUid); 5455 long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS; 5456 advanceElapsedClock(elapsedGracePeriodMs); 5457 synchronized (mQuotaController.mLock) { 5458 mQuotaController.maybeStopTrackingJobLocked(job3, null); 5459 } 5460 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS + elapsedGracePeriodMs, 1)); 5461 assertEquals(expected, 5462 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5463 5464 advanceElapsedClock(SECOND_IN_MILLIS); 5465 elapsedGracePeriodMs += SECOND_IN_MILLIS; 5466 5467 // Job starts during grace period and ends after grace period ends 5468 synchronized (mQuotaController.mLock) { 5469 mQuotaController.maybeStartTrackingJobLocked(job4, null); 5470 mQuotaController.prepareForExecutionLocked(job4); 5471 } 5472 final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs; 5473 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5474 advanceElapsedClock(remainingGracePeriod); 5475 // Wait for handler to update Timer 5476 // Can't directly evaluate the message because for some reason, the captured message returns 5477 // the wrong 'what' even though the correct message goes to the handler and the correct 5478 // path executes. 5479 verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any()); 5480 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5481 expected.add(createTimingSession(start, remainingGracePeriod + 10 * SECOND_IN_MILLIS, 1)); 5482 synchronized (mQuotaController.mLock) { 5483 mQuotaController.maybeStopTrackingJobLocked(job4, job4); 5484 } 5485 assertEquals(expected, 5486 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5487 5488 // Job starts and runs completely after temp allowlist grace period. 5489 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5490 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 5491 synchronized (mQuotaController.mLock) { 5492 mQuotaController.maybeStartTrackingJobLocked(job5, null); 5493 mQuotaController.prepareForExecutionLocked(job5); 5494 } 5495 advanceElapsedClock(10 * SECOND_IN_MILLIS); 5496 synchronized (mQuotaController.mLock) { 5497 mQuotaController.maybeStopTrackingJobLocked(job5, job5); 5498 } 5499 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 5500 assertEquals(expected, 5501 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 5502 } 5503 5504 /** 5505 * Tests that TOP jobs aren't stopped when an app runs out of quota. 5506 */ 5507 @Test 5508 @DisableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) testTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling()5509 public void testTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling() { 5510 setDischarging(); 5511 5512 JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1); 5513 JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2); 5514 trackJobs(jobBg, jobTop); 5515 setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window 5516 // Now the package only has 20 seconds to run. 5517 final long remainingTimeMs = 20 * SECOND_IN_MILLIS; 5518 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 5519 createTimingSession( 5520 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, 5521 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false); 5522 5523 InOrder inOrder = inOrder(mJobSchedulerService); 5524 5525 // UID starts out inactive. 5526 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 5527 // Start the job. 5528 synchronized (mQuotaController.mLock) { 5529 mQuotaController.prepareForExecutionLocked(jobBg); 5530 } 5531 advanceElapsedClock(remainingTimeMs / 2); 5532 // New job starts after UID is in the foreground. Since the app is now in the foreground, it 5533 // should continue to have remainingTimeMs / 2 time remaining. 5534 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5535 synchronized (mQuotaController.mLock) { 5536 mQuotaController.prepareForExecutionLocked(jobTop); 5537 } 5538 advanceElapsedClock(remainingTimeMs); 5539 5540 // Wait for some extra time to allow for job processing. 5541 inOrder.verify(mJobSchedulerService, 5542 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) 5543 .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); 5544 synchronized (mQuotaController.mLock) { 5545 assertEquals(remainingTimeMs / 2, 5546 mQuotaController.getRemainingExecutionTimeLocked(jobBg)); 5547 assertEquals(remainingTimeMs / 2, 5548 mQuotaController.getRemainingExecutionTimeLocked(jobTop)); 5549 } 5550 // Go to a background state. 5551 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 5552 advanceElapsedClock(remainingTimeMs / 2 + 1); 5553 // Only Bg job will be changed from in-quota to out-of-quota. 5554 inOrder.verify(mJobSchedulerService, 5555 timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) 5556 .onControllerStateChanged(argThat(jobs -> jobs.size() == 1)); 5557 // Top job should still be allowed to run. 5558 assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5559 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5560 5561 // New jobs to run. 5562 JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3); 5563 JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4); 5564 JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5); 5565 setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); 5566 5567 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5568 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5569 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 5570 .onControllerStateChanged(argThat(jobs -> jobs.size() == 1)); 5571 trackJobs(jobFg, jobTop); 5572 synchronized (mQuotaController.mLock) { 5573 mQuotaController.prepareForExecutionLocked(jobTop); 5574 } 5575 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5576 assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5577 assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5578 5579 // App still in foreground so everything should be in quota. 5580 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5581 setProcessState(getProcessStateQuotaFreeThreshold()); 5582 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5583 assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5584 assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5585 5586 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5587 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 5588 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 5589 .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); 5590 // App is now in background and out of quota. Fg should now change to out of quota since it 5591 // wasn't started. Top should remain in quota since it started when the app was in TOP. 5592 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5593 assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5594 assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5595 trackJobs(jobBg2); 5596 assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5597 } 5598 5599 @Test 5600 @RequiresFlagsEnabled({Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS, 5601 Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS}) testTracking_OutOfQuota_ForegroundAndBackground_CompactChangeOverrides()5602 public void testTracking_OutOfQuota_ForegroundAndBackground_CompactChangeOverrides() { 5603 setDischarging(); 5604 5605 // Mock the OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS compat change overrides. 5606 doReturn(true).when(mPlatformCompat).isChangeEnabledByUid( 5607 eq(QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS), anyInt()); 5608 doReturn(true).when(mPlatformCompat).isChangeEnabledByUid( 5609 eq(QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS), anyInt()); 5610 5611 JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1); 5612 JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2); 5613 trackJobs(jobBg, jobTop); 5614 setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window 5615 // Now the package only has 20 seconds to run. 5616 final long remainingTimeMs = 20 * SECOND_IN_MILLIS; 5617 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 5618 createTimingSession( 5619 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, 5620 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false); 5621 5622 InOrder inOrder = inOrder(mJobSchedulerService); 5623 5624 // UID starts out inactive. 5625 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 5626 // Start the job. 5627 synchronized (mQuotaController.mLock) { 5628 mQuotaController.prepareForExecutionLocked(jobBg); 5629 } 5630 advanceElapsedClock(remainingTimeMs / 2); 5631 // New job starts after UID is in the foreground. Since the app is now in the foreground, it 5632 // should continue to have remainingTimeMs / 2 time remaining. 5633 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5634 synchronized (mQuotaController.mLock) { 5635 mQuotaController.prepareForExecutionLocked(jobTop); 5636 } 5637 advanceElapsedClock(remainingTimeMs); 5638 5639 // Wait for some extra time to allow for job processing. 5640 inOrder.verify(mJobSchedulerService, 5641 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) 5642 .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); 5643 synchronized (mQuotaController.mLock) { 5644 assertEquals(remainingTimeMs / 2, 5645 mQuotaController.getRemainingExecutionTimeLocked(jobBg)); 5646 assertEquals(remainingTimeMs / 2, 5647 mQuotaController.getRemainingExecutionTimeLocked(jobTop)); 5648 } 5649 // Go to a background state. 5650 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 5651 advanceElapsedClock(remainingTimeMs / 2 + 1); 5652 // Only Bg job will be changed from in-quota to out-of-quota. 5653 inOrder.verify(mJobSchedulerService, 5654 timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) 5655 .onControllerStateChanged(argThat(jobs -> jobs.size() == 1)); 5656 // Top job should still be allowed to run. 5657 assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5658 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5659 5660 // New jobs to run. 5661 JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3); 5662 JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4); 5663 JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5); 5664 setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); 5665 5666 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5667 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5668 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 5669 .onControllerStateChanged(argThat(jobs -> jobs.size() == 1)); 5670 trackJobs(jobFg, jobTop); 5671 synchronized (mQuotaController.mLock) { 5672 mQuotaController.prepareForExecutionLocked(jobTop); 5673 } 5674 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5675 assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5676 assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5677 5678 // App still in foreground so everything should be in quota. 5679 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5680 setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); 5681 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5682 assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5683 assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5684 5685 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5686 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 5687 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 5688 .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); 5689 // App is now in background and out of quota. Fg should now change to out of quota since it 5690 // wasn't started. Top should remain in quota since it started when the app was in TOP. 5691 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5692 assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5693 assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5694 trackJobs(jobBg2); 5695 assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5696 } 5697 5698 /** 5699 * Tests that TOP jobs are stopped when an app runs out of quota. 5700 */ 5701 @Test 5702 @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) testTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling()5703 public void testTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling() { 5704 setDischarging(); 5705 5706 JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1); 5707 JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2); 5708 trackJobs(jobBg, jobTop); 5709 setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window 5710 // Now the package only has 20 seconds to run. 5711 final long remainingTimeMs = 20 * SECOND_IN_MILLIS; 5712 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 5713 createTimingSession( 5714 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, 5715 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false); 5716 5717 InOrder inOrder = inOrder(mJobSchedulerService); 5718 5719 // UID starts out inactive. 5720 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 5721 // Start the job. 5722 synchronized (mQuotaController.mLock) { 5723 mQuotaController.prepareForExecutionLocked(jobBg); 5724 } 5725 advanceElapsedClock(remainingTimeMs / 2); 5726 // New job starts after UID is in the foreground. Since the app is now in the foreground, it 5727 // should continue to have remainingTimeMs / 2 time remaining. 5728 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5729 synchronized (mQuotaController.mLock) { 5730 mQuotaController.prepareForExecutionLocked(jobTop); 5731 } 5732 advanceElapsedClock(remainingTimeMs); 5733 5734 // Wait for some extra time to allow for job processing. 5735 inOrder.verify(mJobSchedulerService, 5736 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) 5737 .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); 5738 synchronized (mQuotaController.mLock) { 5739 assertEquals(remainingTimeMs / 2, 5740 mQuotaController.getRemainingExecutionTimeLocked(jobBg)); 5741 assertEquals(remainingTimeMs / 2, 5742 mQuotaController.getRemainingExecutionTimeLocked(jobTop)); 5743 } 5744 // Go to a background state. 5745 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 5746 advanceElapsedClock(remainingTimeMs / 2 + 1); 5747 // Both Bg and Top jobs should be changed from in-quota to out-of-quota 5748 inOrder.verify(mJobSchedulerService, 5749 timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) 5750 .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); 5751 // Top job should NOT be allowed to run. 5752 assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5753 assertFalse(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5754 5755 // New jobs to run. 5756 JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3); 5757 JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4); 5758 JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5); 5759 setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); 5760 5761 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5762 setProcessState(ActivityManager.PROCESS_STATE_TOP); 5763 // Both Bg and Top jobs should be changed from out-of-quota to in-quota. 5764 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 5765 .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); 5766 trackJobs(jobFg, jobTop); 5767 synchronized (mQuotaController.mLock) { 5768 mQuotaController.prepareForExecutionLocked(jobTop); 5769 } 5770 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5771 assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5772 assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5773 5774 // App still in foreground so everything should be in quota. 5775 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5776 setProcessState(getProcessStateQuotaFreeThreshold()); 5777 assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5778 assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5779 assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5780 5781 advanceElapsedClock(20 * SECOND_IN_MILLIS); 5782 // App is in background so everything should be out of quota. 5783 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 5784 // Bg, Fg and Top jobs should be changed from in-quota to out-of-quota. 5785 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 5786 .onControllerStateChanged(argThat(jobs -> jobs.size() == 3)); 5787 // App is now in background and out of quota. Fg should now change to out of quota 5788 // since it wasn't started. Top should now changed to out of quota even it started 5789 // when the app was in TOP. 5790 assertFalse(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5791 assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5792 assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5793 trackJobs(jobBg2); 5794 assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5795 } 5796 5797 /** 5798 * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches 5799 * its quota. 5800 */ 5801 @Test testTracking_OutOfQuota()5802 public void testTracking_OutOfQuota() { 5803 JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1); 5804 synchronized (mQuotaController.mLock) { 5805 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 5806 } 5807 setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window 5808 setProcessState(ActivityManager.PROCESS_STATE_HOME); 5809 // Now the package only has two seconds to run. 5810 final long remainingTimeMs = 2 * SECOND_IN_MILLIS; 5811 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 5812 createTimingSession( 5813 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, 5814 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false); 5815 5816 // Start the job. 5817 synchronized (mQuotaController.mLock) { 5818 mQuotaController.prepareForExecutionLocked(jobStatus); 5819 } 5820 advanceElapsedClock(remainingTimeMs); 5821 5822 // Wait for some extra time to allow for job processing. 5823 verify(mJobSchedulerService, 5824 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1)) 5825 .onControllerStateChanged(argThat(jobs -> jobs.size() == 1)); 5826 assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5827 assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(), 5828 jobStatus.getWhenStandbyDeferred()); 5829 } 5830 5831 /** 5832 * Tests that a job is properly handled when it's at the edge of its quota and the old quota is 5833 * being phased out. 5834 */ 5835 @Test testTracking_RollingQuota()5836 public void testTracking_RollingQuota() { 5837 JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1); 5838 synchronized (mQuotaController.mLock) { 5839 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 5840 } 5841 setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window 5842 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 5843 Handler handler = mQuotaController.getHandler(); 5844 spyOn(handler); 5845 5846 long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 5847 final long remainingTimeMs = SECOND_IN_MILLIS; 5848 // The package only has one second to run, but this session is at the edge of the rolling 5849 // window, so as the package "reaches its quota" it will have more to keep running. 5850 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 5851 createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, 5852 10 * SECOND_IN_MILLIS - remainingTimeMs, 1), false); 5853 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 5854 createTimingSession(now - HOUR_IN_MILLIS, 5855 9 * MINUTE_IN_MILLIS + 50 * SECOND_IN_MILLIS, 1), false); 5856 5857 synchronized (mQuotaController.mLock) { 5858 assertEquals(remainingTimeMs, 5859 mQuotaController.getRemainingExecutionTimeLocked(jobStatus)); 5860 5861 // Start the job. 5862 mQuotaController.prepareForExecutionLocked(jobStatus); 5863 } 5864 advanceElapsedClock(remainingTimeMs); 5865 5866 // Wait for some extra time to allow for job processing. 5867 verify(mJobSchedulerService, 5868 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) 5869 .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); 5870 assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5871 // The job used up the remaining quota, but in that time, the same amount of time in the 5872 // old TimingSession also fell out of the quota window, so it should still have the same 5873 // amount of remaining time left its quota. 5874 synchronized (mQuotaController.mLock) { 5875 assertEquals(remainingTimeMs, 5876 mQuotaController.getRemainingExecutionTimeLocked( 5877 SOURCE_USER_ID, SOURCE_PACKAGE)); 5878 } 5879 // Handler is told to check when the quota will be consumed, not when the initial 5880 // remaining time is over. 5881 verify(handler, atLeast(1)).sendMessageDelayed( 5882 argThat(msg -> msg.what == QuotaController.MSG_REACHED_TIME_QUOTA), 5883 eq(10 * SECOND_IN_MILLIS)); 5884 verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); 5885 5886 // After 10 seconds, the job should finally be out of quota. 5887 advanceElapsedClock(10 * SECOND_IN_MILLIS - remainingTimeMs); 5888 // Wait for some extra time to allow for job processing. 5889 verify(mJobSchedulerService, 5890 timeout(12 * SECOND_IN_MILLIS).times(1)) 5891 .onControllerStateChanged(argThat(jobs -> jobs.size() == 1)); 5892 assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5893 verify(handler, never()).sendMessageDelayed(any(), anyInt()); 5894 } 5895 5896 /** 5897 * Tests that the start alarm is properly scheduled when a job has been throttled due to the job 5898 * count rate limiting. 5899 */ 5900 @Test testStartAlarmScheduled_JobCount_RateLimitingWindow()5901 public void testStartAlarmScheduled_JobCount_RateLimitingWindow() { 5902 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 5903 // because it schedules an alarm too. Prevent it from doing so. 5904 spyOn(mQuotaController); 5905 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 5906 5907 // Essentially disable session throttling. 5908 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, Integer.MAX_VALUE); 5909 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 5910 Integer.MAX_VALUE); 5911 5912 final int standbyBucket = WORKING_INDEX; 5913 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 5914 5915 // No sessions saved yet. 5916 synchronized (mQuotaController.mLock) { 5917 mQuotaController.maybeScheduleStartAlarmLocked( 5918 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 5919 } 5920 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 5921 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 5922 5923 // Ran jobs up to the job limit. All of them should be allowed to run. 5924 for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; ++i) { 5925 JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i); 5926 setStandbyBucket(WORKING_INDEX, job); 5927 synchronized (mQuotaController.mLock) { 5928 mQuotaController.maybeStartTrackingJobLocked(job, null); 5929 assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5930 mQuotaController.prepareForExecutionLocked(job); 5931 } 5932 advanceElapsedClock(SECOND_IN_MILLIS); 5933 synchronized (mQuotaController.mLock) { 5934 mQuotaController.maybeStopTrackingJobLocked(job, null); 5935 } 5936 advanceElapsedClock(SECOND_IN_MILLIS); 5937 } 5938 // Start alarm shouldn't have been scheduled since the app was in quota up until this point. 5939 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 5940 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 5941 5942 // The app is now out of job count quota 5943 JobStatus throttledJob = createJobStatus( 5944 "testStartAlarmScheduled_JobCount_AllowedTime", 42); 5945 setStandbyBucket(WORKING_INDEX, throttledJob); 5946 synchronized (mQuotaController.mLock) { 5947 mQuotaController.maybeStartTrackingJobLocked(throttledJob, null); 5948 } 5949 assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 5950 5951 ExecutionStats stats; 5952 synchronized (mQuotaController.mLock) { 5953 stats = mQuotaController.getExecutionStatsLocked( 5954 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 5955 } 5956 final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed; 5957 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 5958 anyInt(), eq(expectedWorkingAlarmTime), anyLong(), 5959 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 5960 } 5961 5962 /** 5963 * Tests that the start alarm is properly scheduled when a job has been throttled due to the 5964 * session count rate limiting. 5965 */ 5966 @Test testStartAlarmScheduled_TimingSessionCount_RateLimitingWindow()5967 public void testStartAlarmScheduled_TimingSessionCount_RateLimitingWindow() { 5968 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 5969 // because it schedules an alarm too. Prevent it from doing so. 5970 spyOn(mQuotaController); 5971 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 5972 5973 // Essentially disable job count throttling. 5974 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, Integer.MAX_VALUE); 5975 setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 5976 Integer.MAX_VALUE); 5977 // Make sure throttling is because of COUNT_PER_RATE_LIMITING_WINDOW. 5978 setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 5979 mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW + 1); 5980 5981 final int standbyBucket = FREQUENT_INDEX; 5982 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 5983 5984 // No sessions saved yet. 5985 synchronized (mQuotaController.mLock) { 5986 mQuotaController.maybeScheduleStartAlarmLocked( 5987 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 5988 } 5989 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 5990 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 5991 5992 // Ran jobs up to the job limit. All of them should be allowed to run. 5993 for (int i = 0; i < mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW; ++i) { 5994 JobStatus job = createJobStatus( 5995 "testStartAlarmScheduled_TimingSessionCount_AllowedTime", i); 5996 setStandbyBucket(FREQUENT_INDEX, job); 5997 synchronized (mQuotaController.mLock) { 5998 mQuotaController.maybeStartTrackingJobLocked(job, null); 5999 assertTrue("Constraint not satisfied for job #" + (i + 1), 6000 job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 6001 mQuotaController.prepareForExecutionLocked(job); 6002 } 6003 advanceElapsedClock(SECOND_IN_MILLIS); 6004 synchronized (mQuotaController.mLock) { 6005 mQuotaController.maybeStopTrackingJobLocked(job, null); 6006 } 6007 advanceElapsedClock(SECOND_IN_MILLIS); 6008 } 6009 // Start alarm shouldn't have been scheduled since the app was in quota up until this point. 6010 verify(mAlarmManager, timeout(1000).times(0)).setWindow( 6011 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 6012 6013 // The app is now out of session count quota 6014 JobStatus throttledJob = createJobStatus( 6015 "testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42); 6016 synchronized (mQuotaController.mLock) { 6017 mQuotaController.maybeStartTrackingJobLocked(throttledJob, null); 6018 } 6019 assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); 6020 assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(), 6021 throttledJob.getWhenStandbyDeferred()); 6022 6023 ExecutionStats stats; 6024 synchronized (mQuotaController.mLock) { 6025 stats = mQuotaController.getExecutionStatsLocked( 6026 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 6027 } 6028 final long expectedWorkingAlarmTime = stats.sessionRateLimitExpirationTimeElapsed; 6029 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6030 anyInt(), eq(expectedWorkingAlarmTime), anyLong(), 6031 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 6032 } 6033 6034 @Test testGetRemainingEJExecutionTimeLocked_NoHistory()6035 public void testGetRemainingEJExecutionTimeLocked_NoHistory() { 6036 final long[] limits = mQuotaController.getEJLimitsMs(); 6037 for (int i = 0; i < limits.length; ++i) { 6038 setStandbyBucket(i); 6039 assertEquals("Got wrong remaining EJ execution time for bucket #" + i, 6040 limits[i], 6041 mQuotaController.getRemainingEJExecutionTimeLocked( 6042 SOURCE_USER_ID, SOURCE_PACKAGE)); 6043 } 6044 } 6045 6046 @Test testGetRemainingEJExecutionTimeLocked_AllSessionsWithinWindow()6047 public void testGetRemainingEJExecutionTimeLocked_AllSessionsWithinWindow() { 6048 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6049 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6050 createTimingSession(now - mQcConstants.EJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), 6051 true); 6052 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6053 createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6054 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6055 createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6056 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6057 createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6058 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6059 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6060 6061 final long[] limits = mQuotaController.getEJLimitsMs(); 6062 for (int i = 0; i < limits.length; ++i) { 6063 setStandbyBucket(i); 6064 assertEquals("Got wrong remaining EJ execution time for bucket #" + i, 6065 i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), 6066 mQuotaController.getRemainingEJExecutionTimeLocked( 6067 SOURCE_USER_ID, SOURCE_PACKAGE)); 6068 } 6069 } 6070 6071 @Test testGetRemainingEJExecutionTimeLocked_Installer()6072 public void testGetRemainingEJExecutionTimeLocked_Installer() { 6073 PackageInfo pi = new PackageInfo(); 6074 pi.packageName = SOURCE_PACKAGE; 6075 pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES}; 6076 pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED}; 6077 pi.applicationInfo = new ApplicationInfo(); 6078 pi.applicationInfo.uid = mSourceUid; 6079 doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt()); 6080 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission( 6081 eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid)); 6082 mQuotaController.onSystemServicesReady(); 6083 6084 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6085 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6086 createTimingSession(now - mQcConstants.EJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), 6087 true); 6088 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6089 createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6090 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6091 createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6092 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6093 createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6094 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6095 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6096 6097 final long[] limits = mQuotaController.getEJLimitsMs(); 6098 for (int i = 0; i < limits.length; ++i) { 6099 setStandbyBucket(i); 6100 assertEquals("Got wrong remaining EJ execution time for bucket #" + i, 6101 i == NEVER_INDEX ? 0 6102 : (limits[i] + mQuotaController.getEjLimitAdditionInstallerMs() 6103 - 5 * MINUTE_IN_MILLIS), 6104 mQuotaController.getRemainingEJExecutionTimeLocked( 6105 SOURCE_USER_ID, SOURCE_PACKAGE)); 6106 } 6107 } 6108 6109 @Test testGetRemainingEJExecutionTimeLocked_OneSessionStraddlesEdge()6110 public void testGetRemainingEJExecutionTimeLocked_OneSessionStraddlesEdge() { 6111 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6112 final long[] limits = mQuotaController.getEJLimitsMs(); 6113 for (int i = 0; i < limits.length; ++i) { 6114 synchronized (mQuotaController.mLock) { 6115 mQuotaController.onUserRemovedLocked(SOURCE_USER_ID); 6116 } 6117 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6118 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), 6119 2 * MINUTE_IN_MILLIS, 5), true); 6120 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6121 createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6122 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6123 createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6124 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6125 createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6126 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6127 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6128 6129 setStandbyBucket(i); 6130 assertEquals("Got wrong remaining EJ execution time for bucket #" + i, 6131 i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), 6132 mQuotaController.getRemainingEJExecutionTimeLocked( 6133 SOURCE_USER_ID, SOURCE_PACKAGE)); 6134 } 6135 } 6136 6137 @Test testGetRemainingEJExecutionTimeLocked_WithStaleSessions()6138 public void testGetRemainingEJExecutionTimeLocked_WithStaleSessions() { 6139 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6140 6141 final long[] limits = mQuotaController.getEJLimitsMs(); 6142 for (int i = 0; i < limits.length; ++i) { 6143 synchronized (mQuotaController.mLock) { 6144 mQuotaController.onUserRemovedLocked(SOURCE_USER_ID); 6145 } 6146 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6147 createTimingSession( 6148 now - (mQcConstants.EJ_WINDOW_SIZE_MS + 10 * MINUTE_IN_MILLIS), 6149 2 * MINUTE_IN_MILLIS, 5), true); 6150 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6151 createTimingSession( 6152 now - (mQcConstants.EJ_WINDOW_SIZE_MS + 5 * MINUTE_IN_MILLIS), 6153 MINUTE_IN_MILLIS, 5), true); 6154 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6155 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), 6156 2 * MINUTE_IN_MILLIS, 5), true); 6157 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6158 createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6159 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6160 createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6161 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6162 createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6163 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6164 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6165 6166 setStandbyBucket(i); 6167 assertEquals("Got wrong remaining EJ execution time for bucket #" + i, 6168 i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), 6169 mQuotaController.getRemainingEJExecutionTimeLocked( 6170 SOURCE_USER_ID, SOURCE_PACKAGE)); 6171 } 6172 } 6173 6174 /** 6175 * Tests that getRemainingEJExecutionTimeLocked returns the correct stats soon after device 6176 * startup. 6177 */ 6178 @Test testGetRemainingEJExecutionTimeLocked_BeginningOfTime()6179 public void testGetRemainingEJExecutionTimeLocked_BeginningOfTime() { 6180 // Set time to 3 minutes after boot. 6181 advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); 6182 advanceElapsedClock(3 * MINUTE_IN_MILLIS); 6183 6184 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6185 createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), true); 6186 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6187 createTimingSession(150 * SECOND_IN_MILLIS, 15 * SECOND_IN_MILLIS, 5), true); 6188 6189 final long[] limits = mQuotaController.getEJLimitsMs(); 6190 for (int i = 0; i < limits.length; ++i) { 6191 setStandbyBucket(i); 6192 assertEquals("Got wrong remaining EJ execution time for bucket #" + i, 6193 i == NEVER_INDEX ? 0 : (limits[i] - 75 * SECOND_IN_MILLIS), 6194 mQuotaController.getRemainingEJExecutionTimeLocked( 6195 SOURCE_USER_ID, SOURCE_PACKAGE)); 6196 } 6197 } 6198 6199 @Test testGetRemainingEJExecutionTimeLocked_IncrementalTimingSessions()6200 public void testGetRemainingEJExecutionTimeLocked_IncrementalTimingSessions() { 6201 setDischarging(); 6202 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6203 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS); 6204 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS); 6205 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS); 6206 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 6207 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); 6208 6209 for (int i = 1; i <= 25; ++i) { 6210 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6211 createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 6212 2), true); 6213 6214 synchronized (mQuotaController.mLock) { 6215 setStandbyBucket(ACTIVE_INDEX); 6216 assertEquals("Active has incorrect remaining EJ time with " + i + " sessions", 6217 (20 - i) * MINUTE_IN_MILLIS, 6218 mQuotaController.getRemainingEJExecutionTimeLocked( 6219 SOURCE_USER_ID, SOURCE_PACKAGE)); 6220 6221 setStandbyBucket(WORKING_INDEX); 6222 assertEquals("Working has incorrect remaining EJ time with " + i + " sessions", 6223 (15 - i) * MINUTE_IN_MILLIS, 6224 mQuotaController.getRemainingEJExecutionTimeLocked( 6225 SOURCE_USER_ID, SOURCE_PACKAGE)); 6226 6227 setStandbyBucket(FREQUENT_INDEX); 6228 assertEquals("Frequent has incorrect remaining EJ time with " + i + " sessions", 6229 (13 - i) * MINUTE_IN_MILLIS, 6230 mQuotaController.getRemainingEJExecutionTimeLocked( 6231 SOURCE_USER_ID, SOURCE_PACKAGE)); 6232 6233 setStandbyBucket(RARE_INDEX); 6234 assertEquals("Rare has incorrect remaining EJ time with " + i + " sessions", 6235 (10 - i) * MINUTE_IN_MILLIS, 6236 mQuotaController.getRemainingEJExecutionTimeLocked( 6237 SOURCE_USER_ID, SOURCE_PACKAGE)); 6238 6239 setStandbyBucket(RESTRICTED_INDEX); 6240 assertEquals("Restricted has incorrect remaining EJ time with " + i + " sessions", 6241 (5 - i) * MINUTE_IN_MILLIS, 6242 mQuotaController.getRemainingEJExecutionTimeLocked( 6243 SOURCE_USER_ID, SOURCE_PACKAGE)); 6244 } 6245 } 6246 } 6247 6248 @Test testGetTimeUntilEJQuotaConsumedLocked_NoHistory()6249 public void testGetTimeUntilEJQuotaConsumedLocked_NoHistory() { 6250 final long[] limits = mQuotaController.getEJLimitsMs(); 6251 for (int i = 0; i < limits.length; ++i) { 6252 setStandbyBucket(i); 6253 assertEquals("Got wrong time until EJ quota consumed for bucket #" + i, 6254 limits[i], mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6255 SOURCE_USER_ID, SOURCE_PACKAGE)); 6256 } 6257 } 6258 6259 @Test testGetTimeUntilEJQuotaConsumedLocked_AllSessionsWithinWindow()6260 public void testGetTimeUntilEJQuotaConsumedLocked_AllSessionsWithinWindow() { 6261 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6262 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6263 createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6264 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6265 createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6266 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6267 createTimingSession(now - 20 * MINUTE_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 5), true); 6268 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6269 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6270 6271 final long[] limits = mQuotaController.getEJLimitsMs(); 6272 for (int i = 0; i < limits.length; ++i) { 6273 setStandbyBucket(i); 6274 assertEquals("Got wrong time until EJ quota consumed for bucket #" + i, 6275 i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS), 6276 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6277 SOURCE_USER_ID, SOURCE_PACKAGE)); 6278 } 6279 } 6280 6281 @Test testGetTimeUntilEJQuotaConsumedLocked_SessionsAtEdgeOfWindow()6282 public void testGetTimeUntilEJQuotaConsumedLocked_SessionsAtEdgeOfWindow() { 6283 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6284 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6285 createTimingSession(now - mQcConstants.EJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), 6286 true); 6287 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6288 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS - 2 * MINUTE_IN_MILLIS), 6289 MINUTE_IN_MILLIS, 5), true); 6290 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6291 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS - 10 * MINUTE_IN_MILLIS), 6292 MINUTE_IN_MILLIS, 5), true); 6293 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6294 createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6295 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6296 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6297 6298 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); 6299 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); 6300 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); 6301 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 6302 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); 6303 6304 setStandbyBucket(ACTIVE_INDEX); 6305 assertEquals("Got wrong time until EJ quota consumed for bucket #" + ACTIVE_INDEX, 6306 28 * MINUTE_IN_MILLIS, 6307 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6308 SOURCE_USER_ID, SOURCE_PACKAGE)); 6309 6310 setStandbyBucket(WORKING_INDEX); 6311 assertEquals("Got wrong time until EJ quota consumed for bucket #" + WORKING_INDEX, 6312 18 * MINUTE_IN_MILLIS, 6313 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6314 SOURCE_USER_ID, SOURCE_PACKAGE)); 6315 6316 setStandbyBucket(FREQUENT_INDEX); 6317 assertEquals("Got wrong time until EJ quota consumed for bucket #" + FREQUENT_INDEX, 6318 13 * MINUTE_IN_MILLIS, 6319 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6320 SOURCE_USER_ID, SOURCE_PACKAGE)); 6321 6322 setStandbyBucket(RARE_INDEX); 6323 assertEquals("Got wrong time until EJ quota consumed for bucket #" + RARE_INDEX, 6324 7 * MINUTE_IN_MILLIS, 6325 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6326 SOURCE_USER_ID, SOURCE_PACKAGE)); 6327 6328 setStandbyBucket(RESTRICTED_INDEX); 6329 assertEquals("Got wrong time until EJ quota consumed for bucket #" + RESTRICTED_INDEX, 6330 MINUTE_IN_MILLIS, 6331 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6332 SOURCE_USER_ID, SOURCE_PACKAGE)); 6333 } 6334 6335 @Test testGetTimeUntilEJQuotaConsumedLocked_OneSessionStraddlesEdge()6336 public void testGetTimeUntilEJQuotaConsumedLocked_OneSessionStraddlesEdge() { 6337 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6338 6339 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6340 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), 6341 2 * MINUTE_IN_MILLIS, 5), true); 6342 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6343 createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6344 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6345 createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6346 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6347 createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6348 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6349 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); 6350 6351 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); 6352 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); 6353 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); 6354 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 6355 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); 6356 6357 setStandbyBucket(ACTIVE_INDEX); 6358 assertEquals("Got wrong time until EJ quota consumed for bucket #" + ACTIVE_INDEX, 6359 26 * MINUTE_IN_MILLIS, 6360 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6361 SOURCE_USER_ID, SOURCE_PACKAGE)); 6362 6363 setStandbyBucket(WORKING_INDEX); 6364 assertEquals("Got wrong time until EJ quota consumed for bucket #" + WORKING_INDEX, 6365 16 * MINUTE_IN_MILLIS, 6366 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6367 SOURCE_USER_ID, SOURCE_PACKAGE)); 6368 6369 setStandbyBucket(FREQUENT_INDEX); 6370 assertEquals("Got wrong time until EJ quota consumed for bucket #" + FREQUENT_INDEX, 6371 11 * MINUTE_IN_MILLIS, 6372 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6373 SOURCE_USER_ID, SOURCE_PACKAGE)); 6374 6375 setStandbyBucket(RARE_INDEX); 6376 assertEquals("Got wrong time until EJ quota consumed for bucket #" + RARE_INDEX, 6377 6 * MINUTE_IN_MILLIS, 6378 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6379 SOURCE_USER_ID, SOURCE_PACKAGE)); 6380 6381 setStandbyBucket(RESTRICTED_INDEX); 6382 assertEquals("Got wrong time until EJ quota consumed for bucket #" + RESTRICTED_INDEX, 6383 MINUTE_IN_MILLIS, 6384 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6385 SOURCE_USER_ID, SOURCE_PACKAGE)); 6386 } 6387 6388 @Test testGetTimeUntilEJQuotaConsumedLocked_WithStaleSessions()6389 public void testGetTimeUntilEJQuotaConsumedLocked_WithStaleSessions() { 6390 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6391 6392 List<TimingSession> timingSessions = new ArrayList<>(); 6393 timingSessions.add( 6394 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS + 10 * MINUTE_IN_MILLIS), 6395 2 * MINUTE_IN_MILLIS, 5)); 6396 timingSessions.add( 6397 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS + 5 * MINUTE_IN_MILLIS), 6398 MINUTE_IN_MILLIS, 5)); 6399 timingSessions.add( 6400 createTimingSession(now - (mQcConstants.EJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS), 6401 2 * MINUTE_IN_MILLIS, 5)); 6402 timingSessions.add( 6403 createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); 6404 timingSessions.add( 6405 createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); 6406 timingSessions.add( 6407 createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); 6408 timingSessions.add( 6409 createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5)); 6410 6411 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); 6412 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); 6413 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); 6414 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 6415 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS); 6416 6417 runTestGetTimeUntilEJQuotaConsumedLocked( 6418 timingSessions, ACTIVE_INDEX, 26 * MINUTE_IN_MILLIS); 6419 runTestGetTimeUntilEJQuotaConsumedLocked( 6420 timingSessions, WORKING_INDEX, 16 * MINUTE_IN_MILLIS); 6421 runTestGetTimeUntilEJQuotaConsumedLocked( 6422 timingSessions, FREQUENT_INDEX, 11 * MINUTE_IN_MILLIS); 6423 runTestGetTimeUntilEJQuotaConsumedLocked(timingSessions, RARE_INDEX, 6 * MINUTE_IN_MILLIS); 6424 runTestGetTimeUntilEJQuotaConsumedLocked( 6425 timingSessions, RESTRICTED_INDEX, MINUTE_IN_MILLIS); 6426 } 6427 6428 /** 6429 * Tests that getTimeUntilEJQuotaConsumedLocked returns the correct stats soon after device 6430 * startup. 6431 */ 6432 @Test testGetTimeUntilEJQuotaConsumedLocked_BeginningOfTime()6433 public void testGetTimeUntilEJQuotaConsumedLocked_BeginningOfTime() { 6434 // Set time to 3 minutes after boot. 6435 advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis()); 6436 advanceElapsedClock(3 * MINUTE_IN_MILLIS); 6437 6438 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6439 createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), true); 6440 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6441 createTimingSession(150 * SECOND_IN_MILLIS, 15 * SECOND_IN_MILLIS, 5), true); 6442 6443 final long[] limits = mQuotaController.getEJLimitsMs(); 6444 for (int i = 0; i < limits.length; ++i) { 6445 setStandbyBucket(i); 6446 assertEquals("Got wrong time until EJ quota consumed for bucket #" + i, 6447 limits[i], // All existing sessions will phase out 6448 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6449 SOURCE_USER_ID, SOURCE_PACKAGE)); 6450 } 6451 } 6452 runTestGetTimeUntilEJQuotaConsumedLocked( List<TimingSession> timingSessions, int bucketIndex, long expectedValue)6453 private void runTestGetTimeUntilEJQuotaConsumedLocked( 6454 List<TimingSession> timingSessions, int bucketIndex, long expectedValue) { 6455 synchronized (mQuotaController.mLock) { 6456 mQuotaController.onUserRemovedLocked(SOURCE_USER_ID); 6457 } 6458 if (timingSessions != null) { 6459 for (TimingSession session : timingSessions) { 6460 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, session, true); 6461 } 6462 } 6463 6464 setStandbyBucket(bucketIndex); 6465 assertEquals("Got wrong time until EJ quota consumed for bucket #" + bucketIndex, 6466 expectedValue, 6467 mQuotaController.getTimeUntilEJQuotaConsumedLocked( 6468 SOURCE_USER_ID, SOURCE_PACKAGE)); 6469 } 6470 6471 @Test testMaybeScheduleStartAlarmLocked_EJ()6472 public void testMaybeScheduleStartAlarmLocked_EJ() { 6473 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 6474 // because it schedules an alarm too. Prevent it from doing so. 6475 spyOn(mQuotaController); 6476 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 6477 6478 synchronized (mQuotaController.mLock) { 6479 mQuotaController.maybeStartTrackingJobLocked( 6480 createJobStatus("testMaybeScheduleStartAlarmLocked_EJ", 1), null); 6481 } 6482 6483 final int standbyBucket = WORKING_INDEX; 6484 setStandbyBucket(standbyBucket); 6485 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); 6486 6487 InOrder inOrder = inOrder(mAlarmManager); 6488 6489 synchronized (mQuotaController.mLock) { 6490 // No sessions saved yet. 6491 mQuotaController.maybeScheduleStartAlarmLocked( 6492 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 6493 } 6494 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6495 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6496 any(Handler.class)); 6497 6498 // Test with timing sessions out of window. 6499 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6500 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6501 createTimingSession(now - 25 * HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS, 1), true); 6502 synchronized (mQuotaController.mLock) { 6503 mQuotaController.maybeScheduleStartAlarmLocked( 6504 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 6505 } 6506 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6507 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6508 any(Handler.class)); 6509 6510 // Test with timing sessions in window but still in quota. 6511 final long end = now - (22 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); 6512 final long expectedAlarmTime = now + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; 6513 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6514 new TimingSession(now - 22 * HOUR_IN_MILLIS, end, 1), true); 6515 synchronized (mQuotaController.mLock) { 6516 mQuotaController.maybeScheduleStartAlarmLocked( 6517 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 6518 } 6519 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6520 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6521 any(Handler.class)); 6522 6523 // Add some more sessions, but still in quota. 6524 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6525 createTimingSession(now - HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), true); 6526 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6527 createTimingSession(now - (50 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 1), true); 6528 synchronized (mQuotaController.mLock) { 6529 mQuotaController.maybeScheduleStartAlarmLocked( 6530 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 6531 } 6532 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6533 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6534 any(Handler.class)); 6535 6536 // Test when out of quota. 6537 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6538 createTimingSession(now - 30 * MINUTE_IN_MILLIS, 6 * MINUTE_IN_MILLIS, 1), true); 6539 synchronized (mQuotaController.mLock) { 6540 mQuotaController.maybeScheduleStartAlarmLocked( 6541 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 6542 } 6543 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6544 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6545 any(Handler.class)); 6546 6547 // Alarm already scheduled, so make sure it's not scheduled again. 6548 synchronized (mQuotaController.mLock) { 6549 mQuotaController.maybeScheduleStartAlarmLocked( 6550 SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); 6551 } 6552 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6553 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6554 any(Handler.class)); 6555 } 6556 6557 /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */ 6558 @Test testMaybeScheduleStartAlarmLocked_Ej_BucketChange()6559 public void testMaybeScheduleStartAlarmLocked_Ej_BucketChange() { 6560 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 6561 // because it schedules an alarm too. Prevent it from doing so. 6562 spyOn(mQuotaController); 6563 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 6564 6565 synchronized (mQuotaController.mLock) { 6566 mQuotaController.maybeStartTrackingJobLocked( 6567 createJobStatus("testMaybeScheduleStartAlarmLocked_Ej_BucketChange", 1), null); 6568 } 6569 6570 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS); 6571 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS); 6572 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS); 6573 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 6574 6575 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6576 // Affects active bucket 6577 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6578 createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3), true); 6579 // Affects active and working buckets 6580 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6581 createTimingSession(now - 4 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 3), true); 6582 // Affects active, working, and frequent buckets 6583 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6584 createTimingSession(now - HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 10), true); 6585 // Affects all buckets 6586 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6587 createTimingSession(now - 5 * MINUTE_IN_MILLIS, 10 * MINUTE_IN_MILLIS, 3), true); 6588 6589 InOrder inOrder = inOrder(mAlarmManager); 6590 6591 // Start in ACTIVE bucket. 6592 setStandbyBucket(ACTIVE_INDEX); 6593 synchronized (mQuotaController.mLock) { 6594 mQuotaController.maybeScheduleStartAlarmLocked( 6595 SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX); 6596 } 6597 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6598 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6599 any(Handler.class)); 6600 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6601 .cancel(any(AlarmManager.OnAlarmListener.class)); 6602 6603 // And down from there. 6604 setStandbyBucket(WORKING_INDEX); 6605 final long expectedWorkingAlarmTime = 6606 (now - 4 * HOUR_IN_MILLIS) + (24 * HOUR_IN_MILLIS) 6607 + mQcConstants.IN_QUOTA_BUFFER_MS; 6608 synchronized (mQuotaController.mLock) { 6609 mQuotaController.maybeScheduleStartAlarmLocked( 6610 SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); 6611 } 6612 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6613 anyInt(), eq(expectedWorkingAlarmTime), anyLong(), 6614 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 6615 6616 setStandbyBucket(FREQUENT_INDEX); 6617 final long expectedFrequentAlarmTime = 6618 (now - HOUR_IN_MILLIS) + (24 * HOUR_IN_MILLIS) + mQcConstants.IN_QUOTA_BUFFER_MS; 6619 synchronized (mQuotaController.mLock) { 6620 mQuotaController.maybeScheduleStartAlarmLocked( 6621 SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX); 6622 } 6623 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6624 anyInt(), eq(expectedFrequentAlarmTime), anyLong(), 6625 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 6626 6627 setStandbyBucket(RARE_INDEX); 6628 final long expectedRareAlarmTime = 6629 (now - 5 * MINUTE_IN_MILLIS) + (24 * HOUR_IN_MILLIS) 6630 + mQcConstants.IN_QUOTA_BUFFER_MS; 6631 synchronized (mQuotaController.mLock) { 6632 mQuotaController.maybeScheduleStartAlarmLocked( 6633 SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX); 6634 } 6635 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6636 anyInt(), eq(expectedRareAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6637 any(Handler.class)); 6638 6639 // And back up again. 6640 setStandbyBucket(FREQUENT_INDEX); 6641 synchronized (mQuotaController.mLock) { 6642 mQuotaController.maybeScheduleStartAlarmLocked( 6643 SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX); 6644 } 6645 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6646 anyInt(), eq(expectedFrequentAlarmTime), anyLong(), 6647 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 6648 6649 setStandbyBucket(WORKING_INDEX); 6650 synchronized (mQuotaController.mLock) { 6651 mQuotaController.maybeScheduleStartAlarmLocked( 6652 SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); 6653 } 6654 inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6655 anyInt(), eq(expectedWorkingAlarmTime), anyLong(), 6656 eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); 6657 6658 setStandbyBucket(ACTIVE_INDEX); 6659 synchronized (mQuotaController.mLock) { 6660 mQuotaController.maybeScheduleStartAlarmLocked( 6661 SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX); 6662 } 6663 inOrder.verify(mAlarmManager, timeout(1000).times(0)) 6664 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6665 any(Handler.class)); 6666 inOrder.verify(mAlarmManager, timeout(1000).times(1)) 6667 .cancel(any(AlarmManager.OnAlarmListener.class)); 6668 } 6669 6670 /** 6671 * Tests that the start alarm is properly rescheduled if the earliest session that contributes 6672 * to the app being out of quota contributes less than the quota buffer time. 6673 */ 6674 @Test testMaybeScheduleStartAlarmLocked_Ej_SmallRollingQuota()6675 public void testMaybeScheduleStartAlarmLocked_Ej_SmallRollingQuota() { 6676 // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests 6677 // because it schedules an alarm too. Prevent it from doing so. 6678 spyOn(mQuotaController); 6679 doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked(); 6680 6681 synchronized (mQuotaController.mLock) { 6682 mQuotaController.maybeStartTrackingJobLocked( 6683 createJobStatus("testMaybeScheduleStartAlarmLocked_Ej_SRQ", 1), null); 6684 } 6685 6686 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 6687 setStandbyBucket(WORKING_INDEX); 6688 final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2; 6689 final long remainingTimeMs = mQcConstants.EJ_LIMIT_WORKING_MS - contributionMs; 6690 6691 // Session straddles edge of bucket window. Only the contribution should be counted towards 6692 // the quota. 6693 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6694 createTimingSession(now - (24 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS), 6695 3 * MINUTE_IN_MILLIS + contributionMs, 3), true); 6696 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 6697 createTimingSession(now - 23 * HOUR_IN_MILLIS, remainingTimeMs, 2), true); 6698 // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which 6699 // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. 6700 final long expectedAlarmTime = 6701 now + HOUR_IN_MILLIS + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); 6702 synchronized (mQuotaController.mLock) { 6703 mQuotaController.maybeScheduleStartAlarmLocked( 6704 SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX); 6705 } 6706 verify(mAlarmManager, timeout(1000).times(1)).setWindow( 6707 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), 6708 any(Handler.class)); 6709 } 6710 6711 /** Tests that TimingSessions aren't saved when the device is charging. */ 6712 @Test testEJTimerTracking_Charging()6713 public void testEJTimerTracking_Charging() { 6714 setCharging(); 6715 6716 JobStatus jobStatus = createExpeditedJobStatus("testEJTimerTracking_Charging", 1); 6717 synchronized (mQuotaController.mLock) { 6718 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6719 } 6720 6721 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6722 6723 synchronized (mQuotaController.mLock) { 6724 mQuotaController.prepareForExecutionLocked(jobStatus); 6725 } 6726 advanceElapsedClock(5 * SECOND_IN_MILLIS); 6727 synchronized (mQuotaController.mLock) { 6728 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 6729 } 6730 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6731 } 6732 6733 /** Tests that TimingSessions are saved properly when the device is discharging. */ 6734 @Test testEJTimerTracking_Discharging()6735 public void testEJTimerTracking_Discharging() { 6736 setDischarging(); 6737 setProcessState(ActivityManager.PROCESS_STATE_BACKUP); 6738 6739 JobStatus jobStatus = createExpeditedJobStatus("testEJTimerTracking_Discharging", 1); 6740 synchronized (mQuotaController.mLock) { 6741 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6742 } 6743 6744 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6745 6746 List<TimingSession> expected = new ArrayList<>(); 6747 6748 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6749 synchronized (mQuotaController.mLock) { 6750 mQuotaController.prepareForExecutionLocked(jobStatus); 6751 } 6752 advanceElapsedClock(5 * SECOND_IN_MILLIS); 6753 synchronized (mQuotaController.mLock) { 6754 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 6755 } 6756 expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); 6757 assertEquals(expected, 6758 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6759 6760 // Test overlapping jobs. 6761 JobStatus jobStatus2 = createExpeditedJobStatus("testEJTimerTracking_Discharging", 2); 6762 synchronized (mQuotaController.mLock) { 6763 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 6764 } 6765 6766 JobStatus jobStatus3 = createExpeditedJobStatus("testEJTimerTracking_Discharging", 3); 6767 synchronized (mQuotaController.mLock) { 6768 mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); 6769 } 6770 6771 advanceElapsedClock(SECOND_IN_MILLIS); 6772 6773 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6774 synchronized (mQuotaController.mLock) { 6775 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6776 mQuotaController.prepareForExecutionLocked(jobStatus); 6777 } 6778 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6779 synchronized (mQuotaController.mLock) { 6780 mQuotaController.prepareForExecutionLocked(jobStatus2); 6781 } 6782 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6783 synchronized (mQuotaController.mLock) { 6784 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 6785 } 6786 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6787 synchronized (mQuotaController.mLock) { 6788 mQuotaController.prepareForExecutionLocked(jobStatus3); 6789 } 6790 advanceElapsedClock(20 * SECOND_IN_MILLIS); 6791 synchronized (mQuotaController.mLock) { 6792 mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null); 6793 } 6794 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6795 synchronized (mQuotaController.mLock) { 6796 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 6797 } 6798 expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); 6799 assertEquals(expected, 6800 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6801 } 6802 6803 /** 6804 * Tests that TimingSessions are saved properly when the device alternates between 6805 * charging and discharging. 6806 */ 6807 @Test testEJTimerTracking_ChargingAndDischarging()6808 public void testEJTimerTracking_ChargingAndDischarging() { 6809 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 6810 6811 JobStatus jobStatus = 6812 createExpeditedJobStatus("testEJTimerTracking_ChargingAndDischarging", 1); 6813 synchronized (mQuotaController.mLock) { 6814 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6815 } 6816 JobStatus jobStatus2 = 6817 createExpeditedJobStatus("testEJTimerTracking_ChargingAndDischarging", 2); 6818 synchronized (mQuotaController.mLock) { 6819 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 6820 } 6821 JobStatus jobStatus3 = 6822 createExpeditedJobStatus("testEJTimerTracking_ChargingAndDischarging", 3); 6823 synchronized (mQuotaController.mLock) { 6824 mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); 6825 } 6826 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6827 List<TimingSession> expected = new ArrayList<>(); 6828 6829 // A job starting while charging. Only the portion that runs during the discharging period 6830 // should be counted. 6831 setCharging(); 6832 6833 synchronized (mQuotaController.mLock) { 6834 mQuotaController.prepareForExecutionLocked(jobStatus); 6835 } 6836 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6837 setDischarging(); 6838 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6839 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6840 synchronized (mQuotaController.mLock) { 6841 mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus); 6842 } 6843 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 6844 assertEquals(expected, 6845 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6846 6847 advanceElapsedClock(SECOND_IN_MILLIS); 6848 6849 // One job starts while discharging, spans a charging session, and ends after the charging 6850 // session. Only the portions during the discharging periods should be counted. This should 6851 // result in two TimingSessions. A second job starts while discharging and ends within the 6852 // charging session. Only the portion during the first discharging portion should be 6853 // counted. A third job starts and ends within the charging session. The third job 6854 // shouldn't be included in either job count. 6855 setDischarging(); 6856 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6857 synchronized (mQuotaController.mLock) { 6858 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6859 mQuotaController.prepareForExecutionLocked(jobStatus); 6860 } 6861 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6862 synchronized (mQuotaController.mLock) { 6863 mQuotaController.prepareForExecutionLocked(jobStatus2); 6864 } 6865 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6866 setCharging(); 6867 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); 6868 synchronized (mQuotaController.mLock) { 6869 mQuotaController.prepareForExecutionLocked(jobStatus3); 6870 } 6871 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6872 synchronized (mQuotaController.mLock) { 6873 mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null); 6874 } 6875 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6876 synchronized (mQuotaController.mLock) { 6877 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 6878 } 6879 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6880 setDischarging(); 6881 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6882 advanceElapsedClock(20 * SECOND_IN_MILLIS); 6883 synchronized (mQuotaController.mLock) { 6884 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 6885 } 6886 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1)); 6887 assertEquals(expected, 6888 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6889 6890 // A job starting while discharging and ending while charging. Only the portion that runs 6891 // during the discharging period should be counted. 6892 setDischarging(); 6893 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6894 synchronized (mQuotaController.mLock) { 6895 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 6896 mQuotaController.prepareForExecutionLocked(jobStatus2); 6897 } 6898 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6899 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 6900 setCharging(); 6901 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6902 synchronized (mQuotaController.mLock) { 6903 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 6904 } 6905 assertEquals(expected, 6906 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6907 } 6908 6909 /** Tests that TimingSessions are saved properly when all the jobs are background jobs. */ 6910 @Test testEJTimerTracking_AllBackground()6911 public void testEJTimerTracking_AllBackground() { 6912 setDischarging(); 6913 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 6914 6915 JobStatus jobStatus = createExpeditedJobStatus("testEJTimerTracking_AllBackground", 1); 6916 synchronized (mQuotaController.mLock) { 6917 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6918 } 6919 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6920 6921 List<TimingSession> expected = new ArrayList<>(); 6922 6923 // Test single job. 6924 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6925 synchronized (mQuotaController.mLock) { 6926 mQuotaController.prepareForExecutionLocked(jobStatus); 6927 } 6928 advanceElapsedClock(5 * SECOND_IN_MILLIS); 6929 synchronized (mQuotaController.mLock) { 6930 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 6931 } 6932 expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1)); 6933 assertEquals(expected, 6934 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6935 6936 // Test overlapping jobs. 6937 JobStatus jobStatus2 = createExpeditedJobStatus("testEJTimerTracking_AllBackground", 2); 6938 synchronized (mQuotaController.mLock) { 6939 mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null); 6940 } 6941 6942 JobStatus jobStatus3 = createExpeditedJobStatus("testEJTimerTracking_AllBackground", 3); 6943 synchronized (mQuotaController.mLock) { 6944 mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null); 6945 } 6946 6947 advanceElapsedClock(SECOND_IN_MILLIS); 6948 6949 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 6950 synchronized (mQuotaController.mLock) { 6951 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6952 mQuotaController.prepareForExecutionLocked(jobStatus); 6953 } 6954 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6955 synchronized (mQuotaController.mLock) { 6956 mQuotaController.prepareForExecutionLocked(jobStatus2); 6957 } 6958 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6959 synchronized (mQuotaController.mLock) { 6960 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 6961 } 6962 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6963 synchronized (mQuotaController.mLock) { 6964 mQuotaController.prepareForExecutionLocked(jobStatus3); 6965 } 6966 advanceElapsedClock(20 * SECOND_IN_MILLIS); 6967 synchronized (mQuotaController.mLock) { 6968 mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null); 6969 } 6970 advanceElapsedClock(10 * SECOND_IN_MILLIS); 6971 synchronized (mQuotaController.mLock) { 6972 mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null); 6973 } 6974 expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3)); 6975 assertEquals(expected, 6976 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6977 } 6978 6979 /** Tests that Timers don't count foreground jobs. */ 6980 @Test testEJTimerTracking_AllForeground()6981 public void testEJTimerTracking_AllForeground() { 6982 setDischarging(); 6983 6984 JobStatus jobStatus = createExpeditedJobStatus("testEJTimerTracking_AllForeground", 1); 6985 setProcessState(ActivityManager.PROCESS_STATE_TOP); 6986 synchronized (mQuotaController.mLock) { 6987 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 6988 } 6989 6990 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 6991 6992 synchronized (mQuotaController.mLock) { 6993 mQuotaController.prepareForExecutionLocked(jobStatus); 6994 } 6995 advanceElapsedClock(5 * SECOND_IN_MILLIS); 6996 // Change to a state that should still be considered foreground. 6997 setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); 6998 advanceElapsedClock(5 * SECOND_IN_MILLIS); 6999 synchronized (mQuotaController.mLock) { 7000 mQuotaController.maybeStopTrackingJobLocked(jobStatus, null); 7001 } 7002 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7003 } 7004 7005 /** 7006 * Tests that Timers properly track sessions when switching between foreground and background 7007 * states. 7008 */ 7009 @Test testEJTimerTracking_ForegroundAndBackground()7010 public void testEJTimerTracking_ForegroundAndBackground() { 7011 setDischarging(); 7012 7013 JobStatus jobBg1 = 7014 createExpeditedJobStatus("testEJTimerTracking_ForegroundAndBackground", 1); 7015 JobStatus jobBg2 = 7016 createExpeditedJobStatus("testEJTimerTracking_ForegroundAndBackground", 2); 7017 JobStatus jobFg3 = 7018 createExpeditedJobStatus("testEJTimerTracking_ForegroundAndBackground", 3); 7019 synchronized (mQuotaController.mLock) { 7020 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 7021 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 7022 mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); 7023 } 7024 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7025 List<TimingSession> expected = new ArrayList<>(); 7026 7027 // UID starts out inactive. 7028 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 7029 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7030 synchronized (mQuotaController.mLock) { 7031 mQuotaController.prepareForExecutionLocked(jobBg1); 7032 } 7033 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7034 synchronized (mQuotaController.mLock) { 7035 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 7036 } 7037 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7038 assertEquals(expected, 7039 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7040 7041 advanceElapsedClock(SECOND_IN_MILLIS); 7042 7043 // Bg job starts while inactive, spans an entire active session, and ends after the 7044 // active session. 7045 // App switching to foreground state then fg job starts. 7046 // App remains in foreground state after coming to foreground, so there should only be one 7047 // session. 7048 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7049 synchronized (mQuotaController.mLock) { 7050 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 7051 mQuotaController.prepareForExecutionLocked(jobBg2); 7052 } 7053 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7054 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7055 setProcessState(getProcessStateQuotaFreeThreshold()); 7056 synchronized (mQuotaController.mLock) { 7057 mQuotaController.prepareForExecutionLocked(jobFg3); 7058 } 7059 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7060 synchronized (mQuotaController.mLock) { 7061 mQuotaController.maybeStopTrackingJobLocked(jobFg3, null); 7062 } 7063 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7064 synchronized (mQuotaController.mLock) { 7065 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 7066 } 7067 assertEquals(expected, 7068 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7069 7070 advanceElapsedClock(SECOND_IN_MILLIS); 7071 7072 // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes 7073 // "inactive" and then bg job 2 starts. Then fg job ends. 7074 // This should result in two TimingSessions: 7075 // * The first should have a count of 1 7076 // * The second should have a count of 2 since it will include both jobs 7077 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7078 synchronized (mQuotaController.mLock) { 7079 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 7080 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 7081 mQuotaController.maybeStartTrackingJobLocked(jobFg3, null); 7082 } 7083 setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); 7084 synchronized (mQuotaController.mLock) { 7085 mQuotaController.prepareForExecutionLocked(jobBg1); 7086 } 7087 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7088 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7089 setProcessState(getProcessStateQuotaFreeThreshold()); 7090 synchronized (mQuotaController.mLock) { 7091 mQuotaController.prepareForExecutionLocked(jobFg3); 7092 } 7093 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7094 synchronized (mQuotaController.mLock) { 7095 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 7096 } 7097 advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now 7098 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7099 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 7100 synchronized (mQuotaController.mLock) { 7101 mQuotaController.prepareForExecutionLocked(jobBg2); 7102 } 7103 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7104 synchronized (mQuotaController.mLock) { 7105 mQuotaController.maybeStopTrackingJobLocked(jobFg3, null); 7106 } 7107 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7108 synchronized (mQuotaController.mLock) { 7109 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 7110 } 7111 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); 7112 assertEquals(expected, 7113 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7114 } 7115 7116 /** 7117 * Tests that Timers properly track overlapping top and background jobs. 7118 */ 7119 @Test testEJTimerTracking_TopAndNonTop()7120 public void testEJTimerTracking_TopAndNonTop() { 7121 setDischarging(); 7122 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0); 7123 7124 JobStatus jobBg1 = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 1); 7125 JobStatus jobBg2 = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 2); 7126 JobStatus jobFg1 = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 3); 7127 JobStatus jobTop = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 4); 7128 synchronized (mQuotaController.mLock) { 7129 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 7130 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 7131 mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); 7132 mQuotaController.maybeStartTrackingJobLocked(jobTop, null); 7133 } 7134 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7135 List<TimingSession> expected = new ArrayList<>(); 7136 7137 // UID starts out inactive. 7138 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 7139 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7140 synchronized (mQuotaController.mLock) { 7141 mQuotaController.prepareForExecutionLocked(jobBg1); 7142 } 7143 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7144 synchronized (mQuotaController.mLock) { 7145 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 7146 } 7147 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7148 assertEquals(expected, 7149 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7150 7151 advanceElapsedClock(SECOND_IN_MILLIS); 7152 7153 // Bg job starts while inactive, spans an entire active session, and ends after the 7154 // active session. 7155 // App switching to top state then fg job starts. 7156 // App remains in top state after coming to top, so there should only be one 7157 // session. 7158 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7159 synchronized (mQuotaController.mLock) { 7160 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 7161 mQuotaController.prepareForExecutionLocked(jobBg2); 7162 } 7163 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7164 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7165 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7166 synchronized (mQuotaController.mLock) { 7167 mQuotaController.prepareForExecutionLocked(jobTop); 7168 } 7169 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7170 synchronized (mQuotaController.mLock) { 7171 mQuotaController.maybeStopTrackingJobLocked(jobTop, null); 7172 } 7173 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7174 synchronized (mQuotaController.mLock) { 7175 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 7176 } 7177 assertEquals(expected, 7178 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7179 7180 advanceElapsedClock(SECOND_IN_MILLIS); 7181 mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 7182 7183 // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to 7184 // foreground_service and a new job starts. Shortly after, uid goes 7185 // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. 7186 // This should result in two TimingSessions: 7187 // * The first should have a count of 1 7188 // * The second should have a count of 2, which accounts for the bg2 and fg, but not top 7189 // jobs. 7190 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7191 synchronized (mQuotaController.mLock) { 7192 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 7193 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 7194 mQuotaController.maybeStartTrackingJobLocked(jobTop, null); 7195 } 7196 setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); 7197 synchronized (mQuotaController.mLock) { 7198 mQuotaController.prepareForExecutionLocked(jobBg1); 7199 } 7200 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7201 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7202 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7203 synchronized (mQuotaController.mLock) { 7204 mQuotaController.prepareForExecutionLocked(jobTop); 7205 } 7206 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7207 synchronized (mQuotaController.mLock) { 7208 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 7209 } 7210 advanceElapsedClock(5 * SECOND_IN_MILLIS); 7211 setProcessState(getProcessStateQuotaFreeThreshold()); 7212 synchronized (mQuotaController.mLock) { 7213 mQuotaController.prepareForExecutionLocked(jobFg1); 7214 } 7215 advanceElapsedClock(5 * SECOND_IN_MILLIS); 7216 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7217 advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now 7218 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7219 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 7220 synchronized (mQuotaController.mLock) { 7221 mQuotaController.prepareForExecutionLocked(jobBg2); 7222 } 7223 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7224 synchronized (mQuotaController.mLock) { 7225 mQuotaController.maybeStopTrackingJobLocked(jobTop, null); 7226 } 7227 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7228 synchronized (mQuotaController.mLock) { 7229 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 7230 mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); 7231 } 7232 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); 7233 assertEquals(expected, 7234 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7235 7236 advanceElapsedClock(SECOND_IN_MILLIS); 7237 mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); 7238 7239 // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to 7240 // foreground_service and a new job starts. Shortly after, uid goes 7241 // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. 7242 // This should result in two TimingSessions: 7243 // * The first should have a count of 1 7244 // * The second should have a count of 3, which accounts for the bg2, fg and top jobs. 7245 // Top started jobs are not quota free any more if the process leaves TOP/BTOP state. 7246 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7247 synchronized (mQuotaController.mLock) { 7248 mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); 7249 mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); 7250 mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); 7251 mQuotaController.maybeStartTrackingJobLocked(jobTop, null); 7252 } 7253 setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); 7254 synchronized (mQuotaController.mLock) { 7255 mQuotaController.prepareForExecutionLocked(jobBg1); 7256 } 7257 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7258 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7259 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7260 synchronized (mQuotaController.mLock) { 7261 mQuotaController.prepareForExecutionLocked(jobTop); 7262 } 7263 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7264 synchronized (mQuotaController.mLock) { 7265 mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); 7266 } 7267 advanceElapsedClock(5 * SECOND_IN_MILLIS); 7268 setProcessState(getProcessStateQuotaFreeThreshold()); 7269 synchronized (mQuotaController.mLock) { 7270 mQuotaController.prepareForExecutionLocked(jobFg1); 7271 } 7272 advanceElapsedClock(5 * SECOND_IN_MILLIS); 7273 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7274 advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now 7275 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7276 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 7277 synchronized (mQuotaController.mLock) { 7278 mQuotaController.prepareForExecutionLocked(jobBg2); 7279 } 7280 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7281 synchronized (mQuotaController.mLock) { 7282 mQuotaController.maybeStopTrackingJobLocked(jobTop, null); 7283 } 7284 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7285 synchronized (mQuotaController.mLock) { 7286 mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); 7287 mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); 7288 } 7289 expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 3)); 7290 assertEquals(expected, 7291 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7292 } 7293 7294 /** 7295 * Tests that Timers properly track sessions when an app is added and removed from the temp 7296 * allowlist. 7297 */ 7298 @Test testEJTimerTracking_TempAllowlisting()7299 public void testEJTimerTracking_TempAllowlisting() { 7300 setDischarging(); 7301 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 7302 final long gracePeriodMs = 15 * SECOND_IN_MILLIS; 7303 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); 7304 Handler handler = mQuotaController.getHandler(); 7305 spyOn(handler); 7306 7307 JobStatus job1 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 1); 7308 JobStatus job2 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 2); 7309 JobStatus job3 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 3); 7310 JobStatus job4 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 4); 7311 JobStatus job5 = createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting", 5); 7312 synchronized (mQuotaController.mLock) { 7313 mQuotaController.maybeStartTrackingJobLocked(job1, null); 7314 } 7315 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7316 List<TimingSession> expected = new ArrayList<>(); 7317 7318 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7319 synchronized (mQuotaController.mLock) { 7320 mQuotaController.prepareForExecutionLocked(job1); 7321 } 7322 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7323 synchronized (mQuotaController.mLock) { 7324 mQuotaController.maybeStopTrackingJobLocked(job1, job1); 7325 } 7326 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7327 assertEquals(expected, 7328 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7329 7330 advanceElapsedClock(SECOND_IN_MILLIS); 7331 7332 // Job starts after app is added to temp allowlist and stops before removal. 7333 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7334 mTempAllowlistListener.onAppAdded(mSourceUid); 7335 synchronized (mQuotaController.mLock) { 7336 mQuotaController.maybeStartTrackingJobLocked(job2, null); 7337 mQuotaController.prepareForExecutionLocked(job2); 7338 } 7339 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7340 synchronized (mQuotaController.mLock) { 7341 mQuotaController.maybeStopTrackingJobLocked(job2, null); 7342 } 7343 assertEquals(expected, 7344 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7345 7346 // Job starts after app is added to temp allowlist and stops after removal, 7347 // before grace period ends. 7348 mTempAllowlistListener.onAppAdded(mSourceUid); 7349 synchronized (mQuotaController.mLock) { 7350 mQuotaController.maybeStartTrackingJobLocked(job3, null); 7351 mQuotaController.prepareForExecutionLocked(job3); 7352 } 7353 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7354 mTempAllowlistListener.onAppRemoved(mSourceUid); 7355 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7356 long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS; 7357 advanceElapsedClock(elapsedGracePeriodMs); 7358 synchronized (mQuotaController.mLock) { 7359 mQuotaController.maybeStopTrackingJobLocked(job3, null); 7360 } 7361 assertEquals(expected, 7362 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7363 7364 advanceElapsedClock(SECOND_IN_MILLIS); 7365 elapsedGracePeriodMs += SECOND_IN_MILLIS; 7366 7367 // Job starts during grace period and ends after grace period ends 7368 synchronized (mQuotaController.mLock) { 7369 mQuotaController.maybeStartTrackingJobLocked(job4, null); 7370 mQuotaController.prepareForExecutionLocked(job4); 7371 } 7372 final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs; 7373 start = JobSchedulerService.sElapsedRealtimeClock.millis() + remainingGracePeriod; 7374 advanceElapsedClock(remainingGracePeriod); 7375 // Wait for handler to update Timer 7376 // Can't directly evaluate the message because for some reason, the captured message returns 7377 // the wrong 'what' even though the correct message goes to the handler and the correct 7378 // path executes. 7379 verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any()); 7380 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7381 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7382 synchronized (mQuotaController.mLock) { 7383 mQuotaController.maybeStopTrackingJobLocked(job4, job4); 7384 } 7385 assertEquals(expected, 7386 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7387 7388 // Job starts and runs completely after temp allowlist grace period. 7389 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7390 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7391 synchronized (mQuotaController.mLock) { 7392 mQuotaController.maybeStartTrackingJobLocked(job5, null); 7393 mQuotaController.prepareForExecutionLocked(job5); 7394 } 7395 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7396 synchronized (mQuotaController.mLock) { 7397 mQuotaController.maybeStopTrackingJobLocked(job5, job5); 7398 } 7399 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7400 assertEquals(expected, 7401 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7402 } 7403 7404 @Test testEJTimerTracking_TempAllowlisting_Restricted()7405 public void testEJTimerTracking_TempAllowlisting_Restricted() { 7406 setDischarging(); 7407 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 7408 final long gracePeriodMs = 15 * SECOND_IN_MILLIS; 7409 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); 7410 Handler handler = mQuotaController.getHandler(); 7411 spyOn(handler); 7412 7413 JobStatus job = 7414 createExpeditedJobStatus("testEJTimerTracking_TempAllowlisting_Restricted", 1); 7415 setStandbyBucket(RESTRICTED_INDEX, job); 7416 synchronized (mQuotaController.mLock) { 7417 mQuotaController.maybeStartTrackingJobLocked(job, null); 7418 } 7419 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7420 List<TimingSession> expected = new ArrayList<>(); 7421 7422 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7423 synchronized (mQuotaController.mLock) { 7424 mQuotaController.prepareForExecutionLocked(job); 7425 } 7426 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7427 synchronized (mQuotaController.mLock) { 7428 mQuotaController.maybeStopTrackingJobLocked(job, job); 7429 } 7430 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7431 assertEquals(expected, 7432 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7433 7434 advanceElapsedClock(SECOND_IN_MILLIS); 7435 7436 // Job starts after app is added to temp allowlist and stops before removal. 7437 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7438 mTempAllowlistListener.onAppAdded(mSourceUid); 7439 synchronized (mQuotaController.mLock) { 7440 mQuotaController.maybeStartTrackingJobLocked(job, null); 7441 mQuotaController.prepareForExecutionLocked(job); 7442 } 7443 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7444 synchronized (mQuotaController.mLock) { 7445 mQuotaController.maybeStopTrackingJobLocked(job, null); 7446 } 7447 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7448 assertEquals(expected, 7449 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7450 7451 // Job starts after app is added to temp allowlist and stops after removal, 7452 // before grace period ends. 7453 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7454 mTempAllowlistListener.onAppAdded(mSourceUid); 7455 synchronized (mQuotaController.mLock) { 7456 mQuotaController.maybeStartTrackingJobLocked(job, null); 7457 mQuotaController.prepareForExecutionLocked(job); 7458 } 7459 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7460 mTempAllowlistListener.onAppRemoved(mSourceUid); 7461 long elapsedGracePeriodMs = 2 * SECOND_IN_MILLIS; 7462 advanceElapsedClock(elapsedGracePeriodMs); 7463 synchronized (mQuotaController.mLock) { 7464 mQuotaController.maybeStopTrackingJobLocked(job, null); 7465 } 7466 expected.add(createTimingSession(start, 12 * SECOND_IN_MILLIS, 1)); 7467 assertEquals(expected, 7468 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7469 7470 advanceElapsedClock(SECOND_IN_MILLIS); 7471 elapsedGracePeriodMs += SECOND_IN_MILLIS; 7472 7473 // Job starts during grace period and ends after grace period ends 7474 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7475 synchronized (mQuotaController.mLock) { 7476 mQuotaController.maybeStartTrackingJobLocked(job, null); 7477 mQuotaController.prepareForExecutionLocked(job); 7478 } 7479 final long remainingGracePeriod = gracePeriodMs - elapsedGracePeriodMs; 7480 advanceElapsedClock(remainingGracePeriod); 7481 // Wait for handler to update Timer 7482 // Can't directly evaluate the message because for some reason, the captured message returns 7483 // the wrong 'what' even though the correct message goes to the handler and the correct 7484 // path executes. 7485 verify(handler, timeout(gracePeriodMs + 5 * SECOND_IN_MILLIS)).handleMessage(any()); 7486 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7487 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS + remainingGracePeriod, 1)); 7488 synchronized (mQuotaController.mLock) { 7489 mQuotaController.maybeStopTrackingJobLocked(job, job); 7490 } 7491 assertEquals(expected, 7492 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7493 7494 // Job starts and runs completely after temp allowlist grace period. 7495 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7496 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7497 synchronized (mQuotaController.mLock) { 7498 mQuotaController.maybeStartTrackingJobLocked(job, null); 7499 mQuotaController.prepareForExecutionLocked(job); 7500 } 7501 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7502 synchronized (mQuotaController.mLock) { 7503 mQuotaController.maybeStopTrackingJobLocked(job, job); 7504 } 7505 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7506 assertEquals(expected, 7507 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7508 } 7509 7510 /** 7511 * Tests that Timers properly track sessions when TOP state and temp allowlisting overlaps. 7512 */ 7513 @Test 7514 @LargeTest testEJTimerTracking_TopAndTempAllowlisting()7515 public void testEJTimerTracking_TopAndTempAllowlisting() throws Exception { 7516 setDischarging(); 7517 setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); 7518 final long gracePeriodMs = 5 * SECOND_IN_MILLIS; 7519 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); 7520 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, gracePeriodMs); 7521 Handler handler = mQuotaController.getHandler(); 7522 spyOn(handler); 7523 7524 JobStatus job1 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 1); 7525 JobStatus job2 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 2); 7526 JobStatus job3 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 3); 7527 JobStatus job4 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 4); 7528 JobStatus job5 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 5); 7529 synchronized (mQuotaController.mLock) { 7530 mQuotaController.maybeStartTrackingJobLocked(job1, null); 7531 } 7532 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7533 List<TimingSession> expected = new ArrayList<>(); 7534 7535 // Case 1: job starts in TA grace period then app becomes TOP 7536 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7537 mTempAllowlistListener.onAppAdded(mSourceUid); 7538 mTempAllowlistListener.onAppRemoved(mSourceUid); 7539 advanceElapsedClock(gracePeriodMs / 2); 7540 synchronized (mQuotaController.mLock) { 7541 mQuotaController.prepareForExecutionLocked(job1); 7542 } 7543 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7544 advanceElapsedClock(gracePeriodMs); 7545 // Wait for the grace period to expire so the handler can process the message. 7546 Thread.sleep(gracePeriodMs); 7547 synchronized (mQuotaController.mLock) { 7548 mQuotaController.maybeStopTrackingJobLocked(job1, job1); 7549 } 7550 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7551 7552 advanceElapsedClock(gracePeriodMs); 7553 7554 // Case 2: job starts in TOP grace period then is TAed 7555 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7556 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7557 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 7558 advanceElapsedClock(gracePeriodMs / 2); 7559 synchronized (mQuotaController.mLock) { 7560 mQuotaController.maybeStartTrackingJobLocked(job2, null); 7561 mQuotaController.prepareForExecutionLocked(job2); 7562 } 7563 mTempAllowlistListener.onAppAdded(mSourceUid); 7564 advanceElapsedClock(gracePeriodMs); 7565 // Wait for the grace period to expire so the handler can process the message. 7566 Thread.sleep(gracePeriodMs); 7567 synchronized (mQuotaController.mLock) { 7568 mQuotaController.maybeStopTrackingJobLocked(job2, null); 7569 } 7570 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7571 7572 advanceElapsedClock(gracePeriodMs); 7573 7574 // Case 3: job starts in TA grace period then app becomes TOP; job ends after TOP grace 7575 mTempAllowlistListener.onAppAdded(mSourceUid); 7576 mTempAllowlistListener.onAppRemoved(mSourceUid); 7577 advanceElapsedClock(gracePeriodMs / 2); 7578 synchronized (mQuotaController.mLock) { 7579 mQuotaController.maybeStartTrackingJobLocked(job3, null); 7580 mQuotaController.prepareForExecutionLocked(job3); 7581 } 7582 advanceElapsedClock(SECOND_IN_MILLIS); 7583 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7584 advanceElapsedClock(gracePeriodMs); 7585 // Wait for the grace period to expire so the handler can process the message. 7586 Thread.sleep(gracePeriodMs); 7587 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 7588 advanceElapsedClock(gracePeriodMs); 7589 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7590 // Wait for the grace period to expire so the handler can process the message. 7591 Thread.sleep(2 * gracePeriodMs); 7592 advanceElapsedClock(gracePeriodMs); 7593 synchronized (mQuotaController.mLock) { 7594 mQuotaController.maybeStopTrackingJobLocked(job3, job3); 7595 } 7596 expected.add(createTimingSession(start, gracePeriodMs, 1)); 7597 assertEquals(expected, 7598 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7599 7600 advanceElapsedClock(gracePeriodMs); 7601 7602 // Case 4: job starts in TOP grace period then app becomes TAed; job ends after TA grace 7603 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7604 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 7605 advanceElapsedClock(gracePeriodMs / 2); 7606 synchronized (mQuotaController.mLock) { 7607 mQuotaController.maybeStartTrackingJobLocked(job4, null); 7608 mQuotaController.prepareForExecutionLocked(job4); 7609 } 7610 advanceElapsedClock(SECOND_IN_MILLIS); 7611 mTempAllowlistListener.onAppAdded(mSourceUid); 7612 advanceElapsedClock(gracePeriodMs); 7613 // Wait for the grace period to expire so the handler can process the message. 7614 Thread.sleep(gracePeriodMs); 7615 mTempAllowlistListener.onAppRemoved(mSourceUid); 7616 advanceElapsedClock(gracePeriodMs); 7617 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7618 // Wait for the grace period to expire so the handler can process the message. 7619 Thread.sleep(2 * gracePeriodMs); 7620 advanceElapsedClock(gracePeriodMs); 7621 synchronized (mQuotaController.mLock) { 7622 mQuotaController.maybeStopTrackingJobLocked(job4, job4); 7623 } 7624 expected.add(createTimingSession(start, gracePeriodMs, 1)); 7625 assertEquals(expected, 7626 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7627 7628 advanceElapsedClock(gracePeriodMs); 7629 7630 // Case 5: job starts during overlapping grace period 7631 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7632 advanceElapsedClock(SECOND_IN_MILLIS); 7633 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 7634 advanceElapsedClock(SECOND_IN_MILLIS); 7635 mTempAllowlistListener.onAppAdded(mSourceUid); 7636 advanceElapsedClock(SECOND_IN_MILLIS); 7637 mTempAllowlistListener.onAppRemoved(mSourceUid); 7638 advanceElapsedClock(gracePeriodMs - SECOND_IN_MILLIS); 7639 synchronized (mQuotaController.mLock) { 7640 mQuotaController.maybeStartTrackingJobLocked(job5, null); 7641 mQuotaController.prepareForExecutionLocked(job5); 7642 } 7643 advanceElapsedClock(SECOND_IN_MILLIS); 7644 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7645 // Wait for the grace period to expire so the handler can process the message. 7646 Thread.sleep(2 * gracePeriodMs); 7647 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7648 synchronized (mQuotaController.mLock) { 7649 mQuotaController.maybeStopTrackingJobLocked(job5, job5); 7650 } 7651 expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7652 assertEquals(expected, 7653 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7654 } 7655 7656 /** 7657 * Tests that expedited jobs aren't stopped when an app runs out of quota. 7658 */ 7659 @Test 7660 @DisableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) testEJTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling()7661 public void testEJTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling() { 7662 setDischarging(); 7663 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0); 7664 7665 JobStatus jobBg = 7666 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 1); 7667 JobStatus jobTop = 7668 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 2); 7669 JobStatus jobUnstarted = 7670 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 3); 7671 trackJobs(jobBg, jobTop, jobUnstarted); 7672 setStandbyBucket(WORKING_INDEX, jobTop, jobBg, jobUnstarted); 7673 // Now the package only has 20 seconds to run. 7674 final long remainingTimeMs = 20 * SECOND_IN_MILLIS; 7675 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 7676 createTimingSession( 7677 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, 7678 mQcConstants.EJ_LIMIT_WORKING_MS - remainingTimeMs, 1), true); 7679 7680 InOrder inOrder = inOrder(mJobSchedulerService); 7681 7682 // UID starts out inactive. 7683 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 7684 // Start the job. 7685 synchronized (mQuotaController.mLock) { 7686 mQuotaController.prepareForExecutionLocked(jobBg); 7687 } 7688 advanceElapsedClock(remainingTimeMs / 2); 7689 // New job starts after UID is in the foreground. Since the app is now in the foreground, it 7690 // should continue to have remainingTimeMs / 2 time remaining. 7691 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7692 synchronized (mQuotaController.mLock) { 7693 mQuotaController.prepareForExecutionLocked(jobTop); 7694 } 7695 advanceElapsedClock(remainingTimeMs); 7696 7697 // Wait for some extra time to allow for job processing. 7698 inOrder.verify(mJobSchedulerService, 7699 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) 7700 .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); 7701 synchronized (mQuotaController.mLock) { 7702 assertEquals(remainingTimeMs / 2, 7703 mQuotaController.getRemainingEJExecutionTimeLocked( 7704 SOURCE_USER_ID, SOURCE_PACKAGE)); 7705 } 7706 // Go to a background state. 7707 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 7708 advanceElapsedClock(remainingTimeMs / 2 + 1); 7709 inOrder.verify(mJobSchedulerService, 7710 timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) 7711 .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); 7712 // Top should still be "in quota" since it started before the app ran on top out of quota. 7713 assertFalse(jobBg.isExpeditedQuotaApproved()); 7714 assertTrue(jobTop.isExpeditedQuotaApproved()); 7715 assertFalse(jobUnstarted.isExpeditedQuotaApproved()); 7716 synchronized (mQuotaController.mLock) { 7717 assertTrue( 7718 0 >= mQuotaController 7719 .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 7720 } 7721 7722 // New jobs to run. 7723 JobStatus jobBg2 = 7724 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 4); 7725 JobStatus jobTop2 = 7726 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 5); 7727 JobStatus jobFg = 7728 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 6); 7729 setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); 7730 7731 advanceElapsedClock(20 * SECOND_IN_MILLIS); 7732 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7733 // Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota. 7734 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 7735 .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); 7736 trackJobs(jobTop2, jobFg); 7737 synchronized (mQuotaController.mLock) { 7738 mQuotaController.prepareForExecutionLocked(jobTop2); 7739 } 7740 assertTrue(jobTop2.isExpeditedQuotaApproved()); 7741 assertTrue(jobFg.isExpeditedQuotaApproved()); 7742 assertTrue(jobBg.isExpeditedQuotaApproved()); 7743 assertTrue(jobUnstarted.isExpeditedQuotaApproved()); 7744 7745 // App still in foreground so everything should be in quota. 7746 advanceElapsedClock(20 * SECOND_IN_MILLIS); 7747 setProcessState(getProcessStateQuotaFreeThreshold()); 7748 assertTrue(jobTop2.isExpeditedQuotaApproved()); 7749 assertTrue(jobFg.isExpeditedQuotaApproved()); 7750 assertTrue(jobBg.isExpeditedQuotaApproved()); 7751 assertTrue(jobUnstarted.isExpeditedQuotaApproved()); 7752 7753 advanceElapsedClock(20 * SECOND_IN_MILLIS); 7754 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 7755 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 7756 .onControllerStateChanged(argThat(jobs -> jobs.size() == 3)); 7757 // App is now in background and out of quota. Fg should now change to out of quota since it 7758 // wasn't started. Top should remain in quota since it started when the app was in TOP. 7759 assertTrue(jobTop2.isExpeditedQuotaApproved()); 7760 assertFalse(jobFg.isExpeditedQuotaApproved()); 7761 assertFalse(jobBg.isExpeditedQuotaApproved()); 7762 trackJobs(jobBg2); 7763 assertFalse(jobBg2.isExpeditedQuotaApproved()); 7764 assertFalse(jobUnstarted.isExpeditedQuotaApproved()); 7765 synchronized (mQuotaController.mLock) { 7766 assertTrue( 7767 0 >= mQuotaController 7768 .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 7769 } 7770 } 7771 7772 /** 7773 * Tests that expedited jobs are stopped when an app runs out of quota. 7774 */ 7775 @Test 7776 @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) testEJTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling()7777 public void testEJTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling() { 7778 setDischarging(); 7779 setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0); 7780 7781 JobStatus jobBg = 7782 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 1); 7783 JobStatus jobTop = 7784 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 2); 7785 JobStatus jobUnstarted = 7786 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 3); 7787 trackJobs(jobBg, jobTop, jobUnstarted); 7788 setStandbyBucket(WORKING_INDEX, jobTop, jobBg, jobUnstarted); 7789 // Now the package only has 20 seconds to run. 7790 final long remainingTimeMs = 20 * SECOND_IN_MILLIS; 7791 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 7792 createTimingSession( 7793 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, 7794 mQcConstants.EJ_LIMIT_WORKING_MS - remainingTimeMs, 1), true); 7795 7796 InOrder inOrder = inOrder(mJobSchedulerService); 7797 7798 // UID starts out inactive. 7799 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 7800 // Start the job. 7801 synchronized (mQuotaController.mLock) { 7802 mQuotaController.prepareForExecutionLocked(jobBg); 7803 } 7804 advanceElapsedClock(remainingTimeMs / 2); 7805 // New job starts after UID is in the foreground. Since the app is now in the foreground, it 7806 // should continue to have remainingTimeMs / 2 time remaining. 7807 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7808 synchronized (mQuotaController.mLock) { 7809 mQuotaController.prepareForExecutionLocked(jobTop); 7810 } 7811 advanceElapsedClock(remainingTimeMs); 7812 7813 // Wait for some extra time to allow for job processing. 7814 inOrder.verify(mJobSchedulerService, 7815 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) 7816 .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); 7817 synchronized (mQuotaController.mLock) { 7818 assertEquals(remainingTimeMs / 2, 7819 mQuotaController.getRemainingEJExecutionTimeLocked( 7820 SOURCE_USER_ID, SOURCE_PACKAGE)); 7821 } 7822 // Go to a background state. 7823 setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); 7824 advanceElapsedClock(remainingTimeMs / 2 + 1); 7825 // Bg, Top and jobUnstarted should be changed from in-quota to out-of-quota. 7826 inOrder.verify(mJobSchedulerService, 7827 timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) 7828 .onControllerStateChanged(argThat(jobs -> jobs.size() == 3)); 7829 // Top should still NOT be "in quota" even it started before the app 7830 // ran on top out of quota. 7831 assertFalse(jobBg.isExpeditedQuotaApproved()); 7832 assertFalse(jobTop.isExpeditedQuotaApproved()); 7833 assertFalse(jobUnstarted.isExpeditedQuotaApproved()); 7834 synchronized (mQuotaController.mLock) { 7835 assertTrue( 7836 0 >= mQuotaController 7837 .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 7838 } 7839 7840 // New jobs to run. 7841 JobStatus jobBg2 = 7842 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 4); 7843 JobStatus jobTop2 = 7844 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 5); 7845 JobStatus jobFg = 7846 createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 6); 7847 setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); 7848 7849 advanceElapsedClock(20 * SECOND_IN_MILLIS); 7850 setProcessState(ActivityManager.PROCESS_STATE_TOP); 7851 // Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota. 7852 // jobBg, jobFg and jobUnstarted are changed from out-of-quota to in-quota. 7853 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 7854 .onControllerStateChanged(argThat(jobs -> jobs.size() == 3)); 7855 trackJobs(jobTop2, jobFg); 7856 synchronized (mQuotaController.mLock) { 7857 mQuotaController.prepareForExecutionLocked(jobTop2); 7858 } 7859 assertTrue(jobTop.isExpeditedQuotaApproved()); 7860 assertTrue(jobTop2.isExpeditedQuotaApproved()); 7861 assertTrue(jobFg.isExpeditedQuotaApproved()); 7862 assertTrue(jobBg.isExpeditedQuotaApproved()); 7863 assertTrue(jobUnstarted.isExpeditedQuotaApproved()); 7864 7865 // App still in foreground so everything should be in quota. 7866 advanceElapsedClock(20 * SECOND_IN_MILLIS); 7867 setProcessState(getProcessStateQuotaFreeThreshold()); 7868 assertTrue(jobTop.isExpeditedQuotaApproved()); 7869 assertTrue(jobTop2.isExpeditedQuotaApproved()); 7870 assertTrue(jobFg.isExpeditedQuotaApproved()); 7871 assertTrue(jobBg.isExpeditedQuotaApproved()); 7872 assertTrue(jobUnstarted.isExpeditedQuotaApproved()); 7873 7874 advanceElapsedClock(20 * SECOND_IN_MILLIS); 7875 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 7876 // Bg, Fg, Top, Top2 and jobUnstarted should be changed from in-quota to out-of-quota 7877 inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) 7878 .onControllerStateChanged(argThat(jobs -> jobs.size() == 5)); 7879 // App is now in background and out of quota. Fg should now change to out of quota since it 7880 // wasn't started. Top should change to out of quota as the app leaves TOP state. 7881 assertFalse(jobTop.isExpeditedQuotaApproved()); 7882 assertFalse(jobTop2.isExpeditedQuotaApproved()); 7883 assertFalse(jobFg.isExpeditedQuotaApproved()); 7884 assertFalse(jobBg.isExpeditedQuotaApproved()); 7885 trackJobs(jobBg2); 7886 assertFalse(jobBg2.isExpeditedQuotaApproved()); 7887 assertFalse(jobUnstarted.isExpeditedQuotaApproved()); 7888 synchronized (mQuotaController.mLock) { 7889 assertTrue( 7890 0 >= mQuotaController 7891 .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 7892 } 7893 } 7894 7895 /** 7896 * Tests that Timers properly track overlapping top and background jobs. 7897 */ 7898 @Test testEJTimerTrackingSeparateFromRegularTracking()7899 public void testEJTimerTrackingSeparateFromRegularTracking() { 7900 setDischarging(); 7901 setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); 7902 7903 JobStatus jobReg1 = createJobStatus("testEJTimerTrackingSeparateFromRegularTracking", 1); 7904 JobStatus jobEJ1 = 7905 createExpeditedJobStatus("testEJTimerTrackingSeparateFromRegularTracking", 2); 7906 JobStatus jobReg2 = createJobStatus("testEJTimerTrackingSeparateFromRegularTracking", 3); 7907 JobStatus jobEJ2 = 7908 createExpeditedJobStatus("testEJTimerTrackingSeparateFromRegularTracking", 4); 7909 synchronized (mQuotaController.mLock) { 7910 mQuotaController.maybeStartTrackingJobLocked(jobReg1, null); 7911 mQuotaController.maybeStartTrackingJobLocked(jobEJ1, null); 7912 mQuotaController.maybeStartTrackingJobLocked(jobReg2, null); 7913 mQuotaController.maybeStartTrackingJobLocked(jobEJ2, null); 7914 } 7915 assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7916 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7917 List<TimingSession> expectedRegular = new ArrayList<>(); 7918 List<TimingSession> expectedEJ = new ArrayList<>(); 7919 7920 // First, regular job runs by itself. 7921 long start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7922 synchronized (mQuotaController.mLock) { 7923 mQuotaController.prepareForExecutionLocked(jobReg1); 7924 } 7925 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7926 synchronized (mQuotaController.mLock) { 7927 mQuotaController.maybeStopTrackingJobLocked(jobReg1, jobReg1); 7928 } 7929 expectedRegular.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7930 assertEquals(expectedRegular, 7931 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7932 assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7933 7934 advanceElapsedClock(SECOND_IN_MILLIS); 7935 7936 // Next, EJ runs by itself. 7937 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7938 synchronized (mQuotaController.mLock) { 7939 mQuotaController.prepareForExecutionLocked(jobEJ1); 7940 } 7941 advanceElapsedClock(10 * SECOND_IN_MILLIS); 7942 synchronized (mQuotaController.mLock) { 7943 mQuotaController.maybeStopTrackingJobLocked(jobEJ1, null); 7944 } 7945 expectedEJ.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7946 assertEquals(expectedRegular, 7947 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7948 assertEquals(expectedEJ, 7949 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7950 7951 advanceElapsedClock(SECOND_IN_MILLIS); 7952 7953 // Finally, a regular job and EJ happen to overlap runs. 7954 start = JobSchedulerService.sElapsedRealtimeClock.millis(); 7955 synchronized (mQuotaController.mLock) { 7956 mQuotaController.prepareForExecutionLocked(jobEJ2); 7957 } 7958 advanceElapsedClock(5 * SECOND_IN_MILLIS); 7959 synchronized (mQuotaController.mLock) { 7960 mQuotaController.prepareForExecutionLocked(jobReg2); 7961 } 7962 advanceElapsedClock(5 * SECOND_IN_MILLIS); 7963 synchronized (mQuotaController.mLock) { 7964 mQuotaController.maybeStopTrackingJobLocked(jobEJ2, null); 7965 } 7966 expectedEJ.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); 7967 advanceElapsedClock(5 * SECOND_IN_MILLIS); 7968 synchronized (mQuotaController.mLock) { 7969 mQuotaController.maybeStopTrackingJobLocked(jobReg2, null); 7970 } 7971 expectedRegular.add( 7972 createTimingSession(start + 5 * SECOND_IN_MILLIS, 10 * SECOND_IN_MILLIS, 1)); 7973 assertEquals(expectedRegular, 7974 mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7975 assertEquals(expectedEJ, 7976 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); 7977 } 7978 7979 /** 7980 * Tests that a job is properly handled when it's at the edge of its quota and the old quota is 7981 * being phased out. 7982 */ 7983 @Test testEJTracking_RollingQuota()7984 public void testEJTracking_RollingQuota() { 7985 JobStatus jobStatus = createExpeditedJobStatus("testEJTracking_RollingQuota", 1); 7986 synchronized (mQuotaController.mLock) { 7987 mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); 7988 } 7989 setStandbyBucket(WORKING_INDEX, jobStatus); 7990 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 7991 Handler handler = mQuotaController.getHandler(); 7992 spyOn(handler); 7993 7994 long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 7995 final long remainingTimeMs = SECOND_IN_MILLIS; 7996 // The package only has one second to run, but this session is at the edge of the rolling 7997 // window, so as the package "reaches its quota" it will have more to keep running. 7998 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 7999 createTimingSession(now - mQcConstants.EJ_WINDOW_SIZE_MS, 8000 10 * SECOND_IN_MILLIS - remainingTimeMs, 1), true); 8001 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, 8002 createTimingSession(now - HOUR_IN_MILLIS, 8003 mQcConstants.EJ_LIMIT_WORKING_MS - 10 * SECOND_IN_MILLIS, 1), true); 8004 8005 synchronized (mQuotaController.mLock) { 8006 assertEquals(remainingTimeMs, 8007 mQuotaController.getRemainingEJExecutionTimeLocked( 8008 SOURCE_USER_ID, SOURCE_PACKAGE)); 8009 8010 // Start the job. 8011 mQuotaController.prepareForExecutionLocked(jobStatus); 8012 } 8013 advanceElapsedClock(remainingTimeMs); 8014 8015 // Wait for some extra time to allow for job processing. 8016 verify(mJobSchedulerService, 8017 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) 8018 .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); 8019 assertTrue(jobStatus.isExpeditedQuotaApproved()); 8020 // The job used up the remaining quota, but in that time, the same amount of time in the 8021 // old TimingSession also fell out of the quota window, so it should still have the same 8022 // amount of remaining time left its quota. 8023 synchronized (mQuotaController.mLock) { 8024 assertEquals(remainingTimeMs, 8025 mQuotaController.getRemainingEJExecutionTimeLocked( 8026 SOURCE_USER_ID, SOURCE_PACKAGE)); 8027 } 8028 // Handler is told to check when the quota will be consumed, not when the initial 8029 // remaining time is over. 8030 verify(handler, atLeast(1)).sendMessageDelayed( 8031 argThat(msg -> msg.what == QuotaController.MSG_REACHED_EJ_TIME_QUOTA), 8032 eq(10 * SECOND_IN_MILLIS)); 8033 verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); 8034 } 8035 8036 @Test testEJDebitTallying()8037 public void testEJDebitTallying() { 8038 setStandbyBucket(RARE_INDEX); 8039 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 8040 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 8041 // 15 seconds for each 30 second chunk. 8042 setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS); 8043 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS); 8044 8045 // No history. Debits should be 0. 8046 ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); 8047 assertEquals(0, debit.getTallyLocked()); 8048 assertEquals(10 * MINUTE_IN_MILLIS, 8049 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8050 8051 // Regular job shouldn't affect EJ tally. 8052 JobStatus regJob = createJobStatus("testEJDebitTallying", 1); 8053 synchronized (mQuotaController.mLock) { 8054 mQuotaController.maybeStartTrackingJobLocked(regJob, null); 8055 mQuotaController.prepareForExecutionLocked(regJob); 8056 } 8057 advanceElapsedClock(5000); 8058 synchronized (mQuotaController.mLock) { 8059 mQuotaController.maybeStopTrackingJobLocked(regJob, null); 8060 } 8061 assertEquals(0, debit.getTallyLocked()); 8062 assertEquals(10 * MINUTE_IN_MILLIS, 8063 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8064 8065 // EJ job should affect EJ tally. 8066 JobStatus eJob = createExpeditedJobStatus("testEJDebitTallying", 2); 8067 synchronized (mQuotaController.mLock) { 8068 mQuotaController.maybeStartTrackingJobLocked(eJob, null); 8069 mQuotaController.prepareForExecutionLocked(eJob); 8070 } 8071 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 8072 synchronized (mQuotaController.mLock) { 8073 mQuotaController.maybeStopTrackingJobLocked(eJob, null); 8074 } 8075 assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked()); 8076 assertEquals(5 * MINUTE_IN_MILLIS, 8077 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8078 8079 // Instantaneous event for a different user shouldn't affect tally. 8080 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 8081 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); 8082 8083 UsageEvents.Event event = 8084 new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); 8085 event.mPackage = SOURCE_PACKAGE; 8086 mUsageEventListener.onUsageEvent(SOURCE_USER_ID + 10, event); 8087 assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked()); 8088 8089 // Instantaneous event for correct user should reduce tally. 8090 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 8091 8092 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8093 waitForNonDelayedMessagesProcessed(); 8094 assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); 8095 assertEquals(6 * MINUTE_IN_MILLIS, 8096 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8097 8098 // Activity start shouldn't reduce tally, but duration with activity started should affect 8099 // remaining EJ time. 8100 advanceElapsedClock(5 * MINUTE_IN_MILLIS); 8101 event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis()); 8102 event.mPackage = SOURCE_PACKAGE; 8103 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8104 waitForNonDelayedMessagesProcessed(); 8105 advanceElapsedClock(30 * SECOND_IN_MILLIS); 8106 assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); 8107 assertEquals(6 * MINUTE_IN_MILLIS + 15 * SECOND_IN_MILLIS, 8108 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8109 advanceElapsedClock(30 * SECOND_IN_MILLIS); 8110 assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); 8111 assertEquals(6 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, 8112 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8113 8114 // With activity pausing/stopping/destroying, tally should be updated. 8115 advanceElapsedClock(MINUTE_IN_MILLIS); 8116 event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis()); 8117 event.mPackage = SOURCE_PACKAGE; 8118 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8119 waitForNonDelayedMessagesProcessed(); 8120 assertEquals(3 * MINUTE_IN_MILLIS, debit.getTallyLocked()); 8121 assertEquals(7 * MINUTE_IN_MILLIS, 8122 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8123 } 8124 8125 @Test testEJDebitTallying_StaleSession()8126 public void testEJDebitTallying_StaleSession() { 8127 setStandbyBucket(RARE_INDEX); 8128 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); 8129 8130 final long nowElapsed = sElapsedRealtimeClock.millis(); 8131 TimingSession ts = new TimingSession(nowElapsed, nowElapsed + 10 * MINUTE_IN_MILLIS, 5); 8132 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); 8133 8134 // Make the session stale. 8135 advanceElapsedClock(12 * MINUTE_IN_MILLIS + mQcConstants.EJ_WINDOW_SIZE_MS); 8136 8137 // With lazy deletion, we don't update the tally until getRemainingEJExecutionTimeLocked() 8138 // is called, so call that first. 8139 assertEquals(10 * MINUTE_IN_MILLIS, 8140 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8141 ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); 8142 assertEquals(0, debit.getTallyLocked()); 8143 } 8144 8145 /** 8146 * Tests that rewards are properly accounted when there's no EJ running and the rewards exceed 8147 * the accumulated debits. 8148 */ 8149 @Test testEJDebitTallying_RewardExceedDebits_NoActiveSession()8150 public void testEJDebitTallying_RewardExceedDebits_NoActiveSession() { 8151 setStandbyBucket(WORKING_INDEX); 8152 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 8153 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS); 8154 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); 8155 8156 final long nowElapsed = sElapsedRealtimeClock.millis(); 8157 TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS, 8158 nowElapsed - 4 * MINUTE_IN_MILLIS, 2); 8159 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); 8160 8161 ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); 8162 assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); 8163 assertEquals(29 * MINUTE_IN_MILLIS, 8164 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8165 8166 advanceElapsedClock(30 * SECOND_IN_MILLIS); 8167 UsageEvents.Event event = 8168 new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); 8169 event.mPackage = SOURCE_PACKAGE; 8170 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8171 waitForNonDelayedMessagesProcessed(); 8172 assertEquals(0, debit.getTallyLocked()); 8173 assertEquals(30 * MINUTE_IN_MILLIS, 8174 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8175 8176 advanceElapsedClock(MINUTE_IN_MILLIS); 8177 assertEquals(0, debit.getTallyLocked()); 8178 assertEquals(30 * MINUTE_IN_MILLIS, 8179 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8180 8181 // Excessive rewards don't increase maximum quota. 8182 event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); 8183 event.mPackage = SOURCE_PACKAGE; 8184 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8185 waitForNonDelayedMessagesProcessed(); 8186 assertEquals(0, debit.getTallyLocked()); 8187 assertEquals(30 * MINUTE_IN_MILLIS, 8188 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8189 } 8190 8191 /** 8192 * Tests that rewards are properly accounted when there's an active EJ running and the rewards 8193 * exceed the accumulated debits. 8194 */ 8195 @Test testEJDebitTallying_RewardExceedDebits_ActiveSession()8196 public void testEJDebitTallying_RewardExceedDebits_ActiveSession() { 8197 setStandbyBucket(WORKING_INDEX); 8198 setProcessState(ActivityManager.PROCESS_STATE_SERVICE); 8199 setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS); 8200 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); 8201 // 15 seconds for each 30 second chunk. 8202 setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS); 8203 setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS); 8204 8205 final long nowElapsed = sElapsedRealtimeClock.millis(); 8206 TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS, 8207 nowElapsed - 4 * MINUTE_IN_MILLIS, 2); 8208 mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); 8209 8210 ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); 8211 assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); 8212 assertEquals(29 * MINUTE_IN_MILLIS, 8213 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8214 8215 // With rewards coming in while an EJ is running, the remaining execution time should be 8216 // adjusted accordingly (decrease due to EJ running + increase from reward). 8217 JobStatus eJob = 8218 createExpeditedJobStatus("testEJDebitTallying_RewardExceedDebits_ActiveSession", 1); 8219 synchronized (mQuotaController.mLock) { 8220 mQuotaController.maybeStartTrackingJobLocked(eJob, null); 8221 mQuotaController.prepareForExecutionLocked(eJob); 8222 } 8223 advanceElapsedClock(30 * SECOND_IN_MILLIS); 8224 assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); 8225 assertEquals(28 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, 8226 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8227 8228 advanceElapsedClock(30 * SECOND_IN_MILLIS); 8229 UsageEvents.Event event = 8230 new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); 8231 event.mPackage = SOURCE_PACKAGE; 8232 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8233 waitForNonDelayedMessagesProcessed(); 8234 assertEquals(0, debit.getTallyLocked()); 8235 assertEquals(29 * MINUTE_IN_MILLIS, 8236 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8237 8238 advanceElapsedClock(MINUTE_IN_MILLIS); 8239 assertEquals(0, debit.getTallyLocked()); 8240 assertEquals(28 * MINUTE_IN_MILLIS, 8241 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8242 8243 // Activity start shouldn't reduce tally, but duration with activity started should affect 8244 // remaining EJ time. 8245 event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis()); 8246 event.mPackage = SOURCE_PACKAGE; 8247 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8248 waitForNonDelayedMessagesProcessed(); 8249 advanceElapsedClock(30 * SECOND_IN_MILLIS); 8250 assertEquals(0, debit.getTallyLocked()); 8251 // Decrease by 30 seconds for running EJ, increase by 15 seconds due to ongoing activity. 8252 assertEquals(27 * MINUTE_IN_MILLIS + 45 * SECOND_IN_MILLIS, 8253 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8254 advanceElapsedClock(30 * SECOND_IN_MILLIS); 8255 assertEquals(0, debit.getTallyLocked()); 8256 assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, 8257 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8258 8259 advanceElapsedClock(MINUTE_IN_MILLIS); 8260 assertEquals(0, debit.getTallyLocked()); 8261 assertEquals(27 * MINUTE_IN_MILLIS, 8262 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8263 8264 event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); 8265 event.mPackage = SOURCE_PACKAGE; 8266 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8267 waitForNonDelayedMessagesProcessed(); 8268 assertEquals(0, debit.getTallyLocked()); 8269 assertEquals(28 * MINUTE_IN_MILLIS, 8270 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8271 8272 advanceElapsedClock(MINUTE_IN_MILLIS); 8273 assertEquals(0, debit.getTallyLocked()); 8274 assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, 8275 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8276 8277 // At this point, with activity pausing/stopping/destroying, since we're giving a reward, 8278 // tally should remain 0, and time remaining shouldn't change since it was accounted for 8279 // at every step. 8280 event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis()); 8281 event.mPackage = SOURCE_PACKAGE; 8282 mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); 8283 waitForNonDelayedMessagesProcessed(); 8284 assertEquals(0, debit.getTallyLocked()); 8285 assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, 8286 mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); 8287 } 8288 } 8289