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