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