1 package com.android.server.deviceconfig; 2 3 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 4 import static com.android.server.deviceconfig.Flags.FLAG_ENABLE_CHARGER_DEPENDENCY_FOR_REBOOT; 5 import static com.android.server.deviceconfig.Flags.FLAG_ENABLE_CUSTOM_REBOOT_TIME_CONFIGURATIONS; 6 import static com.android.server.deviceconfig.Flags.FLAG_ENABLE_SIM_PIN_REPLAY; 7 8 import static com.android.server.deviceconfig.UnattendedRebootManager.ACTION_RESUME_ON_REBOOT_LSKF_CAPTURED; 9 import static com.android.server.deviceconfig.UnattendedRebootManager.ACTION_TRIGGER_PREPARATION_FALLBACK; 10 import static com.android.server.deviceconfig.UnattendedRebootManager.ACTION_TRIGGER_REBOOT; 11 import static com.google.common.truth.Truth.assertThat; 12 import static org.junit.Assert.assertFalse; 13 import static org.junit.Assert.assertTrue; 14 import static org.junit.Assume.assumeTrue; 15 import static org.mockito.ArgumentMatchers.any; 16 import static org.mockito.ArgumentMatchers.anyInt; 17 import static org.mockito.Mockito.mock; 18 import static org.mockito.Mockito.verify; 19 import static org.mockito.Mockito.when; 20 21 import android.platform.test.flag.junit.SetFlagsRule; 22 import androidx.annotation.NonNull; 23 import androidx.annotation.Nullable; 24 import android.app.KeyguardManager; 25 import android.content.BroadcastReceiver; 26 import android.content.ContextWrapper; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.IntentSender; 31 import android.content.res.Resources; 32 import android.net.ConnectivityManager; 33 import android.net.NetworkCapabilities; 34 import android.os.BatteryManager; 35 import android.util.Log; 36 37 import androidx.test.filters.SmallTest; 38 import java.time.ZoneId; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Queue; 42 import java.util.concurrent.CountDownLatch; 43 import java.util.concurrent.TimeUnit; 44 import java.util.stream.Collectors; 45 46 import org.junit.Before; 47 import org.junit.Rule; 48 import org.junit.Test; 49 import org.mockito.ArgumentCaptor; 50 51 import com.android.modules.utils.build.SdkLevel; 52 53 @SmallTest 54 public class UnattendedRebootManagerTest { 55 56 private static final String TAG = "UnattendedRebootManagerTest"; 57 58 private static final int REBOOT_FREQUENCY = 1; 59 private static final int REBOOT_START_HOUR = 2; 60 private static final int REBOOT_END_HOUR = 3; 61 62 private static final long CURRENT_TIME = 1696452549304L; // 2023-10-04T13:49:09.304 63 private static final long REBOOT_TIME = 1696497120000L; // 2023-10-05T02:12:00 64 private static final long RESCHEDULED_REBOOT_TIME = 1696583520000L; // 2023-10-06T02:12:00 65 private static final long OUTSIDE_WINDOW_REBOOT_TIME = 1696587000000L; // 2023-10-06T03:10:00 66 private static final long RESCHEDULED_OUTSIDE_WINDOW_REBOOT_TIME = 67 1696669920000L; // 2023-10-07T02:12:00 68 private static final long ELAPSED_REALTIME_1_DAY = 86400000L; 69 70 private final List<BroadcastReceiverRegistration> mRegisteredReceivers = new ArrayList<>(); 71 72 private Context mContext; 73 private BatteryManager mBatterManager; 74 private KeyguardManager mKeyguardManager; 75 private ConnectivityManager mConnectivityManager; 76 private RebootTimingConfiguration mRebootTimingConfiguration; 77 private FakeInjector mFakeInjector; 78 private UnattendedRebootManager mRebootManager; 79 private SimPinReplayManager mSimPinReplayManager; 80 81 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 82 83 @Before setUp()84 public void setUp() throws Exception { 85 assumeTrue(SdkLevel.isAtLeastV()); 86 87 mSetFlagsRule.enableFlags( 88 FLAG_ENABLE_SIM_PIN_REPLAY, FLAG_ENABLE_CHARGER_DEPENDENCY_FOR_REBOOT); 89 90 mSimPinReplayManager = mock(SimPinReplayManager.class); 91 mKeyguardManager = mock(KeyguardManager.class); 92 mConnectivityManager = mock(ConnectivityManager.class); 93 mBatterManager = mock(BatteryManager.class); 94 95 mRebootTimingConfiguration = 96 new RebootTimingConfiguration(REBOOT_START_HOUR, REBOOT_END_HOUR, REBOOT_FREQUENCY); 97 98 mContext = 99 new ContextWrapper(getInstrumentation().getTargetContext()) { 100 @Override 101 public Object getSystemService(String name) { 102 if (name.equals(Context.KEYGUARD_SERVICE)) { 103 return mKeyguardManager; 104 } else if (name.equals(Context.CONNECTIVITY_SERVICE)) { 105 return mConnectivityManager; 106 } else if (name.equals(Context.BATTERY_SERVICE)) { 107 return mBatterManager; 108 } 109 return super.getSystemService(name); 110 } 111 112 @Override 113 public Intent registerReceiver( 114 @Nullable BroadcastReceiver receiver, IntentFilter filter, int flags) { 115 mRegisteredReceivers.add(new BroadcastReceiverRegistration(receiver, filter, flags)); 116 return super.registerReceiver(receiver, filter, flags); 117 } 118 }; 119 120 mFakeInjector = new FakeInjector(); 121 mRebootManager = 122 new UnattendedRebootManager( 123 mContext, mFakeInjector, mSimPinReplayManager, mRebootTimingConfiguration); 124 125 // Need to register receiver in tests so that the test doesn't trigger reboot requested by 126 // deviceconfig. 127 mContext.registerReceiver( 128 new BroadcastReceiver() { 129 @Override 130 public void onReceive(Context context, Intent intent) { 131 mRebootManager.tryRebootOrSchedule(); 132 } 133 }, 134 new IntentFilter(ACTION_TRIGGER_REBOOT), 135 Context.RECEIVER_EXPORTED); 136 137 mContext.registerReceiver( 138 new BroadcastReceiver() { 139 @Override 140 public void onReceive(Context context, Intent intent) { 141 mRebootManager.prepareUnattendedReboot(); 142 } 143 }, 144 new IntentFilter(ACTION_TRIGGER_PREPARATION_FALLBACK), 145 Context.RECEIVER_EXPORTED); 146 147 mFakeInjector.setElapsedRealtime(ELAPSED_REALTIME_1_DAY); 148 149 mFakeInjector.setRequiresChargingForReboot(true); 150 when(mBatterManager.isCharging()).thenReturn(true); 151 } 152 153 @Test maybePrepareUnattendedReboot()154 public void maybePrepareUnattendedReboot() { 155 assumeTrue(SdkLevel.isAtLeastV()); 156 157 // After normal flow 158 Log.i(TAG, "maybePrepareUnattendedReboot"); 159 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 160 161 mRebootManager.maybePrepareUnattendedReboot(); 162 163 assertTrue(mFakeInjector.isPreparedForUnattendedUpdate(mContext)); 164 } 165 166 @Test scheduleReboot()167 public void scheduleReboot() { 168 assumeTrue(SdkLevel.isAtLeastV()); 169 170 Log.i(TAG, "scheduleReboot"); 171 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 172 when(mConnectivityManager.getNetworkCapabilities(any())) 173 .thenReturn( 174 new NetworkCapabilities.Builder() 175 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 176 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 177 .build()); 178 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 179 180 mRebootManager.prepareUnattendedReboot(); 181 mRebootManager.scheduleReboot(); 182 183 assertTrue(mFakeInjector.isRebootAndApplied()); 184 assertFalse(mFakeInjector.isRegularRebooted()); 185 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(REBOOT_TIME); 186 } 187 188 @Test scheduleReboot_requiresCharging_notCharging()189 public void scheduleReboot_requiresCharging_notCharging() { 190 assumeTrue(SdkLevel.isAtLeastV()); 191 192 Log.i(TAG, "scheduleReboot_requiresCharging_notCharging"); 193 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 194 when(mConnectivityManager.getNetworkCapabilities(any())) 195 .thenReturn( 196 new NetworkCapabilities.Builder() 197 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 198 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 199 .build()); 200 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 201 mSetFlagsRule.enableFlags(FLAG_ENABLE_CHARGER_DEPENDENCY_FOR_REBOOT); 202 mFakeInjector.setRequiresChargingForReboot(true); 203 when(mBatterManager.isCharging()).thenReturn(false); 204 205 mRebootManager.prepareUnattendedReboot(); 206 mRebootManager.scheduleReboot(); 207 208 // Charging is required and device is not charging, so reboot should not be triggered. 209 assertFalse(mFakeInjector.isRebootAndApplied()); 210 assertFalse(mFakeInjector.isRegularRebooted()); 211 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(REBOOT_TIME); 212 List<BroadcastReceiverRegistration> chargingStateReceiverRegistrations = 213 getRegistrationsForAction(BatteryManager.ACTION_CHARGING); 214 assertThat(chargingStateReceiverRegistrations).hasSize(1); 215 216 // Now mimic a change in a charging state changed, and verify that we do the reboot once 217 // device 218 // is charging. 219 when(mBatterManager.isCharging()).thenReturn(true); 220 BroadcastReceiver chargingStateReceiver = chargingStateReceiverRegistrations.get(0).mReceiver; 221 chargingStateReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); 222 223 assertTrue(mFakeInjector.isRebootAndApplied()); 224 } 225 226 @Test scheduleReboot_doesNotRequireCharging_notCharging()227 public void scheduleReboot_doesNotRequireCharging_notCharging() { 228 assumeTrue(SdkLevel.isAtLeastV()); 229 Log.i(TAG, "scheduleReboot_doesNotRequireCharging_notCharging"); 230 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 231 when(mConnectivityManager.getNetworkCapabilities(any())) 232 .thenReturn( 233 new NetworkCapabilities.Builder() 234 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 235 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 236 .build()); 237 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 238 mSetFlagsRule.enableFlags(FLAG_ENABLE_CHARGER_DEPENDENCY_FOR_REBOOT); 239 mFakeInjector.setRequiresChargingForReboot(false); 240 when(mBatterManager.isCharging()).thenReturn(false); 241 242 mRebootManager.prepareUnattendedReboot(); 243 mRebootManager.scheduleReboot(); 244 245 // Charging is not required, so reboot should be triggered despite the fact that the device 246 // is not charging. 247 assertTrue(mFakeInjector.isRebootAndApplied()); 248 assertFalse(mFakeInjector.isRegularRebooted()); 249 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(REBOOT_TIME); 250 assertThat(getRegistrationsForAction(BatteryManager.ACTION_CHARGING)).isEmpty(); 251 } 252 253 @Test scheduleReboot_requiresCharging_flagNotEnabled()254 public void scheduleReboot_requiresCharging_flagNotEnabled() { 255 assumeTrue(SdkLevel.isAtLeastV()); 256 Log.i(TAG, "scheduleReboot_requiresCharging_flagNotEnabled"); 257 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 258 when(mConnectivityManager.getNetworkCapabilities(any())) 259 .thenReturn( 260 new NetworkCapabilities.Builder() 261 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 262 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 263 .build()); 264 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 265 mSetFlagsRule.disableFlags(FLAG_ENABLE_CHARGER_DEPENDENCY_FOR_REBOOT); 266 mFakeInjector.setRequiresChargingForReboot(true); 267 when(mBatterManager.isCharging()).thenReturn(false); 268 269 mRebootManager.prepareUnattendedReboot(); 270 mRebootManager.scheduleReboot(); 271 272 // Charging is required, but the flag that controls the feature to depend on charging is not 273 // enabled, so eboot should be triggered despite the fact that the device is not charging. 274 assertTrue(mFakeInjector.isRebootAndApplied()); 275 assertFalse(mFakeInjector.isRegularRebooted()); 276 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(REBOOT_TIME); 277 assertThat(getRegistrationsForAction(BatteryManager.ACTION_CHARGING)).isEmpty(); 278 } 279 280 @Test scheduleReboot_noPinLock()281 public void scheduleReboot_noPinLock() { 282 assumeTrue(SdkLevel.isAtLeastV()); 283 Log.i(TAG, "scheduleReboot_noPinLock"); 284 when(mKeyguardManager.isDeviceSecure()).thenReturn(false); 285 when(mConnectivityManager.getNetworkCapabilities(any())) 286 .thenReturn( 287 new NetworkCapabilities.Builder() 288 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 289 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 290 .build()); 291 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 292 293 mRebootManager.prepareUnattendedReboot(); 294 mRebootManager.scheduleReboot(); 295 296 assertFalse(mFakeInjector.isRebootAndApplied()); 297 assertTrue(mFakeInjector.isRegularRebooted()); 298 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(REBOOT_TIME); 299 } 300 301 @Test scheduleReboot_noPreparation()302 public void scheduleReboot_noPreparation() { 303 assumeTrue(SdkLevel.isAtLeastV()); 304 Log.i(TAG, "scheduleReboot_noPreparation"); 305 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 306 when(mConnectivityManager.getNetworkCapabilities(any())) 307 .thenReturn( 308 new NetworkCapabilities.Builder() 309 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 310 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 311 .build()); 312 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 313 314 mRebootManager.scheduleReboot(); 315 316 assertFalse(mFakeInjector.isRebootAndApplied()); 317 assertFalse(mFakeInjector.isRegularRebooted()); 318 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(RESCHEDULED_REBOOT_TIME); 319 } 320 321 @Test scheduleReboot_simPinPreparationFailed()322 public void scheduleReboot_simPinPreparationFailed() { 323 assumeTrue(SdkLevel.isAtLeastV()); 324 Log.i(TAG, "scheduleReboot"); 325 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 326 when(mConnectivityManager.getNetworkCapabilities(any())) 327 .thenReturn( 328 new NetworkCapabilities.Builder() 329 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 330 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 331 .build()); 332 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(false).thenReturn(true); 333 334 mRebootManager.prepareUnattendedReboot(); 335 mRebootManager.scheduleReboot(); 336 337 assertTrue(mFakeInjector.isRebootAndApplied()); 338 assertFalse(mFakeInjector.isRegularRebooted()); 339 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(RESCHEDULED_REBOOT_TIME); 340 } 341 342 @Test scheduleReboot_noInternet()343 public void scheduleReboot_noInternet() { 344 assumeTrue(SdkLevel.isAtLeastV()); 345 Log.i(TAG, "scheduleReboot_noInternet"); 346 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 347 when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(new NetworkCapabilities()); 348 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 349 350 mRebootManager.prepareUnattendedReboot(); 351 mRebootManager.scheduleReboot(); 352 353 assertFalse(mFakeInjector.isRebootAndApplied()); 354 assertFalse(mFakeInjector.isRegularRebooted()); 355 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(REBOOT_TIME); 356 assertTrue(mFakeInjector.isRequestedNetwork()); 357 } 358 359 @Test scheduleReboot_noInternetValidation()360 public void scheduleReboot_noInternetValidation() { 361 assumeTrue(SdkLevel.isAtLeastV()); 362 Log.i(TAG, "scheduleReboot_noInternetValidation"); 363 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 364 when(mConnectivityManager.getNetworkCapabilities(any())) 365 .thenReturn( 366 new NetworkCapabilities.Builder() 367 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 368 .build()); 369 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 370 371 mRebootManager.prepareUnattendedReboot(); 372 mRebootManager.scheduleReboot(); 373 374 assertFalse(mFakeInjector.isRebootAndApplied()); 375 assertFalse(mFakeInjector.isRegularRebooted()); 376 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(REBOOT_TIME); 377 assertTrue(mFakeInjector.isRequestedNetwork()); 378 } 379 380 @Test scheduleReboot_elapsedRealtimeLessThanFrequency_withDefaultTimeConfigurations()381 public void scheduleReboot_elapsedRealtimeLessThanFrequency_withDefaultTimeConfigurations() { 382 assumeTrue(SdkLevel.isAtLeastV()); 383 scheduleReboot_elapsedRealtimeLessThanFrequency(/* enableCustomTimeConfig= */ false); 384 } 385 386 @Test scheduleReboot_elapsedRealtimeLessThanFrequency_withCustomTimeConfigurations()387 public void scheduleReboot_elapsedRealtimeLessThanFrequency_withCustomTimeConfigurations() { 388 assumeTrue(SdkLevel.isAtLeastV()); 389 scheduleReboot_elapsedRealtimeLessThanFrequency(/* enableCustomTimeConfig= */ true); 390 } 391 scheduleReboot_elapsedRealtimeLessThanFrequency(boolean enableCustomTimeConfig)392 private void scheduleReboot_elapsedRealtimeLessThanFrequency(boolean enableCustomTimeConfig) { 393 assumeTrue(SdkLevel.isAtLeastV()); 394 if (enableCustomTimeConfig) { 395 mSetFlagsRule.enableFlags(FLAG_ENABLE_CUSTOM_REBOOT_TIME_CONFIGURATIONS); 396 } else { 397 mSetFlagsRule.disableFlags(FLAG_ENABLE_CUSTOM_REBOOT_TIME_CONFIGURATIONS); 398 } 399 Log.i(TAG, "scheduleReboot_elapsedRealtimeLessThanFrequency"); 400 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 401 when(mConnectivityManager.getNetworkCapabilities(any())) 402 .thenReturn( 403 new NetworkCapabilities.Builder() 404 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 405 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 406 .build()); 407 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 408 mFakeInjector.setElapsedRealtime(82800000); // 23 hours 409 410 mRebootManager.prepareUnattendedReboot(); 411 mRebootManager.scheduleReboot(); 412 413 assertFalse(mFakeInjector.isRebootAndApplied()); 414 assertFalse(mFakeInjector.isRegularRebooted()); 415 assertThat(mFakeInjector.getActualRebootTime()).isEqualTo(RESCHEDULED_REBOOT_TIME); 416 } 417 418 @Test tryRebootOrSchedule_outsideRebootWindow_withDefaultTimeConfigurations()419 public void tryRebootOrSchedule_outsideRebootWindow_withDefaultTimeConfigurations() { 420 assumeTrue(SdkLevel.isAtLeastV()); 421 tryRebootOrSchedule_outsideRebootWindow(/* enableCustomTimeConfig= */ false); 422 } 423 424 @Test tryRebootOrSchedule_outsideRebootWindow_withCustomTimeConfigurations()425 public void tryRebootOrSchedule_outsideRebootWindow_withCustomTimeConfigurations() { 426 assumeTrue(SdkLevel.isAtLeastV()); 427 tryRebootOrSchedule_outsideRebootWindow(/* enableCustomTimeConfig= */ true); 428 } 429 tryRebootOrSchedule_outsideRebootWindow(boolean enableCustomTimeConfig)430 private void tryRebootOrSchedule_outsideRebootWindow(boolean enableCustomTimeConfig) { 431 assumeTrue(SdkLevel.isAtLeastV()); 432 if (enableCustomTimeConfig) { 433 mSetFlagsRule.enableFlags(FLAG_ENABLE_CUSTOM_REBOOT_TIME_CONFIGURATIONS); 434 } 435 Log.i(TAG, "scheduleReboot_internetOutsideRebootWindow"); 436 when(mKeyguardManager.isDeviceSecure()).thenReturn(true); 437 when(mConnectivityManager.getNetworkCapabilities(any())) 438 .thenReturn( 439 new NetworkCapabilities.Builder() 440 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 441 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 442 .build()); 443 when(mSimPinReplayManager.prepareSimPinReplay()).thenReturn(true); 444 mFakeInjector.setNow(OUTSIDE_WINDOW_REBOOT_TIME); 445 446 mRebootManager.prepareUnattendedReboot(); 447 // Simulating case when reboot is tried after network connection is established outside the 448 // reboot window. 449 mRebootManager.tryRebootOrSchedule(); 450 451 assertTrue(mFakeInjector.isRebootAndApplied()); 452 assertFalse(mFakeInjector.isRegularRebooted()); 453 assertThat(mFakeInjector.getActualRebootTime()) 454 .isEqualTo(RESCHEDULED_OUTSIDE_WINDOW_REBOOT_TIME); 455 } 456 457 static class FakeInjector implements UnattendedRebootManagerInjector { 458 459 private boolean isPreparedForUnattendedReboot; 460 private boolean requiresChargingForReboot; 461 private boolean rebootAndApplied; 462 private boolean regularRebooted; 463 private boolean requestedNetwork; 464 private long actualRebootTime; 465 private boolean scheduledReboot; 466 467 private long nowMillis; 468 469 private long elapsedRealtimeMillis; 470 FakeInjector()471 FakeInjector() { 472 nowMillis = CURRENT_TIME; 473 } 474 475 @Override prepareForUnattendedUpdate( @onNull Context context, @NonNull String updateToken, @Nullable IntentSender intentSender)476 public void prepareForUnattendedUpdate( 477 @NonNull Context context, 478 @NonNull String updateToken, 479 @Nullable IntentSender intentSender) { 480 context.sendBroadcast(new Intent(ACTION_RESUME_ON_REBOOT_LSKF_CAPTURED)); 481 isPreparedForUnattendedReboot = true; 482 } 483 484 @Override isPreparedForUnattendedUpdate(@onNull Context context)485 public boolean isPreparedForUnattendedUpdate(@NonNull Context context) { 486 return isPreparedForUnattendedReboot; 487 } 488 489 @Override requiresChargingForReboot(Context context)490 public boolean requiresChargingForReboot(Context context) { 491 return requiresChargingForReboot; 492 } 493 setRequiresChargingForReboot(boolean requiresCharging)494 void setRequiresChargingForReboot(boolean requiresCharging) { 495 requiresChargingForReboot = requiresCharging; 496 } 497 498 @Override rebootAndApply( @onNull Context context, @NonNull String reason, boolean slotSwitch)499 public int rebootAndApply( 500 @NonNull Context context, @NonNull String reason, boolean slotSwitch) { 501 rebootAndApplied = true; 502 return 0; // No error. 503 } 504 505 @Override getRebootFrequency()506 public int getRebootFrequency() { 507 return REBOOT_FREQUENCY; 508 } 509 510 @Override setRebootAlarm(Context context, long rebootTimeMillis)511 public void setRebootAlarm(Context context, long rebootTimeMillis) { 512 // To prevent infinite loop, do not simulate another reboot if reboot was already 513 // scheduled. 514 if (scheduledReboot) { 515 actualRebootTime = rebootTimeMillis; 516 actualRebootTime = rebootTimeMillis; 517 return; 518 } 519 // Advance now to reboot time and reboot immediately. 520 scheduledReboot = true; 521 actualRebootTime = rebootTimeMillis; 522 triggerAlarmImmediately(context, rebootTimeMillis, ACTION_TRIGGER_REBOOT); 523 } 524 triggerAlarmImmediately(Context context, long time, String intent)525 private void triggerAlarmImmediately(Context context, long time, String intent) { 526 setNow(time); 527 528 LatchingBroadcastReceiver rebootReceiver = new LatchingBroadcastReceiver(); 529 530 // Wait for reboot broadcast to be sent. 531 context.sendOrderedBroadcast(new Intent(intent), null, rebootReceiver, null, 0, null, null); 532 533 rebootReceiver.await(20, TimeUnit.SECONDS); 534 } 535 536 @Override setPrepareForUnattendedRebootFallbackAlarm(Context context, long delayMillis)537 public void setPrepareForUnattendedRebootFallbackAlarm(Context context, long delayMillis) { 538 triggerAlarmImmediately(context, delayMillis, ACTION_TRIGGER_PREPARATION_FALLBACK); 539 } 540 541 @Override cancelPrepareForUnattendedRebootFallbackAlarm(Context context)542 public void cancelPrepareForUnattendedRebootFallbackAlarm(Context context) { 543 /*no op */ 544 } 545 546 @Override triggerRebootOnNetworkAvailable(Context context)547 public void triggerRebootOnNetworkAvailable(Context context) { 548 requestedNetwork = true; 549 } 550 isRequestedNetwork()551 public boolean isRequestedNetwork() { 552 return requestedNetwork; 553 } 554 555 @Override getRebootStartTime()556 public int getRebootStartTime() { 557 return REBOOT_START_HOUR; 558 } 559 560 @Override getRebootEndTime()561 public int getRebootEndTime() { 562 return REBOOT_END_HOUR; 563 } 564 565 @Override now()566 public long now() { 567 return nowMillis; 568 } 569 setNow(long nowMillis)570 public void setNow(long nowMillis) { 571 this.nowMillis = nowMillis; 572 } 573 574 @Override zoneId()575 public ZoneId zoneId() { 576 return ZoneId.of("America/Los_Angeles"); 577 } 578 579 @Override elapsedRealtime()580 public long elapsedRealtime() { 581 return elapsedRealtimeMillis; 582 } 583 setElapsedRealtime(long elapsedRealtimeMillis)584 public void setElapsedRealtime(long elapsedRealtimeMillis) { 585 this.elapsedRealtimeMillis = elapsedRealtimeMillis; 586 } 587 588 @Override regularReboot(Context context)589 public void regularReboot(Context context) { 590 regularRebooted = true; 591 } 592 isRebootAndApplied()593 boolean isRebootAndApplied() { 594 return rebootAndApplied; 595 } 596 isRegularRebooted()597 boolean isRegularRebooted() { 598 return regularRebooted; 599 } 600 getActualRebootTime()601 public long getActualRebootTime() { 602 return actualRebootTime; 603 } 604 } 605 606 /** 607 * A {@link BroadcastReceiver} with an internal latch that unblocks once any intent is received. 608 */ 609 private static class LatchingBroadcastReceiver extends BroadcastReceiver { 610 private CountDownLatch latch = new CountDownLatch(1); 611 612 @Override onReceive(Context context, Intent intent)613 public void onReceive(Context context, Intent intent) { 614 latch.countDown(); 615 } 616 await(long timeoutInMs, TimeUnit timeUnit)617 public boolean await(long timeoutInMs, TimeUnit timeUnit) { 618 try { 619 return latch.await(timeoutInMs, timeUnit); 620 } catch (InterruptedException e) { 621 throw new RuntimeException(e); 622 } 623 } 624 } 625 getRegistrationsForAction(String action)626 private List<BroadcastReceiverRegistration> getRegistrationsForAction(String action) { 627 return mRegisteredReceivers.stream() 628 .filter(r -> r.mFilter.hasAction(action)) 629 .collect(Collectors.toList()); 630 } 631 632 /** Data class to store BroadcastReceiver registration info. */ 633 private static final class BroadcastReceiverRegistration { 634 final BroadcastReceiver mReceiver; 635 final IntentFilter mFilter; 636 final int mFlags; 637 BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter, int flags)638 BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter, int flags) { 639 mReceiver = receiver; 640 mFilter = filter; 641 mFlags = flags; 642 } 643 } 644 } 645