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