1 /* 2 * Copyright (C) 2019 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; 18 19 import static android.text.format.DateUtils.DAY_IN_MILLIS; 20 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 21 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 22 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 28 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; 29 import static com.android.server.job.JobSchedulerService.RARE_INDEX; 30 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 31 import static com.android.server.job.JobSchedulerService.sUptimeMillisClock; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertFalse; 35 import static org.junit.Assert.assertNotEquals; 36 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertNull; 38 import static org.junit.Assert.assertTrue; 39 import static org.junit.Assert.fail; 40 import static org.mockito.ArgumentMatchers.any; 41 import static org.mockito.ArgumentMatchers.anyBoolean; 42 import static org.mockito.ArgumentMatchers.anyInt; 43 import static org.mockito.ArgumentMatchers.anyString; 44 import static org.mockito.ArgumentMatchers.eq; 45 import static org.mockito.Mockito.when; 46 47 import android.app.ActivityManager; 48 import android.app.ActivityManagerInternal; 49 import android.app.IActivityManager; 50 import android.app.UiModeManager; 51 import android.app.job.JobInfo; 52 import android.app.job.JobParameters; 53 import android.app.job.JobScheduler; 54 import android.app.job.JobWorkItem; 55 import android.app.usage.UsageStatsManagerInternal; 56 import android.content.ComponentName; 57 import android.content.Context; 58 import android.content.PermissionChecker; 59 import android.content.pm.PackageManager; 60 import android.content.pm.PackageManagerInternal; 61 import android.content.res.Resources; 62 import android.net.ConnectivityManager; 63 import android.net.NetworkPolicyManager; 64 import android.os.BatteryManagerInternal; 65 import android.os.Looper; 66 import android.os.RemoteException; 67 import android.os.ServiceManager; 68 import android.os.SystemClock; 69 70 import com.android.server.AppStateTracker; 71 import com.android.server.AppStateTrackerImpl; 72 import com.android.server.DeviceIdleInternal; 73 import com.android.server.LocalServices; 74 import com.android.server.PowerAllowlistInternal; 75 import com.android.server.SystemServiceManager; 76 import com.android.server.job.controllers.ConnectivityController; 77 import com.android.server.job.controllers.JobStatus; 78 import com.android.server.job.controllers.QuotaController; 79 import com.android.server.job.controllers.TareController; 80 import com.android.server.pm.UserManagerInternal; 81 import com.android.server.usage.AppStandbyInternal; 82 83 import org.junit.After; 84 import org.junit.Before; 85 import org.junit.Test; 86 import org.mockito.Mock; 87 import org.mockito.MockitoSession; 88 import org.mockito.quality.Strictness; 89 90 import java.time.Clock; 91 import java.time.Duration; 92 import java.time.ZoneOffset; 93 94 public class JobSchedulerServiceTest { 95 private static final String TAG = JobSchedulerServiceTest.class.getSimpleName(); 96 private static final int TEST_UID = 10123; 97 98 private JobSchedulerService mService; 99 100 private MockitoSession mMockingSession; 101 @Mock 102 private ActivityManagerInternal mActivityMangerInternal; 103 @Mock 104 private Context mContext; 105 @Mock 106 private PackageManagerInternal mPackageManagerInternal; 107 108 private class TestJobSchedulerService extends JobSchedulerService { TestJobSchedulerService(Context context)109 TestJobSchedulerService(Context context) { 110 super(context); 111 mAppStateTracker = mock(AppStateTrackerImpl.class); 112 } 113 } 114 115 @Before setUp()116 public void setUp() { 117 mMockingSession = mockitoSession() 118 .initMocks(this) 119 .strictness(Strictness.LENIENT) 120 .mockStatic(LocalServices.class) 121 .mockStatic(PermissionChecker.class) 122 .mockStatic(ServiceManager.class) 123 .startMocking(); 124 125 // Called in JobSchedulerService constructor. 126 when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); 127 doReturn(mActivityMangerInternal) 128 .when(() -> LocalServices.getService(ActivityManagerInternal.class)); 129 doReturn(mock(AppStandbyInternal.class)) 130 .when(() -> LocalServices.getService(AppStandbyInternal.class)); 131 doReturn(mock(BatteryManagerInternal.class)) 132 .when(() -> LocalServices.getService(BatteryManagerInternal.class)); 133 doReturn(mPackageManagerInternal) 134 .when(() -> LocalServices.getService(PackageManagerInternal.class)); 135 doReturn(mock(UsageStatsManagerInternal.class)) 136 .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); 137 when(mContext.getString(anyInt())).thenReturn("some_test_string"); 138 // Called in BackgroundJobsController constructor. 139 doReturn(mock(AppStateTrackerImpl.class)) 140 .when(() -> LocalServices.getService(AppStateTracker.class)); 141 // Called in ConnectivityController constructor. 142 when(mContext.getSystemService(ConnectivityManager.class)) 143 .thenReturn(mock(ConnectivityManager.class)); 144 when(mContext.getSystemService(NetworkPolicyManager.class)) 145 .thenReturn(mock(NetworkPolicyManager.class)); 146 // Called in DeviceIdleJobsController constructor. 147 doReturn(mock(DeviceIdleInternal.class)) 148 .when(() -> LocalServices.getService(DeviceIdleInternal.class)); 149 // Used in JobConcurrencyManager. 150 doReturn(mock(UserManagerInternal.class)) 151 .when(() -> LocalServices.getService(UserManagerInternal.class)); 152 // Used in JobStatus. 153 doReturn(mock(JobSchedulerInternal.class)) 154 .when(() -> LocalServices.getService(JobSchedulerInternal.class)); 155 // Called via IdleController constructor. 156 when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); 157 when(mContext.getResources()).thenReturn(mock(Resources.class)); 158 // Called in QuotaController constructor. 159 doReturn(mock(PowerAllowlistInternal.class)) 160 .when(() -> LocalServices.getService(PowerAllowlistInternal.class)); 161 IActivityManager activityManager = ActivityManager.getService(); 162 spyOn(activityManager); 163 try { 164 doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); 165 } catch (RemoteException e) { 166 fail("registerUidObserver threw exception: " + e.getMessage()); 167 } 168 // Called by QuotaTracker 169 doReturn(mock(SystemServiceManager.class)) 170 .when(() -> LocalServices.getService(SystemServiceManager.class)); 171 172 JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); 173 JobSchedulerService.sElapsedRealtimeClock = 174 Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); 175 // Make sure the uptime is at least 24 hours so that tests that rely on high uptime work. 176 sUptimeMillisClock = getAdvancedClock(sUptimeMillisClock, 24 * HOUR_IN_MILLIS); 177 // Called by DeviceIdlenessTracker 178 when(mContext.getSystemService(UiModeManager.class)).thenReturn(mock(UiModeManager.class)); 179 180 mService = new TestJobSchedulerService(mContext); 181 } 182 183 @After tearDown()184 public void tearDown() { 185 if (mMockingSession != null) { 186 mMockingSession.finishMocking(); 187 } 188 mService.cancelJobsForUid(TEST_UID, true, 189 JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN, 190 "test cleanup"); 191 } 192 getAdvancedClock(Clock clock, long incrementMs)193 private Clock getAdvancedClock(Clock clock, long incrementMs) { 194 return Clock.offset(clock, Duration.ofMillis(incrementMs)); 195 } 196 advanceElapsedClock(long incrementMs)197 private void advanceElapsedClock(long incrementMs) { 198 JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock( 199 JobSchedulerService.sElapsedRealtimeClock, incrementMs); 200 } 201 createJobInfo()202 private static JobInfo.Builder createJobInfo() { 203 return createJobInfo(351); 204 } 205 createJobInfo(int jobId)206 private static JobInfo.Builder createJobInfo(int jobId) { 207 return new JobInfo.Builder(jobId, new ComponentName("foo", "bar")); 208 } 209 createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder)210 private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder) { 211 return createJobStatus(testTag, jobInfoBuilder, 1234); 212 } 213 createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, int callingUid)214 private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, 215 int callingUid) { 216 return createJobStatus(testTag, jobInfoBuilder, callingUid, "com.android.test"); 217 } 218 createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, int callingUid, String sourcePkg)219 private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, 220 int callingUid, String sourcePkg) { 221 return JobStatus.createFromJobInfo( 222 jobInfoBuilder.build(), callingUid, sourcePkg, 0, "JSSTest", testTag); 223 } 224 grantRunUserInitiatedJobsPermission(boolean grant)225 private void grantRunUserInitiatedJobsPermission(boolean grant) { 226 final int permissionStatus = grant 227 ? PermissionChecker.PERMISSION_GRANTED : PermissionChecker.PERMISSION_HARD_DENIED; 228 doReturn(permissionStatus) 229 .when(() -> PermissionChecker.checkPermissionForPreflight( 230 any(), eq(android.Manifest.permission.RUN_USER_INITIATED_JOBS), 231 anyInt(), anyInt(), anyString())); 232 } 233 234 @Test testGetMinJobExecutionGuaranteeMs()235 public void testGetMinJobExecutionGuaranteeMs() { 236 JobStatus ejMax = createJobStatus("testGetMinJobExecutionGuaranteeMs", 237 createJobInfo(1).setExpedited(true)); 238 JobStatus ejHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", 239 createJobInfo(2).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); 240 JobStatus ejMaxDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", 241 createJobInfo(3).setExpedited(true)); 242 JobStatus ejHighDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", 243 createJobInfo(4).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); 244 JobStatus jobHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", 245 createJobInfo(5).setPriority(JobInfo.PRIORITY_HIGH)); 246 JobStatus jobDef = createJobStatus("testGetMinJobExecutionGuaranteeMs", 247 createJobInfo(6)); 248 JobStatus jobUIDT = createJobStatus("testGetMinJobExecutionGuaranteeMs", 249 createJobInfo(9) 250 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 251 252 spyOn(ejMax); 253 spyOn(ejHigh); 254 spyOn(ejMaxDowngraded); 255 spyOn(ejHighDowngraded); 256 spyOn(jobHigh); 257 spyOn(jobDef); 258 spyOn(jobUIDT); 259 260 when(ejMax.shouldTreatAsExpeditedJob()).thenReturn(true); 261 when(ejHigh.shouldTreatAsExpeditedJob()).thenReturn(true); 262 when(ejMaxDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); 263 when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); 264 when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false); 265 when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false); 266 when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true); 267 268 ConnectivityController connectivityController = mService.getConnectivityController(); 269 spyOn(connectivityController); 270 mService.mConstants.RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS; 271 mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS = 2 * HOUR_IN_MILLIS; 272 mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 1.5f; 273 mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = HOUR_IN_MILLIS; 274 mService.mConstants.RUNTIME_UI_LIMIT_MS = 6 * HOUR_IN_MILLIS; 275 276 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 277 mService.getMinJobExecutionGuaranteeMs(ejMax)); 278 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 279 mService.getMinJobExecutionGuaranteeMs(ejHigh)); 280 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 281 mService.getMinJobExecutionGuaranteeMs(ejMaxDowngraded)); 282 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 283 mService.getMinJobExecutionGuaranteeMs(ejHighDowngraded)); 284 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 285 mService.getMinJobExecutionGuaranteeMs(jobHigh)); 286 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 287 mService.getMinJobExecutionGuaranteeMs(jobDef)); 288 // UserInitiated 289 grantRunUserInitiatedJobsPermission(false); 290 // Permission isn't granted, so it should just be treated as a regular job. 291 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 292 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 293 294 grantRunUserInitiatedJobsPermission(true); // With permission 295 mService.mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = true; 296 doReturn(ConnectivityController.UNKNOWN_TIME) 297 .when(connectivityController).getEstimatedTransferTimeMs(any()); 298 assertEquals(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, 299 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 300 doReturn(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS / 2) 301 .when(connectivityController).getEstimatedTransferTimeMs(any()); 302 assertEquals(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, 303 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 304 final long estimatedTransferTimeMs = 305 mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS * 2; 306 doReturn(estimatedTransferTimeMs) 307 .when(connectivityController).getEstimatedTransferTimeMs(any()); 308 assertEquals((long) (estimatedTransferTimeMs 309 * mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR), 310 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 311 doReturn(mService.mConstants.RUNTIME_UI_LIMIT_MS * 2) 312 .when(connectivityController).getEstimatedTransferTimeMs(any()); 313 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 314 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 315 316 mService.mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false; 317 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 318 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 319 } 320 321 @Test testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_disabled()322 public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_disabled() { 323 JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 324 createJobInfo(1) 325 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 326 JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 327 createJobInfo(2).setExpedited(true)); 328 JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 329 createJobInfo(3)); 330 spyOn(jobUij); 331 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 332 jobUij.startedAsUserInitiatedJob = true; 333 spyOn(jobEj); 334 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 335 jobEj.startedAsExpeditedJob = true; 336 337 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; 338 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 339 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 340 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 341 mService.updateQuotaTracker(); 342 mService.resetScheduleQuota(); 343 344 // Safeguards disabled -> no penalties. 345 grantRunUserInitiatedJobsPermission(true); 346 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 347 mService.getMinJobExecutionGuaranteeMs(jobUij)); 348 grantRunUserInitiatedJobsPermission(false); 349 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 350 mService.getMinJobExecutionGuaranteeMs(jobUij)); 351 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 352 mService.getMinJobExecutionGuaranteeMs(jobEj)); 353 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 354 mService.getMinJobExecutionGuaranteeMs(jobReg)); 355 356 // 1 UIJ timeout. No max execution penalty yet. 357 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 358 grantRunUserInitiatedJobsPermission(true); 359 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 360 mService.getMinJobExecutionGuaranteeMs(jobUij)); 361 grantRunUserInitiatedJobsPermission(false); 362 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 363 mService.getMinJobExecutionGuaranteeMs(jobUij)); 364 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 365 mService.getMinJobExecutionGuaranteeMs(jobEj)); 366 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 367 mService.getMinJobExecutionGuaranteeMs(jobReg)); 368 369 // 2 UIJ timeouts. Safeguards disabled -> no penalties. 370 jobUij.madeActive = 371 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 372 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 373 grantRunUserInitiatedJobsPermission(true); 374 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 375 mService.getMinJobExecutionGuaranteeMs(jobUij)); 376 grantRunUserInitiatedJobsPermission(false); 377 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 378 mService.getMinJobExecutionGuaranteeMs(jobUij)); 379 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 380 mService.getMinJobExecutionGuaranteeMs(jobEj)); 381 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 382 mService.getMinJobExecutionGuaranteeMs(jobReg)); 383 384 // 1 EJ timeout. No max execution penalty yet. 385 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 386 grantRunUserInitiatedJobsPermission(true); 387 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 388 mService.getMinJobExecutionGuaranteeMs(jobUij)); 389 grantRunUserInitiatedJobsPermission(false); 390 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 391 mService.getMinJobExecutionGuaranteeMs(jobUij)); 392 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 393 mService.getMinJobExecutionGuaranteeMs(jobEj)); 394 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 395 mService.getMinJobExecutionGuaranteeMs(jobReg)); 396 397 // 2 EJ timeouts. Safeguards disabled -> no penalties. 398 jobEj.madeActive = 399 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 400 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 401 grantRunUserInitiatedJobsPermission(true); 402 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 403 mService.getMinJobExecutionGuaranteeMs(jobUij)); 404 grantRunUserInitiatedJobsPermission(false); 405 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 406 mService.getMinJobExecutionGuaranteeMs(jobUij)); 407 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 408 mService.getMinJobExecutionGuaranteeMs(jobEj)); 409 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 410 mService.getMinJobExecutionGuaranteeMs(jobReg)); 411 412 // 1 reg timeout. No max execution penalty yet. 413 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 414 grantRunUserInitiatedJobsPermission(true); 415 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 416 mService.getMinJobExecutionGuaranteeMs(jobUij)); 417 grantRunUserInitiatedJobsPermission(false); 418 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 419 mService.getMinJobExecutionGuaranteeMs(jobUij)); 420 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 421 mService.getMinJobExecutionGuaranteeMs(jobEj)); 422 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 423 mService.getMinJobExecutionGuaranteeMs(jobReg)); 424 425 // 2 Reg timeouts. Safeguards disabled -> no penalties. 426 jobReg.madeActive = 427 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 428 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 429 grantRunUserInitiatedJobsPermission(true); 430 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 431 mService.getMinJobExecutionGuaranteeMs(jobUij)); 432 grantRunUserInitiatedJobsPermission(false); 433 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 434 mService.getMinJobExecutionGuaranteeMs(jobUij)); 435 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 436 mService.getMinJobExecutionGuaranteeMs(jobEj)); 437 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 438 mService.getMinJobExecutionGuaranteeMs(jobReg)); 439 } 440 441 @Test testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_enabled()442 public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_enabled() { 443 JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 444 createJobInfo(1) 445 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 446 JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 447 createJobInfo(2).setExpedited(true)); 448 JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 449 createJobInfo(3)); 450 spyOn(jobUij); 451 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 452 jobUij.startedAsUserInitiatedJob = true; 453 spyOn(jobEj); 454 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 455 jobEj.startedAsExpeditedJob = true; 456 457 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; 458 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 459 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 460 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 461 mService.updateQuotaTracker(); 462 mService.resetScheduleQuota(); 463 464 // No timeouts -> no penalties. 465 grantRunUserInitiatedJobsPermission(true); 466 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 467 mService.getMinJobExecutionGuaranteeMs(jobUij)); 468 grantRunUserInitiatedJobsPermission(false); 469 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 470 mService.getMinJobExecutionGuaranteeMs(jobUij)); 471 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 472 mService.getMinJobExecutionGuaranteeMs(jobEj)); 473 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 474 mService.getMinJobExecutionGuaranteeMs(jobReg)); 475 476 // 1 UIJ timeout. No execution penalty yet. 477 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 478 grantRunUserInitiatedJobsPermission(true); 479 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 480 mService.getMinJobExecutionGuaranteeMs(jobUij)); 481 grantRunUserInitiatedJobsPermission(false); 482 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 483 mService.getMinJobExecutionGuaranteeMs(jobUij)); 484 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 485 mService.getMinJobExecutionGuaranteeMs(jobEj)); 486 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 487 mService.getMinJobExecutionGuaranteeMs(jobReg)); 488 489 // Not a timeout -> 1 UIJ timeout. No execution penalty yet. 490 jobUij.madeActive = sUptimeMillisClock.millis() - 1; 491 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 492 grantRunUserInitiatedJobsPermission(true); 493 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 494 mService.getMinJobExecutionGuaranteeMs(jobUij)); 495 grantRunUserInitiatedJobsPermission(false); 496 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 497 mService.getMinJobExecutionGuaranteeMs(jobUij)); 498 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 499 mService.getMinJobExecutionGuaranteeMs(jobEj)); 500 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 501 mService.getMinJobExecutionGuaranteeMs(jobReg)); 502 503 // 2 UIJ timeouts. Min execution penalty only for UIJs. 504 jobUij.madeActive = 505 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 506 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 507 grantRunUserInitiatedJobsPermission(true); 508 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 509 mService.getMinJobExecutionGuaranteeMs(jobUij)); 510 grantRunUserInitiatedJobsPermission(false); 511 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 512 mService.getMinJobExecutionGuaranteeMs(jobUij)); 513 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 514 mService.getMinJobExecutionGuaranteeMs(jobEj)); 515 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 516 mService.getMinJobExecutionGuaranteeMs(jobReg)); 517 518 // 1 EJ timeout. No max execution penalty yet. 519 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 520 grantRunUserInitiatedJobsPermission(true); 521 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 522 mService.getMinJobExecutionGuaranteeMs(jobUij)); 523 grantRunUserInitiatedJobsPermission(false); 524 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 525 mService.getMinJobExecutionGuaranteeMs(jobUij)); 526 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 527 mService.getMinJobExecutionGuaranteeMs(jobEj)); 528 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 529 mService.getMinJobExecutionGuaranteeMs(jobReg)); 530 531 // 2 EJ timeouts. Max execution penalty for EJs. 532 jobEj.madeActive = 533 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 534 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 535 grantRunUserInitiatedJobsPermission(true); 536 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 537 mService.getMinJobExecutionGuaranteeMs(jobUij)); 538 grantRunUserInitiatedJobsPermission(false); 539 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 540 mService.getMinJobExecutionGuaranteeMs(jobUij)); 541 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 542 mService.getMinJobExecutionGuaranteeMs(jobEj)); 543 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 544 mService.getMinJobExecutionGuaranteeMs(jobReg)); 545 546 // 1 reg timeout. No max execution penalty yet. 547 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 548 grantRunUserInitiatedJobsPermission(true); 549 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 550 mService.getMinJobExecutionGuaranteeMs(jobUij)); 551 grantRunUserInitiatedJobsPermission(false); 552 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 553 mService.getMinJobExecutionGuaranteeMs(jobUij)); 554 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 555 mService.getMinJobExecutionGuaranteeMs(jobEj)); 556 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 557 mService.getMinJobExecutionGuaranteeMs(jobReg)); 558 559 // 2 Reg timeouts. Max execution penalty for regular jobs. 560 jobReg.madeActive = 561 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 562 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 563 grantRunUserInitiatedJobsPermission(true); 564 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 565 mService.getMinJobExecutionGuaranteeMs(jobUij)); 566 grantRunUserInitiatedJobsPermission(false); 567 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 568 mService.getMinJobExecutionGuaranteeMs(jobUij)); 569 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 570 mService.getMinJobExecutionGuaranteeMs(jobEj)); 571 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 572 mService.getMinJobExecutionGuaranteeMs(jobReg)); 573 } 574 575 @Test testGetMaxJobExecutionTimeMs()576 public void testGetMaxJobExecutionTimeMs() { 577 JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs", 578 createJobInfo(10) 579 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 580 spyOn(jobUIDT); 581 when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true); 582 583 QuotaController quotaController = mService.getQuotaController(); 584 spyOn(quotaController); 585 TareController tareController = mService.getTareController(); 586 spyOn(tareController); 587 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 588 .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); 589 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 590 .when(tareController).getMaxJobExecutionTimeMsLocked(any()); 591 592 grantRunUserInitiatedJobsPermission(true); 593 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 594 mService.getMaxJobExecutionTimeMs(jobUIDT)); 595 grantRunUserInitiatedJobsPermission(false); 596 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 597 mService.getMaxJobExecutionTimeMs(jobUIDT)); 598 } 599 600 @Test testGetMaxJobExecutionTimeMs_timeoutSafeguards_disabled()601 public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_disabled() { 602 JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 603 createJobInfo(1) 604 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 605 JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 606 createJobInfo(2).setExpedited(true)); 607 JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 608 createJobInfo(3)); 609 spyOn(jobUij); 610 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 611 jobUij.startedAsUserInitiatedJob = true; 612 spyOn(jobEj); 613 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 614 jobEj.startedAsExpeditedJob = true; 615 616 QuotaController quotaController = mService.getQuotaController(); 617 spyOn(quotaController); 618 TareController tareController = mService.getTareController(); 619 spyOn(tareController); 620 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 621 .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); 622 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 623 .when(tareController).getMaxJobExecutionTimeMsLocked(any()); 624 625 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; 626 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 627 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 628 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 629 mService.updateQuotaTracker(); 630 mService.resetScheduleQuota(); 631 632 // Safeguards disabled -> no penalties. 633 grantRunUserInitiatedJobsPermission(true); 634 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 635 mService.getMaxJobExecutionTimeMs(jobUij)); 636 grantRunUserInitiatedJobsPermission(false); 637 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 638 mService.getMaxJobExecutionTimeMs(jobUij)); 639 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 640 mService.getMaxJobExecutionTimeMs(jobEj)); 641 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 642 mService.getMaxJobExecutionTimeMs(jobReg)); 643 644 // 1 UIJ timeout. No max execution penalty yet. 645 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 646 grantRunUserInitiatedJobsPermission(true); 647 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 648 mService.getMaxJobExecutionTimeMs(jobUij)); 649 grantRunUserInitiatedJobsPermission(false); 650 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 651 mService.getMaxJobExecutionTimeMs(jobUij)); 652 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 653 mService.getMaxJobExecutionTimeMs(jobEj)); 654 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 655 mService.getMaxJobExecutionTimeMs(jobReg)); 656 657 // 2 UIJ timeouts. Safeguards disabled -> no penalties. 658 jobUij.madeActive = 659 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 660 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 661 grantRunUserInitiatedJobsPermission(true); 662 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 663 mService.getMaxJobExecutionTimeMs(jobUij)); 664 grantRunUserInitiatedJobsPermission(false); 665 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 666 mService.getMaxJobExecutionTimeMs(jobUij)); 667 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 668 mService.getMaxJobExecutionTimeMs(jobEj)); 669 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 670 mService.getMaxJobExecutionTimeMs(jobReg)); 671 672 // 1 EJ timeout. No max execution penalty yet. 673 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 674 grantRunUserInitiatedJobsPermission(true); 675 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 676 mService.getMaxJobExecutionTimeMs(jobUij)); 677 grantRunUserInitiatedJobsPermission(false); 678 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 679 mService.getMaxJobExecutionTimeMs(jobUij)); 680 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 681 mService.getMaxJobExecutionTimeMs(jobEj)); 682 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 683 mService.getMaxJobExecutionTimeMs(jobReg)); 684 685 // 2 EJ timeouts. Safeguards disabled -> no penalties. 686 jobEj.madeActive = 687 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 688 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 689 grantRunUserInitiatedJobsPermission(true); 690 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 691 mService.getMaxJobExecutionTimeMs(jobUij)); 692 grantRunUserInitiatedJobsPermission(false); 693 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 694 mService.getMaxJobExecutionTimeMs(jobUij)); 695 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 696 mService.getMaxJobExecutionTimeMs(jobEj)); 697 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 698 mService.getMaxJobExecutionTimeMs(jobReg)); 699 700 // 1 reg timeout. No max execution penalty yet. 701 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 702 grantRunUserInitiatedJobsPermission(true); 703 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 704 mService.getMaxJobExecutionTimeMs(jobUij)); 705 grantRunUserInitiatedJobsPermission(false); 706 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 707 mService.getMaxJobExecutionTimeMs(jobUij)); 708 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 709 mService.getMaxJobExecutionTimeMs(jobEj)); 710 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 711 mService.getMaxJobExecutionTimeMs(jobReg)); 712 713 // 2 Reg timeouts. Safeguards disabled -> no penalties. 714 jobReg.madeActive = 715 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 716 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 717 grantRunUserInitiatedJobsPermission(true); 718 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 719 mService.getMaxJobExecutionTimeMs(jobUij)); 720 grantRunUserInitiatedJobsPermission(false); 721 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 722 mService.getMaxJobExecutionTimeMs(jobUij)); 723 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 724 mService.getMaxJobExecutionTimeMs(jobEj)); 725 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 726 mService.getMaxJobExecutionTimeMs(jobReg)); 727 } 728 729 @Test testGetMaxJobExecutionTimeMs_timeoutSafeguards_enabled()730 public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_enabled() { 731 JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 732 createJobInfo(1) 733 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 734 JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 735 createJobInfo(2).setExpedited(true)); 736 JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 737 createJobInfo(3)); 738 spyOn(jobUij); 739 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 740 jobUij.startedAsUserInitiatedJob = true; 741 spyOn(jobEj); 742 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 743 jobEj.startedAsExpeditedJob = true; 744 745 QuotaController quotaController = mService.getQuotaController(); 746 spyOn(quotaController); 747 TareController tareController = mService.getTareController(); 748 spyOn(tareController); 749 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 750 .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); 751 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 752 .when(tareController).getMaxJobExecutionTimeMsLocked(any()); 753 754 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; 755 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 756 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 757 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 758 mService.updateQuotaTracker(); 759 mService.resetScheduleQuota(); 760 761 // No timeouts -> no penalties. 762 grantRunUserInitiatedJobsPermission(true); 763 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 764 mService.getMaxJobExecutionTimeMs(jobUij)); 765 grantRunUserInitiatedJobsPermission(false); 766 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 767 mService.getMaxJobExecutionTimeMs(jobUij)); 768 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 769 mService.getMaxJobExecutionTimeMs(jobEj)); 770 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 771 mService.getMaxJobExecutionTimeMs(jobReg)); 772 773 // 1 UIJ timeout. No max execution penalty yet. 774 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 775 grantRunUserInitiatedJobsPermission(true); 776 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 777 mService.getMaxJobExecutionTimeMs(jobUij)); 778 grantRunUserInitiatedJobsPermission(false); 779 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 780 mService.getMaxJobExecutionTimeMs(jobUij)); 781 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 782 mService.getMaxJobExecutionTimeMs(jobEj)); 783 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 784 mService.getMaxJobExecutionTimeMs(jobReg)); 785 786 // Not a timeout -> 1 UIJ timeout. No max execution penalty yet. 787 jobUij.madeActive = sUptimeMillisClock.millis() - 1; 788 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 789 grantRunUserInitiatedJobsPermission(true); 790 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 791 mService.getMaxJobExecutionTimeMs(jobUij)); 792 grantRunUserInitiatedJobsPermission(false); 793 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 794 mService.getMaxJobExecutionTimeMs(jobUij)); 795 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 796 mService.getMaxJobExecutionTimeMs(jobEj)); 797 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 798 mService.getMaxJobExecutionTimeMs(jobReg)); 799 800 // 2 UIJ timeouts. Max execution penalty only for UIJs. 801 jobUij.madeActive = 802 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 803 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 804 grantRunUserInitiatedJobsPermission(true); 805 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 806 mService.getMaxJobExecutionTimeMs(jobUij)); 807 grantRunUserInitiatedJobsPermission(false); 808 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 809 mService.getMaxJobExecutionTimeMs(jobUij)); 810 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 811 mService.getMaxJobExecutionTimeMs(jobEj)); 812 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 813 mService.getMaxJobExecutionTimeMs(jobReg)); 814 815 // 1 EJ timeout. No max execution penalty yet. 816 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 817 grantRunUserInitiatedJobsPermission(true); 818 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 819 mService.getMaxJobExecutionTimeMs(jobUij)); 820 grantRunUserInitiatedJobsPermission(false); 821 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 822 mService.getMaxJobExecutionTimeMs(jobUij)); 823 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 824 mService.getMaxJobExecutionTimeMs(jobEj)); 825 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 826 mService.getMaxJobExecutionTimeMs(jobReg)); 827 828 // Not a timeout -> 1 EJ timeout. No max execution penalty yet. 829 jobEj.madeActive = sUptimeMillisClock.millis() - 1; 830 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 831 grantRunUserInitiatedJobsPermission(true); 832 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 833 mService.getMaxJobExecutionTimeMs(jobUij)); 834 grantRunUserInitiatedJobsPermission(false); 835 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 836 mService.getMaxJobExecutionTimeMs(jobUij)); 837 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 838 mService.getMaxJobExecutionTimeMs(jobEj)); 839 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 840 mService.getMaxJobExecutionTimeMs(jobReg)); 841 842 // 2 EJ timeouts. Max execution penalty for EJs. 843 jobEj.madeActive = 844 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 845 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 846 grantRunUserInitiatedJobsPermission(true); 847 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 848 mService.getMaxJobExecutionTimeMs(jobUij)); 849 grantRunUserInitiatedJobsPermission(false); 850 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 851 mService.getMaxJobExecutionTimeMs(jobUij)); 852 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 853 mService.getMaxJobExecutionTimeMs(jobEj)); 854 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 855 mService.getMaxJobExecutionTimeMs(jobReg)); 856 857 // 1 reg timeout. No max execution penalty yet. 858 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 859 grantRunUserInitiatedJobsPermission(true); 860 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 861 mService.getMaxJobExecutionTimeMs(jobUij)); 862 grantRunUserInitiatedJobsPermission(false); 863 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 864 mService.getMaxJobExecutionTimeMs(jobUij)); 865 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 866 mService.getMaxJobExecutionTimeMs(jobEj)); 867 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 868 mService.getMaxJobExecutionTimeMs(jobReg)); 869 870 // Not a timeout -> 1 reg timeout. No max execution penalty yet. 871 jobReg.madeActive = sUptimeMillisClock.millis() - 1; 872 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 873 grantRunUserInitiatedJobsPermission(true); 874 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 875 mService.getMaxJobExecutionTimeMs(jobUij)); 876 grantRunUserInitiatedJobsPermission(false); 877 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 878 mService.getMaxJobExecutionTimeMs(jobUij)); 879 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 880 mService.getMaxJobExecutionTimeMs(jobEj)); 881 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 882 mService.getMaxJobExecutionTimeMs(jobReg)); 883 884 // 2 Reg timeouts. Max execution penalty for regular jobs. 885 jobReg.madeActive = 886 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 887 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 888 grantRunUserInitiatedJobsPermission(true); 889 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 890 mService.getMaxJobExecutionTimeMs(jobUij)); 891 grantRunUserInitiatedJobsPermission(false); 892 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 893 mService.getMaxJobExecutionTimeMs(jobUij)); 894 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 895 mService.getMaxJobExecutionTimeMs(jobEj)); 896 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 897 mService.getMaxJobExecutionTimeMs(jobReg)); 898 } 899 900 /** 901 * Confirm that 902 * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} 903 * returns a job that is no longer allowed to run as a user-initiated job after it hits 904 * the cumulative execution limit. 905 */ 906 @Test testGetRescheduleJobForFailure_cumulativeExecution()907 public void testGetRescheduleJobForFailure_cumulativeExecution() { 908 JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", 909 createJobInfo() 910 .setUserInitiated(true) 911 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 912 assertTrue(originalJob.shouldTreatAsUserInitiatedJob()); 913 914 // Cumulative time = 0 915 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, 916 JobParameters.STOP_REASON_UNDEFINED, 917 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 918 assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); 919 920 // Cumulative time = 50% of limit 921 rescheduledJob.incrementCumulativeExecutionTime( 922 mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2); 923 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 924 JobParameters.STOP_REASON_UNDEFINED, 925 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 926 assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); 927 928 // Cumulative time = 99.999999% of limit 929 rescheduledJob.incrementCumulativeExecutionTime( 930 mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2 - 1); 931 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 932 JobParameters.STOP_REASON_UNDEFINED, 933 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 934 assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); 935 936 // Cumulative time = 100+% of limit 937 rescheduledJob.incrementCumulativeExecutionTime(2); 938 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 939 JobParameters.STOP_REASON_UNDEFINED, 940 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 941 assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); 942 } 943 944 /** 945 * Confirm that 946 * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} 947 * returns a job with the correct delay and deadline constraints. 948 */ 949 @Test testGetRescheduleJobForFailure_timingCalculations()950 public void testGetRescheduleJobForFailure_timingCalculations() { 951 final long nowElapsed = sElapsedRealtimeClock.millis(); 952 final long initialBackoffMs = MINUTE_IN_MILLIS; 953 mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3; 954 955 JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", 956 createJobInfo() 957 .setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR)); 958 assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime()); 959 assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed()); 960 961 // failure = 0, systemStop = 1 962 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, 963 JobParameters.STOP_REASON_DEVICE_STATE, 964 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 965 assertEquals(JobStatus.NO_EARLIEST_RUNTIME, rescheduledJob.getEarliestRunTime()); 966 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 967 968 // failure = 0, systemStop = 2 969 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 970 JobParameters.STOP_REASON_DEVICE_STATE, 971 JobParameters.INTERNAL_STOP_REASON_PREEMPT); 972 assertEquals(JobStatus.NO_EARLIEST_RUNTIME, rescheduledJob.getEarliestRunTime()); 973 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 974 // failure = 0, systemStop = 3 975 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 976 JobParameters.STOP_REASON_CONSTRAINT_CHARGING, 977 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED); 978 assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime()); 979 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 980 981 // failure = 0, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 982 for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) { 983 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 984 JobParameters.STOP_REASON_SYSTEM_PROCESSING, 985 JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED); 986 } 987 assertEquals(nowElapsed + 2 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 988 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 989 990 // failure = 1, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 991 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 992 JobParameters.STOP_REASON_TIMEOUT, 993 JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 994 assertEquals(nowElapsed + 3 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 995 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 996 997 // failure = 2, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 998 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 999 JobParameters.STOP_REASON_UNDEFINED, 1000 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1001 assertEquals(nowElapsed + 4 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 1002 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1003 1004 // failure = 3, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 1005 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1006 JobParameters.STOP_REASON_UNDEFINED, 1007 JobParameters.INTERNAL_STOP_REASON_ANR); 1008 assertEquals(nowElapsed + 5 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 1009 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1010 } 1011 1012 /** 1013 * Confirm that 1014 * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} 1015 * returns a job that is correctly marked as demoted by the user. 1016 */ 1017 @Test testGetRescheduleJobForFailure_userDemotion()1018 public void testGetRescheduleJobForFailure_userDemotion() { 1019 JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo()); 1020 assertEquals(0, originalJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1021 1022 // Reschedule for a non-user reason 1023 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, 1024 JobParameters.STOP_REASON_DEVICE_STATE, 1025 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1026 assertEquals(0, 1027 rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1028 1029 // Reschedule for a user reason 1030 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1031 JobParameters.STOP_REASON_USER, 1032 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1033 assertNotEquals(0, 1034 rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1035 1036 // Reschedule a previously demoted job for a non-user reason 1037 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1038 JobParameters.STOP_REASON_CONSTRAINT_CHARGING, 1039 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED); 1040 assertNotEquals(0, 1041 rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1042 } 1043 1044 /** 1045 * Confirm that 1046 * returns {@code null} when for user-visible jobs stopped by the user. 1047 */ 1048 @Test testGetRescheduleJobForFailure_userStopped()1049 public void testGetRescheduleJobForFailure_userStopped() { 1050 JobStatus uiJob = createJobStatus("testGetRescheduleJobForFailure", 1051 createJobInfo().setUserInitiated(true) 1052 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1053 JobStatus uvJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo()); 1054 spyOn(uvJob); 1055 doReturn(true).when(uvJob).isUserVisibleJob(); 1056 JobStatus regJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo()); 1057 1058 // Reschedule for a non-user reason 1059 JobStatus rescheduledUiJob = mService.getRescheduleJobForFailureLocked(uiJob, 1060 JobParameters.STOP_REASON_DEVICE_STATE, 1061 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1062 JobStatus rescheduledUvJob = mService.getRescheduleJobForFailureLocked(uvJob, 1063 JobParameters.STOP_REASON_DEVICE_STATE, 1064 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1065 JobStatus rescheduledRegJob = mService.getRescheduleJobForFailureLocked(regJob, 1066 JobParameters.STOP_REASON_DEVICE_STATE, 1067 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1068 assertNotNull(rescheduledUiJob); 1069 assertNotNull(rescheduledUvJob); 1070 assertNotNull(rescheduledRegJob); 1071 1072 // Reschedule for a user reason. The user-visible jobs shouldn't be rescheduled. 1073 spyOn(rescheduledUvJob); 1074 doReturn(true).when(rescheduledUvJob).isUserVisibleJob(); 1075 rescheduledUiJob = mService.getRescheduleJobForFailureLocked(rescheduledUiJob, 1076 JobParameters.STOP_REASON_USER, 1077 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1078 rescheduledUvJob = mService.getRescheduleJobForFailureLocked(rescheduledUvJob, 1079 JobParameters.STOP_REASON_USER, 1080 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1081 rescheduledRegJob = mService.getRescheduleJobForFailureLocked(rescheduledRegJob, 1082 JobParameters.STOP_REASON_USER, 1083 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1084 assertNull(rescheduledUiJob); 1085 assertNull(rescheduledUvJob); 1086 assertNotNull(rescheduledRegJob); 1087 } 1088 1089 /** 1090 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1091 * with the correct delay and deadline constraints if the periodic job is scheduled with the 1092 * minimum possible period. 1093 */ 1094 @Test testGetRescheduleJobForPeriodic_minPeriod()1095 public void testGetRescheduleJobForPeriodic_minPeriod() { 1096 final long now = sElapsedRealtimeClock.millis(); 1097 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow", 1098 createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS)); 1099 final long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS; 1100 final long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS; 1101 1102 for (int i = 0; i < 25; i++) { 1103 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1104 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1105 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1106 advanceElapsedClock(30_000); // 30 seconds 1107 } 1108 1109 for (int i = 0; i < 5; i++) { 1110 // Window buffering in last 1/6 of window. 1111 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1112 assertEquals(nextWindowStartTime + i * 30_000, rescheduledJob.getEarliestRunTime()); 1113 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1114 advanceElapsedClock(30_000); // 30 seconds 1115 } 1116 } 1117 1118 /** 1119 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1120 * with the correct delay and deadline constraints if the periodic job is scheduled with a 1121 * period that's too large. 1122 */ 1123 @Test testGetRescheduleJobForPeriodic_largePeriod()1124 public void testGetRescheduleJobForPeriodic_largePeriod() { 1125 final long now = sElapsedRealtimeClock.millis(); 1126 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow", 1127 createJobInfo().setPeriodic(2 * 365 * DAY_IN_MILLIS)); 1128 assertEquals(now, job.getEarliestRunTime()); 1129 // Periods are capped at 365 days (1 year). 1130 assertEquals(now + 365 * DAY_IN_MILLIS, job.getLatestRunTimeElapsed()); 1131 final long nextWindowStartTime = now + 365 * DAY_IN_MILLIS; 1132 final long nextWindowEndTime = nextWindowStartTime + 365 * DAY_IN_MILLIS; 1133 1134 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1135 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1136 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1137 } 1138 1139 /** 1140 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1141 * with the correct delay and deadline constraints if the periodic job is completed and 1142 * rescheduled while run in its expected running window. 1143 */ 1144 @Test testGetRescheduleJobForPeriodic_insideWindow()1145 public void testGetRescheduleJobForPeriodic_insideWindow() { 1146 final long now = sElapsedRealtimeClock.millis(); 1147 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow", 1148 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1149 final long nextWindowStartTime = now + HOUR_IN_MILLIS; 1150 final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1151 1152 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1153 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1154 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1155 1156 advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes 1157 1158 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1159 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1160 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1161 1162 advanceElapsedClock(20 * MINUTE_IN_MILLIS); // now + 30 minutes 1163 1164 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1165 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1166 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1167 1168 advanceElapsedClock(25 * MINUTE_IN_MILLIS); // now + 55 minutes 1169 1170 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1171 // Shifted because it's close to the end of the window. 1172 assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, 1173 rescheduledJob.getEarliestRunTime()); 1174 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1175 1176 advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes 1177 1178 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1179 // Shifted because it's close to the end of the window. 1180 assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, 1181 rescheduledJob.getEarliestRunTime()); 1182 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1183 } 1184 1185 /** 1186 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1187 * with an extra delay and correct deadline constraint if the periodic job is completed near the 1188 * end of its expected running window. 1189 */ 1190 @Test testGetRescheduleJobForPeriodic_closeToEndOfWindow()1191 public void testGetRescheduleJobForPeriodic_closeToEndOfWindow() { 1192 JobStatus frequentJob = createJobStatus( 1193 "testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1194 createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS)); 1195 long now = sElapsedRealtimeClock.millis(); 1196 long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS; 1197 long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS; 1198 1199 // At the beginning of the window. Next window should be unaffected. 1200 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); 1201 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1202 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1203 1204 // Halfway through window. Next window should be unaffected. 1205 advanceElapsedClock((long) (7.5 * MINUTE_IN_MILLIS)); 1206 rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); 1207 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1208 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1209 1210 // In last 1/6 of window. Next window start time should be shifted slightly. 1211 advanceElapsedClock(6 * MINUTE_IN_MILLIS); 1212 rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); 1213 assertEquals(nextWindowStartTime + MINUTE_IN_MILLIS, 1214 rescheduledJob.getEarliestRunTime()); 1215 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1216 1217 JobStatus mediumJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1218 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1219 now = sElapsedRealtimeClock.millis(); 1220 nextWindowStartTime = now + HOUR_IN_MILLIS; 1221 nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1222 1223 // At the beginning of the window. Next window should be unaffected. 1224 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 1225 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1226 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1227 1228 // Halfway through window. Next window should be unaffected. 1229 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1230 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 1231 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1232 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1233 1234 // At the edge 1/6 of window. Next window should be unaffected. 1235 advanceElapsedClock(20 * MINUTE_IN_MILLIS); 1236 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 1237 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1238 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1239 1240 // In last 1/6 of window. Next window start time should be shifted slightly. 1241 advanceElapsedClock(6 * MINUTE_IN_MILLIS); 1242 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 1243 assertEquals(nextWindowStartTime + (6 * MINUTE_IN_MILLIS), 1244 rescheduledJob.getEarliestRunTime()); 1245 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1246 1247 JobStatus longJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1248 createJobInfo().setPeriodic(6 * HOUR_IN_MILLIS)); 1249 now = sElapsedRealtimeClock.millis(); 1250 nextWindowStartTime = now + 6 * HOUR_IN_MILLIS; 1251 nextWindowEndTime = now + 12 * HOUR_IN_MILLIS; 1252 1253 // At the beginning of the window. Next window should be unaffected. 1254 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1255 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1256 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1257 1258 // Halfway through window. Next window should be unaffected. 1259 advanceElapsedClock(3 * HOUR_IN_MILLIS); 1260 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1261 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1262 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1263 1264 // At the edge 1/6 of window. Next window should be unaffected. 1265 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1266 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1267 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1268 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1269 1270 // In last 1/6 of window. Next window should be unaffected since we're over the shift cap. 1271 advanceElapsedClock(15 * MINUTE_IN_MILLIS); 1272 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1273 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1274 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1275 1276 // In last 1/6 of window. Next window start time should be shifted slightly. 1277 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1278 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1279 assertEquals(nextWindowStartTime + (30 * MINUTE_IN_MILLIS), 1280 rescheduledJob.getEarliestRunTime()); 1281 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1282 1283 // Flex duration close to period duration. 1284 JobStatus gameyFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1285 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 59 * MINUTE_IN_MILLIS)); 1286 now = sElapsedRealtimeClock.millis(); 1287 nextWindowStartTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1288 nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1289 advanceElapsedClock(MINUTE_IN_MILLIS); 1290 1291 // At the beginning of the window. Next window should be unaffected. 1292 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1293 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1294 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1295 1296 // Halfway through window. Next window should be unaffected. 1297 advanceElapsedClock(29 * MINUTE_IN_MILLIS); 1298 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1299 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1300 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1301 1302 // At the edge 1/6 of window. Next window should be unaffected. 1303 advanceElapsedClock(20 * MINUTE_IN_MILLIS); 1304 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1305 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1306 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1307 1308 // In last 1/6 of window. Next window start time should be shifted slightly. 1309 advanceElapsedClock(6 * MINUTE_IN_MILLIS); 1310 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1311 assertEquals(nextWindowStartTime + (5 * MINUTE_IN_MILLIS), 1312 rescheduledJob.getEarliestRunTime()); 1313 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1314 1315 // Very short flex duration compared to period duration. 1316 JobStatus superFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1317 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 10 * MINUTE_IN_MILLIS)); 1318 now = sElapsedRealtimeClock.millis(); 1319 nextWindowStartTime = now + HOUR_IN_MILLIS + 50 * MINUTE_IN_MILLIS; 1320 nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1321 advanceElapsedClock(MINUTE_IN_MILLIS); 1322 1323 // At the beginning of the window. Next window should be unaffected. 1324 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1325 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1326 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1327 1328 // Halfway through window. Next window should be unaffected. 1329 advanceElapsedClock(29 * MINUTE_IN_MILLIS); 1330 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1331 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1332 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1333 1334 // At the edge 1/6 of window. Next window should be unaffected. 1335 advanceElapsedClock(20 * MINUTE_IN_MILLIS); 1336 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1337 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1338 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1339 1340 // In last 1/6 of window. Next window should be unaffected since the flex duration pushes 1341 // the next window start time far enough away. 1342 advanceElapsedClock(6 * MINUTE_IN_MILLIS); 1343 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1344 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1345 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1346 } 1347 1348 /** 1349 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1350 * with the correct delay and deadline constraints if the periodic job with a custom flex 1351 * setting is completed and rescheduled while run in its expected running window. 1352 */ 1353 @Test testGetRescheduleJobForPeriodic_insideWindow_flex()1354 public void testGetRescheduleJobForPeriodic_insideWindow_flex() { 1355 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_flex", 1356 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); 1357 // First window starts 30 minutes from now. 1358 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1359 final long now = sElapsedRealtimeClock.millis(); 1360 final long nextWindowStartTime = now + HOUR_IN_MILLIS; 1361 final long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; 1362 1363 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1364 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1365 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1366 1367 advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes 1368 1369 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1370 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1371 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1372 1373 advanceElapsedClock(15 * MINUTE_IN_MILLIS); // now + 25 minutes 1374 1375 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1376 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1377 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1378 1379 advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 29 minutes 1380 1381 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1382 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1383 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1384 } 1385 1386 /** 1387 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1388 * with the correct delay and deadline constraints if the periodic job failed but then ran 1389 * successfully and was rescheduled while run in its expected running window. 1390 */ 1391 @Test testGetRescheduleJobForPeriodic_insideWindow_failedJob()1392 public void testGetRescheduleJobForPeriodic_insideWindow_failedJob() { 1393 final long now = sElapsedRealtimeClock.millis(); 1394 final long nextWindowStartTime = now + HOUR_IN_MILLIS; 1395 final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1396 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob", 1397 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1398 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1399 JobParameters.STOP_REASON_UNDEFINED, 1400 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1401 1402 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1403 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1404 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1405 1406 advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes 1407 failedJob = mService.getRescheduleJobForFailureLocked(job, 1408 JobParameters.STOP_REASON_UNDEFINED, 1409 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1410 advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes 1411 1412 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1413 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1414 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1415 1416 advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes 1417 failedJob = mService.getRescheduleJobForFailureLocked(job, 1418 JobParameters.STOP_REASON_UNDEFINED, 1419 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1420 advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes 1421 1422 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1423 // Shifted because it's close to the end of the window. 1424 assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, 1425 rescheduledJob.getEarliestRunTime()); 1426 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1427 1428 advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes 1429 failedJob = mService.getRescheduleJobForFailureLocked(job, 1430 JobParameters.STOP_REASON_UNDEFINED, 1431 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1432 advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes 1433 1434 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1435 // Shifted because it's close to the end of the window. 1436 assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, 1437 rescheduledJob.getEarliestRunTime()); 1438 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1439 } 1440 1441 /** 1442 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1443 * with the correct delay and deadline constraints if the periodic job is completed and 1444 * rescheduled when run after its expected running window. 1445 */ 1446 @Test testGetRescheduleJobForPeriodic_outsideWindow()1447 public void testGetRescheduleJobForPeriodic_outsideWindow() { 1448 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow", 1449 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1450 long now = sElapsedRealtimeClock.millis(); 1451 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1452 long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1453 1454 advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS); 1455 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1456 // have consistent windows, so the new window should start as soon as the previous window 1457 // ended and end PERIOD time after the previous window ended. 1458 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1459 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1460 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1461 1462 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1463 // Say that the job ran at this point, possibly due to device idle. 1464 // The next window should be consistent (start and end at the time it would have had the job 1465 // run normally in previous windows). 1466 nextWindowStartTime += 2 * HOUR_IN_MILLIS; 1467 nextWindowEndTime += 2 * HOUR_IN_MILLIS; 1468 1469 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1470 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1471 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1472 } 1473 1474 /** 1475 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1476 * with the correct delay and deadline constraints if the periodic job with a custom flex 1477 * setting is completed and rescheduled when run after its expected running window. 1478 */ 1479 @Test testGetRescheduleJobForPeriodic_outsideWindow_flex()1480 public void testGetRescheduleJobForPeriodic_outsideWindow_flex() { 1481 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_flex", 1482 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); 1483 // First window starts 30 minutes from now. 1484 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1485 long now = sElapsedRealtimeClock.millis(); 1486 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1487 long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; 1488 1489 advanceElapsedClock(31 * MINUTE_IN_MILLIS); 1490 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1491 // have consistent windows, so the new window should start as soon as the previous window 1492 // ended and end PERIOD time after the previous window ended. 1493 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1494 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1495 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1496 1497 // 5 minutes before the start of the next window. It's too close to the next window, so the 1498 // returned job should be for the window after. 1499 advanceElapsedClock(24 * MINUTE_IN_MILLIS); 1500 nextWindowStartTime += HOUR_IN_MILLIS; 1501 nextWindowEndTime += HOUR_IN_MILLIS; 1502 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1503 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1504 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1505 1506 advanceElapsedClock(2 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS); 1507 // Say that the job ran at this point, possibly due to device idle. 1508 // The next window should be consistent (start and end at the time it would have had the job 1509 // run normally in previous windows). 1510 nextWindowStartTime += 2 * HOUR_IN_MILLIS; 1511 nextWindowEndTime += 2 * HOUR_IN_MILLIS; 1512 1513 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1514 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1515 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1516 } 1517 1518 /** 1519 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1520 * with the correct delay and deadline constraints if the periodic job failed but then ran 1521 * successfully and was rescheduled when run after its expected running window. 1522 */ 1523 @Test testGetRescheduleJobForPeriodic_outsideWindow_failedJob()1524 public void testGetRescheduleJobForPeriodic_outsideWindow_failedJob() { 1525 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob", 1526 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1527 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1528 JobParameters.STOP_REASON_UNDEFINED, 1529 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1530 long now = sElapsedRealtimeClock.millis(); 1531 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1532 long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1533 1534 advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS); 1535 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1536 // have consistent windows, so the new window should start as soon as the previous window 1537 // ended and end PERIOD time after the previous window ended. 1538 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1539 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1540 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1541 1542 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1543 // Say that the job ran at this point, possibly due to device idle. 1544 // The next window should be consistent (start and end at the time it would have had the job 1545 // run normally in previous windows). 1546 nextWindowStartTime += 2 * HOUR_IN_MILLIS; 1547 nextWindowEndTime += 2 * HOUR_IN_MILLIS; 1548 1549 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1550 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1551 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1552 } 1553 1554 /** 1555 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1556 * with the correct delay and deadline constraints if the periodic job with a custom flex 1557 * setting failed but then ran successfully and was rescheduled when run after its expected 1558 * running window. 1559 */ 1560 @Test testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob()1561 public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob() { 1562 JobStatus job = createJobStatus( 1563 "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob", 1564 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); 1565 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1566 JobParameters.STOP_REASON_UNDEFINED, 1567 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1568 // First window starts 30 minutes from now. 1569 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1570 long now = sElapsedRealtimeClock.millis(); 1571 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1572 long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; 1573 1574 advanceElapsedClock(31 * MINUTE_IN_MILLIS); 1575 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1576 // have consistent windows, so the new window should start as soon as the previous window 1577 // ended and end PERIOD time after the previous window ended. 1578 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1579 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1580 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1581 1582 // 5 minutes before the start of the next window. It's too close to the next window, so the 1583 // returned job should be for the window after. 1584 advanceElapsedClock(24 * MINUTE_IN_MILLIS); 1585 nextWindowStartTime += HOUR_IN_MILLIS; 1586 nextWindowEndTime += HOUR_IN_MILLIS; 1587 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1588 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1589 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1590 1591 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1592 // Say that the job ran at this point, possibly due to device idle. 1593 // The next window should be consistent (start and end at the time it would have had the job 1594 // run normally in previous windows). 1595 nextWindowStartTime += 2 * HOUR_IN_MILLIS; 1596 nextWindowEndTime += 2 * HOUR_IN_MILLIS; 1597 1598 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1599 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1600 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1601 } 1602 1603 @Test testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod()1604 public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod() { 1605 JobStatus job = createJobStatus( 1606 "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod", 1607 createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS)); 1608 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1609 JobParameters.STOP_REASON_UNDEFINED, 1610 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1611 // First window starts 6.625 days from now. 1612 advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS); 1613 long now = sElapsedRealtimeClock.millis(); 1614 long nextWindowStartTime = now + 7 * DAY_IN_MILLIS; 1615 long nextWindowEndTime = nextWindowStartTime + 9 * HOUR_IN_MILLIS; 1616 1617 advanceElapsedClock(6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS); 1618 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1619 // have consistent windows, so the new window should start as soon as the previous window 1620 // ended and end PERIOD time after the previous window ended. 1621 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1622 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1623 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1624 1625 advanceElapsedClock(DAY_IN_MILLIS); 1626 // Say the job ran a day late. Since the period is massive compared to the flex, JSS should 1627 // put the rescheduled job in the original window. 1628 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1629 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1630 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1631 1632 // 1 day before the start of the next window. Given the large period, respect the original 1633 // next window. 1634 advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); 1635 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1636 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1637 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1638 1639 // 1 hour before the start of the next window. It's too close to the next window, so the 1640 // returned job should be for the window after. 1641 long oneHourBeforeNextWindow = 1642 nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; 1643 long fiveMinsBeforeNextWindow = 1644 nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; 1645 advanceElapsedClock(oneHourBeforeNextWindow); 1646 nextWindowStartTime += 7 * DAY_IN_MILLIS; 1647 nextWindowEndTime += 7 * DAY_IN_MILLIS; 1648 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1649 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1650 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1651 1652 // 5 minutes before the start of the next window. It's too close to the next window, so the 1653 // returned job should be for the window after. 1654 advanceElapsedClock(fiveMinsBeforeNextWindow); 1655 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1656 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1657 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1658 1659 advanceElapsedClock(14 * DAY_IN_MILLIS); 1660 // Say that the job ran at this point, probably because the phone was off the entire time. 1661 // The next window should be consistent (start and end at the time it would have had the job 1662 // run normally in previous windows). 1663 nextWindowStartTime += 14 * DAY_IN_MILLIS; 1664 nextWindowEndTime += 14 * DAY_IN_MILLIS; 1665 1666 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1667 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1668 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1669 1670 // Test original job again but with a huge delay from the original execution window 1671 1672 // 1 day before the start of the next window. Given the large period, respect the original 1673 // next window. 1674 advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); 1675 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1676 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1677 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1678 1679 // 1 hour before the start of the next window. It's too close to the next window, so the 1680 // returned job should be for the window after. 1681 oneHourBeforeNextWindow = 1682 nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; 1683 fiveMinsBeforeNextWindow = 1684 nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; 1685 advanceElapsedClock(oneHourBeforeNextWindow); 1686 nextWindowStartTime += 7 * DAY_IN_MILLIS; 1687 nextWindowEndTime += 7 * DAY_IN_MILLIS; 1688 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1689 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1690 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1691 1692 // 5 minutes before the start of the next window. It's too close to the next window, so the 1693 // returned job should be for the window after. 1694 advanceElapsedClock(fiveMinsBeforeNextWindow); 1695 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1696 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1697 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1698 } 1699 1700 /** Tests that rare job batching works as expected. */ 1701 @Test testRareJobBatching()1702 public void testRareJobBatching() { 1703 spyOn(mService); 1704 doReturn(false).when(mService).evaluateControllerStatesLocked(any()); 1705 doNothing().when(mService).noteJobsPending(any()); 1706 doReturn(true).when(mService).isReadyToBeExecutedLocked(any(), anyBoolean()); 1707 advanceElapsedClock(24 * HOUR_IN_MILLIS); 1708 1709 JobSchedulerService.MaybeReadyJobQueueFunctor maybeQueueFunctor = 1710 mService.new MaybeReadyJobQueueFunctor(); 1711 mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; 1712 mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = HOUR_IN_MILLIS; 1713 1714 JobStatus job = createJobStatus( 1715 "testRareJobBatching", 1716 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1717 job.setStandbyBucket(RARE_INDEX); 1718 1719 // Not enough RARE jobs to run. 1720 mService.getPendingJobQueue().clear(); 1721 maybeQueueFunctor.reset(); 1722 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { 1723 maybeQueueFunctor.accept(job); 1724 assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount); 1725 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1726 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1727 } 1728 maybeQueueFunctor.postProcessLocked(); 1729 assertEquals(0, mService.getPendingJobQueue().size()); 1730 1731 // Enough RARE jobs to run. 1732 mService.getPendingJobQueue().clear(); 1733 maybeQueueFunctor.reset(); 1734 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) { 1735 maybeQueueFunctor.accept(job); 1736 assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount); 1737 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1738 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1739 } 1740 maybeQueueFunctor.postProcessLocked(); 1741 assertEquals(5, mService.getPendingJobQueue().size()); 1742 1743 // Not enough RARE jobs to run, but a non-batched job saves the day. 1744 mService.getPendingJobQueue().clear(); 1745 maybeQueueFunctor.reset(); 1746 JobStatus activeJob = createJobStatus( 1747 "testRareJobBatching", 1748 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1749 activeJob.setStandbyBucket(ACTIVE_INDEX); 1750 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { 1751 maybeQueueFunctor.accept(job); 1752 assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount); 1753 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1754 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1755 } 1756 maybeQueueFunctor.accept(activeJob); 1757 maybeQueueFunctor.postProcessLocked(); 1758 assertEquals(3, mService.getPendingJobQueue().size()); 1759 1760 // Not enough RARE jobs to run, but an old RARE job saves the day. 1761 mService.getPendingJobQueue().clear(); 1762 maybeQueueFunctor.reset(); 1763 JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo()); 1764 oldRareJob.setStandbyBucket(RARE_INDEX); 1765 final long oldBatchTime = sElapsedRealtimeClock.millis() 1766 - 2 * mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS; 1767 oldRareJob.setFirstForceBatchedTimeElapsed(oldBatchTime); 1768 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { 1769 maybeQueueFunctor.accept(job); 1770 assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount); 1771 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1772 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1773 } 1774 maybeQueueFunctor.accept(oldRareJob); 1775 assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed()); 1776 maybeQueueFunctor.postProcessLocked(); 1777 assertEquals(3, mService.getPendingJobQueue().size()); 1778 } 1779 1780 /** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */ 1781 @Test testScheduleLimiting_RegularSchedule_Blocked()1782 public void testScheduleLimiting_RegularSchedule_Blocked() { 1783 mService.mConstants.ENABLE_API_QUOTAS = true; 1784 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 1785 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 1786 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 1787 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; 1788 mService.updateQuotaTracker(); 1789 mService.resetScheduleQuota(); 1790 1791 final JobInfo job = createJobInfo().setPersisted(true).build(); 1792 for (int i = 0; i < 500; ++i) { 1793 final int expected = 1794 i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE; 1795 assertEquals("Got unexpected result for schedule #" + (i + 1), 1796 expected, 1797 mService.scheduleAsPackage(job, null, TEST_UID, null, 0, "JSSTest", "")); 1798 } 1799 } 1800 1801 /** 1802 * Tests that jobs scheduled by the app itself succeed even if the app is above the scheduling 1803 * limit. 1804 */ 1805 @Test 1806 public void testScheduleLimiting_RegularSchedule_Allowed() { 1807 mService.mConstants.ENABLE_API_QUOTAS = true; 1808 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 1809 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 1810 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 1811 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 1812 mService.updateQuotaTracker(); 1813 mService.resetScheduleQuota(); 1814 1815 final JobInfo job = createJobInfo().setPersisted(true).build(); 1816 for (int i = 0; i < 500; ++i) { 1817 assertEquals("Got unexpected result for schedule #" + (i + 1), 1818 JobScheduler.RESULT_SUCCESS, 1819 mService.scheduleAsPackage(job, null, TEST_UID, null, 0, "JSSTest", "")); 1820 } 1821 } 1822 1823 /** 1824 * Tests that jobs scheduled through a proxy (eg. system server) don't count towards scheduling 1825 * limits. 1826 */ 1827 @Test 1828 public void testScheduleLimiting_Proxy() { 1829 mService.mConstants.ENABLE_API_QUOTAS = true; 1830 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 1831 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 1832 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 1833 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; 1834 mService.updateQuotaTracker(); 1835 mService.resetScheduleQuota(); 1836 1837 final JobInfo job = createJobInfo().setPersisted(true).build(); 1838 for (int i = 0; i < 500; ++i) { 1839 assertEquals("Got unexpected result for schedule #" + (i + 1), 1840 JobScheduler.RESULT_SUCCESS, 1841 mService.scheduleAsPackage(job, null, TEST_UID, "proxied.package", 0, "JSSTest", 1842 "")); 1843 } 1844 } 1845 1846 /** 1847 * Tests that jobs scheduled by an app for itself as if through a proxy are counted towards 1848 * scheduling limits. 1849 */ 1850 @Test 1851 public void testScheduleLimiting_SelfProxy() { 1852 mService.mConstants.ENABLE_API_QUOTAS = true; 1853 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 1854 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 1855 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 1856 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; 1857 mService.updateQuotaTracker(); 1858 mService.resetScheduleQuota(); 1859 1860 final JobInfo job = createJobInfo().setPersisted(true).build(); 1861 for (int i = 0; i < 500; ++i) { 1862 final int expected = 1863 i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE; 1864 assertEquals("Got unexpected result for schedule #" + (i + 1), 1865 expected, 1866 mService.scheduleAsPackage(job, null, TEST_UID, 1867 job.getService().getPackageName(), 1868 0, "JSSTest", "")); 1869 } 1870 } 1871 1872 /** 1873 * Tests that the number of persisted JobWorkItems is capped. 1874 */ 1875 @Test 1876 public void testScheduleLimiting_JobWorkItems_Nonpersisted() { 1877 mService.mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 500; 1878 mService.mConstants.ENABLE_API_QUOTAS = false; 1879 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 1880 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 1881 mService.updateQuotaTracker(); 1882 mService.resetScheduleQuota(); 1883 1884 final JobInfo job = createJobInfo().setPersisted(false).build(); 1885 final JobWorkItem item = new JobWorkItem.Builder().build(); 1886 for (int i = 0; i < 1000; ++i) { 1887 assertEquals("Got unexpected result for schedule #" + (i + 1), 1888 JobScheduler.RESULT_SUCCESS, 1889 mService.scheduleAsPackage(job, item, TEST_UID, 1890 job.getService().getPackageName(), 1891 0, "JSSTest", "")); 1892 } 1893 } 1894 1895 /** 1896 * Tests that the number of persisted JobWorkItems is capped. 1897 */ 1898 @Test 1899 public void testScheduleLimiting_JobWorkItems_Persisted() { 1900 mService.mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 500; 1901 mService.mConstants.ENABLE_API_QUOTAS = false; 1902 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 1903 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 1904 mService.updateQuotaTracker(); 1905 mService.resetScheduleQuota(); 1906 1907 final JobInfo job = createJobInfo().setPersisted(true).build(); 1908 final JobWorkItem item = new JobWorkItem.Builder().build(); 1909 for (int i = 0; i < 500; ++i) { 1910 assertEquals("Got unexpected result for schedule #" + (i + 1), 1911 JobScheduler.RESULT_SUCCESS, 1912 mService.scheduleAsPackage(job, item, TEST_UID, 1913 job.getService().getPackageName(), 1914 0, "JSSTest", "")); 1915 } 1916 try { 1917 mService.scheduleAsPackage(job, item, TEST_UID, job.getService().getPackageName(), 1918 0, "JSSTest", ""); 1919 fail("Added more items than allowed"); 1920 } catch (IllegalStateException expected) { 1921 // Success 1922 } 1923 } 1924 1925 /** Tests that jobs are removed from the pending list if the user stops the app. */ 1926 @Test 1927 public void testUserStopRemovesPending() { 1928 spyOn(mService); 1929 1930 JobStatus job1a = createJobStatus("testUserStopRemovesPending", 1931 createJobInfo(1), 1, "pkg1"); 1932 JobStatus job1b = createJobStatus("testUserStopRemovesPending", 1933 createJobInfo(2), 1, "pkg1"); 1934 JobStatus job2a = createJobStatus("testUserStopRemovesPending", 1935 createJobInfo(1), 2, "pkg2"); 1936 JobStatus job2b = createJobStatus("testUserStopRemovesPending", 1937 createJobInfo(2), 2, "pkg2"); 1938 doReturn(1).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 0); 1939 doReturn(11).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 1); 1940 doReturn(2).when(mPackageManagerInternal).getPackageUid("pkg2", 0, 0); 1941 1942 mService.getPendingJobQueue().clear(); 1943 mService.getPendingJobQueue().add(job1a); 1944 mService.getPendingJobQueue().add(job1b); 1945 mService.getPendingJobQueue().add(job2a); 1946 mService.getPendingJobQueue().add(job2b); 1947 mService.getJobStore().add(job1a); 1948 mService.getJobStore().add(job1b); 1949 mService.getJobStore().add(job2a); 1950 mService.getJobStore().add(job2b); 1951 1952 mService.notePendingUserRequestedAppStopInternal("pkg1", 1, "test"); 1953 assertEquals(4, mService.getPendingJobQueue().size()); 1954 assertTrue(mService.getPendingJobQueue().contains(job1a)); 1955 assertTrue(mService.getPendingJobQueue().contains(job1b)); 1956 assertTrue(mService.getPendingJobQueue().contains(job2a)); 1957 assertTrue(mService.getPendingJobQueue().contains(job2b)); 1958 1959 mService.notePendingUserRequestedAppStopInternal("pkg1", 0, "test"); 1960 assertEquals(2, mService.getPendingJobQueue().size()); 1961 assertFalse(mService.getPendingJobQueue().contains(job1a)); 1962 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job1a)); 1963 assertFalse(mService.getPendingJobQueue().contains(job1b)); 1964 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job1b)); 1965 assertTrue(mService.getPendingJobQueue().contains(job2a)); 1966 assertTrue(mService.getPendingJobQueue().contains(job2b)); 1967 1968 mService.notePendingUserRequestedAppStopInternal("pkg2", 0, "test"); 1969 assertEquals(0, mService.getPendingJobQueue().size()); 1970 assertFalse(mService.getPendingJobQueue().contains(job1a)); 1971 assertFalse(mService.getPendingJobQueue().contains(job1b)); 1972 assertFalse(mService.getPendingJobQueue().contains(job2a)); 1973 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2a)); 1974 assertFalse(mService.getPendingJobQueue().contains(job2b)); 1975 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2b)); 1976 } 1977 } 1978