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