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