1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.job.controllers; 18 19 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 20 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 21 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 27 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; 28 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 29 import static com.android.server.job.JobSchedulerService.sSystemClock; 30 31 import static org.junit.Assert.assertEquals; 32 import static org.junit.Assert.assertFalse; 33 import static org.junit.Assert.assertTrue; 34 import static org.mockito.ArgumentMatchers.any; 35 import static org.mockito.ArgumentMatchers.anyInt; 36 import static org.mockito.ArgumentMatchers.anyLong; 37 import static org.mockito.ArgumentMatchers.anyString; 38 import static org.mockito.Mockito.eq; 39 import static org.mockito.Mockito.mock; 40 import static org.mockito.Mockito.timeout; 41 import static org.mockito.Mockito.verify; 42 43 import android.app.AlarmManager; 44 import android.app.job.JobInfo; 45 import android.app.usage.UsageStatsManagerInternal; 46 import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; 47 import android.appwidget.AppWidgetManager; 48 import android.content.ComponentName; 49 import android.content.Context; 50 import android.content.pm.ServiceInfo; 51 import android.os.Looper; 52 import android.os.Process; 53 import android.os.SystemClock; 54 import android.provider.DeviceConfig; 55 import android.util.ArraySet; 56 import android.util.SparseArray; 57 58 import androidx.test.runner.AndroidJUnit4; 59 60 import com.android.server.LocalServices; 61 import com.android.server.job.JobSchedulerService; 62 import com.android.server.job.controllers.PrefetchController.PcConstants; 63 64 import org.junit.After; 65 import org.junit.Before; 66 import org.junit.Test; 67 import org.junit.runner.RunWith; 68 import org.mockito.ArgumentCaptor; 69 import org.mockito.ArgumentMatchers; 70 import org.mockito.InOrder; 71 import org.mockito.Mock; 72 import org.mockito.MockitoSession; 73 import org.mockito.quality.Strictness; 74 import org.mockito.stubbing.Answer; 75 76 import java.time.Clock; 77 import java.time.Duration; 78 import java.time.ZoneOffset; 79 import java.util.concurrent.Executor; 80 81 @RunWith(AndroidJUnit4.class) 82 public class PrefetchControllerTest { 83 private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; 84 private static final int SOURCE_USER_ID = 0; 85 private static final int CALLING_UID = 1000; 86 private static final long DEFAULT_WAIT_MS = 3000; 87 private static final String TAG_PREFETCH = "*job.prefetch*"; 88 89 private PrefetchController mPrefetchController; 90 private PcConstants mPcConstants; 91 private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; 92 private EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener; 93 private SparseArray<ArraySet<String>> mPackagesForUid = new SparseArray<>(); 94 95 private MockitoSession mMockingSession; 96 @Mock 97 private AlarmManager mAlarmManager; 98 @Mock 99 private Context mContext; 100 @Mock 101 private JobSchedulerService mJobSchedulerService; 102 @Mock 103 private UsageStatsManagerInternal mUsageStatsManagerInternal; 104 105 @Before setUp()106 public void setUp() { 107 mMockingSession = mockitoSession() 108 .initMocks(this) 109 .strictness(Strictness.LENIENT) 110 .spyStatic(DeviceConfig.class) 111 .mockStatic(LocalServices.class) 112 .startMocking(); 113 114 // Called in StateController constructor. 115 when(mJobSchedulerService.getTestableContext()).thenReturn(mContext); 116 when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); 117 // Called in PrefetchController constructor. 118 doReturn(mUsageStatsManagerInternal) 119 .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); 120 when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); 121 when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); 122 // Used in PrefetchController.PcConstants 123 doAnswer((Answer<Void>) invocationOnMock -> null) 124 .when(() -> DeviceConfig.addOnPropertiesChangedListener( 125 anyString(), any(Executor.class), 126 any(DeviceConfig.OnPropertiesChangedListener.class))); 127 mDeviceConfigPropertiesBuilder = 128 new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); 129 doAnswer( 130 (Answer<DeviceConfig.Properties>) invocationOnMock 131 -> mDeviceConfigPropertiesBuilder.build()) 132 .when(() -> DeviceConfig.getProperties( 133 eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.<String>any())); 134 // Used in PrefetchController.maybeUpdateConstraintForUid 135 when(mJobSchedulerService.getPackagesForUidLocked(anyInt())) 136 .thenAnswer(invocationOnMock 137 -> mPackagesForUid.get(invocationOnMock.getArgument(0))); 138 139 // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions 140 // in the past, and PrefetchController sometimes floors values at 0, so if the test time 141 // causes sessions with negative timestamps, they will fail. 142 sSystemClock = 143 getShiftedClock(Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC), 144 24 * HOUR_IN_MILLIS); 145 JobSchedulerService.sUptimeMillisClock = getShiftedClock( 146 Clock.fixed(SystemClock.uptimeClock().instant(), ZoneOffset.UTC), 147 24 * HOUR_IN_MILLIS); 148 JobSchedulerService.sElapsedRealtimeClock = getShiftedClock( 149 Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC), 150 24 * HOUR_IN_MILLIS); 151 152 // Initialize real objects. 153 // Capture the listeners. 154 ArgumentCaptor<EstimatedLaunchTimeChangedListener> eltListenerCaptor = 155 ArgumentCaptor.forClass(EstimatedLaunchTimeChangedListener.class); 156 mPrefetchController = new PrefetchController(mJobSchedulerService); 157 mPcConstants = mPrefetchController.getPcConstants(); 158 159 setUidBias(Process.myUid(), JobInfo.BIAS_DEFAULT); 160 161 verify(mUsageStatsManagerInternal) 162 .registerLaunchTimeChangedListener(eltListenerCaptor.capture()); 163 mEstimatedLaunchTimeChangedListener = eltListenerCaptor.getValue(); 164 } 165 166 @After tearDown()167 public void tearDown() { 168 if (mMockingSession != null) { 169 mMockingSession.finishMocking(); 170 } 171 } 172 createJobInfo(int jobId)173 private JobInfo createJobInfo(int jobId) { 174 return new JobInfo.Builder(jobId, 175 new ComponentName(mContext, "TestPrefetchJobService")) 176 .setPrefetch(true) 177 .build(); 178 } 179 createJobStatus(String testTag, int jobId)180 private JobStatus createJobStatus(String testTag, int jobId) { 181 return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, createJobInfo(jobId)); 182 } 183 createJobStatus(String testTag, String packageName, int callingUid, JobInfo jobInfo)184 private static JobStatus createJobStatus(String testTag, String packageName, int callingUid, 185 JobInfo jobInfo) { 186 JobStatus js = JobStatus.createFromJobInfo( 187 jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag); 188 js.serviceInfo = mock(ServiceInfo.class); 189 js.setStandbyBucket(FREQUENT_INDEX); 190 // Make sure Doze and background-not-restricted don't affect tests. 191 js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(), 192 /* state */ true, /* allowlisted */false); 193 js.setBackgroundNotRestrictedConstraintSatisfied( 194 sElapsedRealtimeClock.millis(), true, false); 195 js.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true); 196 js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true); 197 js.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), true); 198 js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true); 199 return js; 200 } 201 getShiftedClock(Clock clock, long incrementMs)202 private Clock getShiftedClock(Clock clock, long incrementMs) { 203 return Clock.offset(clock, Duration.ofMillis(incrementMs)); 204 } 205 setUidBias(int uid, int bias)206 private void setUidBias(int uid, int bias) { 207 int prevBias = mJobSchedulerService.getUidBias(uid); 208 doReturn(bias).when(mJobSchedulerService).getUidBias(uid); 209 synchronized (mPrefetchController.mLock) { 210 mPrefetchController.onUidBiasChangedLocked(uid, prevBias, bias); 211 } 212 } 213 setDeviceConfigLong(String key, long val)214 private void setDeviceConfigLong(String key, long val) { 215 mDeviceConfigPropertiesBuilder.setLong(key, val); 216 synchronized (mPrefetchController.mLock) { 217 mPrefetchController.prepareForUpdatedConstantsLocked(); 218 mPcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); 219 mPrefetchController.onConstantsUpdatedLocked(); 220 } 221 } 222 trackJobs(JobStatus... jobs)223 private void trackJobs(JobStatus... jobs) { 224 for (JobStatus job : jobs) { 225 ArraySet<String> pkgs = mPackagesForUid.get(job.getSourceUid()); 226 if (pkgs == null) { 227 pkgs = new ArraySet<>(); 228 mPackagesForUid.put(job.getSourceUid(), pkgs); 229 } 230 pkgs.add(job.getSourcePackageName()); 231 synchronized (mPrefetchController.mLock) { 232 mPrefetchController.maybeStartTrackingJobLocked(job, null); 233 } 234 } 235 } 236 237 @Test testConstantsUpdating_ValidValues()238 public void testConstantsUpdating_ValidValues() { 239 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 5 * HOUR_IN_MILLIS); 240 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 5 * MINUTE_IN_MILLIS); 241 242 assertEquals(5 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs()); 243 assertEquals(5 * MINUTE_IN_MILLIS, mPrefetchController.getLaunchTimeAllowanceMs()); 244 } 245 246 @Test testConstantsUpdating_InvalidValues()247 public void testConstantsUpdating_InvalidValues() { 248 // Test negatives/too low. 249 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 4 * MINUTE_IN_MILLIS); 250 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, -MINUTE_IN_MILLIS); 251 252 assertEquals(HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs()); 253 assertEquals(0, mPrefetchController.getLaunchTimeAllowanceMs()); 254 255 // Test larger than a day. Controller should cap at one day. 256 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 25 * HOUR_IN_MILLIS); 257 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 5 * HOUR_IN_MILLIS); 258 259 assertEquals(24 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs()); 260 assertEquals(2 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeAllowanceMs()); 261 } 262 263 @Test testConstantsUpdating_ThresholdChangesAlarms()264 public void testConstantsUpdating_ThresholdChangesAlarms() { 265 final long launchDelayMs = 11 * HOUR_IN_MILLIS; 266 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); 267 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); 268 when(mUsageStatsManagerInternal 269 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 270 .thenReturn(sSystemClock.millis() + launchDelayMs); 271 JobStatus jobStatus = createJobStatus("testConstantsUpdating_ThresholdChangesAlarms", 1); 272 trackJobs(jobStatus); 273 274 InOrder inOrder = inOrder(mAlarmManager); 275 276 inOrder.verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1)) 277 .setWindow( 278 anyInt(), eq(sElapsedRealtimeClock.millis() + 4 * HOUR_IN_MILLIS), 279 anyLong(), eq(TAG_PREFETCH), any(), any()); 280 281 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 3 * HOUR_IN_MILLIS); 282 inOrder.verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1)) 283 .setWindow( 284 anyInt(), eq(sElapsedRealtimeClock.millis() + 8 * HOUR_IN_MILLIS), 285 anyLong(), eq(TAG_PREFETCH), any(), any()); 286 } 287 288 @Test testConstraintNotSatisfiedWhenLaunchLate()289 public void testConstraintNotSatisfiedWhenLaunchLate() { 290 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); 291 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); 292 293 final JobStatus job = createJobStatus("testConstraintNotSatisfiedWhenLaunchLate", 1); 294 when(mUsageStatsManagerInternal 295 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 296 .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS); 297 trackJobs(job); 298 verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) 299 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 300 assertFalse(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 301 assertFalse(job.isReady()); 302 } 303 304 @Test testConstraintSatisfiedWhenLaunchSoon()305 public void testConstraintSatisfiedWhenLaunchSoon() { 306 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); 307 308 final JobStatus job = createJobStatus("testConstraintSatisfiedWhenLaunchSoon", 2); 309 when(mUsageStatsManagerInternal 310 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 311 .thenReturn(sSystemClock.millis() + MINUTE_IN_MILLIS); 312 trackJobs(job); 313 verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) 314 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 315 verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any()); 316 assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 317 assertTrue(job.isReady()); 318 } 319 320 @Test testConstraintSatisfiedWhenTop()321 public void testConstraintSatisfiedWhenTop() { 322 final JobStatus jobPending = createJobStatus("testConstraintSatisfiedWhenTop", 1); 323 final JobStatus jobRunning = createJobStatus("testConstraintSatisfiedWhenTop", 2); 324 final int uid = jobPending.getSourceUid(); 325 326 when(mJobSchedulerService.isCurrentlyRunningLocked(jobPending)).thenReturn(false); 327 when(mJobSchedulerService.isCurrentlyRunningLocked(jobRunning)).thenReturn(true); 328 329 InOrder inOrder = inOrder(mJobSchedulerService); 330 331 when(mUsageStatsManagerInternal 332 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 333 .thenReturn(sSystemClock.millis() + 10 * MINUTE_IN_MILLIS); 334 trackJobs(jobPending, jobRunning); 335 verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) 336 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 337 inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)) 338 .onControllerStateChanged(any()); 339 assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 340 assertTrue(jobPending.isReady()); 341 assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 342 assertTrue(jobRunning.isReady()); 343 setUidBias(uid, JobInfo.BIAS_TOP_APP); 344 // Processing happens on the handler, so wait until we're sure the change has been processed 345 inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)) 346 .onControllerStateChanged(any()); 347 // Already running job should continue but pending job must wait. 348 assertFalse(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 349 assertFalse(jobPending.isReady()); 350 assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 351 assertTrue(jobRunning.isReady()); 352 setUidBias(uid, JobInfo.BIAS_DEFAULT); 353 inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)) 354 .onControllerStateChanged(any()); 355 assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 356 assertTrue(jobPending.isReady()); 357 assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 358 assertTrue(jobRunning.isReady()); 359 } 360 361 @Test testConstraintSatisfiedWhenWidget()362 public void testConstraintSatisfiedWhenWidget() { 363 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); 364 365 final JobStatus jobNonWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 1); 366 final JobStatus jobWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 2); 367 368 when(mUsageStatsManagerInternal 369 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 370 .thenReturn(sSystemClock.millis() + 100 * HOUR_IN_MILLIS); 371 372 final AppWidgetManager appWidgetManager = mock(AppWidgetManager.class); 373 when(mContext.getSystemService(AppWidgetManager.class)).thenReturn(appWidgetManager); 374 mPrefetchController.onSystemServicesReady(); 375 376 when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID)) 377 .thenReturn(false); 378 trackJobs(jobNonWidget); 379 verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) 380 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 381 assertFalse(jobNonWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 382 assertFalse(jobNonWidget.isReady()); 383 384 when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID)) 385 .thenReturn(true); 386 trackJobs(jobWidget); 387 assertTrue(jobWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 388 assertTrue(jobWidget.isReady()); 389 } 390 391 @Test testEstimatedLaunchTimeChangedToLate()392 public void testEstimatedLaunchTimeChangedToLate() { 393 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); 394 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); 395 when(mUsageStatsManagerInternal 396 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 397 .thenReturn(sSystemClock.millis() + HOUR_IN_MILLIS); 398 399 InOrder inOrder = inOrder(mUsageStatsManagerInternal); 400 401 JobStatus jobStatus = createJobStatus("testEstimatedLaunchTimeChangedToLate", 1); 402 trackJobs(jobStatus); 403 inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) 404 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 405 verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any()); 406 assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 407 assertTrue(jobStatus.isReady()); 408 409 mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID, 410 SOURCE_PACKAGE, sSystemClock.millis() + 10 * HOUR_IN_MILLIS); 411 412 inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0)) 413 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 414 verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1)) 415 .setWindow( 416 anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS), 417 anyLong(), eq(TAG_PREFETCH), any(), any()); 418 assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 419 assertFalse(jobStatus.isReady()); 420 } 421 422 @Test testEstimatedLaunchTimeChangedToSoon()423 public void testEstimatedLaunchTimeChangedToSoon() { 424 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); 425 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0); 426 when(mUsageStatsManagerInternal 427 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 428 .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS); 429 430 InOrder inOrder = inOrder(mUsageStatsManagerInternal); 431 432 JobStatus jobStatus = createJobStatus("testEstimatedLaunchTimeChangedToSoon", 1); 433 trackJobs(jobStatus); 434 inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) 435 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 436 assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 437 assertFalse(jobStatus.isReady()); 438 439 mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID, 440 SOURCE_PACKAGE, sSystemClock.millis() + MINUTE_IN_MILLIS); 441 442 inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0)) 443 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 444 verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any()); 445 assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 446 assertTrue(jobStatus.isReady()); 447 } 448 449 @Test testEstimatedLaunchTimeAllowance()450 public void testEstimatedLaunchTimeAllowance() { 451 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); 452 setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 15 * MINUTE_IN_MILLIS); 453 when(mUsageStatsManagerInternal 454 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) 455 .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS); 456 457 InOrder inOrder = inOrder(mUsageStatsManagerInternal); 458 459 JobStatus jobStatus = createJobStatus("testEstimatedLaunchTimeAllowance", 1); 460 trackJobs(jobStatus); 461 inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) 462 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 463 // The allowance shouldn't shift the alarm 464 verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1)) 465 .setWindow( 466 anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS), 467 anyLong(), eq(TAG_PREFETCH), any(), any()); 468 assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 469 assertFalse(jobStatus.isReady()); 470 471 mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID, 472 SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS); 473 474 inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0)) 475 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); 476 verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any()); 477 assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); 478 assertTrue(jobStatus.isReady()); 479 480 sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS); 481 } 482 } 483