• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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