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 import static com.android.server.job.Flags.FLAG_BATCH_ACTIVE_BUCKET_JOBS; 33 import static com.android.server.job.Flags.FLAG_BATCH_CONNECTIVITY_JOBS_PER_NETWORK; 34 import static com.android.server.job.Flags.FLAG_THERMAL_RESTRICTIONS_TO_FGS_JOBS; 35 36 import static org.junit.Assert.assertEquals; 37 import static org.junit.Assert.assertFalse; 38 import static org.junit.Assert.assertNotEquals; 39 import static org.junit.Assert.assertNotNull; 40 import static org.junit.Assert.assertNull; 41 import static org.junit.Assert.assertTrue; 42 import static org.junit.Assert.fail; 43 import static org.mockito.ArgumentMatchers.any; 44 import static org.mockito.ArgumentMatchers.anyBoolean; 45 import static org.mockito.ArgumentMatchers.anyInt; 46 import static org.mockito.ArgumentMatchers.anyString; 47 import static org.mockito.ArgumentMatchers.eq; 48 import static org.mockito.Mockito.verify; 49 import static org.mockito.Mockito.when; 50 51 import android.app.ActivityManager; 52 import android.app.ActivityManagerInternal; 53 import android.app.IActivityManager; 54 import android.app.UiModeManager; 55 import android.app.job.JobInfo; 56 import android.app.job.JobParameters; 57 import android.app.job.JobScheduler; 58 import android.app.job.JobWorkItem; 59 import android.app.usage.UsageStatsManagerInternal; 60 import android.content.ComponentName; 61 import android.content.Context; 62 import android.content.Intent; 63 import android.content.PermissionChecker; 64 import android.content.pm.PackageManager; 65 import android.content.pm.PackageManagerInternal; 66 import android.content.res.Resources; 67 import android.net.ConnectivityManager; 68 import android.net.Network; 69 import android.net.NetworkCapabilities; 70 import android.net.NetworkPolicyManager; 71 import android.os.BatteryManager; 72 import android.os.BatteryManagerInternal; 73 import android.os.BatteryManagerInternal.ChargingPolicyChangeListener; 74 import android.os.Looper; 75 import android.os.RemoteException; 76 import android.os.ServiceManager; 77 import android.os.SystemClock; 78 import android.platform.test.annotations.RequiresFlagsDisabled; 79 import android.platform.test.flag.junit.CheckFlagsRule; 80 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 81 import android.platform.test.flag.junit.SetFlagsRule; 82 83 import com.android.server.AppStateTracker; 84 import com.android.server.AppStateTrackerImpl; 85 import com.android.server.DeviceIdleInternal; 86 import com.android.server.LocalServices; 87 import com.android.server.PowerAllowlistInternal; 88 import com.android.server.SystemServiceManager; 89 import com.android.server.job.controllers.ConnectivityController; 90 import com.android.server.job.controllers.JobStatus; 91 import com.android.server.job.controllers.QuotaController; 92 import com.android.server.job.restrictions.JobRestriction; 93 import com.android.server.job.restrictions.ThermalStatusRestriction; 94 import com.android.server.pm.UserManagerInternal; 95 import com.android.server.usage.AppStandbyInternal; 96 97 import org.junit.After; 98 import org.junit.Before; 99 import org.junit.Rule; 100 import org.junit.Test; 101 import org.mockito.ArgumentCaptor; 102 import org.mockito.ArgumentMatchers; 103 import org.mockito.Mock; 104 import org.mockito.MockitoSession; 105 import org.mockito.quality.Strictness; 106 107 import java.time.Clock; 108 import java.time.Duration; 109 import java.time.ZoneOffset; 110 111 public class JobSchedulerServiceTest { 112 private static final String TAG = JobSchedulerServiceTest.class.getSimpleName(); 113 private static final int TEST_UID = 10123; 114 115 private JobSchedulerService mService; 116 117 private MockitoSession mMockingSession; 118 @Mock 119 private ActivityManagerInternal mActivityMangerInternal; 120 @Mock 121 private BatteryManagerInternal mBatteryManagerInternal; 122 @Mock 123 private Context mContext; 124 @Mock 125 private PackageManagerInternal mPackageManagerInternal; 126 127 @Rule 128 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 129 130 @Rule 131 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 132 133 private ChargingPolicyChangeListener mChargingPolicyChangeListener; 134 135 private class TestJobSchedulerService extends JobSchedulerService { TestJobSchedulerService(Context context)136 TestJobSchedulerService(Context context) { 137 super(context); 138 mAppStateTracker = mock(AppStateTrackerImpl.class); 139 } 140 } 141 142 @Before setUp()143 public void setUp() throws Exception { 144 mMockingSession = mockitoSession() 145 .initMocks(this) 146 .strictness(Strictness.LENIENT) 147 .mockStatic(LocalServices.class) 148 .mockStatic(PermissionChecker.class) 149 .mockStatic(ServiceManager.class) 150 .startMocking(); 151 152 // Called in JobSchedulerService constructor. 153 when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); 154 doReturn(mActivityMangerInternal) 155 .when(() -> LocalServices.getService(ActivityManagerInternal.class)); 156 doReturn(mock(AppStandbyInternal.class)) 157 .when(() -> LocalServices.getService(AppStandbyInternal.class)); 158 doReturn(mBatteryManagerInternal) 159 .when(() -> LocalServices.getService(BatteryManagerInternal.class)); 160 doReturn(mPackageManagerInternal) 161 .when(() -> LocalServices.getService(PackageManagerInternal.class)); 162 doReturn(mock(UsageStatsManagerInternal.class)) 163 .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); 164 when(mContext.getString(anyInt())).thenReturn("some_test_string"); 165 // Called in BackgroundJobsController constructor. 166 doReturn(mock(AppStateTrackerImpl.class)) 167 .when(() -> LocalServices.getService(AppStateTracker.class)); 168 // Called in ConnectivityController constructor. 169 when(mContext.getSystemService(ConnectivityManager.class)) 170 .thenReturn(mock(ConnectivityManager.class)); 171 when(mContext.getSystemService(NetworkPolicyManager.class)) 172 .thenReturn(mock(NetworkPolicyManager.class)); 173 // Called in DeviceIdleJobsController constructor. 174 doReturn(mock(DeviceIdleInternal.class)) 175 .when(() -> LocalServices.getService(DeviceIdleInternal.class)); 176 // Used in JobConcurrencyManager. 177 doReturn(mock(UserManagerInternal.class)) 178 .when(() -> LocalServices.getService(UserManagerInternal.class)); 179 // Used in JobStatus. 180 doReturn(mock(JobSchedulerInternal.class)) 181 .when(() -> LocalServices.getService(JobSchedulerInternal.class)); 182 // Called via IdleController constructor. 183 when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); 184 when(mContext.getResources()).thenReturn(mock(Resources.class)); 185 // Called in QuotaController constructor. 186 doReturn(mock(PowerAllowlistInternal.class)) 187 .when(() -> LocalServices.getService(PowerAllowlistInternal.class)); 188 IActivityManager activityManager = ActivityManager.getService(); 189 spyOn(activityManager); 190 try { 191 doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); 192 } catch (RemoteException e) { 193 fail("registerUidObserver threw exception: " + e.getMessage()); 194 } 195 // Called by QuotaTracker 196 doReturn(mock(SystemServiceManager.class)) 197 .when(() -> LocalServices.getService(SystemServiceManager.class)); 198 199 JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); 200 JobSchedulerService.sElapsedRealtimeClock = 201 Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); 202 // Make sure the uptime is at least 24 hours so that tests that rely on high uptime work. 203 sUptimeMillisClock = getAdvancedClock(sUptimeMillisClock, 24 * HOUR_IN_MILLIS); 204 // Called by DeviceIdlenessTracker 205 when(mContext.getSystemService(UiModeManager.class)).thenReturn(mock(UiModeManager.class)); 206 207 setChargingPolicy(Integer.MIN_VALUE); 208 209 ArgumentCaptor<ChargingPolicyChangeListener> chargingPolicyChangeListenerCaptor = 210 ArgumentCaptor.forClass(ChargingPolicyChangeListener.class); 211 212 mService = new TestJobSchedulerService(mContext); 213 mService.waitOnAsyncLoadingForTesting(); 214 215 verify(mBatteryManagerInternal).registerChargingPolicyChangeListener( 216 chargingPolicyChangeListenerCaptor.capture()); 217 mChargingPolicyChangeListener = chargingPolicyChangeListenerCaptor.getValue(); 218 } 219 220 @After tearDown()221 public void tearDown() { 222 if (mMockingSession != null) { 223 mMockingSession.finishMocking(); 224 } 225 mService.cancelJobsForUid(TEST_UID, true, 226 JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN, 227 "test cleanup"); 228 } 229 getAdvancedClock(Clock clock, long incrementMs)230 private Clock getAdvancedClock(Clock clock, long incrementMs) { 231 return Clock.offset(clock, Duration.ofMillis(incrementMs)); 232 } 233 advanceElapsedClock(long incrementMs)234 private void advanceElapsedClock(long incrementMs) { 235 JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock( 236 JobSchedulerService.sElapsedRealtimeClock, incrementMs); 237 } 238 createJobInfo()239 private static JobInfo.Builder createJobInfo() { 240 return createJobInfo(351); 241 } 242 createJobInfo(int jobId)243 private static JobInfo.Builder createJobInfo(int jobId) { 244 return new JobInfo.Builder(jobId, new ComponentName("foo", "bar")); 245 } 246 createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder)247 private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder) { 248 return createJobStatus(testTag, jobInfoBuilder, 1234); 249 } 250 createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, int callingUid)251 private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, 252 int callingUid) { 253 return createJobStatus(testTag, jobInfoBuilder, callingUid, "com.android.test"); 254 } 255 createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, int callingUid, String sourcePkg)256 private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder, 257 int callingUid, String sourcePkg) { 258 return JobStatus.createFromJobInfo( 259 jobInfoBuilder.build(), callingUid, sourcePkg, 0, "JSSTest", testTag); 260 } 261 grantRunUserInitiatedJobsPermission(boolean grant)262 private void grantRunUserInitiatedJobsPermission(boolean grant) { 263 final int permissionStatus = grant 264 ? PermissionChecker.PERMISSION_GRANTED : PermissionChecker.PERMISSION_HARD_DENIED; 265 doReturn(permissionStatus) 266 .when(() -> PermissionChecker.checkPermissionForPreflight( 267 any(), eq(android.Manifest.permission.RUN_USER_INITIATED_JOBS), 268 anyInt(), anyInt(), anyString())); 269 } 270 271 @Test testGetMinJobExecutionGuaranteeMs()272 public void testGetMinJobExecutionGuaranteeMs() { 273 JobStatus ejMax = createJobStatus("testGetMinJobExecutionGuaranteeMs", 274 createJobInfo(1).setExpedited(true)); 275 JobStatus ejHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", 276 createJobInfo(2).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); 277 JobStatus ejMaxDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", 278 createJobInfo(3).setExpedited(true)); 279 JobStatus ejHighDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", 280 createJobInfo(4).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); 281 JobStatus jobHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", 282 createJobInfo(5).setPriority(JobInfo.PRIORITY_HIGH)); 283 JobStatus jobDef = createJobStatus("testGetMinJobExecutionGuaranteeMs", 284 createJobInfo(6)); 285 JobStatus jobUIDT = createJobStatus("testGetMinJobExecutionGuaranteeMs", 286 createJobInfo(9) 287 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 288 289 spyOn(ejMax); 290 spyOn(ejHigh); 291 spyOn(ejMaxDowngraded); 292 spyOn(ejHighDowngraded); 293 spyOn(jobHigh); 294 spyOn(jobDef); 295 spyOn(jobUIDT); 296 297 when(ejMax.shouldTreatAsExpeditedJob()).thenReturn(true); 298 when(ejHigh.shouldTreatAsExpeditedJob()).thenReturn(true); 299 when(ejMaxDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); 300 when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); 301 when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false); 302 when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false); 303 when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true); 304 305 ConnectivityController connectivityController = mService.getConnectivityController(); 306 spyOn(connectivityController); 307 mService.mConstants.RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS; 308 mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS = 2 * HOUR_IN_MILLIS; 309 mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 1.5f; 310 mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = HOUR_IN_MILLIS; 311 mService.mConstants.RUNTIME_UI_LIMIT_MS = 6 * HOUR_IN_MILLIS; 312 313 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 314 mService.getMinJobExecutionGuaranteeMs(ejMax)); 315 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 316 mService.getMinJobExecutionGuaranteeMs(ejHigh)); 317 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 318 mService.getMinJobExecutionGuaranteeMs(ejMaxDowngraded)); 319 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 320 mService.getMinJobExecutionGuaranteeMs(ejHighDowngraded)); 321 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 322 mService.getMinJobExecutionGuaranteeMs(jobHigh)); 323 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 324 mService.getMinJobExecutionGuaranteeMs(jobDef)); 325 // UserInitiated 326 grantRunUserInitiatedJobsPermission(false); 327 // Permission isn't granted, so it should just be treated as a regular job. 328 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 329 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 330 331 grantRunUserInitiatedJobsPermission(true); // With permission 332 mService.mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = true; 333 doReturn(ConnectivityController.UNKNOWN_TIME) 334 .when(connectivityController).getEstimatedTransferTimeMs(any()); 335 assertEquals(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, 336 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 337 doReturn(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS / 2) 338 .when(connectivityController).getEstimatedTransferTimeMs(any()); 339 assertEquals(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, 340 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 341 final long estimatedTransferTimeMs = 342 mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS * 2; 343 doReturn(estimatedTransferTimeMs) 344 .when(connectivityController).getEstimatedTransferTimeMs(any()); 345 assertEquals((long) (estimatedTransferTimeMs 346 * mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR), 347 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 348 doReturn(mService.mConstants.RUNTIME_UI_LIMIT_MS * 2) 349 .when(connectivityController).getEstimatedTransferTimeMs(any()); 350 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 351 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 352 353 mService.mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false; 354 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 355 mService.getMinJobExecutionGuaranteeMs(jobUIDT)); 356 } 357 358 @Test testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_disabled()359 public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_disabled() { 360 JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 361 createJobInfo(1) 362 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 363 JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 364 createJobInfo(2).setExpedited(true)); 365 JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 366 createJobInfo(3)); 367 spyOn(jobUij); 368 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 369 jobUij.startedAsUserInitiatedJob = true; 370 spyOn(jobEj); 371 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 372 jobEj.startedAsExpeditedJob = true; 373 374 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; 375 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 376 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 377 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 378 mService.updateQuotaTracker(); 379 mService.resetScheduleQuota(); 380 381 // Safeguards disabled -> no penalties. 382 grantRunUserInitiatedJobsPermission(true); 383 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 384 mService.getMinJobExecutionGuaranteeMs(jobUij)); 385 grantRunUserInitiatedJobsPermission(false); 386 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 387 mService.getMinJobExecutionGuaranteeMs(jobUij)); 388 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 389 mService.getMinJobExecutionGuaranteeMs(jobEj)); 390 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 391 mService.getMinJobExecutionGuaranteeMs(jobReg)); 392 393 // 1 UIJ timeout. No max execution penalty yet. 394 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 395 grantRunUserInitiatedJobsPermission(true); 396 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 397 mService.getMinJobExecutionGuaranteeMs(jobUij)); 398 grantRunUserInitiatedJobsPermission(false); 399 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 400 mService.getMinJobExecutionGuaranteeMs(jobUij)); 401 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 402 mService.getMinJobExecutionGuaranteeMs(jobEj)); 403 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 404 mService.getMinJobExecutionGuaranteeMs(jobReg)); 405 406 // 2 UIJ timeouts. Safeguards disabled -> no penalties. 407 jobUij.madeActive = 408 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 409 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 410 grantRunUserInitiatedJobsPermission(true); 411 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 412 mService.getMinJobExecutionGuaranteeMs(jobUij)); 413 grantRunUserInitiatedJobsPermission(false); 414 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 415 mService.getMinJobExecutionGuaranteeMs(jobUij)); 416 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 417 mService.getMinJobExecutionGuaranteeMs(jobEj)); 418 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 419 mService.getMinJobExecutionGuaranteeMs(jobReg)); 420 421 // 1 EJ timeout. No max execution penalty yet. 422 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 423 grantRunUserInitiatedJobsPermission(true); 424 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 425 mService.getMinJobExecutionGuaranteeMs(jobUij)); 426 grantRunUserInitiatedJobsPermission(false); 427 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 428 mService.getMinJobExecutionGuaranteeMs(jobUij)); 429 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 430 mService.getMinJobExecutionGuaranteeMs(jobEj)); 431 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 432 mService.getMinJobExecutionGuaranteeMs(jobReg)); 433 434 // 2 EJ timeouts. Safeguards disabled -> no penalties. 435 jobEj.madeActive = 436 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 437 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 438 grantRunUserInitiatedJobsPermission(true); 439 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 440 mService.getMinJobExecutionGuaranteeMs(jobUij)); 441 grantRunUserInitiatedJobsPermission(false); 442 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 443 mService.getMinJobExecutionGuaranteeMs(jobUij)); 444 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 445 mService.getMinJobExecutionGuaranteeMs(jobEj)); 446 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 447 mService.getMinJobExecutionGuaranteeMs(jobReg)); 448 449 // 1 reg timeout. No max execution penalty yet. 450 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 451 grantRunUserInitiatedJobsPermission(true); 452 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 453 mService.getMinJobExecutionGuaranteeMs(jobUij)); 454 grantRunUserInitiatedJobsPermission(false); 455 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 456 mService.getMinJobExecutionGuaranteeMs(jobUij)); 457 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 458 mService.getMinJobExecutionGuaranteeMs(jobEj)); 459 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 460 mService.getMinJobExecutionGuaranteeMs(jobReg)); 461 462 // 2 Reg timeouts. Safeguards disabled -> no penalties. 463 jobReg.madeActive = 464 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 465 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 466 grantRunUserInitiatedJobsPermission(true); 467 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 468 mService.getMinJobExecutionGuaranteeMs(jobUij)); 469 grantRunUserInitiatedJobsPermission(false); 470 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 471 mService.getMinJobExecutionGuaranteeMs(jobUij)); 472 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 473 mService.getMinJobExecutionGuaranteeMs(jobEj)); 474 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 475 mService.getMinJobExecutionGuaranteeMs(jobReg)); 476 } 477 478 @Test testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_enabled()479 public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_enabled() { 480 JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 481 createJobInfo(1) 482 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 483 JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 484 createJobInfo(2).setExpedited(true)); 485 JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", 486 createJobInfo(3)); 487 spyOn(jobUij); 488 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 489 jobUij.startedAsUserInitiatedJob = true; 490 spyOn(jobEj); 491 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 492 jobEj.startedAsExpeditedJob = true; 493 494 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; 495 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 496 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 497 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 498 mService.updateQuotaTracker(); 499 mService.resetScheduleQuota(); 500 501 // No timeouts -> no penalties. 502 grantRunUserInitiatedJobsPermission(true); 503 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 504 mService.getMinJobExecutionGuaranteeMs(jobUij)); 505 grantRunUserInitiatedJobsPermission(false); 506 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 507 mService.getMinJobExecutionGuaranteeMs(jobUij)); 508 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 509 mService.getMinJobExecutionGuaranteeMs(jobEj)); 510 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 511 mService.getMinJobExecutionGuaranteeMs(jobReg)); 512 513 // 1 UIJ timeout. No execution penalty yet. 514 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 515 grantRunUserInitiatedJobsPermission(true); 516 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 517 mService.getMinJobExecutionGuaranteeMs(jobUij)); 518 grantRunUserInitiatedJobsPermission(false); 519 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 520 mService.getMinJobExecutionGuaranteeMs(jobUij)); 521 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 522 mService.getMinJobExecutionGuaranteeMs(jobEj)); 523 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 524 mService.getMinJobExecutionGuaranteeMs(jobReg)); 525 526 // Not a timeout -> 1 UIJ timeout. No execution penalty yet. 527 jobUij.madeActive = sUptimeMillisClock.millis() - 1; 528 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 529 grantRunUserInitiatedJobsPermission(true); 530 assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 531 mService.getMinJobExecutionGuaranteeMs(jobUij)); 532 grantRunUserInitiatedJobsPermission(false); 533 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 534 mService.getMinJobExecutionGuaranteeMs(jobUij)); 535 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 536 mService.getMinJobExecutionGuaranteeMs(jobEj)); 537 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 538 mService.getMinJobExecutionGuaranteeMs(jobReg)); 539 540 // 2 UIJ timeouts. Min execution penalty only for UIJs. 541 jobUij.madeActive = 542 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 543 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 544 grantRunUserInitiatedJobsPermission(true); 545 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 546 mService.getMinJobExecutionGuaranteeMs(jobUij)); 547 grantRunUserInitiatedJobsPermission(false); 548 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 549 mService.getMinJobExecutionGuaranteeMs(jobUij)); 550 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 551 mService.getMinJobExecutionGuaranteeMs(jobEj)); 552 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 553 mService.getMinJobExecutionGuaranteeMs(jobReg)); 554 555 // 1 EJ timeout. No max execution penalty yet. 556 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 557 grantRunUserInitiatedJobsPermission(true); 558 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 559 mService.getMinJobExecutionGuaranteeMs(jobUij)); 560 grantRunUserInitiatedJobsPermission(false); 561 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 562 mService.getMinJobExecutionGuaranteeMs(jobUij)); 563 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 564 mService.getMinJobExecutionGuaranteeMs(jobEj)); 565 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 566 mService.getMinJobExecutionGuaranteeMs(jobReg)); 567 568 // 2 EJ timeouts. Max execution penalty for EJs. 569 jobEj.madeActive = 570 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 571 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 572 grantRunUserInitiatedJobsPermission(true); 573 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 574 mService.getMinJobExecutionGuaranteeMs(jobUij)); 575 grantRunUserInitiatedJobsPermission(false); 576 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 577 mService.getMinJobExecutionGuaranteeMs(jobUij)); 578 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 579 mService.getMinJobExecutionGuaranteeMs(jobEj)); 580 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 581 mService.getMinJobExecutionGuaranteeMs(jobReg)); 582 583 // 1 reg timeout. No max execution penalty yet. 584 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 585 grantRunUserInitiatedJobsPermission(true); 586 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 587 mService.getMinJobExecutionGuaranteeMs(jobUij)); 588 grantRunUserInitiatedJobsPermission(false); 589 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 590 mService.getMinJobExecutionGuaranteeMs(jobUij)); 591 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 592 mService.getMinJobExecutionGuaranteeMs(jobEj)); 593 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 594 mService.getMinJobExecutionGuaranteeMs(jobReg)); 595 596 // 2 Reg timeouts. Max execution penalty for regular jobs. 597 jobReg.madeActive = 598 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 599 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 600 grantRunUserInitiatedJobsPermission(true); 601 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 602 mService.getMinJobExecutionGuaranteeMs(jobUij)); 603 grantRunUserInitiatedJobsPermission(false); 604 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 605 mService.getMinJobExecutionGuaranteeMs(jobUij)); 606 assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 607 mService.getMinJobExecutionGuaranteeMs(jobEj)); 608 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 609 mService.getMinJobExecutionGuaranteeMs(jobReg)); 610 } 611 612 @Test testGetMaxJobExecutionTimeMs()613 public void testGetMaxJobExecutionTimeMs() { 614 JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs", 615 createJobInfo(10) 616 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 617 JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs", 618 createJobInfo(2).setExpedited(true)); 619 JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs", 620 createJobInfo(3)); 621 spyOn(jobUIDT); 622 when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true); 623 spyOn(jobEj); 624 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 625 626 QuotaController quotaController = mService.getQuotaController(); 627 spyOn(quotaController); 628 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 629 .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); 630 631 grantRunUserInitiatedJobsPermission(true); 632 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 633 mService.getMaxJobExecutionTimeMs(jobUIDT)); 634 grantRunUserInitiatedJobsPermission(false); 635 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 636 mService.getMaxJobExecutionTimeMs(jobUIDT)); 637 638 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 639 mService.getMaxJobExecutionTimeMs(jobEj)); 640 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 641 mService.getMaxJobExecutionTimeMs(jobReg)); 642 } 643 644 @Test testGetMaxJobExecutionTimeMs_timeoutSafeguards_disabled()645 public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_disabled() { 646 JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 647 createJobInfo(1) 648 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 649 JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 650 createJobInfo(2).setExpedited(true)); 651 JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 652 createJobInfo(3)); 653 spyOn(jobUij); 654 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 655 jobUij.startedAsUserInitiatedJob = true; 656 spyOn(jobEj); 657 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 658 jobEj.startedAsExpeditedJob = true; 659 660 QuotaController quotaController = mService.getQuotaController(); 661 spyOn(quotaController); 662 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 663 .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); 664 665 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; 666 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 667 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 668 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 669 mService.updateQuotaTracker(); 670 mService.resetScheduleQuota(); 671 672 // Safeguards disabled -> no penalties. 673 grantRunUserInitiatedJobsPermission(true); 674 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 675 mService.getMaxJobExecutionTimeMs(jobUij)); 676 grantRunUserInitiatedJobsPermission(false); 677 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 678 mService.getMaxJobExecutionTimeMs(jobUij)); 679 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 680 mService.getMaxJobExecutionTimeMs(jobEj)); 681 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 682 mService.getMaxJobExecutionTimeMs(jobReg)); 683 684 // 1 UIJ timeout. No max execution penalty yet. 685 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 686 grantRunUserInitiatedJobsPermission(true); 687 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 688 mService.getMaxJobExecutionTimeMs(jobUij)); 689 grantRunUserInitiatedJobsPermission(false); 690 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 691 mService.getMaxJobExecutionTimeMs(jobUij)); 692 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 693 mService.getMaxJobExecutionTimeMs(jobEj)); 694 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 695 mService.getMaxJobExecutionTimeMs(jobReg)); 696 697 // 2 UIJ timeouts. Safeguards disabled -> no penalties. 698 jobUij.madeActive = 699 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 700 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 701 grantRunUserInitiatedJobsPermission(true); 702 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 703 mService.getMaxJobExecutionTimeMs(jobUij)); 704 grantRunUserInitiatedJobsPermission(false); 705 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 706 mService.getMaxJobExecutionTimeMs(jobUij)); 707 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 708 mService.getMaxJobExecutionTimeMs(jobEj)); 709 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 710 mService.getMaxJobExecutionTimeMs(jobReg)); 711 712 // 1 EJ timeout. No max execution penalty yet. 713 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 714 grantRunUserInitiatedJobsPermission(true); 715 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 716 mService.getMaxJobExecutionTimeMs(jobUij)); 717 grantRunUserInitiatedJobsPermission(false); 718 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 719 mService.getMaxJobExecutionTimeMs(jobUij)); 720 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 721 mService.getMaxJobExecutionTimeMs(jobEj)); 722 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 723 mService.getMaxJobExecutionTimeMs(jobReg)); 724 725 // 2 EJ timeouts. Safeguards disabled -> no penalties. 726 jobEj.madeActive = 727 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 728 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 729 grantRunUserInitiatedJobsPermission(true); 730 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 731 mService.getMaxJobExecutionTimeMs(jobUij)); 732 grantRunUserInitiatedJobsPermission(false); 733 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 734 mService.getMaxJobExecutionTimeMs(jobUij)); 735 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 736 mService.getMaxJobExecutionTimeMs(jobEj)); 737 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 738 mService.getMaxJobExecutionTimeMs(jobReg)); 739 740 // 1 reg timeout. No max execution penalty yet. 741 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 742 grantRunUserInitiatedJobsPermission(true); 743 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 744 mService.getMaxJobExecutionTimeMs(jobUij)); 745 grantRunUserInitiatedJobsPermission(false); 746 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 747 mService.getMaxJobExecutionTimeMs(jobUij)); 748 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 749 mService.getMaxJobExecutionTimeMs(jobEj)); 750 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 751 mService.getMaxJobExecutionTimeMs(jobReg)); 752 753 // 2 Reg timeouts. Safeguards disabled -> no penalties. 754 jobReg.madeActive = 755 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 756 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 757 grantRunUserInitiatedJobsPermission(true); 758 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 759 mService.getMaxJobExecutionTimeMs(jobUij)); 760 grantRunUserInitiatedJobsPermission(false); 761 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 762 mService.getMaxJobExecutionTimeMs(jobUij)); 763 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 764 mService.getMaxJobExecutionTimeMs(jobEj)); 765 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 766 mService.getMaxJobExecutionTimeMs(jobReg)); 767 } 768 769 @Test testGetMaxJobExecutionTimeMs_timeoutSafeguards_enabled()770 public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_enabled() { 771 JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 772 createJobInfo(1) 773 .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 774 JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 775 createJobInfo(2).setExpedited(true)); 776 JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", 777 createJobInfo(3)); 778 spyOn(jobUij); 779 when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); 780 jobUij.startedAsUserInitiatedJob = true; 781 spyOn(jobEj); 782 when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); 783 jobEj.startedAsExpeditedJob = true; 784 785 QuotaController quotaController = mService.getQuotaController(); 786 spyOn(quotaController); 787 doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 788 .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); 789 790 mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; 791 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 792 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; 793 mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; 794 mService.updateQuotaTracker(); 795 mService.resetScheduleQuota(); 796 797 // No timeouts -> no penalties. 798 grantRunUserInitiatedJobsPermission(true); 799 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 800 mService.getMaxJobExecutionTimeMs(jobUij)); 801 grantRunUserInitiatedJobsPermission(false); 802 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 803 mService.getMaxJobExecutionTimeMs(jobUij)); 804 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 805 mService.getMaxJobExecutionTimeMs(jobEj)); 806 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 807 mService.getMaxJobExecutionTimeMs(jobReg)); 808 809 // 1 UIJ timeout. No max execution penalty yet. 810 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 811 grantRunUserInitiatedJobsPermission(true); 812 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 813 mService.getMaxJobExecutionTimeMs(jobUij)); 814 grantRunUserInitiatedJobsPermission(false); 815 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 816 mService.getMaxJobExecutionTimeMs(jobUij)); 817 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 818 mService.getMaxJobExecutionTimeMs(jobEj)); 819 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 820 mService.getMaxJobExecutionTimeMs(jobReg)); 821 822 // Not a timeout -> 1 UIJ timeout. No max execution penalty yet. 823 jobUij.madeActive = sUptimeMillisClock.millis() - 1; 824 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 825 grantRunUserInitiatedJobsPermission(true); 826 assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, 827 mService.getMaxJobExecutionTimeMs(jobUij)); 828 grantRunUserInitiatedJobsPermission(false); 829 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 830 mService.getMaxJobExecutionTimeMs(jobUij)); 831 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 832 mService.getMaxJobExecutionTimeMs(jobEj)); 833 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 834 mService.getMaxJobExecutionTimeMs(jobReg)); 835 836 // 2 UIJ timeouts. Max execution penalty only for UIJs. 837 jobUij.madeActive = 838 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 839 mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 840 grantRunUserInitiatedJobsPermission(true); 841 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 842 mService.getMaxJobExecutionTimeMs(jobUij)); 843 grantRunUserInitiatedJobsPermission(false); 844 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 845 mService.getMaxJobExecutionTimeMs(jobUij)); 846 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 847 mService.getMaxJobExecutionTimeMs(jobEj)); 848 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 849 mService.getMaxJobExecutionTimeMs(jobReg)); 850 851 // 1 EJ timeout. No max execution penalty yet. 852 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 853 grantRunUserInitiatedJobsPermission(true); 854 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 855 mService.getMaxJobExecutionTimeMs(jobUij)); 856 grantRunUserInitiatedJobsPermission(false); 857 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 858 mService.getMaxJobExecutionTimeMs(jobUij)); 859 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 860 mService.getMaxJobExecutionTimeMs(jobEj)); 861 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 862 mService.getMaxJobExecutionTimeMs(jobReg)); 863 864 // Not a timeout -> 1 EJ timeout. No max execution penalty yet. 865 jobEj.madeActive = sUptimeMillisClock.millis() - 1; 866 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 867 grantRunUserInitiatedJobsPermission(true); 868 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 869 mService.getMaxJobExecutionTimeMs(jobUij)); 870 grantRunUserInitiatedJobsPermission(false); 871 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 872 mService.getMaxJobExecutionTimeMs(jobUij)); 873 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 874 mService.getMaxJobExecutionTimeMs(jobEj)); 875 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 876 mService.getMaxJobExecutionTimeMs(jobReg)); 877 878 // 2 EJ timeouts. Max execution penalty for EJs. 879 jobEj.madeActive = 880 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 881 mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 882 grantRunUserInitiatedJobsPermission(true); 883 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 884 mService.getMaxJobExecutionTimeMs(jobUij)); 885 grantRunUserInitiatedJobsPermission(false); 886 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 887 mService.getMaxJobExecutionTimeMs(jobUij)); 888 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 889 mService.getMaxJobExecutionTimeMs(jobEj)); 890 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 891 mService.getMaxJobExecutionTimeMs(jobReg)); 892 893 // 1 reg timeout. No max execution penalty yet. 894 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 895 grantRunUserInitiatedJobsPermission(true); 896 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 897 mService.getMaxJobExecutionTimeMs(jobUij)); 898 grantRunUserInitiatedJobsPermission(false); 899 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 900 mService.getMaxJobExecutionTimeMs(jobUij)); 901 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 902 mService.getMaxJobExecutionTimeMs(jobEj)); 903 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 904 mService.getMaxJobExecutionTimeMs(jobReg)); 905 906 // Not a timeout -> 1 reg timeout. No max execution penalty yet. 907 jobReg.madeActive = sUptimeMillisClock.millis() - 1; 908 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 909 grantRunUserInitiatedJobsPermission(true); 910 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 911 mService.getMaxJobExecutionTimeMs(jobUij)); 912 grantRunUserInitiatedJobsPermission(false); 913 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 914 mService.getMaxJobExecutionTimeMs(jobUij)); 915 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 916 mService.getMaxJobExecutionTimeMs(jobEj)); 917 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 918 mService.getMaxJobExecutionTimeMs(jobReg)); 919 920 // 2 Reg timeouts. Max execution penalty for regular jobs. 921 jobReg.madeActive = 922 sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; 923 mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 924 grantRunUserInitiatedJobsPermission(true); 925 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 926 mService.getMaxJobExecutionTimeMs(jobUij)); 927 grantRunUserInitiatedJobsPermission(false); 928 assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 929 mService.getMaxJobExecutionTimeMs(jobUij)); 930 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 931 mService.getMaxJobExecutionTimeMs(jobEj)); 932 assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, 933 mService.getMaxJobExecutionTimeMs(jobReg)); 934 } 935 936 /** 937 * Confirm that 938 * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} 939 * returns a job that is no longer allowed to run as a user-initiated job after it hits 940 * the cumulative execution limit. 941 */ 942 @Test testGetRescheduleJobForFailure_cumulativeExecution()943 public void testGetRescheduleJobForFailure_cumulativeExecution() { 944 JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", 945 createJobInfo() 946 .setUserInitiated(true) 947 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 948 assertTrue(originalJob.shouldTreatAsUserInitiatedJob()); 949 950 // Cumulative time = 0 951 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, 952 JobParameters.STOP_REASON_UNDEFINED, 953 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 954 assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); 955 956 // Cumulative time = 50% of limit 957 rescheduledJob.incrementCumulativeExecutionTime( 958 mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2); 959 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 960 JobParameters.STOP_REASON_UNDEFINED, 961 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 962 assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); 963 964 // Cumulative time = 99.999999% of limit 965 rescheduledJob.incrementCumulativeExecutionTime( 966 mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2 - 1); 967 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 968 JobParameters.STOP_REASON_UNDEFINED, 969 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 970 assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); 971 972 // Cumulative time = 100+% of limit 973 rescheduledJob.incrementCumulativeExecutionTime(2); 974 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 975 JobParameters.STOP_REASON_UNDEFINED, 976 JobParameters.INTERNAL_STOP_REASON_UNKNOWN); 977 assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); 978 } 979 980 /** 981 * Confirm that 982 * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} 983 * returns a job with the correct delay and deadline constraints. 984 */ 985 @Test testGetRescheduleJobForFailure_timingCalculations()986 public void testGetRescheduleJobForFailure_timingCalculations() { 987 final long nowElapsed = sElapsedRealtimeClock.millis(); 988 final long initialBackoffMs = MINUTE_IN_MILLIS; 989 mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3; 990 991 JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", 992 createJobInfo() 993 .setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR)); 994 assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime()); 995 assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed()); 996 997 // failure = 0, systemStop = 1 998 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, 999 JobParameters.STOP_REASON_DEVICE_STATE, 1000 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1001 assertEquals(JobStatus.NO_EARLIEST_RUNTIME, rescheduledJob.getEarliestRunTime()); 1002 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1003 1004 // failure = 0, systemStop = 2 1005 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1006 JobParameters.STOP_REASON_DEVICE_STATE, 1007 JobParameters.INTERNAL_STOP_REASON_PREEMPT); 1008 assertEquals(JobStatus.NO_EARLIEST_RUNTIME, rescheduledJob.getEarliestRunTime()); 1009 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1010 // failure = 0, systemStop = 3 1011 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1012 JobParameters.STOP_REASON_CONSTRAINT_CHARGING, 1013 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED); 1014 assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime()); 1015 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1016 1017 // failure = 0, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 1018 for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) { 1019 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1020 JobParameters.STOP_REASON_SYSTEM_PROCESSING, 1021 JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED); 1022 } 1023 assertEquals(nowElapsed + 2 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 1024 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1025 1026 // failure = 1, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 1027 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1028 JobParameters.STOP_REASON_TIMEOUT, 1029 JobParameters.INTERNAL_STOP_REASON_TIMEOUT); 1030 assertEquals(nowElapsed + 3 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 1031 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1032 1033 // failure = 2, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 1034 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1035 JobParameters.STOP_REASON_UNDEFINED, 1036 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1037 assertEquals(nowElapsed + 4 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 1038 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1039 1040 // failure = 3, systemStop = 2 * SYSTEM_STOP_TO_FAILURE_RATIO 1041 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1042 JobParameters.STOP_REASON_UNDEFINED, 1043 JobParameters.INTERNAL_STOP_REASON_ANR); 1044 assertEquals(nowElapsed + 5 * initialBackoffMs, rescheduledJob.getEarliestRunTime()); 1045 assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); 1046 } 1047 1048 /** 1049 * Confirm that 1050 * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} 1051 * returns a job that is correctly marked as demoted by the user. 1052 */ 1053 @Test testGetRescheduleJobForFailure_userDemotion()1054 public void testGetRescheduleJobForFailure_userDemotion() { 1055 JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo()); 1056 assertEquals(0, originalJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1057 1058 // Reschedule for a non-user reason 1059 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, 1060 JobParameters.STOP_REASON_DEVICE_STATE, 1061 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1062 assertEquals(0, 1063 rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1064 1065 // Reschedule for a user reason 1066 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1067 JobParameters.STOP_REASON_USER, 1068 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1069 assertNotEquals(0, 1070 rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1071 1072 // Reschedule a previously demoted job for a non-user reason 1073 rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, 1074 JobParameters.STOP_REASON_CONSTRAINT_CHARGING, 1075 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED); 1076 assertNotEquals(0, 1077 rescheduledJob.getInternalFlags() & JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 1078 } 1079 1080 /** 1081 * Confirm that 1082 * returns {@code null} when for user-visible jobs stopped by the user. 1083 */ 1084 @Test testGetRescheduleJobForFailure_userStopped()1085 public void testGetRescheduleJobForFailure_userStopped() { 1086 JobStatus uiJob = createJobStatus("testGetRescheduleJobForFailure", 1087 createJobInfo().setUserInitiated(true) 1088 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1089 JobStatus uvJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo()); 1090 spyOn(uvJob); 1091 doReturn(true).when(uvJob).isUserVisibleJob(); 1092 JobStatus regJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo()); 1093 1094 // Reschedule for a non-user reason 1095 JobStatus rescheduledUiJob = mService.getRescheduleJobForFailureLocked(uiJob, 1096 JobParameters.STOP_REASON_DEVICE_STATE, 1097 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1098 JobStatus rescheduledUvJob = mService.getRescheduleJobForFailureLocked(uvJob, 1099 JobParameters.STOP_REASON_DEVICE_STATE, 1100 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1101 JobStatus rescheduledRegJob = mService.getRescheduleJobForFailureLocked(regJob, 1102 JobParameters.STOP_REASON_DEVICE_STATE, 1103 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 1104 assertNotNull(rescheduledUiJob); 1105 assertNotNull(rescheduledUvJob); 1106 assertNotNull(rescheduledRegJob); 1107 1108 // Reschedule for a user reason. The user-visible jobs shouldn't be rescheduled. 1109 spyOn(rescheduledUvJob); 1110 doReturn(true).when(rescheduledUvJob).isUserVisibleJob(); 1111 rescheduledUiJob = mService.getRescheduleJobForFailureLocked(rescheduledUiJob, 1112 JobParameters.STOP_REASON_USER, 1113 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1114 rescheduledUvJob = mService.getRescheduleJobForFailureLocked(rescheduledUvJob, 1115 JobParameters.STOP_REASON_USER, 1116 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1117 rescheduledRegJob = mService.getRescheduleJobForFailureLocked(rescheduledRegJob, 1118 JobParameters.STOP_REASON_USER, 1119 JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP); 1120 assertNull(rescheduledUiJob); 1121 assertNull(rescheduledUvJob); 1122 assertNotNull(rescheduledRegJob); 1123 } 1124 1125 /** 1126 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1127 * with the correct delay and deadline constraints if the periodic job is scheduled with the 1128 * minimum possible period. 1129 */ 1130 @Test testGetRescheduleJobForPeriodic_minPeriod()1131 public void testGetRescheduleJobForPeriodic_minPeriod() { 1132 final long now = sElapsedRealtimeClock.millis(); 1133 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow", 1134 createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS)); 1135 final long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS; 1136 final long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS; 1137 1138 for (int i = 0; i < 25; i++) { 1139 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1140 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1141 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1142 advanceElapsedClock(30_000); // 30 seconds 1143 } 1144 1145 for (int i = 0; i < 5; i++) { 1146 // Window buffering in last 1/6 of window. 1147 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1148 assertEquals(nextWindowStartTime + i * 30_000, rescheduledJob.getEarliestRunTime()); 1149 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1150 advanceElapsedClock(30_000); // 30 seconds 1151 } 1152 } 1153 1154 /** 1155 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1156 * with the correct delay and deadline constraints if the periodic job is scheduled with a 1157 * period that's too large. 1158 */ 1159 @Test testGetRescheduleJobForPeriodic_largePeriod()1160 public void testGetRescheduleJobForPeriodic_largePeriod() { 1161 final long now = sElapsedRealtimeClock.millis(); 1162 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow", 1163 createJobInfo().setPeriodic(2 * 365 * DAY_IN_MILLIS)); 1164 assertEquals(now, job.getEarliestRunTime()); 1165 // Periods are capped at 365 days (1 year). 1166 assertEquals(now + 365 * DAY_IN_MILLIS, job.getLatestRunTimeElapsed()); 1167 final long nextWindowStartTime = now + 365 * DAY_IN_MILLIS; 1168 final long nextWindowEndTime = nextWindowStartTime + 365 * DAY_IN_MILLIS; 1169 1170 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1171 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1172 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1173 } 1174 1175 /** 1176 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1177 * with the correct delay and deadline constraints if the periodic job is completed and 1178 * rescheduled while run in its expected running window. 1179 */ 1180 @Test testGetRescheduleJobForPeriodic_insideWindow()1181 public void testGetRescheduleJobForPeriodic_insideWindow() { 1182 final long now = sElapsedRealtimeClock.millis(); 1183 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow", 1184 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1185 final long nextWindowStartTime = now + HOUR_IN_MILLIS; 1186 final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1187 1188 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1189 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1190 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1191 1192 advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes 1193 1194 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1195 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1196 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1197 1198 advanceElapsedClock(20 * MINUTE_IN_MILLIS); // now + 30 minutes 1199 1200 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1201 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1202 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1203 1204 advanceElapsedClock(25 * MINUTE_IN_MILLIS); // now + 55 minutes 1205 1206 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1207 // Shifted because it's close to the end of the window. 1208 assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, 1209 rescheduledJob.getEarliestRunTime()); 1210 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1211 1212 advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes 1213 1214 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1215 // Shifted because it's close to the end of the window. 1216 assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, 1217 rescheduledJob.getEarliestRunTime()); 1218 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1219 } 1220 1221 /** 1222 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1223 * with an extra delay and correct deadline constraint if the periodic job is completed near the 1224 * end of its expected running window. 1225 */ 1226 @Test testGetRescheduleJobForPeriodic_closeToEndOfWindow()1227 public void testGetRescheduleJobForPeriodic_closeToEndOfWindow() { 1228 JobStatus frequentJob = createJobStatus( 1229 "testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1230 createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS)); 1231 long now = sElapsedRealtimeClock.millis(); 1232 long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS; 1233 long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS; 1234 1235 // At the beginning of the window. Next window should be unaffected. 1236 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); 1237 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1238 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1239 1240 // Halfway through window. Next window should be unaffected. 1241 advanceElapsedClock((long) (7.5 * MINUTE_IN_MILLIS)); 1242 rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); 1243 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1244 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1245 1246 // In last 1/6 of window. Next window start time should be shifted slightly. 1247 advanceElapsedClock(6 * MINUTE_IN_MILLIS); 1248 rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob); 1249 assertEquals(nextWindowStartTime + MINUTE_IN_MILLIS, 1250 rescheduledJob.getEarliestRunTime()); 1251 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1252 1253 JobStatus mediumJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1254 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1255 now = sElapsedRealtimeClock.millis(); 1256 nextWindowStartTime = now + HOUR_IN_MILLIS; 1257 nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1258 1259 // At the beginning of the window. Next window should be unaffected. 1260 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 1261 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1262 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1263 1264 // Halfway through window. Next window should be unaffected. 1265 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1266 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 1267 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1268 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1269 1270 // At the edge 1/6 of window. Next window should be unaffected. 1271 advanceElapsedClock(20 * MINUTE_IN_MILLIS); 1272 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 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(6 * MINUTE_IN_MILLIS); 1278 rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob); 1279 assertEquals(nextWindowStartTime + (6 * MINUTE_IN_MILLIS), 1280 rescheduledJob.getEarliestRunTime()); 1281 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1282 1283 JobStatus longJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1284 createJobInfo().setPeriodic(6 * HOUR_IN_MILLIS)); 1285 now = sElapsedRealtimeClock.millis(); 1286 nextWindowStartTime = now + 6 * HOUR_IN_MILLIS; 1287 nextWindowEndTime = now + 12 * HOUR_IN_MILLIS; 1288 1289 // At the beginning of the window. Next window should be unaffected. 1290 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1291 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1292 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1293 1294 // Halfway through window. Next window should be unaffected. 1295 advanceElapsedClock(3 * HOUR_IN_MILLIS); 1296 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1297 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1298 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1299 1300 // At the edge 1/6 of window. Next window should be unaffected. 1301 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1302 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1303 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1304 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1305 1306 // In last 1/6 of window. Next window should be unaffected since we're over the shift cap. 1307 advanceElapsedClock(15 * MINUTE_IN_MILLIS); 1308 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1309 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1310 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1311 1312 // In last 1/6 of window. Next window start time should be shifted slightly. 1313 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1314 rescheduledJob = mService.getRescheduleJobForPeriodic(longJob); 1315 assertEquals(nextWindowStartTime + (30 * MINUTE_IN_MILLIS), 1316 rescheduledJob.getEarliestRunTime()); 1317 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1318 1319 // Flex duration close to period duration. 1320 JobStatus gameyFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1321 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 59 * MINUTE_IN_MILLIS)); 1322 now = sElapsedRealtimeClock.millis(); 1323 nextWindowStartTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS; 1324 nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1325 advanceElapsedClock(MINUTE_IN_MILLIS); 1326 1327 // At the beginning of the window. Next window should be unaffected. 1328 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1329 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1330 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1331 1332 // Halfway through window. Next window should be unaffected. 1333 advanceElapsedClock(29 * MINUTE_IN_MILLIS); 1334 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1335 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1336 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1337 1338 // At the edge 1/6 of window. Next window should be unaffected. 1339 advanceElapsedClock(20 * MINUTE_IN_MILLIS); 1340 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1341 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1342 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1343 1344 // In last 1/6 of window. Next window start time should be shifted slightly. 1345 advanceElapsedClock(6 * MINUTE_IN_MILLIS); 1346 rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex); 1347 assertEquals(nextWindowStartTime + (5 * MINUTE_IN_MILLIS), 1348 rescheduledJob.getEarliestRunTime()); 1349 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1350 1351 // Very short flex duration compared to period duration. 1352 JobStatus superFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow", 1353 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 10 * MINUTE_IN_MILLIS)); 1354 now = sElapsedRealtimeClock.millis(); 1355 nextWindowStartTime = now + HOUR_IN_MILLIS + 50 * MINUTE_IN_MILLIS; 1356 nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1357 advanceElapsedClock(MINUTE_IN_MILLIS); 1358 1359 // At the beginning of the window. Next window should be unaffected. 1360 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1361 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1362 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1363 1364 // Halfway through window. Next window should be unaffected. 1365 advanceElapsedClock(29 * MINUTE_IN_MILLIS); 1366 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1367 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1368 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1369 1370 // At the edge 1/6 of window. Next window should be unaffected. 1371 advanceElapsedClock(20 * MINUTE_IN_MILLIS); 1372 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1373 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1374 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1375 1376 // In last 1/6 of window. Next window should be unaffected since the flex duration pushes 1377 // the next window start time far enough away. 1378 advanceElapsedClock(6 * MINUTE_IN_MILLIS); 1379 rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex); 1380 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1381 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1382 } 1383 1384 /** 1385 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1386 * with the correct delay and deadline constraints if the periodic job with a custom flex 1387 * setting is completed and rescheduled while run in its expected running window. 1388 */ 1389 @Test testGetRescheduleJobForPeriodic_insideWindow_flex()1390 public void testGetRescheduleJobForPeriodic_insideWindow_flex() { 1391 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_flex", 1392 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); 1393 // First window starts 30 minutes from now. 1394 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1395 final long now = sElapsedRealtimeClock.millis(); 1396 final long nextWindowStartTime = now + HOUR_IN_MILLIS; 1397 final long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; 1398 1399 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1400 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1401 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1402 1403 advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes 1404 1405 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1406 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1407 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1408 1409 advanceElapsedClock(15 * MINUTE_IN_MILLIS); // now + 25 minutes 1410 1411 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1412 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1413 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1414 1415 advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 29 minutes 1416 1417 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1418 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1419 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1420 } 1421 1422 /** 1423 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1424 * with the correct delay and deadline constraints if the periodic job failed but then ran 1425 * successfully and was rescheduled while run in its expected running window. 1426 */ 1427 @Test testGetRescheduleJobForPeriodic_insideWindow_failedJob()1428 public void testGetRescheduleJobForPeriodic_insideWindow_failedJob() { 1429 final long now = sElapsedRealtimeClock.millis(); 1430 final long nextWindowStartTime = now + HOUR_IN_MILLIS; 1431 final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1432 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob", 1433 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1434 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1435 JobParameters.STOP_REASON_UNDEFINED, 1436 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1437 1438 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1439 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1440 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1441 1442 advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes 1443 failedJob = mService.getRescheduleJobForFailureLocked(job, 1444 JobParameters.STOP_REASON_UNDEFINED, 1445 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1446 advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes 1447 1448 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1449 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1450 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1451 1452 advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes 1453 failedJob = mService.getRescheduleJobForFailureLocked(job, 1454 JobParameters.STOP_REASON_UNDEFINED, 1455 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1456 advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes 1457 1458 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1459 // Shifted because it's close to the end of the window. 1460 assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS, 1461 rescheduledJob.getEarliestRunTime()); 1462 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1463 1464 advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes 1465 failedJob = mService.getRescheduleJobForFailureLocked(job, 1466 JobParameters.STOP_REASON_UNDEFINED, 1467 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1468 advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes 1469 1470 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1471 // Shifted because it's close to the end of the window. 1472 assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS, 1473 rescheduledJob.getEarliestRunTime()); 1474 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1475 } 1476 1477 /** 1478 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1479 * with the correct delay and deadline constraints if the periodic job is completed and 1480 * rescheduled when run after its expected running window. 1481 */ 1482 @Test testGetRescheduleJobForPeriodic_outsideWindow()1483 public void testGetRescheduleJobForPeriodic_outsideWindow() { 1484 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow", 1485 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1486 long now = sElapsedRealtimeClock.millis(); 1487 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1488 long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1489 1490 advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS); 1491 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1492 // have consistent windows, so the new window should start as soon as the previous window 1493 // ended and end PERIOD time after the previous window ended. 1494 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1495 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1496 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1497 1498 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1499 // Say that the job ran at this point, possibly due to device idle. 1500 // The next window should be consistent (start and end at the time it would have had the job 1501 // run normally in previous windows). 1502 nextWindowStartTime += 2 * HOUR_IN_MILLIS; 1503 nextWindowEndTime += 2 * HOUR_IN_MILLIS; 1504 1505 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1506 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1507 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1508 } 1509 1510 /** 1511 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1512 * with the correct delay and deadline constraints if the periodic job with a custom flex 1513 * setting is completed and rescheduled when run after its expected running window. 1514 */ 1515 @Test testGetRescheduleJobForPeriodic_outsideWindow_flex()1516 public void testGetRescheduleJobForPeriodic_outsideWindow_flex() { 1517 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_flex", 1518 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); 1519 // First window starts 30 minutes from now. 1520 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1521 long now = sElapsedRealtimeClock.millis(); 1522 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1523 long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; 1524 1525 advanceElapsedClock(31 * MINUTE_IN_MILLIS); 1526 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1527 // have consistent windows, so the new window should start as soon as the previous window 1528 // ended and end PERIOD time after the previous window ended. 1529 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1530 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1531 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1532 1533 // 5 minutes before the start of the next window. It's too close to the next window, so the 1534 // returned job should be for the window after. 1535 advanceElapsedClock(24 * MINUTE_IN_MILLIS); 1536 nextWindowStartTime += HOUR_IN_MILLIS; 1537 nextWindowEndTime += HOUR_IN_MILLIS; 1538 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1539 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1540 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1541 1542 advanceElapsedClock(2 * HOUR_IN_MILLIS + 10 * MINUTE_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(job); 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 failed but then ran 1557 * successfully and was rescheduled when run after its expected running window. 1558 */ 1559 @Test testGetRescheduleJobForPeriodic_outsideWindow_failedJob()1560 public void testGetRescheduleJobForPeriodic_outsideWindow_failedJob() { 1561 JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob", 1562 createJobInfo().setPeriodic(HOUR_IN_MILLIS)); 1563 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1564 JobParameters.STOP_REASON_UNDEFINED, 1565 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1566 long now = sElapsedRealtimeClock.millis(); 1567 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1568 long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; 1569 1570 advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS); 1571 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1572 // have consistent windows, so the new window should start as soon as the previous window 1573 // ended and end PERIOD time after the previous window ended. 1574 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1575 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1576 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1577 1578 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1579 // Say that the job ran at this point, possibly due to device idle. 1580 // The next window should be consistent (start and end at the time it would have had the job 1581 // run normally in previous windows). 1582 nextWindowStartTime += 2 * HOUR_IN_MILLIS; 1583 nextWindowEndTime += 2 * HOUR_IN_MILLIS; 1584 1585 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1586 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1587 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1588 } 1589 1590 /** 1591 * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job 1592 * with the correct delay and deadline constraints if the periodic job with a custom flex 1593 * setting failed but then ran successfully and was rescheduled when run after its expected 1594 * running window. 1595 */ 1596 @Test testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob()1597 public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob() { 1598 JobStatus job = createJobStatus( 1599 "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob", 1600 createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); 1601 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1602 JobParameters.STOP_REASON_UNDEFINED, 1603 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1604 // First window starts 30 minutes from now. 1605 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1606 long now = sElapsedRealtimeClock.millis(); 1607 long nextWindowStartTime = now + HOUR_IN_MILLIS; 1608 long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; 1609 1610 advanceElapsedClock(31 * MINUTE_IN_MILLIS); 1611 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1612 // have consistent windows, so the new window should start as soon as the previous window 1613 // ended and end PERIOD time after the previous window ended. 1614 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1615 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1616 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1617 1618 // 5 minutes before the start of the next window. It's too close to the next window, so the 1619 // returned job should be for the window after. 1620 advanceElapsedClock(24 * MINUTE_IN_MILLIS); 1621 nextWindowStartTime += HOUR_IN_MILLIS; 1622 nextWindowEndTime += HOUR_IN_MILLIS; 1623 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1624 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1625 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1626 1627 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1628 // Say that the job ran at this point, possibly due to device idle. 1629 // The next window should be consistent (start and end at the time it would have had the job 1630 // run normally in previous windows). 1631 nextWindowStartTime += 2 * HOUR_IN_MILLIS; 1632 nextWindowEndTime += 2 * HOUR_IN_MILLIS; 1633 1634 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1635 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1636 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1637 } 1638 1639 @Test testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod()1640 public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod() { 1641 JobStatus job = createJobStatus( 1642 "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod", 1643 createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS)); 1644 JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job, 1645 JobParameters.STOP_REASON_UNDEFINED, 1646 JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); 1647 // First window starts 6.625 days from now. 1648 advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS); 1649 long now = sElapsedRealtimeClock.millis(); 1650 long nextWindowStartTime = now + 7 * DAY_IN_MILLIS; 1651 long nextWindowEndTime = nextWindowStartTime + 9 * HOUR_IN_MILLIS; 1652 1653 advanceElapsedClock(6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS); 1654 // Say the job ran at the very end of its previous window. The intended JSS behavior is to 1655 // have consistent windows, so the new window should start as soon as the previous window 1656 // ended and end PERIOD time after the previous window ended. 1657 JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1658 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1659 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1660 1661 advanceElapsedClock(DAY_IN_MILLIS); 1662 // Say the job ran a day late. Since the period is massive compared to the flex, JSS should 1663 // put the rescheduled job in the original window. 1664 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1665 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1666 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1667 1668 // 1 day before the start of the next window. Given the large period, respect the original 1669 // next window. 1670 advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); 1671 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1672 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1673 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1674 1675 // 1 hour before the start of the next window. It's too close to the next window, so the 1676 // returned job should be for the window after. 1677 long oneHourBeforeNextWindow = 1678 nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; 1679 long fiveMinsBeforeNextWindow = 1680 nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; 1681 advanceElapsedClock(oneHourBeforeNextWindow); 1682 nextWindowStartTime += 7 * DAY_IN_MILLIS; 1683 nextWindowEndTime += 7 * DAY_IN_MILLIS; 1684 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1685 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1686 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1687 1688 // 5 minutes before the start of the next window. It's too close to the next window, so the 1689 // returned job should be for the window after. 1690 advanceElapsedClock(fiveMinsBeforeNextWindow); 1691 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1692 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1693 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1694 1695 advanceElapsedClock(14 * DAY_IN_MILLIS); 1696 // Say that the job ran at this point, probably because the phone was off the entire time. 1697 // The next window should be consistent (start and end at the time it would have had the job 1698 // run normally in previous windows). 1699 nextWindowStartTime += 14 * DAY_IN_MILLIS; 1700 nextWindowEndTime += 14 * DAY_IN_MILLIS; 1701 1702 rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); 1703 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1704 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1705 1706 // Test original job again but with a huge delay from the original execution window 1707 1708 // 1 day before the start of the next window. Given the large period, respect the original 1709 // next window. 1710 advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS); 1711 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1712 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1713 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1714 1715 // 1 hour before the start of the next window. It's too close to the next window, so the 1716 // returned job should be for the window after. 1717 oneHourBeforeNextWindow = 1718 nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS; 1719 fiveMinsBeforeNextWindow = 1720 nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS; 1721 advanceElapsedClock(oneHourBeforeNextWindow); 1722 nextWindowStartTime += 7 * DAY_IN_MILLIS; 1723 nextWindowEndTime += 7 * DAY_IN_MILLIS; 1724 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1725 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1726 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1727 1728 // 5 minutes before the start of the next window. It's too close to the next window, so the 1729 // returned job should be for the window after. 1730 advanceElapsedClock(fiveMinsBeforeNextWindow); 1731 rescheduledJob = mService.getRescheduleJobForPeriodic(job); 1732 assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); 1733 assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); 1734 } 1735 1736 @Test testBatteryStateTrackerRegistersForImportantIntents()1737 public void testBatteryStateTrackerRegistersForImportantIntents() { 1738 verify(mContext).registerReceiver(any(), ArgumentMatchers.argThat(filter -> true 1739 && filter.hasAction(BatteryManager.ACTION_CHARGING) 1740 && filter.hasAction(BatteryManager.ACTION_DISCHARGING) 1741 && filter.hasAction(Intent.ACTION_BATTERY_LEVEL_CHANGED) 1742 && filter.hasAction(Intent.ACTION_BATTERY_LOW) 1743 && filter.hasAction(Intent.ACTION_BATTERY_OKAY) 1744 && filter.hasAction(Intent.ACTION_POWER_CONNECTED) 1745 && filter.hasAction(Intent.ACTION_POWER_DISCONNECTED))); 1746 } 1747 1748 @Test testIsCharging_standardChargingIntent()1749 public void testIsCharging_standardChargingIntent() { 1750 JobSchedulerService.BatteryStateTracker tracker = mService.mBatteryStateTracker; 1751 1752 Intent chargingIntent = new Intent(BatteryManager.ACTION_CHARGING); 1753 Intent dischargingIntent = new Intent(BatteryManager.ACTION_DISCHARGING); 1754 tracker.onReceive(mContext, dischargingIntent); 1755 assertFalse(tracker.isCharging()); 1756 assertFalse(mService.isBatteryCharging()); 1757 1758 tracker.onReceive(mContext, chargingIntent); 1759 assertTrue(tracker.isCharging()); 1760 assertTrue(mService.isBatteryCharging()); 1761 1762 tracker.onReceive(mContext, dischargingIntent); 1763 assertFalse(tracker.isCharging()); 1764 assertFalse(mService.isBatteryCharging()); 1765 } 1766 1767 @Test testIsCharging_adaptiveCharging_batteryTooLow()1768 public void testIsCharging_adaptiveCharging_batteryTooLow() { 1769 JobSchedulerService.BatteryStateTracker tracker = mService.mBatteryStateTracker; 1770 1771 tracker.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); 1772 assertFalse(tracker.isCharging()); 1773 assertFalse(mService.isBatteryCharging()); 1774 1775 setBatteryLevel(15); 1776 setChargingPolicy(BatteryManager.CHARGING_POLICY_ADAPTIVE_AC); 1777 assertFalse(tracker.isCharging()); 1778 assertFalse(mService.isBatteryCharging()); 1779 1780 tracker.onReceive(mContext, new Intent(Intent.ACTION_POWER_CONNECTED)); 1781 1782 setBatteryLevel(70); 1783 assertTrue(tracker.isCharging()); 1784 assertTrue(mService.isBatteryCharging()); 1785 } 1786 1787 @Test testIsCharging_adaptiveCharging_chargeBelowThreshold()1788 public void testIsCharging_adaptiveCharging_chargeBelowThreshold() { 1789 JobSchedulerService.BatteryStateTracker tracker = mService.mBatteryStateTracker; 1790 1791 setChargingPolicy(BatteryManager.CHARGING_POLICY_ADAPTIVE_AC); 1792 tracker.onReceive(mContext, new Intent(Intent.ACTION_POWER_CONNECTED)); 1793 setBatteryLevel(5); 1794 1795 tracker.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); 1796 assertTrue(tracker.isCharging()); 1797 assertTrue(mService.isBatteryCharging()); 1798 1799 for (int level = 5; level < 80; ++level) { 1800 setBatteryLevel(level); 1801 assertTrue(tracker.isCharging()); 1802 assertTrue(mService.isBatteryCharging()); 1803 } 1804 } 1805 1806 @Test testIsCharging_adaptiveCharging_dischargeAboveThreshold()1807 public void testIsCharging_adaptiveCharging_dischargeAboveThreshold() { 1808 JobSchedulerService.BatteryStateTracker tracker = mService.mBatteryStateTracker; 1809 1810 setChargingPolicy(BatteryManager.CHARGING_POLICY_ADAPTIVE_AC); 1811 tracker.onReceive(mContext, new Intent(Intent.ACTION_POWER_CONNECTED)); 1812 setBatteryLevel(80); 1813 1814 tracker.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); 1815 assertTrue(tracker.isCharging()); 1816 assertTrue(mService.isBatteryCharging()); 1817 1818 for (int level = 80; level > 60; --level) { 1819 setBatteryLevel(level); 1820 assertEquals(level >= 70, tracker.isCharging()); 1821 assertEquals(level >= 70, mService.isBatteryCharging()); 1822 } 1823 } 1824 1825 @Test testIsCharging_adaptiveCharging_notPluggedIn()1826 public void testIsCharging_adaptiveCharging_notPluggedIn() { 1827 JobSchedulerService.BatteryStateTracker tracker = mService.mBatteryStateTracker; 1828 1829 tracker.onReceive(mContext, new Intent(Intent.ACTION_POWER_DISCONNECTED)); 1830 tracker.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); 1831 assertFalse(tracker.isCharging()); 1832 assertFalse(mService.isBatteryCharging()); 1833 1834 setBatteryLevel(15); 1835 setChargingPolicy(BatteryManager.CHARGING_POLICY_ADAPTIVE_AC); 1836 assertFalse(tracker.isCharging()); 1837 assertFalse(mService.isBatteryCharging()); 1838 1839 setBatteryLevel(50); 1840 setChargingPolicy(BatteryManager.CHARGING_POLICY_ADAPTIVE_AC); 1841 assertFalse(tracker.isCharging()); 1842 assertFalse(mService.isBatteryCharging()); 1843 1844 setBatteryLevel(70); 1845 assertFalse(tracker.isCharging()); 1846 assertFalse(mService.isBatteryCharging()); 1847 1848 setBatteryLevel(95); 1849 assertFalse(tracker.isCharging()); 1850 assertFalse(mService.isBatteryCharging()); 1851 1852 setBatteryLevel(100); 1853 assertFalse(tracker.isCharging()); 1854 assertFalse(mService.isBatteryCharging()); 1855 } 1856 1857 /** Tests that rare job batching works as expected. */ 1858 @Test testConnectivityJobBatching()1859 public void testConnectivityJobBatching() { 1860 mSetFlagsRule.enableFlags(FLAG_BATCH_CONNECTIVITY_JOBS_PER_NETWORK); 1861 1862 spyOn(mService); 1863 doReturn(false).when(mService).evaluateControllerStatesLocked(any()); 1864 doNothing().when(mService).noteJobsPending(any()); 1865 doReturn(true).when(mService).isReadyToBeExecutedLocked(any(), anyBoolean()); 1866 ConnectivityController connectivityController = mService.getConnectivityController(); 1867 spyOn(connectivityController); 1868 advanceElapsedClock(24 * HOUR_IN_MILLIS); 1869 1870 JobSchedulerService.MaybeReadyJobQueueFunctor maybeQueueFunctor = 1871 mService.new MaybeReadyJobQueueFunctor(); 1872 mService.mConstants.CONN_TRANSPORT_BATCH_THRESHOLD.clear(); 1873 mService.mConstants.CONN_TRANSPORT_BATCH_THRESHOLD 1874 .put(NetworkCapabilities.TRANSPORT_CELLULAR, 5); 1875 mService.mConstants.CONN_TRANSPORT_BATCH_THRESHOLD 1876 .put(NetworkCapabilities.TRANSPORT_WIFI, 2); 1877 mService.mConstants.CONN_MAX_CONNECTIVITY_JOB_BATCH_DELAY_MS = HOUR_IN_MILLIS; 1878 1879 final Network network = mock(Network.class); 1880 1881 // Not enough connectivity jobs to run. 1882 mService.getPendingJobQueue().clear(); 1883 maybeQueueFunctor.reset(); 1884 NetworkCapabilities capabilities = new NetworkCapabilities.Builder() 1885 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 1886 .build(); 1887 doReturn(capabilities).when(connectivityController).getNetworkCapabilities(network); 1888 doReturn(false).when(connectivityController).isNetworkInStateForJobRunLocked(any()); 1889 for (int i = 0; i < 4; ++i) { 1890 JobStatus job = createJobStatus( 1891 "testConnectivityJobBatching", 1892 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1893 job.setStandbyBucket(ACTIVE_INDEX); 1894 job.network = network; 1895 1896 maybeQueueFunctor.accept(job); 1897 assertNull(maybeQueueFunctor.mBatches.get(null)); 1898 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(network).size()); 1899 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1900 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1901 } 1902 maybeQueueFunctor.postProcessLocked(); 1903 assertEquals(0, mService.getPendingJobQueue().size()); 1904 1905 // Not enough connectivity jobs to run, but the network is already active 1906 mService.getPendingJobQueue().clear(); 1907 maybeQueueFunctor.reset(); 1908 doReturn(capabilities).when(connectivityController).getNetworkCapabilities(network); 1909 doReturn(true).when(connectivityController).isNetworkInStateForJobRunLocked(any()); 1910 for (int i = 0; i < 4; ++i) { 1911 JobStatus job = createJobStatus( 1912 "testConnectivityJobBatching", 1913 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1914 job.setStandbyBucket(ACTIVE_INDEX); 1915 job.network = network; 1916 1917 maybeQueueFunctor.accept(job); 1918 assertNull(maybeQueueFunctor.mBatches.get(null)); 1919 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(network).size()); 1920 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1921 assertEquals(0, job.getFirstForceBatchedTimeElapsed()); 1922 } 1923 maybeQueueFunctor.postProcessLocked(); 1924 assertEquals(4, mService.getPendingJobQueue().size()); 1925 1926 // Enough connectivity jobs to run. 1927 mService.getPendingJobQueue().clear(); 1928 maybeQueueFunctor.reset(); 1929 capabilities = new NetworkCapabilities.Builder() 1930 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 1931 .build(); 1932 doReturn(capabilities).when(connectivityController).getNetworkCapabilities(network); 1933 doReturn(false).when(connectivityController).isNetworkInStateForJobRunLocked(any()); 1934 for (int i = 0; i < 3; ++i) { 1935 JobStatus job = createJobStatus( 1936 "testConnectivityJobBatching", 1937 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1938 job.setStandbyBucket(ACTIVE_INDEX); 1939 job.network = network; 1940 1941 maybeQueueFunctor.accept(job); 1942 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(network).size()); 1943 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1944 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1945 } 1946 maybeQueueFunctor.postProcessLocked(); 1947 assertEquals(3, mService.getPendingJobQueue().size()); 1948 1949 // Not enough connectivity jobs to run, but a non-batched job saves the day. 1950 mService.getPendingJobQueue().clear(); 1951 maybeQueueFunctor.reset(); 1952 JobStatus runningJob = createJobStatus( 1953 "testConnectivityJobBatching", 1954 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1955 runningJob.network = network; 1956 doReturn(true).when(mService).isCurrentlyRunningLocked(runningJob); 1957 capabilities = new NetworkCapabilities.Builder() 1958 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 1959 .build(); 1960 doReturn(capabilities).when(connectivityController).getNetworkCapabilities(network); 1961 for (int i = 0; i < 3; ++i) { 1962 JobStatus job = createJobStatus( 1963 "testConnectivityJobBatching", 1964 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1965 job.setStandbyBucket(ACTIVE_INDEX); 1966 job.network = network; 1967 1968 maybeQueueFunctor.accept(job); 1969 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(network).size()); 1970 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1971 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1972 } 1973 maybeQueueFunctor.accept(runningJob); 1974 maybeQueueFunctor.postProcessLocked(); 1975 assertEquals(3, mService.getPendingJobQueue().size()); 1976 1977 // Not enough connectivity jobs to run, but an old connectivity job saves the day. 1978 mService.getPendingJobQueue().clear(); 1979 maybeQueueFunctor.reset(); 1980 JobStatus oldConnJob = createJobStatus("testConnectivityJobBatching", 1981 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1982 oldConnJob.network = network; 1983 final long oldBatchTime = sElapsedRealtimeClock.millis() 1984 - 2 * mService.mConstants.CONN_MAX_CONNECTIVITY_JOB_BATCH_DELAY_MS; 1985 oldConnJob.setFirstForceBatchedTimeElapsed(oldBatchTime); 1986 for (int i = 0; i < 2; ++i) { 1987 JobStatus job = createJobStatus( 1988 "testConnectivityJobBatching", 1989 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1990 job.setStandbyBucket(ACTIVE_INDEX); 1991 job.network = network; 1992 1993 maybeQueueFunctor.accept(job); 1994 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(network).size()); 1995 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 1996 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 1997 } 1998 maybeQueueFunctor.accept(oldConnJob); 1999 assertEquals(oldBatchTime, oldConnJob.getFirstForceBatchedTimeElapsed()); 2000 maybeQueueFunctor.postProcessLocked(); 2001 assertEquals(3, mService.getPendingJobQueue().size()); 2002 2003 // Transport type doesn't have a set threshold. One job should be the default threshold. 2004 mService.getPendingJobQueue().clear(); 2005 maybeQueueFunctor.reset(); 2006 capabilities = new NetworkCapabilities.Builder() 2007 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) 2008 .build(); 2009 doReturn(capabilities).when(connectivityController).getNetworkCapabilities(network); 2010 JobStatus job = createJobStatus( 2011 "testConnectivityJobBatching", 2012 createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 2013 job.setStandbyBucket(ACTIVE_INDEX); 2014 job.network = network; 2015 maybeQueueFunctor.accept(job); 2016 assertEquals(1, maybeQueueFunctor.mBatches.get(network).size()); 2017 assertEquals(1, maybeQueueFunctor.runnableJobs.size()); 2018 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2019 maybeQueueFunctor.postProcessLocked(); 2020 assertEquals(1, mService.getPendingJobQueue().size()); 2021 } 2022 2023 /** Tests that active job batching works as expected. */ 2024 @Test testActiveJobBatching_activeBatchingEnabled()2025 public void testActiveJobBatching_activeBatchingEnabled() { 2026 mSetFlagsRule.enableFlags(FLAG_BATCH_ACTIVE_BUCKET_JOBS); 2027 2028 spyOn(mService); 2029 doReturn(false).when(mService).evaluateControllerStatesLocked(any()); 2030 doNothing().when(mService).noteJobsPending(any()); 2031 doReturn(true).when(mService).isReadyToBeExecutedLocked(any(), anyBoolean()); 2032 advanceElapsedClock(24 * HOUR_IN_MILLIS); 2033 2034 JobSchedulerService.MaybeReadyJobQueueFunctor maybeQueueFunctor = 2035 mService.new MaybeReadyJobQueueFunctor(); 2036 mService.mConstants.MIN_READY_CPU_ONLY_JOBS_COUNT = 5; 2037 mService.mConstants.MAX_CPU_ONLY_JOB_BATCH_DELAY_MS = HOUR_IN_MILLIS; 2038 2039 // Not enough ACTIVE jobs to run. 2040 mService.getPendingJobQueue().clear(); 2041 maybeQueueFunctor.reset(); 2042 for (int i = 0; i < mService.mConstants.MIN_READY_CPU_ONLY_JOBS_COUNT / 2; ++i) { 2043 JobStatus job = createJobStatus("testActiveJobBatching", createJobInfo()); 2044 job.setStandbyBucket(ACTIVE_INDEX); 2045 2046 maybeQueueFunctor.accept(job); 2047 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2048 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2049 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2050 } 2051 maybeQueueFunctor.postProcessLocked(); 2052 assertEquals(0, mService.getPendingJobQueue().size()); 2053 2054 // Enough ACTIVE jobs to run. 2055 mService.getPendingJobQueue().clear(); 2056 maybeQueueFunctor.reset(); 2057 for (int i = 0; i < mService.mConstants.MIN_READY_CPU_ONLY_JOBS_COUNT; ++i) { 2058 JobStatus job = createJobStatus("testActiveJobBatching", createJobInfo()); 2059 job.setStandbyBucket(ACTIVE_INDEX); 2060 2061 maybeQueueFunctor.accept(job); 2062 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2063 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2064 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2065 } 2066 maybeQueueFunctor.postProcessLocked(); 2067 assertEquals(5, mService.getPendingJobQueue().size()); 2068 2069 // Not enough ACTIVE jobs to run, but a non-batched job saves the day. 2070 mService.getPendingJobQueue().clear(); 2071 maybeQueueFunctor.reset(); 2072 JobStatus expeditedJob = createJobStatus("testActiveJobBatching", 2073 createJobInfo().setExpedited(true)); 2074 spyOn(expeditedJob); 2075 when(expeditedJob.shouldTreatAsExpeditedJob()).thenReturn(true); 2076 expeditedJob.setStandbyBucket(RARE_INDEX); 2077 for (int i = 0; i < mService.mConstants.MIN_READY_CPU_ONLY_JOBS_COUNT / 2; ++i) { 2078 JobStatus job = createJobStatus("testActiveJobBatching", createJobInfo()); 2079 job.setStandbyBucket(ACTIVE_INDEX); 2080 2081 maybeQueueFunctor.accept(job); 2082 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2083 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2084 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2085 } 2086 maybeQueueFunctor.accept(expeditedJob); 2087 maybeQueueFunctor.postProcessLocked(); 2088 assertEquals(3, mService.getPendingJobQueue().size()); 2089 2090 // Not enough ACTIVE jobs to run, but an old ACTIVE job saves the day. 2091 mService.getPendingJobQueue().clear(); 2092 maybeQueueFunctor.reset(); 2093 JobStatus oldActiveJob = createJobStatus("testActiveJobBatching", createJobInfo()); 2094 oldActiveJob.setStandbyBucket(ACTIVE_INDEX); 2095 final long oldBatchTime = sElapsedRealtimeClock.millis() 2096 - 2 * mService.mConstants.MAX_CPU_ONLY_JOB_BATCH_DELAY_MS; 2097 oldActiveJob.setFirstForceBatchedTimeElapsed(oldBatchTime); 2098 for (int i = 0; i < mService.mConstants.MIN_READY_CPU_ONLY_JOBS_COUNT / 2; ++i) { 2099 JobStatus job = createJobStatus("testActiveJobBatching", createJobInfo()); 2100 job.setStandbyBucket(ACTIVE_INDEX); 2101 2102 maybeQueueFunctor.accept(job); 2103 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2104 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2105 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2106 } 2107 maybeQueueFunctor.accept(oldActiveJob); 2108 assertEquals(oldBatchTime, oldActiveJob.getFirstForceBatchedTimeElapsed()); 2109 maybeQueueFunctor.postProcessLocked(); 2110 assertEquals(3, mService.getPendingJobQueue().size()); 2111 } 2112 2113 /** Tests that rare job batching works as expected. */ 2114 @Test testRareJobBatching()2115 public void testRareJobBatching() { 2116 spyOn(mService); 2117 doReturn(false).when(mService).evaluateControllerStatesLocked(any()); 2118 doNothing().when(mService).noteJobsPending(any()); 2119 doReturn(true).when(mService).isReadyToBeExecutedLocked(any(), anyBoolean()); 2120 advanceElapsedClock(24 * HOUR_IN_MILLIS); 2121 2122 JobSchedulerService.MaybeReadyJobQueueFunctor maybeQueueFunctor = 2123 mService.new MaybeReadyJobQueueFunctor(); 2124 mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; 2125 mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = HOUR_IN_MILLIS; 2126 2127 // Not enough RARE jobs to run. 2128 mService.getPendingJobQueue().clear(); 2129 maybeQueueFunctor.reset(); 2130 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { 2131 JobStatus job = createJobStatus("testRareJobBatching", createJobInfo()); 2132 job.setStandbyBucket(RARE_INDEX); 2133 2134 maybeQueueFunctor.accept(job); 2135 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2136 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2137 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2138 } 2139 maybeQueueFunctor.postProcessLocked(); 2140 assertEquals(0, mService.getPendingJobQueue().size()); 2141 2142 // Enough RARE jobs to run. 2143 mService.getPendingJobQueue().clear(); 2144 maybeQueueFunctor.reset(); 2145 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) { 2146 JobStatus job = createJobStatus("testRareJobBatching", createJobInfo()); 2147 job.setStandbyBucket(RARE_INDEX); 2148 2149 maybeQueueFunctor.accept(job); 2150 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2151 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2152 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2153 } 2154 maybeQueueFunctor.postProcessLocked(); 2155 assertEquals(5, mService.getPendingJobQueue().size()); 2156 2157 // Not enough RARE jobs to run, but a non-batched job saves the day. 2158 mSetFlagsRule.disableFlags(FLAG_BATCH_ACTIVE_BUCKET_JOBS); 2159 mService.getPendingJobQueue().clear(); 2160 maybeQueueFunctor.reset(); 2161 JobStatus activeJob = createJobStatus("testRareJobBatching", createJobInfo()); 2162 activeJob.setStandbyBucket(ACTIVE_INDEX); 2163 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { 2164 JobStatus job = createJobStatus("testRareJobBatching", createJobInfo()); 2165 job.setStandbyBucket(RARE_INDEX); 2166 2167 maybeQueueFunctor.accept(job); 2168 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2169 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2170 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2171 } 2172 maybeQueueFunctor.accept(activeJob); 2173 maybeQueueFunctor.postProcessLocked(); 2174 assertEquals(3, mService.getPendingJobQueue().size()); 2175 2176 // Not enough RARE jobs to run, but an old RARE job saves the day. 2177 mService.getPendingJobQueue().clear(); 2178 maybeQueueFunctor.reset(); 2179 JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo()); 2180 oldRareJob.setStandbyBucket(RARE_INDEX); 2181 final long oldBatchTime = sElapsedRealtimeClock.millis() 2182 - 2 * mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS; 2183 oldRareJob.setFirstForceBatchedTimeElapsed(oldBatchTime); 2184 for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) { 2185 JobStatus job = createJobStatus("testRareJobBatching", createJobInfo()); 2186 job.setStandbyBucket(RARE_INDEX); 2187 2188 maybeQueueFunctor.accept(job); 2189 assertEquals(i + 1, maybeQueueFunctor.mBatches.get(null).size()); 2190 assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size()); 2191 assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed()); 2192 } 2193 maybeQueueFunctor.accept(oldRareJob); 2194 assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed()); 2195 maybeQueueFunctor.postProcessLocked(); 2196 assertEquals(3, mService.getPendingJobQueue().size()); 2197 } 2198 2199 /** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */ 2200 @Test testScheduleLimiting_RegularSchedule_Blocked()2201 public void testScheduleLimiting_RegularSchedule_Blocked() { 2202 mService.mConstants.ENABLE_API_QUOTAS = true; 2203 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 2204 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 2205 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 2206 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; 2207 mService.updateQuotaTracker(); 2208 mService.resetScheduleQuota(); 2209 2210 final JobInfo job = createJobInfo().setPersisted(true).build(); 2211 for (int i = 0; i < 500; ++i) { 2212 final int expected = 2213 i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE; 2214 assertEquals("Got unexpected result for schedule #" + (i + 1), 2215 expected, 2216 mService.scheduleAsPackage(job, null, TEST_UID, null, 0, "JSSTest", "")); 2217 } 2218 } 2219 2220 /** 2221 * Tests that jobs scheduled by the app itself succeed even if the app is above the scheduling 2222 * limit. 2223 */ 2224 @Test 2225 public void testScheduleLimiting_RegularSchedule_Allowed() { 2226 mService.mConstants.ENABLE_API_QUOTAS = true; 2227 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 2228 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 2229 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 2230 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 2231 mService.updateQuotaTracker(); 2232 mService.resetScheduleQuota(); 2233 2234 final JobInfo job = createJobInfo().setPersisted(true).build(); 2235 for (int i = 0; i < 500; ++i) { 2236 assertEquals("Got unexpected result for schedule #" + (i + 1), 2237 JobScheduler.RESULT_SUCCESS, 2238 mService.scheduleAsPackage(job, null, TEST_UID, null, 0, "JSSTest", "")); 2239 } 2240 } 2241 2242 /** 2243 * Tests that jobs scheduled through a proxy (eg. system server) don't count towards scheduling 2244 * limits. 2245 */ 2246 @Test 2247 public void testScheduleLimiting_Proxy() { 2248 mService.mConstants.ENABLE_API_QUOTAS = true; 2249 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 2250 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 2251 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 2252 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; 2253 mService.updateQuotaTracker(); 2254 mService.resetScheduleQuota(); 2255 2256 final JobInfo job = createJobInfo().setPersisted(true).build(); 2257 for (int i = 0; i < 500; ++i) { 2258 assertEquals("Got unexpected result for schedule #" + (i + 1), 2259 JobScheduler.RESULT_SUCCESS, 2260 mService.scheduleAsPackage(job, null, TEST_UID, "proxied.package", 0, "JSSTest", 2261 "")); 2262 } 2263 } 2264 2265 /** 2266 * Tests that jobs scheduled by an app for itself as if through a proxy are counted towards 2267 * scheduling limits. 2268 */ 2269 @Test 2270 public void testScheduleLimiting_SelfProxy() { 2271 mService.mConstants.ENABLE_API_QUOTAS = true; 2272 mService.mConstants.API_QUOTA_SCHEDULE_COUNT = 300; 2273 mService.mConstants.API_QUOTA_SCHEDULE_WINDOW_MS = 300000; 2274 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 2275 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; 2276 mService.updateQuotaTracker(); 2277 mService.resetScheduleQuota(); 2278 2279 final JobInfo job = createJobInfo().setPersisted(true).build(); 2280 for (int i = 0; i < 500; ++i) { 2281 final int expected = 2282 i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE; 2283 assertEquals("Got unexpected result for schedule #" + (i + 1), 2284 expected, 2285 mService.scheduleAsPackage(job, null, TEST_UID, 2286 job.getService().getPackageName(), 2287 0, "JSSTest", "")); 2288 } 2289 } 2290 2291 /** 2292 * Tests that the number of persisted JobWorkItems is capped. 2293 */ 2294 @Test 2295 public void testScheduleLimiting_JobWorkItems_Nonpersisted() { 2296 mService.mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 500; 2297 mService.mConstants.ENABLE_API_QUOTAS = false; 2298 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 2299 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 2300 mService.updateQuotaTracker(); 2301 mService.resetScheduleQuota(); 2302 2303 final JobInfo job = createJobInfo().setPersisted(false).build(); 2304 final JobWorkItem item = new JobWorkItem.Builder().build(); 2305 for (int i = 0; i < 1000; ++i) { 2306 assertEquals("Got unexpected result for schedule #" + (i + 1), 2307 JobScheduler.RESULT_SUCCESS, 2308 mService.scheduleAsPackage(job, item, TEST_UID, 2309 job.getService().getPackageName(), 2310 0, "JSSTest", "")); 2311 } 2312 } 2313 2314 /** 2315 * Tests that the number of persisted JobWorkItems is capped. 2316 */ 2317 @Test 2318 public void testScheduleLimiting_JobWorkItems_Persisted() { 2319 mService.mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 500; 2320 mService.mConstants.ENABLE_API_QUOTAS = false; 2321 mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; 2322 mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 2323 mService.updateQuotaTracker(); 2324 mService.resetScheduleQuota(); 2325 2326 final JobInfo job = createJobInfo().setPersisted(true).build(); 2327 final JobWorkItem item = new JobWorkItem.Builder().build(); 2328 for (int i = 0; i < 500; ++i) { 2329 assertEquals("Got unexpected result for schedule #" + (i + 1), 2330 JobScheduler.RESULT_SUCCESS, 2331 mService.scheduleAsPackage(job, item, TEST_UID, 2332 job.getService().getPackageName(), 2333 0, "JSSTest", "")); 2334 } 2335 try { 2336 mService.scheduleAsPackage(job, item, TEST_UID, job.getService().getPackageName(), 2337 0, "JSSTest", ""); 2338 fail("Added more items than allowed"); 2339 } catch (IllegalStateException expected) { 2340 // Success 2341 } 2342 } 2343 2344 /** Tests that jobs are removed from the pending list if the user stops the app. */ 2345 @Test 2346 public void testUserStopRemovesPending() { 2347 spyOn(mService); 2348 2349 JobStatus job1a = createJobStatus("testUserStopRemovesPending", 2350 createJobInfo(1), 1, "pkg1"); 2351 JobStatus job1b = createJobStatus("testUserStopRemovesPending", 2352 createJobInfo(2), 1, "pkg1"); 2353 JobStatus job2a = createJobStatus("testUserStopRemovesPending", 2354 createJobInfo(1), 2, "pkg2"); 2355 JobStatus job2b = createJobStatus("testUserStopRemovesPending", 2356 createJobInfo(2), 2, "pkg2"); 2357 doReturn(1).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 0); 2358 doReturn(11).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 1); 2359 doReturn(2).when(mPackageManagerInternal).getPackageUid("pkg2", 0, 0); 2360 2361 mService.getPendingJobQueue().clear(); 2362 mService.getPendingJobQueue().add(job1a); 2363 mService.getPendingJobQueue().add(job1b); 2364 mService.getPendingJobQueue().add(job2a); 2365 mService.getPendingJobQueue().add(job2b); 2366 mService.getJobStore().add(job1a); 2367 mService.getJobStore().add(job1b); 2368 mService.getJobStore().add(job2a); 2369 mService.getJobStore().add(job2b); 2370 2371 mService.notePendingUserRequestedAppStopInternal("pkg1", 1, "test"); 2372 assertEquals(4, mService.getPendingJobQueue().size()); 2373 assertTrue(mService.getPendingJobQueue().contains(job1a)); 2374 assertTrue(mService.getPendingJobQueue().contains(job1b)); 2375 assertTrue(mService.getPendingJobQueue().contains(job2a)); 2376 assertTrue(mService.getPendingJobQueue().contains(job2b)); 2377 2378 mService.notePendingUserRequestedAppStopInternal("pkg1", 0, "test"); 2379 assertEquals(2, mService.getPendingJobQueue().size()); 2380 assertFalse(mService.getPendingJobQueue().contains(job1a)); 2381 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job1a)); 2382 assertFalse(mService.getPendingJobQueue().contains(job1b)); 2383 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job1b)); 2384 assertTrue(mService.getPendingJobQueue().contains(job2a)); 2385 assertTrue(mService.getPendingJobQueue().contains(job2b)); 2386 2387 mService.notePendingUserRequestedAppStopInternal("pkg2", 0, "test"); 2388 assertEquals(0, mService.getPendingJobQueue().size()); 2389 assertFalse(mService.getPendingJobQueue().contains(job1a)); 2390 assertFalse(mService.getPendingJobQueue().contains(job1b)); 2391 assertFalse(mService.getPendingJobQueue().contains(job2a)); 2392 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2a)); 2393 assertFalse(mService.getPendingJobQueue().contains(job2b)); 2394 assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2b)); 2395 } 2396 2397 /** 2398 * Unit tests {@link JobSchedulerService#checkIfRestricted(JobStatus)} with single {@link 2399 * JobRestriction} registered. 2400 */ 2401 @Test 2402 public void testCheckIfRestrictedSingleRestriction() { 2403 int bias = JobInfo.BIAS_BOUND_FOREGROUND_SERVICE; 2404 JobStatus fgsJob = 2405 createJobStatus( 2406 "testCheckIfRestrictedSingleRestriction", createJobInfo(1).setBias(bias)); 2407 ThermalStatusRestriction mockThermalStatusRestriction = 2408 mock(ThermalStatusRestriction.class); 2409 mService.mJobRestrictions.clear(); 2410 mService.mJobRestrictions.add(mockThermalStatusRestriction); 2411 when(mockThermalStatusRestriction.isJobRestricted(fgsJob, bias)).thenReturn(true); 2412 2413 synchronized (mService.mLock) { 2414 assertEquals(mService.checkIfRestricted(fgsJob), mockThermalStatusRestriction); 2415 } 2416 2417 when(mockThermalStatusRestriction.isJobRestricted(fgsJob, bias)).thenReturn(false); 2418 synchronized (mService.mLock) { 2419 assertNull(mService.checkIfRestricted(fgsJob)); 2420 } 2421 } 2422 2423 /** 2424 * Unit tests {@link JobSchedulerService#checkIfRestricted(JobStatus)} with multiple {@link 2425 * JobRestriction} registered. 2426 */ 2427 @Test 2428 public void testCheckIfRestrictedMultipleRestrictions() { 2429 int bias = JobInfo.BIAS_BOUND_FOREGROUND_SERVICE; 2430 JobStatus fgsJob = 2431 createJobStatus( 2432 "testGetMinJobExecutionGuaranteeMs", createJobInfo(1).setBias(bias)); 2433 JobRestriction mock1JobRestriction = mock(JobRestriction.class); 2434 JobRestriction mock2JobRestriction = mock(JobRestriction.class); 2435 mService.mJobRestrictions.clear(); 2436 mService.mJobRestrictions.add(mock1JobRestriction); 2437 mService.mJobRestrictions.add(mock2JobRestriction); 2438 2439 // Jobs will be restricted if any one of the registered {@link JobRestriction} 2440 // reports true. 2441 when(mock1JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(true); 2442 when(mock2JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(false); 2443 synchronized (mService.mLock) { 2444 assertEquals(mService.checkIfRestricted(fgsJob), mock1JobRestriction); 2445 } 2446 2447 when(mock1JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(false); 2448 when(mock2JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(true); 2449 synchronized (mService.mLock) { 2450 assertEquals(mService.checkIfRestricted(fgsJob), mock2JobRestriction); 2451 } 2452 2453 when(mock1JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(false); 2454 when(mock2JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(false); 2455 synchronized (mService.mLock) { 2456 assertNull(mService.checkIfRestricted(fgsJob)); 2457 } 2458 2459 when(mock1JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(true); 2460 when(mock2JobRestriction.isJobRestricted(fgsJob, bias)).thenReturn(true); 2461 synchronized (mService.mLock) { 2462 assertNotEquals(mService.checkIfRestricted(fgsJob), mock1JobRestriction); 2463 } 2464 } 2465 2466 /** 2467 * Jobs with foreground service and top app biases must not be restricted when the flag is 2468 * disabled. 2469 */ 2470 @Test 2471 @RequiresFlagsDisabled(FLAG_THERMAL_RESTRICTIONS_TO_FGS_JOBS) 2472 public void testCheckIfRestricted_highJobBias_flagThermalRestrictionsToFgsJobsDisabled() { 2473 JobStatus fgsJob = 2474 createJobStatus( 2475 "testCheckIfRestrictedJobBiasFgs", 2476 createJobInfo(1).setBias(JobInfo.BIAS_FOREGROUND_SERVICE)); 2477 JobStatus topAppJob = 2478 createJobStatus( 2479 "testCheckIfRestrictedJobBiasTopApp", 2480 createJobInfo(2).setBias(JobInfo.BIAS_TOP_APP)); 2481 2482 synchronized (mService.mLock) { 2483 assertNull(mService.checkIfRestricted(fgsJob)); 2484 assertNull(mService.checkIfRestricted(topAppJob)); 2485 } 2486 } 2487 2488 /** Jobs with top app biases must not be restricted. */ 2489 @Test 2490 public void testCheckIfRestricted_highJobBias() { 2491 JobStatus topAppJob = createJobStatus( 2492 "testCheckIfRestrictedJobBiasTopApp", 2493 createJobInfo(1).setBias(JobInfo.BIAS_TOP_APP)); 2494 synchronized (mService.mLock) { 2495 assertNull(mService.checkIfRestricted(topAppJob)); 2496 } 2497 } 2498 2499 private void setBatteryLevel(int level) { 2500 doReturn(level).when(mBatteryManagerInternal).getBatteryLevel(); 2501 mService.mBatteryStateTracker 2502 .onReceive(mContext, new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED)); 2503 } 2504 2505 private void setChargingPolicy(int policy) { 2506 doReturn(policy).when(mBatteryManagerInternal).getChargingPolicy(); 2507 if (mChargingPolicyChangeListener != null) { 2508 mChargingPolicyChangeListener.onChargingPolicyChanged(policy); 2509 } 2510 } 2511 } 2512