1 /* 2 * Copyright (C) 2023 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.devicelockcontroller.schedule; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; 23 24 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.PROVISION_PAUSED; 25 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.UNPROVISIONED; 26 import static com.android.devicelockcontroller.schedule.DeviceLockControllerSchedulerImpl.DEVICE_CHECK_IN_WORK_NAME; 27 28 import static com.google.common.truth.Truth.assertThat; 29 30 import android.app.AlarmManager; 31 import android.app.PendingIntent; 32 import android.net.NetworkRequest; 33 import android.os.SystemClock; 34 35 import androidx.test.core.app.ApplicationProvider; 36 import androidx.work.Configuration; 37 import androidx.work.ExistingWorkPolicy; 38 import androidx.work.OneTimeWorkRequest; 39 import androidx.work.WorkInfo; 40 import androidx.work.WorkManager; 41 import androidx.work.testing.SynchronousExecutor; 42 import androidx.work.testing.WorkManagerTestInitHelper; 43 44 import com.android.devicelockcontroller.TestDeviceLockControllerApplication; 45 import com.android.devicelockcontroller.common.DeviceLockConstants; 46 import com.android.devicelockcontroller.provision.worker.DeviceCheckInWorker; 47 import com.android.devicelockcontroller.storage.UserParameters; 48 import com.android.devicelockcontroller.util.ThreadUtils; 49 50 import com.google.common.util.concurrent.Futures; 51 52 import org.junit.Before; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.robolectric.RobolectricTestRunner; 56 import org.robolectric.Shadows; 57 import org.robolectric.shadows.ShadowAlarmManager; 58 59 import java.time.Clock; 60 import java.time.Duration; 61 import java.time.Instant; 62 import java.time.ZoneOffset; 63 import java.util.List; 64 import java.util.concurrent.TimeUnit; 65 66 @RunWith(RobolectricTestRunner.class) 67 public final class DeviceLockControllerSchedulerImplTest { 68 private static final long PROVISION_PAUSED_MILLIS = TimeUnit.MINUTES.toMillis( 69 DeviceLockControllerSchedulerImpl.PROVISION_PAUSED_MINUTES_DEFAULT); 70 private static final Duration TEST_RETRY_CHECK_IN_DELAY = Duration.ofDays(30); 71 private static final long TEST_NEXT_CHECK_IN_TIME_MILLIS = Duration.ofHours(10).toMillis(); 72 private static final long TEST_RESUME_PROVISION_TIME_MILLIS = Duration.ofHours(20).toMillis(); 73 private static final long TEST_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS = Duration.ofHours( 74 2).toMillis(); 75 private static final long TEST_RESET_DEVICE_TIME_MILLIS = Duration.ofHours( 76 50).toMillis(); 77 78 private static final long PROVISION_STATE_REPORT_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis( 79 DeviceLockControllerSchedulerImpl.PROVISION_STATE_REPORT_INTERVAL_DEFAULT_MINUTES); 80 private static final long TEST_CURRENT_TIME_MILLIS = Duration.ofHours(5).toMillis(); 81 private static final Clock TEST_CLOCK = Clock.fixed( 82 Instant.ofEpochMilli(TEST_CURRENT_TIME_MILLIS), 83 ZoneOffset.UTC); 84 public static final long TEST_POSITIVE_DELTA_MILLIS = Duration.ofHours(5).toMillis(); 85 public static final long TEST_NEGATIVE_DELTA_MILLIS = Duration.ofHours(-5).toMillis(); 86 private static final long RESET_DEVICE_MILLIS = TimeUnit.MINUTES.toMillis( 87 DeviceLockConstants.NON_MANDATORY_PROVISION_DEVICE_RESET_COUNTDOWN_MINUTE); 88 DeviceLockControllerSchedulerImpl mScheduler; 89 TestDeviceLockControllerApplication mTestApp; 90 91 @Before setUp()92 public void setUp() throws Exception { 93 mTestApp = ApplicationProvider.getApplicationContext(); 94 mScheduler = new DeviceLockControllerSchedulerImpl(mTestApp, TEST_CLOCK, 95 mTestApp.getProvisionStateController()); 96 Configuration config = new Configuration.Builder() 97 .setMinimumLoggingLevel(android.util.Log.DEBUG) 98 .setExecutor(new SynchronousExecutor()) 99 .build(); 100 WorkManagerTestInitHelper.initializeTestWorkManager(mTestApp, config); 101 } 102 103 @Test correctExpectedToRunTime_retryCheckInExpected_positiveDelta_shouldUpdate()104 public void correctExpectedToRunTime_retryCheckInExpected_positiveDelta_shouldUpdate() { 105 // GIVEN retry check in is expected 106 UserParameters.setNextCheckInTimeMillis(mTestApp, TEST_NEXT_CHECK_IN_TIME_MILLIS); 107 108 // GIVEN time change delta is positive. 109 UserParameters.setBootTimeMillis(mTestApp, 110 TEST_CURRENT_TIME_MILLIS - TEST_POSITIVE_DELTA_MILLIS 111 - SystemClock.elapsedRealtime()); 112 113 114 runBySequentialExecutor(() -> { 115 // WHEN correct expected to run time for UNPROVISIONED state 116 mScheduler.correctStoredTime(UNPROVISIONED); 117 118 // THEN next check in time should be updated 119 long expectedToRunAfterChange = 120 TEST_NEXT_CHECK_IN_TIME_MILLIS + TEST_POSITIVE_DELTA_MILLIS; 121 assertThat(UserParameters.getNextCheckInTimeMillis(mTestApp)).isEqualTo( 122 expectedToRunAfterChange); 123 }); 124 } 125 126 127 @Test correctExpectedToRunTime_retryCheckInExpected_negativeDelta_shouldUpdate()128 public void correctExpectedToRunTime_retryCheckInExpected_negativeDelta_shouldUpdate() { 129 // GIVEN retry check in is expected 130 UserParameters.setNextCheckInTimeMillis(mTestApp, TEST_NEXT_CHECK_IN_TIME_MILLIS); 131 132 // GIVEN time change delta is negative. 133 UserParameters.setBootTimeMillis(mTestApp, 134 TEST_CURRENT_TIME_MILLIS - TEST_NEGATIVE_DELTA_MILLIS 135 - SystemClock.elapsedRealtime()); 136 137 runBySequentialExecutor(() -> { 138 // WHEN correct expected to run time for UNPROVISIONED state 139 mScheduler.correctStoredTime(UNPROVISIONED); 140 141 // THEN next check in time should be updated 142 long expectedToRunAfterChange = 143 TEST_NEXT_CHECK_IN_TIME_MILLIS + TEST_NEGATIVE_DELTA_MILLIS; 144 assertThat(UserParameters.getNextCheckInTimeMillis(mTestApp)).isEqualTo( 145 expectedToRunAfterChange); 146 }); 147 } 148 149 @Test correctExpectedToRunTime_resumeProvisionExpected_positiveDelta_shouldUpdate()150 public void correctExpectedToRunTime_resumeProvisionExpected_positiveDelta_shouldUpdate() { 151 // GIVEN retry check in is expected 152 UserParameters.setResumeProvisionTimeMillis(mTestApp, TEST_RESUME_PROVISION_TIME_MILLIS); 153 154 // GIVEN time change delta is positive. 155 UserParameters.setBootTimeMillis(mTestApp, 156 TEST_CURRENT_TIME_MILLIS - TEST_POSITIVE_DELTA_MILLIS 157 - SystemClock.elapsedRealtime()); 158 159 runBySequentialExecutor(() -> { 160 // WHEN correct expected to run time for PROVISION_PAUSED state 161 mScheduler.correctStoredTime(PROVISION_PAUSED); 162 163 // THEN next check in time should be updated 164 long expectedToRunAfterChange = 165 TEST_RESUME_PROVISION_TIME_MILLIS + TEST_POSITIVE_DELTA_MILLIS; 166 assertThat(UserParameters.getResumeProvisionTimeMillis(mTestApp)).isEqualTo( 167 expectedToRunAfterChange); 168 }); 169 } 170 171 @Test correctExpectedToRunTime_resumeProvisionExpected_negativeDelta_shouldUpdate()172 public void correctExpectedToRunTime_resumeProvisionExpected_negativeDelta_shouldUpdate() { 173 // GIVEN retry check in is expected 174 UserParameters.setResumeProvisionTimeMillis(mTestApp, TEST_RESUME_PROVISION_TIME_MILLIS); 175 176 // GIVEN time change delta is negative. 177 UserParameters.setBootTimeMillis(mTestApp, 178 TEST_CURRENT_TIME_MILLIS - TEST_NEGATIVE_DELTA_MILLIS 179 - SystemClock.elapsedRealtime()); 180 181 runBySequentialExecutor(() -> { 182 // WHEN correct expected to run time for PROVISION_PAUSED state 183 mScheduler.correctStoredTime(PROVISION_PAUSED); 184 185 // THEN next check in time should be updated 186 long expectedToRunAfterChange = 187 TEST_RESUME_PROVISION_TIME_MILLIS + TEST_NEGATIVE_DELTA_MILLIS; 188 assertThat(UserParameters.getResumeProvisionTimeMillis(mTestApp)).isEqualTo( 189 expectedToRunAfterChange); 190 }); 191 } 192 193 @Test scheduleResumeProvisionAlarm()194 public void scheduleResumeProvisionAlarm() { 195 // GIVEN no alarm is scheduled and no expected resume time 196 ShadowAlarmManager alarmManager = Shadows.shadowOf( 197 mTestApp.getSystemService(AlarmManager.class)); 198 assertThat(alarmManager.peekNextScheduledAlarm()).isNull(); 199 runBySequentialExecutor(() -> 200 assertThat(UserParameters.getResumeProvisionTimeMillis(mTestApp)).isEqualTo(0)); 201 202 // WHEN resume provision alarm is scheduled 203 mScheduler.scheduleResumeProvisionAlarm(); 204 205 // THEN correct alarm should be scheduled 206 PendingIntent actualPendingIntent = alarmManager.peekNextScheduledAlarm().operation; 207 assertThat(actualPendingIntent.isBroadcast()).isTrue(); 208 209 // THEN alarm should be scheduled at correct time 210 long actualTriggerTime = alarmManager.peekNextScheduledAlarm().triggerAtTime; 211 assertThat(actualTriggerTime).isEqualTo( 212 SystemClock.elapsedRealtime() + PROVISION_PAUSED_MILLIS); 213 214 // THEN expected trigger time should be stored in storage 215 runBySequentialExecutor(() -> { 216 assertThat(UserParameters.getResumeProvisionTimeMillis(mTestApp)).isEqualTo( 217 TEST_CURRENT_TIME_MILLIS + PROVISION_PAUSED_MILLIS); 218 }); 219 } 220 221 @Test rescheduleResumeProvisionAlarm()222 public void rescheduleResumeProvisionAlarm() { 223 // GIVEN no alarm is scheduled 224 ShadowAlarmManager alarmManager = Shadows.shadowOf( 225 mTestApp.getSystemService(AlarmManager.class)); 226 assertThat(alarmManager.peekNextScheduledAlarm()).isNull(); 227 228 // GIVEN expected resume time in storage 229 UserParameters.setResumeProvisionTimeMillis(mTestApp, TEST_RESUME_PROVISION_TIME_MILLIS); 230 231 // WHEN resume provision alarm is rescheduled 232 runBySequentialExecutor(mScheduler::rescheduleResumeProvisionAlarmIfNeeded); 233 234 // THEN correct alarm should be scheduled at correct time 235 PendingIntent actualPendingIntent = alarmManager.peekNextScheduledAlarm().operation; 236 assertThat(actualPendingIntent.isBroadcast()).isTrue(); 237 238 239 long expectedTriggerTime = SystemClock.elapsedRealtime() 240 + (TEST_RESUME_PROVISION_TIME_MILLIS - TEST_CURRENT_TIME_MILLIS); 241 assertThat(alarmManager.peekNextScheduledAlarm().triggerAtTime).isEqualTo( 242 expectedTriggerTime); 243 } 244 245 @Test scheduleInitialCheckInWork()246 public void scheduleInitialCheckInWork() throws Exception { 247 // GIVEN check-in work is not scheduled 248 WorkManager workManager = WorkManager.getInstance(mTestApp); 249 assertThat(workManager.getWorkInfosForUniqueWork( 250 DEVICE_CHECK_IN_WORK_NAME).get()).isEmpty(); 251 252 // WHEN schedule initial check-in work 253 mScheduler.scheduleInitialCheckInWork(); 254 255 // THEN check-in work should be scheduled 256 List<WorkInfo> actualWorks = Futures.getUnchecked(workManager.getWorkInfosForUniqueWork( 257 DEVICE_CHECK_IN_WORK_NAME)); 258 assertThat(actualWorks.size()).isEqualTo(1); 259 WorkInfo actualWorkInfo = actualWorks.get(0); 260 NetworkRequest networkRequest = actualWorkInfo.getConstraints().getRequiredNetworkRequest(); 261 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)).isTrue(); 262 assertThat(networkRequest.hasCapability(NET_CAPABILITY_TRUSTED)).isTrue(); 263 assertThat(networkRequest.hasCapability(NET_CAPABILITY_INTERNET)).isTrue(); 264 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_VPN)).isTrue(); 265 assertThat(actualWorkInfo.getInitialDelayMillis()).isEqualTo(0); 266 } 267 268 @Test scheduleRetryCheckInWork()269 public void scheduleRetryCheckInWork() throws Exception { 270 // GIVEN check-in work is not scheduled 271 WorkManager workManager = WorkManager.getInstance(mTestApp); 272 assertThat(workManager.getWorkInfosForUniqueWork( 273 DEVICE_CHECK_IN_WORK_NAME).get()).isEmpty(); 274 275 // WHEN schedule retry check-in work 276 mScheduler.scheduleRetryCheckInWork(TEST_RETRY_CHECK_IN_DELAY); 277 278 // THEN retry check-in work should be scheduled 279 List<WorkInfo> actualWorks = Futures.getUnchecked(workManager.getWorkInfosForUniqueWork( 280 DEVICE_CHECK_IN_WORK_NAME)); 281 assertThat(actualWorks.size()).isEqualTo(1); 282 WorkInfo actualWorkInfo = actualWorks.get(0); 283 NetworkRequest networkRequest = actualWorkInfo.getConstraints().getRequiredNetworkRequest(); 284 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)).isTrue(); 285 assertThat(networkRequest.hasCapability(NET_CAPABILITY_TRUSTED)).isTrue(); 286 assertThat(networkRequest.hasCapability(NET_CAPABILITY_INTERNET)).isTrue(); 287 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_VPN)).isTrue(); 288 assertThat(actualWorkInfo.getInitialDelayMillis()).isEqualTo( 289 TEST_RETRY_CHECK_IN_DELAY.toMillis()); 290 291 // THEN expected trigger time is stored in storage 292 long expectedTriggerTime = 293 TEST_CURRENT_TIME_MILLIS + TEST_RETRY_CHECK_IN_DELAY.toMillis(); 294 runBySequentialExecutor( 295 () -> assertThat(UserParameters.getNextCheckInTimeMillis(mTestApp)).isEqualTo( 296 expectedTriggerTime)); 297 } 298 299 @Test rescheduleRetryCheckInWork()300 public void rescheduleRetryCheckInWork() { 301 // GIVEN check-in work is scheduled with original delay 302 OneTimeWorkRequest request = 303 new OneTimeWorkRequest.Builder(DeviceCheckInWorker.class) 304 .setInitialDelay(TEST_RETRY_CHECK_IN_DELAY).build(); 305 WorkManager workManager = WorkManager.getInstance(mTestApp); 306 workManager.enqueueUniqueWork(DEVICE_CHECK_IN_WORK_NAME, 307 ExistingWorkPolicy.REPLACE, 308 request); 309 310 // GIVEN expected trigger time 311 UserParameters.setNextCheckInTimeMillis(mTestApp, TEST_NEXT_CHECK_IN_TIME_MILLIS); 312 313 // WHEN reschedule retry check-in work 314 runBySequentialExecutor(mScheduler::rescheduleRetryCheckInWork); 315 316 // THEN retry check-in work should be scheduled with correct delay 317 List<WorkInfo> actualWorks = Futures.getUnchecked(workManager.getWorkInfosForUniqueWork( 318 DEVICE_CHECK_IN_WORK_NAME)); 319 assertThat(actualWorks.size()).isEqualTo(1); 320 WorkInfo actualWorkInfo = actualWorks.get(0); 321 NetworkRequest networkRequest = actualWorkInfo.getConstraints().getRequiredNetworkRequest(); 322 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)).isTrue(); 323 assertThat(networkRequest.hasCapability(NET_CAPABILITY_TRUSTED)).isTrue(); 324 assertThat(networkRequest.hasCapability(NET_CAPABILITY_INTERNET)).isTrue(); 325 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_VPN)).isTrue(); 326 327 long expectedDelay = TEST_NEXT_CHECK_IN_TIME_MILLIS - TEST_CURRENT_TIME_MILLIS; 328 assertThat(actualWorkInfo.getInitialDelayMillis()).isEqualTo(expectedDelay); 329 } 330 331 @Test maybeScheduleInitialCheckIn_needInitialCheckIn_enqueuesWorker()332 public void maybeScheduleInitialCheckIn_needInitialCheckIn_enqueuesWorker() throws Exception { 333 // Make sure the initial state is "initial check in needed" 334 runBySequentialExecutor( 335 () -> assertThat(UserParameters.needInitialCheckIn(mTestApp)).isTrue()); 336 337 // GIVEN check-in work is not scheduled 338 WorkManager workManager = WorkManager.getInstance(mTestApp); 339 assertThat(workManager.getWorkInfosForUniqueWork( 340 DEVICE_CHECK_IN_WORK_NAME).get()).isEmpty(); 341 342 // WHEN maybe schedule initial check-in work 343 mScheduler.maybeScheduleInitialCheckIn().get(); 344 345 // THEN check-in work should be scheduled 346 assertThat(workManager.getWorkInfosForUniqueWork( 347 DEVICE_CHECK_IN_WORK_NAME).get()).isNotEmpty(); 348 } 349 350 @Test maybeScheduleInitialCheckIn_NoNeedInitialCheckIn_noWorker()351 public void maybeScheduleInitialCheckIn_NoNeedInitialCheckIn_noWorker() throws Exception { 352 // GIVEN initial check in marked as scheduled 353 UserParameters.initialCheckInScheduled(mTestApp); 354 355 // GIVEN check-in work is not scheduled 356 WorkManager workManager = WorkManager.getInstance(mTestApp); 357 assertThat(workManager.getWorkInfosForUniqueWork( 358 DEVICE_CHECK_IN_WORK_NAME).get()).isEmpty(); 359 360 // WHEN maybe schedule initial check-in work 361 mScheduler.maybeScheduleInitialCheckIn().get(); 362 363 // THEN check-in work should not be scheduled 364 assertThat(workManager.getWorkInfosForUniqueWork( 365 DEVICE_CHECK_IN_WORK_NAME).get()).isEmpty(); 366 } 367 368 @Test maybeScheduleInitialCheckIn_noNeedCheckIn_reschedule()369 public void maybeScheduleInitialCheckIn_noNeedCheckIn_reschedule() throws Exception { 370 // GIVEN check-in work is scheduled with original delay 371 OneTimeWorkRequest request = 372 new OneTimeWorkRequest.Builder(DeviceCheckInWorker.class) 373 .setInitialDelay(TEST_RETRY_CHECK_IN_DELAY).build(); 374 WorkManager workManager = WorkManager.getInstance(mTestApp); 375 workManager.enqueueUniqueWork(DEVICE_CHECK_IN_WORK_NAME, 376 ExistingWorkPolicy.REPLACE, 377 request); 378 379 // GIVEN initial check in marked as scheduled 380 UserParameters.initialCheckInScheduled(mTestApp); 381 382 // GIVEN expected trigger time 383 UserParameters.setNextCheckInTimeMillis(mTestApp, TEST_NEXT_CHECK_IN_TIME_MILLIS); 384 385 // WHEN maybe schedule initial check-in work 386 mScheduler.maybeScheduleInitialCheckIn().get(); 387 388 // THEN retry check-in work should be scheduled with correct delay 389 List<WorkInfo> actualWorks = Futures.getUnchecked(workManager.getWorkInfosForUniqueWork( 390 DEVICE_CHECK_IN_WORK_NAME)); 391 assertThat(actualWorks.size()).isEqualTo(1); 392 WorkInfo actualWorkInfo = actualWorks.get(0); 393 NetworkRequest networkRequest = actualWorkInfo.getConstraints().getRequiredNetworkRequest(); 394 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)).isTrue(); 395 assertThat(networkRequest.hasCapability(NET_CAPABILITY_TRUSTED)).isTrue(); 396 assertThat(networkRequest.hasCapability(NET_CAPABILITY_INTERNET)).isTrue(); 397 assertThat(networkRequest.hasCapability(NET_CAPABILITY_NOT_VPN)).isTrue(); 398 399 long expectedDelay = TEST_NEXT_CHECK_IN_TIME_MILLIS - TEST_CURRENT_TIME_MILLIS; 400 assertThat(actualWorkInfo.getInitialDelayMillis()).isEqualTo(expectedDelay); 401 } 402 403 @Test scheduleNextProvisionFailedStepAlarm_initialStep_setsAlarm()404 public void scheduleNextProvisionFailedStepAlarm_initialStep_setsAlarm() { 405 // GIVEN no alarm is scheduled 406 ShadowAlarmManager alarmManager = Shadows.shadowOf( 407 mTestApp.getSystemService(AlarmManager.class)); 408 assertThat(alarmManager.peekNextScheduledAlarm()).isNull(); 409 410 // GIVEN no existing timestamp 411 runBySequentialExecutor(() -> assertThat( 412 UserParameters.getNextProvisionFailedStepTimeMills(mTestApp)).isEqualTo(0)); 413 414 // WHEN schedule next provision failed step alarm 415 runBySequentialExecutor(() -> mScheduler.scheduleNextProvisionFailedStepAlarm()); 416 417 // THEN correct alarm should be scheduled 418 PendingIntent actualPendingIntent = alarmManager.peekNextScheduledAlarm().operation; 419 assertThat(actualPendingIntent.isBroadcast()).isTrue(); 420 421 // THEN alarm should be scheduled at correct time 422 long actualTriggerTime = alarmManager.peekNextScheduledAlarm().triggerAtTime; 423 assertThat(actualTriggerTime).isEqualTo( 424 SystemClock.elapsedRealtime() + PROVISION_STATE_REPORT_INTERVAL_MILLIS); 425 426 // THEN expected trigger time should be stored in storage 427 runBySequentialExecutor(() -> assertThat( 428 UserParameters.getNextProvisionFailedStepTimeMills(mTestApp)).isEqualTo( 429 TEST_CURRENT_TIME_MILLIS + PROVISION_STATE_REPORT_INTERVAL_MILLIS)); 430 } 431 432 @Test scheduleNextProvisionFailedStepAlarm_followUpStep_setsAlarm()433 public void scheduleNextProvisionFailedStepAlarm_followUpStep_setsAlarm() { 434 // GIVEN no alarm is scheduled 435 ShadowAlarmManager alarmManager = Shadows.shadowOf( 436 mTestApp.getSystemService(AlarmManager.class)); 437 assertThat(alarmManager.peekNextScheduledAlarm()).isNull(); 438 439 // GIVEN timestamp exists for last performed step. 440 UserParameters.setNextProvisionFailedStepTimeMills(mTestApp, 441 TEST_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS); 442 443 // WHEN schedule next provision failed step alarm 444 runBySequentialExecutor(() -> mScheduler.scheduleNextProvisionFailedStepAlarm()); 445 446 // THEN correct alarm should be scheduled 447 PendingIntent actualPendingIntent = alarmManager.peekNextScheduledAlarm().operation; 448 assertThat(actualPendingIntent.isBroadcast()).isTrue(); 449 450 // THEN alarm should be scheduled at correct time 451 long actualTriggerTime = alarmManager.peekNextScheduledAlarm().triggerAtTime; 452 long expectedTriggerTime = TEST_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS 453 + PROVISION_STATE_REPORT_INTERVAL_MILLIS - TEST_CURRENT_TIME_MILLIS 454 + SystemClock.elapsedRealtime(); 455 assertThat(actualTriggerTime).isEqualTo(expectedTriggerTime); 456 457 458 // THEN expected trigger time should be stored in storage 459 runBySequentialExecutor(() -> assertThat( 460 UserParameters.getNextProvisionFailedStepTimeMills(mTestApp)).isEqualTo( 461 TEST_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS 462 + PROVISION_STATE_REPORT_INTERVAL_MILLIS)); 463 } 464 465 @Test scheduleResetDeviceAlarm()466 public void scheduleResetDeviceAlarm() { 467 // GIVEN no alarm is scheduled 468 ShadowAlarmManager alarmManager = Shadows.shadowOf( 469 mTestApp.getSystemService(AlarmManager.class)); 470 assertThat(alarmManager.peekNextScheduledAlarm()).isNull(); 471 runBySequentialExecutor( 472 () -> assertThat(UserParameters.getResetDeviceTimeMillis(mTestApp)).isEqualTo(0)); 473 474 // WHEN schedule reset device alarm 475 mScheduler.scheduleResetDeviceAlarm(); 476 477 // THEN correct alarm should be scheduled 478 PendingIntent actualPendingIntent = alarmManager.peekNextScheduledAlarm().operation; 479 assertThat(actualPendingIntent.isBroadcast()).isTrue(); 480 481 // THEN alarm should be scheduled at correct time 482 long actualTriggerTime = alarmManager.peekNextScheduledAlarm().triggerAtTime; 483 assertThat(actualTriggerTime).isEqualTo( 484 SystemClock.elapsedRealtime() + RESET_DEVICE_MILLIS); 485 486 // THEN expected trigger time should be stored in storage 487 runBySequentialExecutor( 488 () -> assertThat(UserParameters.getResetDeviceTimeMillis(mTestApp)).isEqualTo( 489 TEST_CURRENT_TIME_MILLIS + RESET_DEVICE_MILLIS)); 490 } 491 492 @Test rescheduleNextProvisionFailedStepAlarm()493 public void rescheduleNextProvisionFailedStepAlarm() { 494 // GIVEN no alarm is scheduled 495 ShadowAlarmManager alarmManager = Shadows.shadowOf( 496 mTestApp.getSystemService(AlarmManager.class)); 497 assertThat(alarmManager.peekNextScheduledAlarm()).isNull(); 498 499 // GIVEN expected time in storage 500 UserParameters.setNextProvisionFailedStepTimeMills(mTestApp, 501 TEST_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS); 502 503 // WHEN next provision failed step alarm is rescheduled 504 runBySequentialExecutor(mScheduler::rescheduleNextProvisionFailedStepAlarmIfNeeded); 505 506 // THEN correct alarm should be scheduled at correct time 507 PendingIntent actualPendingIntent = alarmManager.peekNextScheduledAlarm().operation; 508 assertThat(actualPendingIntent.isBroadcast()).isTrue(); 509 510 long expectedTriggerTime = SystemClock.elapsedRealtime() 511 + (TEST_NEXT_PROVISION_FAILED_STEP_TIME_MILLIS - TEST_CURRENT_TIME_MILLIS); 512 assertThat(alarmManager.peekNextScheduledAlarm().triggerAtTime).isEqualTo( 513 expectedTriggerTime); 514 } 515 516 @Test rescheduleResetDeviceAlarm()517 public void rescheduleResetDeviceAlarm() { 518 // GIVEN no alarm is scheduled 519 ShadowAlarmManager alarmManager = Shadows.shadowOf( 520 mTestApp.getSystemService(AlarmManager.class)); 521 assertThat(alarmManager.peekNextScheduledAlarm()).isNull(); 522 523 // GIVEN expected reset device time in storage 524 UserParameters.setResetDeviceTimeMillis(mTestApp, TEST_RESET_DEVICE_TIME_MILLIS); 525 526 // WHEN reset device alarm is rescheduled 527 runBySequentialExecutor(mScheduler::rescheduleResetDeviceAlarmIfNeeded); 528 529 // THEN correct alarm should be scheduled at correct time 530 PendingIntent actualPendingIntent = alarmManager.peekNextScheduledAlarm().operation; 531 assertThat(actualPendingIntent.isBroadcast()).isTrue(); 532 533 long expectedTriggerTime = SystemClock.elapsedRealtime() 534 + (TEST_RESET_DEVICE_TIME_MILLIS - TEST_CURRENT_TIME_MILLIS); 535 assertThat(alarmManager.peekNextScheduledAlarm().triggerAtTime).isEqualTo( 536 expectedTriggerTime); 537 } 538 runBySequentialExecutor(Runnable runnable)539 private static void runBySequentialExecutor(Runnable runnable) { 540 Futures.getUnchecked( 541 Futures.submit(runnable, ThreadUtils.getSequentialSchedulerExecutor())); 542 } 543 } 544