1use anyhow::{Context, Error, bail};
6use carnelian::input::consumer_control::Phase;
7use fidl_fuchsia_input::ConsumerControlButton;
8use fidl_fuchsia_paver::{BootManagerMarker, BootManagerProxy, Configuration, PaverMarker};
9use fidl_fuchsia_recovery::FactoryResetMarker;
10use fuchsia_component::client::connect_to_protocol;
11
12#[derive(Debug, PartialEq, Copy, Clone)]
13pub enum FactoryResetState {
14 Waiting,
15 AwaitingPolicy(usize),
16 StartCountdown,
17 CancelCountdown,
18 ExecuteReset,
19 AwaitingReset,
20}
21
22#[derive(Debug, PartialEq, Copy, Clone)]
23pub enum ResetEvent {
24 ButtonPress(ConsumerControlButton, Phase),
25 AwaitPolicyResult(usize, bool),
26 CountdownFinished,
27 CountdownCancelled,
28}
29
30pub struct FactoryResetStateMachine {
31 volume_up_phase: Phase,
32 volume_down_phase: Phase,
33 state: FactoryResetState,
34 last_policy_check_id: usize,
35}
36
37impl FactoryResetStateMachine {
38 pub fn new() -> FactoryResetStateMachine {
39 FactoryResetStateMachine {
40 volume_down_phase: Phase::Up,
41 volume_up_phase: Phase::Up,
42 state: FactoryResetState::Waiting,
43 last_policy_check_id: 0,
44 }
45 }
46
47 pub fn is_counting_down(&self) -> bool {
48 self.state == FactoryResetState::StartCountdown
49 }
50
51 fn update_button_state(&mut self, button: ConsumerControlButton, phase: Phase) {
52 match button {
53 ConsumerControlButton::VolumeUp => self.volume_up_phase = phase,
54 ConsumerControlButton::VolumeDown => self.volume_down_phase = phase,
55 _ => panic!("Invalid button provided {:?}", button),
56 };
57 }
58
59 fn check_buttons_pressed(&self) -> bool {
60 match (self.volume_up_phase, self.volume_down_phase) {
61 (Phase::Down, Phase::Down) => true,
62 _ => false,
63 }
64 }
65
66 pub fn handle_event(&mut self, event: ResetEvent) -> FactoryResetState {
70 if let ResetEvent::ButtonPress(button, phase) = event {
71 self.update_button_state(button, phase);
73 }
74 let new_state = match self.state {
75 FactoryResetState::Waiting => match event {
76 ResetEvent::ButtonPress(_, _) => {
77 if self.check_buttons_pressed() {
78 self.last_policy_check_id += 1;
79 FactoryResetState::AwaitingPolicy(self.last_policy_check_id)
80 } else {
81 FactoryResetState::Waiting
82 }
83 }
84 ResetEvent::CountdownFinished => {
85 panic!("Not expecting timer updates when in waiting state")
86 }
87 ResetEvent::CountdownCancelled | ResetEvent::AwaitPolicyResult(_, _) => {
88 FactoryResetState::Waiting
89 }
90 },
91 FactoryResetState::AwaitingPolicy(check_id) => match event {
92 ResetEvent::ButtonPress(_, _) => {
93 if !self.check_buttons_pressed() {
94 FactoryResetState::Waiting
95 } else {
96 FactoryResetState::AwaitingPolicy(check_id)
97 }
98 }
99 ResetEvent::AwaitPolicyResult(check_id, fdr_enabled)
100 if check_id == self.last_policy_check_id =>
101 {
102 if fdr_enabled {
103 println!("recovery: start reset countdown");
104 FactoryResetState::StartCountdown
105 } else {
106 FactoryResetState::Waiting
107 }
108 }
109 _ => FactoryResetState::Waiting,
110 },
111 FactoryResetState::StartCountdown => match event {
112 ResetEvent::ButtonPress(_, _) => {
113 if self.check_buttons_pressed() {
114 panic!(
115 "Not expecting both buttons to be pressed while in StartCountdown state"
116 );
117 } else {
118 println!("recovery: cancel reset countdown");
119 FactoryResetState::CancelCountdown
120 }
121 }
122 ResetEvent::CountdownCancelled => {
123 panic!(
124 "Not expecting CountdownCancelled here, expecting input event to \
125 move to CancelCountdown state first."
126 );
127 }
128 ResetEvent::CountdownFinished => {
129 println!("recovery: execute factory reset");
130 FactoryResetState::ExecuteReset
131 }
132 ResetEvent::AwaitPolicyResult(_, _) => FactoryResetState::StartCountdown,
133 },
134 FactoryResetState::CancelCountdown => match event {
135 ResetEvent::CountdownCancelled => FactoryResetState::Waiting,
136 ResetEvent::AwaitPolicyResult(_, _) | ResetEvent::ButtonPress(_, _) => {
137 FactoryResetState::CancelCountdown
138 }
139 _ => panic!("Only expecting CountdownCancelled event in CancelCountdown state."),
140 },
141 FactoryResetState::ExecuteReset => match event {
142 ResetEvent::AwaitPolicyResult(_, _) => FactoryResetState::AwaitingReset,
143 ResetEvent::ButtonPress(_, _) => FactoryResetState::AwaitingReset,
145 _ => {
146 panic!("Not expecting countdown events while in ExecuteReset state")
147 }
148 },
149 FactoryResetState::AwaitingReset => match event {
150 ResetEvent::AwaitPolicyResult(_, _) => FactoryResetState::AwaitingReset,
151 ResetEvent::ButtonPress(_, _) => FactoryResetState::AwaitingReset,
152 _ => panic!("Not expecting countdown events while in ExecuteReset state"),
153 },
154 };
155
156 self.state = new_state;
157 new_state
158 }
159
160 #[cfg(test)]
161 pub fn get_state(&self) -> FactoryResetState {
162 return self.state;
163 }
164}
165
166fn get_other_slot_config(config: Configuration) -> Configuration {
167 match config {
168 Configuration::A => Configuration::B,
169 Configuration::B => Configuration::A,
170 Configuration::Recovery => Configuration::Recovery,
172 }
173}
174
175pub async fn reset_active_slot() -> Result<(), Error> {
184 let paver_proxy = connect_to_protocol::<PaverMarker>().context("failed to connect to paver")?;
185 let (boot_manager, server) = fidl::endpoints::create_proxy::<BootManagerMarker>();
186
187 paver_proxy.find_boot_manager(server).context("failed to find boot manager")?;
188
189 reset_active_slot_with_proxy(boot_manager).await
190}
191
192async fn reset_active_slot_with_proxy(boot_manager: BootManagerProxy) -> Result<(), Error> {
193 let last_active_config = match boot_manager.query_configuration_last_set_active().await {
194 Ok(Ok(config)) => config,
195 Ok(Err(err)) => bail!("Failure status querying last active config: {:?}", err),
196 Err(err) => bail!("Error querying last active configuration: {:?}", err),
197 };
198
199 if last_active_config == Configuration::Recovery {
200 eprintln!(
201 "Last active config is recovery: no information to decide which other config to set active. Leaving as is."
202 );
203 return Ok(());
204 }
205
206 let inactive_config = get_other_slot_config(last_active_config);
208 boot_manager
209 .set_configuration_active(inactive_config)
210 .await
211 .context("failed to set inactive config")?;
212 boot_manager
213 .set_configuration_active(last_active_config)
214 .await
215 .context("failed to set last active config")?;
216
217 Ok(())
218}
219
220pub async fn execute_reset() -> Result<(), Error> {
221 let factory_reset_service = connect_to_protocol::<FactoryResetMarker>();
222 let proxy = match factory_reset_service {
223 Ok(marker) => marker.clone(),
224 Err(error) => {
225 bail!("Could not connect to factory_reset_service: {:?}", error);
226 }
227 };
228
229 println!("recovery: Executing factory reset command");
230
231 match proxy.reset().await {
232 Ok(_) => {}
233 Err(error) => {
234 bail!("Error executing factory reset command : {:?}", error);
235 }
236 };
237 Ok(())
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use fidl_fuchsia_paver::BootManagerRequest;
244 use fuchsia_async as fasync;
245 use futures::channel::mpsc;
246 use futures::{StreamExt, TryStreamExt};
247
248 fn create_mock_boot_manager(
251 last_active_config: Configuration,
252 ) -> Result<
253 (BootManagerProxy, mpsc::Receiver<Configuration>, mpsc::Receiver<Configuration>),
254 Error,
255 > {
256 let (mut query_last_active_sender, query_last_active_receiver) = mpsc::channel(10);
257 let (mut set_active_sender, set_active_receiver) = mpsc::channel(10);
258 let (proxy, mut request_stream) =
259 fidl::endpoints::create_proxy_and_stream::<BootManagerMarker>();
260
261 fasync::Task::local(async move {
262 while let Some(request) =
263 request_stream.try_next().await.expect("failed to read mock request")
264 {
265 match request {
266 BootManagerRequest::QueryConfigurationLastSetActive { responder } => {
267 query_last_active_sender.start_send(last_active_config.clone()).unwrap();
268 responder.send(Ok(last_active_config)).unwrap();
269 }
270 BootManagerRequest::SetConfigurationActive { configuration, responder } => {
271 set_active_sender.start_send(configuration.clone()).unwrap();
272 responder.send(zx::Status::OK.into_raw()).unwrap();
273 }
274 _ => {
275 panic!("Unexpected request sent to mock boot manager");
276 }
277 }
278 }
279 })
280 .detach();
281
282 Ok((proxy, query_last_active_receiver, set_active_receiver))
283 }
284
285 #[fuchsia::test]
286 async fn test_set_last_config_a() {
287 let last_active_config = Configuration::A;
288 let (boot_manager_proxy, mut query_last_active_listener, mut set_active_listener) =
289 create_mock_boot_manager(last_active_config).unwrap();
290
291 let _res = reset_active_slot_with_proxy(boot_manager_proxy).await.unwrap();
292
293 assert_eq!(Configuration::A, query_last_active_listener.next().await.unwrap());
299 assert_eq!(Configuration::B, set_active_listener.next().await.unwrap());
300 assert_eq!(Configuration::A, set_active_listener.next().await.unwrap());
301 }
302
303 #[fuchsia::test]
304 async fn test_set_last_config_b() {
305 let last_active_config = Configuration::B;
306 let (boot_manager_proxy, mut query_last_active_listener, mut set_active_listener) =
307 create_mock_boot_manager(last_active_config).unwrap();
308
309 let _res = reset_active_slot_with_proxy(boot_manager_proxy).await.unwrap();
310
311 assert_eq!(Configuration::B, query_last_active_listener.next().await.unwrap());
317 assert_eq!(Configuration::A, set_active_listener.next().await.unwrap());
318 assert_eq!(Configuration::B, set_active_listener.next().await.unwrap());
319 }
320
321 #[fuchsia::test]
322 async fn test_set_last_config_recovery() {
323 let last_active_config = Configuration::Recovery;
324 let (boot_manager_proxy, mut query_last_active_listener, set_active_listener) =
325 create_mock_boot_manager(last_active_config).unwrap();
326
327 let _res = reset_active_slot_with_proxy(boot_manager_proxy).await;
328
329 assert_eq!(Configuration::Recovery, query_last_active_listener.next().await.unwrap());
331 assert_eq!(0, set_active_listener.count().await);
332 }
333
334 #[test]
335 fn test_reset_complete() -> std::result::Result<(), anyhow::Error> {
336 let mut state_machine = FactoryResetStateMachine::new();
337 let state = state_machine.get_state();
338 assert_eq!(state, FactoryResetState::Waiting);
339 let state = state_machine
340 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
341 assert_eq!(state, FactoryResetState::Waiting);
342 let state = state_machine
343 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
344 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
345 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
346 assert_eq!(state, FactoryResetState::StartCountdown);
347 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
348 assert_eq!(state, FactoryResetState::ExecuteReset);
349 Ok(())
350 }
351
352 #[test]
353 fn test_reset_complete_reverse() -> std::result::Result<(), anyhow::Error> {
354 let mut state_machine = FactoryResetStateMachine::new();
355 let state = state_machine.get_state();
356 assert_eq!(state, FactoryResetState::Waiting);
357 let state = state_machine
358 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
359 assert_eq!(state, FactoryResetState::Waiting);
360 let state = state_machine
361 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
362 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
363 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
364 assert_eq!(state, FactoryResetState::StartCountdown);
365 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
366 assert_eq!(state, FactoryResetState::ExecuteReset);
367 Ok(())
368 }
369
370 #[test]
371 fn test_reset_cancelled() -> std::result::Result<(), anyhow::Error> {
372 test_reset_cancelled_button(ConsumerControlButton::VolumeUp);
373 test_reset_cancelled_button(ConsumerControlButton::VolumeDown);
374 Ok(())
375 }
376
377 fn test_reset_cancelled_button(button: ConsumerControlButton) {
378 let mut state_machine = FactoryResetStateMachine::new();
379 let state = state_machine
380 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
381 assert_eq!(state, FactoryResetState::Waiting);
382 let state = state_machine
383 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
384 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
385 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
386 assert_eq!(state, FactoryResetState::StartCountdown);
387 let state = state_machine.handle_event(ResetEvent::ButtonPress(button, Phase::Up));
388 assert_eq!(state, FactoryResetState::CancelCountdown);
389 let state = state_machine.handle_event(ResetEvent::CountdownCancelled);
390 assert_eq!(state, FactoryResetState::Waiting);
391 let state = state_machine.handle_event(ResetEvent::ButtonPress(button, Phase::Down));
392 assert_eq!(state, FactoryResetState::AwaitingPolicy(2));
393 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(2, true));
394 assert_eq!(state, FactoryResetState::StartCountdown);
395 }
396
397 #[test]
398 #[should_panic]
399 fn test_early_complete_countdown() {
400 let mut state_machine = FactoryResetStateMachine::new();
401 let state = state_machine
402 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
403 assert_eq!(state, FactoryResetState::Waiting);
404 let _state = state_machine.handle_event(ResetEvent::CountdownFinished);
405 }
406
407 #[test]
408 #[should_panic]
409 fn test_cancelled_countdown_not_complete() {
410 let mut state_machine = FactoryResetStateMachine::new();
411 let state = state_machine
412 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
413 assert_eq!(state, FactoryResetState::Waiting);
414 let state = state_machine
415 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
416 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
417 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
418 assert_eq!(state, FactoryResetState::StartCountdown);
419 let state = state_machine
420 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
421 assert_eq!(state, FactoryResetState::CancelCountdown);
422 let _state = state_machine.handle_event(ResetEvent::CountdownFinished);
423 }
424
425 #[test]
426 fn test_cancelled_countdown_with_extra_button_press() {
427 let mut state_machine = FactoryResetStateMachine::new();
428 let state = state_machine
429 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
430 assert_eq!(state, FactoryResetState::Waiting);
431 let state = state_machine
432 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
433 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
434 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
435 assert_eq!(state, FactoryResetState::StartCountdown);
436 let state = state_machine
437 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
438 assert_eq!(state, FactoryResetState::CancelCountdown);
439 let state = state_machine
440 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Up));
441 assert_eq!(state, FactoryResetState::CancelCountdown);
442 }
443
444 #[test]
445 fn test_reset_complete_button_released() -> std::result::Result<(), anyhow::Error> {
446 let mut state_machine = FactoryResetStateMachine::new();
447 let state = state_machine.get_state();
448 assert_eq!(state, FactoryResetState::Waiting);
449 let state = state_machine
450 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
451 assert_eq!(state, FactoryResetState::Waiting);
452 let state = state_machine
453 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
454 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
455 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
456 assert_eq!(state, FactoryResetState::StartCountdown);
457 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
458 assert_eq!(state, FactoryResetState::ExecuteReset);
459 let state = state_machine
460 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
461 assert_eq!(state, FactoryResetState::AwaitingReset);
462 Ok(())
463 }
464
465 #[test]
466 fn test_reset_complete_multiple_button_presses() -> std::result::Result<(), anyhow::Error> {
467 let mut state_machine = FactoryResetStateMachine::new();
469 let state = state_machine.get_state();
470 assert_eq!(state, FactoryResetState::Waiting);
471 let state = state_machine
472 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
473 assert_eq!(state, FactoryResetState::Waiting);
474 let state = state_machine
475 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
476 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
477 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
478 assert_eq!(state, FactoryResetState::StartCountdown);
479 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
480 assert_eq!(state, FactoryResetState::ExecuteReset);
481 let state = state_machine
482 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
483 assert_eq!(state, FactoryResetState::AwaitingReset);
484 let state = state_machine
485 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
486 assert_eq!(state, FactoryResetState::AwaitingReset);
487 let state = state_machine
488 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Up));
489 assert_eq!(state, FactoryResetState::AwaitingReset);
490 let state = state_machine
491 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
492 assert_eq!(state, FactoryResetState::AwaitingReset);
493 Ok(())
494 }
495}