1use anyhow::{bail, Context, Error};
6use carnelian::input::consumer_control::Phase;
7use fidl_fuchsia_input_report::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!("Last active config is recovery: no information to decide which other config to set active. Leaving as is.");
201 return Ok(());
202 }
203
204 let inactive_config = get_other_slot_config(last_active_config);
206 boot_manager
207 .set_configuration_active(inactive_config)
208 .await
209 .context("failed to set inactive config")?;
210 boot_manager
211 .set_configuration_active(last_active_config)
212 .await
213 .context("failed to set last active config")?;
214
215 Ok(())
216}
217
218pub async fn execute_reset() -> Result<(), Error> {
219 let factory_reset_service = connect_to_protocol::<FactoryResetMarker>();
220 let proxy = match factory_reset_service {
221 Ok(marker) => marker.clone(),
222 Err(error) => {
223 bail!("Could not connect to factory_reset_service: {:?}", error);
224 }
225 };
226
227 println!("recovery: Executing factory reset command");
228
229 match proxy.reset().await {
230 Ok(_) => {}
231 Err(error) => {
232 bail!("Error executing factory reset command : {:?}", error);
233 }
234 };
235 Ok(())
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use fidl_fuchsia_paver::BootManagerRequest;
242 use fuchsia_async as fasync;
243 use futures::channel::mpsc;
244 use futures::{StreamExt, TryStreamExt};
245
246 fn create_mock_boot_manager(
249 last_active_config: Configuration,
250 ) -> Result<
251 (BootManagerProxy, mpsc::Receiver<Configuration>, mpsc::Receiver<Configuration>),
252 Error,
253 > {
254 let (mut query_last_active_sender, query_last_active_receiver) = mpsc::channel(10);
255 let (mut set_active_sender, set_active_receiver) = mpsc::channel(10);
256 let (proxy, mut request_stream) =
257 fidl::endpoints::create_proxy_and_stream::<BootManagerMarker>();
258
259 fasync::Task::local(async move {
260 while let Some(request) =
261 request_stream.try_next().await.expect("failed to read mock request")
262 {
263 match request {
264 BootManagerRequest::QueryConfigurationLastSetActive { responder } => {
265 query_last_active_sender.start_send(last_active_config.clone()).unwrap();
266 responder.send(Ok(last_active_config)).unwrap();
267 }
268 BootManagerRequest::SetConfigurationActive { configuration, responder } => {
269 set_active_sender.start_send(configuration.clone()).unwrap();
270 responder.send(zx::Status::OK.into_raw()).unwrap();
271 }
272 _ => {
273 panic!("Unexpected request sent to mock boot manager");
274 }
275 }
276 }
277 })
278 .detach();
279
280 Ok((proxy, query_last_active_receiver, set_active_receiver))
281 }
282
283 #[fuchsia::test]
284 async fn test_set_last_config_a() {
285 let last_active_config = Configuration::A;
286 let (boot_manager_proxy, mut query_last_active_listener, mut set_active_listener) =
287 create_mock_boot_manager(last_active_config).unwrap();
288
289 let _res = reset_active_slot_with_proxy(boot_manager_proxy).await.unwrap();
290
291 assert_eq!(Configuration::A, query_last_active_listener.next().await.unwrap());
297 assert_eq!(Configuration::B, set_active_listener.next().await.unwrap());
298 assert_eq!(Configuration::A, set_active_listener.next().await.unwrap());
299 }
300
301 #[fuchsia::test]
302 async fn test_set_last_config_b() {
303 let last_active_config = Configuration::B;
304 let (boot_manager_proxy, mut query_last_active_listener, mut set_active_listener) =
305 create_mock_boot_manager(last_active_config).unwrap();
306
307 let _res = reset_active_slot_with_proxy(boot_manager_proxy).await.unwrap();
308
309 assert_eq!(Configuration::B, query_last_active_listener.next().await.unwrap());
315 assert_eq!(Configuration::A, set_active_listener.next().await.unwrap());
316 assert_eq!(Configuration::B, set_active_listener.next().await.unwrap());
317 }
318
319 #[fuchsia::test]
320 async fn test_set_last_config_recovery() {
321 let last_active_config = Configuration::Recovery;
322 let (boot_manager_proxy, mut query_last_active_listener, set_active_listener) =
323 create_mock_boot_manager(last_active_config).unwrap();
324
325 let _res = reset_active_slot_with_proxy(boot_manager_proxy).await;
326
327 assert_eq!(Configuration::Recovery, query_last_active_listener.next().await.unwrap());
329 assert_eq!(0, set_active_listener.count().await);
330 }
331
332 #[test]
333 fn test_reset_complete() -> std::result::Result<(), anyhow::Error> {
334 let mut state_machine = FactoryResetStateMachine::new();
335 let state = state_machine.get_state();
336 assert_eq!(state, FactoryResetState::Waiting);
337 let state = state_machine
338 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
339 assert_eq!(state, FactoryResetState::Waiting);
340 let state = state_machine
341 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
342 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
343 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
344 assert_eq!(state, FactoryResetState::StartCountdown);
345 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
346 assert_eq!(state, FactoryResetState::ExecuteReset);
347 Ok(())
348 }
349
350 #[test]
351 fn test_reset_complete_reverse() -> std::result::Result<(), anyhow::Error> {
352 let mut state_machine = FactoryResetStateMachine::new();
353 let state = state_machine.get_state();
354 assert_eq!(state, FactoryResetState::Waiting);
355 let state = state_machine
356 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
357 assert_eq!(state, FactoryResetState::Waiting);
358 let state = state_machine
359 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
360 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
361 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
362 assert_eq!(state, FactoryResetState::StartCountdown);
363 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
364 assert_eq!(state, FactoryResetState::ExecuteReset);
365 Ok(())
366 }
367
368 #[test]
369 fn test_reset_cancelled() -> std::result::Result<(), anyhow::Error> {
370 test_reset_cancelled_button(ConsumerControlButton::VolumeUp);
371 test_reset_cancelled_button(ConsumerControlButton::VolumeDown);
372 Ok(())
373 }
374
375 fn test_reset_cancelled_button(button: ConsumerControlButton) {
376 let mut state_machine = FactoryResetStateMachine::new();
377 let state = state_machine
378 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
379 assert_eq!(state, FactoryResetState::Waiting);
380 let state = state_machine
381 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
382 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
383 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
384 assert_eq!(state, FactoryResetState::StartCountdown);
385 let state = state_machine.handle_event(ResetEvent::ButtonPress(button, Phase::Up));
386 assert_eq!(state, FactoryResetState::CancelCountdown);
387 let state = state_machine.handle_event(ResetEvent::CountdownCancelled);
388 assert_eq!(state, FactoryResetState::Waiting);
389 let state = state_machine.handle_event(ResetEvent::ButtonPress(button, Phase::Down));
390 assert_eq!(state, FactoryResetState::AwaitingPolicy(2));
391 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(2, true));
392 assert_eq!(state, FactoryResetState::StartCountdown);
393 }
394
395 #[test]
396 #[should_panic]
397 fn test_early_complete_countdown() {
398 let mut state_machine = FactoryResetStateMachine::new();
399 let state = state_machine
400 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
401 assert_eq!(state, FactoryResetState::Waiting);
402 let _state = state_machine.handle_event(ResetEvent::CountdownFinished);
403 }
404
405 #[test]
406 #[should_panic]
407 fn test_cancelled_countdown_not_complete() {
408 let mut state_machine = FactoryResetStateMachine::new();
409 let state = state_machine
410 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
411 assert_eq!(state, FactoryResetState::Waiting);
412 let state = state_machine
413 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
414 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
415 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
416 assert_eq!(state, FactoryResetState::StartCountdown);
417 let state = state_machine
418 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
419 assert_eq!(state, FactoryResetState::CancelCountdown);
420 let _state = state_machine.handle_event(ResetEvent::CountdownFinished);
421 }
422
423 #[test]
424 fn test_cancelled_countdown_with_extra_button_press() {
425 let mut state_machine = FactoryResetStateMachine::new();
426 let state = state_machine
427 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
428 assert_eq!(state, FactoryResetState::Waiting);
429 let state = state_machine
430 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
431 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
432 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
433 assert_eq!(state, FactoryResetState::StartCountdown);
434 let state = state_machine
435 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
436 assert_eq!(state, FactoryResetState::CancelCountdown);
437 let state = state_machine
438 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Up));
439 assert_eq!(state, FactoryResetState::CancelCountdown);
440 }
441
442 #[test]
443 fn test_reset_complete_button_released() -> std::result::Result<(), anyhow::Error> {
444 let mut state_machine = FactoryResetStateMachine::new();
445 let state = state_machine.get_state();
446 assert_eq!(state, FactoryResetState::Waiting);
447 let state = state_machine
448 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
449 assert_eq!(state, FactoryResetState::Waiting);
450 let state = state_machine
451 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
452 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
453 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
454 assert_eq!(state, FactoryResetState::StartCountdown);
455 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
456 assert_eq!(state, FactoryResetState::ExecuteReset);
457 let state = state_machine
458 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
459 assert_eq!(state, FactoryResetState::AwaitingReset);
460 Ok(())
461 }
462
463 #[test]
464 fn test_reset_complete_multiple_button_presses() -> std::result::Result<(), anyhow::Error> {
465 let mut state_machine = FactoryResetStateMachine::new();
467 let state = state_machine.get_state();
468 assert_eq!(state, FactoryResetState::Waiting);
469 let state = state_machine
470 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
471 assert_eq!(state, FactoryResetState::Waiting);
472 let state = state_machine
473 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
474 assert_eq!(state, FactoryResetState::AwaitingPolicy(1));
475 let state = state_machine.handle_event(ResetEvent::AwaitPolicyResult(1, true));
476 assert_eq!(state, FactoryResetState::StartCountdown);
477 let state = state_machine.handle_event(ResetEvent::CountdownFinished);
478 assert_eq!(state, FactoryResetState::ExecuteReset);
479 let state = state_machine
480 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Up));
481 assert_eq!(state, FactoryResetState::AwaitingReset);
482 let state = state_machine
483 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeDown, Phase::Down));
484 assert_eq!(state, FactoryResetState::AwaitingReset);
485 let state = state_machine
486 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Up));
487 assert_eq!(state, FactoryResetState::AwaitingReset);
488 let state = state_machine
489 .handle_event(ResetEvent::ButtonPress(ConsumerControlButton::VolumeUp, Phase::Down));
490 assert_eq!(state, FactoryResetState::AwaitingReset);
491 Ok(())
492 }
493}