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