1use crate::consumer_controls_binding::ConsumerControlsEvent;
6use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
7use crate::{input_device, metrics};
8use anyhow::{Context as _, Error, anyhow};
9use async_trait::async_trait;
10use async_utils::hanging_get::server::HangingGet;
11use fidl::endpoints::DiscoverableProtocolMarker as _;
12use fidl_fuchsia_media::AudioRenderUsage2;
13use fidl_fuchsia_media_sounds::{PlaySoundError, PlayerMarker};
14use fidl_fuchsia_recovery::FactoryResetMarker;
15use fidl_fuchsia_recovery_policy::{DeviceRequest, DeviceRequestStream};
16use fidl_fuchsia_recovery_ui::{
17 FactoryResetCountdownRequestStream, FactoryResetCountdownState,
18 FactoryResetCountdownWatchResponder,
19};
20use fuchsia_async::{MonotonicDuration, MonotonicInstant, Task, TimeoutExt, Timer};
21use fuchsia_inspect::health::Reporter;
22use futures::StreamExt;
23use metrics_registry::*;
24use std::cell::RefCell;
25use std::fs::{self, File};
26use std::path::Path;
27use std::rc::Rc;
28use {fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_io as fio};
29
30#[derive(Clone, Copy, Debug, PartialEq)]
72enum FactoryResetState {
73 Disallowed,
74 Idle,
75 ButtonCountdown { deadline: MonotonicInstant },
76 ResetCountdown { deadline: MonotonicInstant },
77 Resetting,
78}
79
80const FACTORY_RESET_DISALLOWED_PATH: &'static str = "/data/factory_reset_disallowed";
81const FACTORY_RESET_SOUND_PATH: &'static str = "/config/data/chirp-start-tone.wav";
82
83const BUTTON_TIMEOUT: MonotonicDuration = MonotonicDuration::from_millis(500);
84const RESET_TIMEOUT: MonotonicDuration = MonotonicDuration::from_seconds(10);
85const EARCON_TIMEOUT: MonotonicDuration = MonotonicDuration::from_millis(2000);
87
88type NotifyFn = Box<
89 dyn Fn(
90 &(FactoryResetState, metrics::MetricsLogger),
91 FactoryResetCountdownWatchResponder,
92 ) -> bool
93 + Send,
94>;
95type ResetCountdownHangingGet = HangingGet<
96 (FactoryResetState, metrics::MetricsLogger),
97 FactoryResetCountdownWatchResponder,
98 NotifyFn,
99>;
100
101pub struct FactoryResetHandler {
104 factory_reset_state: RefCell<FactoryResetState>,
105 countdown_hanging_get: RefCell<ResetCountdownHangingGet>,
106
107 pub inspect_status: InputHandlerStatus,
109
110 metrics_logger: metrics::MetricsLogger,
111}
112
113fn is_reset_requested(event: &ConsumerControlsEvent) -> bool {
118 event.pressed_buttons.iter().any(|button| match button {
119 fidl_input_report::ConsumerControlButton::FactoryReset => true,
120 _ => false,
121 })
122}
123
124impl FactoryResetHandler {
125 pub fn new(
128 input_handlers_node: &fuchsia_inspect::Node,
129 metrics_logger: metrics::MetricsLogger,
130 ) -> Rc<Self> {
131 let initial_state = if Path::new(FACTORY_RESET_DISALLOWED_PATH).exists() {
132 FactoryResetState::Disallowed
133 } else {
134 FactoryResetState::Idle
135 };
136
137 let countdown_hanging_get =
138 FactoryResetHandler::init_hanging_get(initial_state, metrics_logger.clone());
139 let inspect_status = InputHandlerStatus::new(
140 input_handlers_node,
141 "factory_reset_handler",
142 false,
143 );
144
145 Rc::new(Self {
146 factory_reset_state: RefCell::new(initial_state),
147 countdown_hanging_get: RefCell::new(countdown_hanging_get),
148 inspect_status,
149 metrics_logger,
150 })
151 }
152
153 pub fn handle_factory_reset_countdown_request_stream(
158 self: Rc<Self>,
159 mut stream: FactoryResetCountdownRequestStream,
160 ) -> impl futures::Future<Output = Result<(), Error>> {
161 let subscriber = self.countdown_hanging_get.borrow_mut().new_subscriber();
162
163 async move {
164 while let Some(request_result) = stream.next().await {
165 let watcher = request_result?
166 .into_watch()
167 .ok_or_else(|| anyhow!("Failed to get FactoryResetCoundown Watcher"))?;
168 subscriber.register(watcher)?;
169 }
170
171 Ok(())
172 }
173 }
174
175 pub fn handle_recovery_policy_device_request_stream(
180 self: Rc<Self>,
181 mut stream: DeviceRequestStream,
182 ) -> impl futures::Future<Output = Result<(), Error>> {
183 async move {
184 while let Some(request_result) = stream.next().await {
185 let DeviceRequest::SetIsLocalResetAllowed { allowed, .. } = request_result?;
186 match self.factory_reset_state() {
187 FactoryResetState::Disallowed if allowed => {
188 self.set_factory_reset_state(FactoryResetState::Idle);
190 fs::remove_file(FACTORY_RESET_DISALLOWED_PATH).with_context(|| {
191 format!("failed to remove {}", FACTORY_RESET_DISALLOWED_PATH)
192 })?
193 }
194 _ if !allowed => {
195 self.set_factory_reset_state(FactoryResetState::Disallowed);
197 let _: File =
198 File::create(FACTORY_RESET_DISALLOWED_PATH).with_context(|| {
199 format!("failed to create {}", FACTORY_RESET_DISALLOWED_PATH)
200 })?;
201 }
202 _ => (),
203 }
204 }
205
206 Ok(())
207 }
208 }
209
210 async fn handle_allowed_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
213 if is_reset_requested(event) {
214 if let Err(error) = self.start_button_countdown().await {
215 self.metrics_logger.log_error(
216 InputPipelineErrorMetricDimensionEvent::FactoryResetFailedToReset,
217 std::format!("Failed to factory reset device: {:?}", error),
218 );
219 }
220 }
221 }
222
223 fn handle_disallowed_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
226 if is_reset_requested(event) {
227 self.metrics_logger.log_error(
228 InputPipelineErrorMetricDimensionEvent::FactoryResetNotAllowedReset,
229 "Attempted to factory reset a device that is not allowed to reset",
230 );
231 }
232 }
233
234 fn handle_button_countdown_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
237 if !is_reset_requested(event) {
238 self.set_factory_reset_state(FactoryResetState::Idle);
240 }
241 }
242
243 fn handle_reset_countdown_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
246 if !is_reset_requested(event) {
247 self.set_factory_reset_state(FactoryResetState::Idle);
249 }
250 }
251
252 fn init_hanging_get(
253 initial_state: FactoryResetState,
254 metrics_logger: metrics::MetricsLogger,
255 ) -> ResetCountdownHangingGet {
256 let notify_fn: NotifyFn = Box::new(|(state, metrics_logger), responder| {
257 let deadline = match state {
258 FactoryResetState::ResetCountdown { deadline } => {
259 Some(deadline.into_nanos() as i64)
260 }
261 _ => None,
262 };
263
264 let countdown_state =
265 FactoryResetCountdownState { scheduled_reset_time: deadline, ..Default::default() };
266
267 if responder.send(&countdown_state).is_err() {
268 metrics_logger.log_error(
269 InputPipelineErrorMetricDimensionEvent::FactoryResetFailedToSendCountdown,
270 "Failed to send factory reset countdown state",
271 );
272 }
273
274 true
275 });
276
277 ResetCountdownHangingGet::new((initial_state, metrics_logger), notify_fn)
278 }
279
280 fn set_factory_reset_state(self: &Rc<Self>, state: FactoryResetState) {
282 *self.factory_reset_state.borrow_mut() = state;
283 self.countdown_hanging_get
284 .borrow_mut()
285 .new_publisher()
286 .set((state, self.metrics_logger.clone()));
287 }
288
289 fn factory_reset_state(self: &Rc<Self>) -> FactoryResetState {
290 *self.factory_reset_state.borrow()
291 }
292
293 async fn start_button_countdown(self: &Rc<Self>) -> Result<(), Error> {
296 let deadline = MonotonicInstant::after(BUTTON_TIMEOUT);
297 self.set_factory_reset_state(FactoryResetState::ButtonCountdown { deadline });
298
299 Timer::new(MonotonicInstant::after(BUTTON_TIMEOUT)).await;
301
302 match self.factory_reset_state() {
304 FactoryResetState::ButtonCountdown { deadline: state_deadline }
305 if state_deadline == deadline =>
306 {
307 self.start_reset_countdown().await?;
309 }
310 _ => {
311 log::info!("Factory reset request cancelled");
312 }
313 }
314
315 Ok(())
316 }
317
318 async fn start_reset_countdown(self: &Rc<Self>) -> Result<(), Error> {
321 let deadline = MonotonicInstant::after(RESET_TIMEOUT);
322 self.set_factory_reset_state(FactoryResetState::ResetCountdown { deadline });
323
324 Timer::new(MonotonicInstant::after(RESET_TIMEOUT)).await;
326
327 match self.factory_reset_state() {
329 FactoryResetState::ResetCountdown { deadline: state_deadline }
330 if state_deadline == deadline =>
331 {
332 self.reset().await?;
334 }
335 _ => {
336 log::info!("Factory reset request cancelled");
337 }
338 }
339
340 Ok(())
341 }
342
343 async fn play_reset_sound(self: &Rc<Self>) -> Result<(), Error> {
345 log::debug!("Getting sound");
346 let (sound_endpoint, server_end) = fidl::endpoints::create_endpoints::<fio::FileMarker>();
348 let () = fuchsia_fs::file::open_channel_in_namespace(
349 FACTORY_RESET_SOUND_PATH,
350 fuchsia_fs::PERM_READABLE,
351 server_end,
352 )
353 .context("Failed to open factory reset sound file")?;
354
355 log::debug!("Playing sound");
356 let sound_player = fuchsia_component::client::connect_to_protocol::<PlayerMarker>()
358 .with_context(|| format!("failed to connect to {}", PlayerMarker::PROTOCOL_NAME))?;
359
360 log::debug!("Connected to player");
361 let sound_id = 0;
362 let _duration: i64 = sound_player
363 .add_sound_from_file(sound_id, sound_endpoint)
364 .await
365 .context("AddSoundFromFile error")?
366 .map_err(zx::Status::from_raw)
367 .context("AddSoundFromFile failed")?;
368 log::debug!("Added sound from file");
369
370 sound_player
371 .play_sound2(sound_id, AudioRenderUsage2::Media)
372 .await
373 .context("PlaySound2 error")?
374 .map_err(|err: PlaySoundError| anyhow!("PlaySound2 failed: {:?}", err))?;
375
376 log::debug!("Played sound");
377
378 Ok(())
379 }
380
381 async fn reset(self: &Rc<Self>) -> Result<(), Error> {
383 log::info!("Beginning reset sequence");
384 if let Err(error) = self
385 .play_reset_sound()
386 .on_timeout(EARCON_TIMEOUT, || Err(anyhow!("play_reset_sound took too long")))
387 .await
388 {
389 log::warn!("Failed to play reset sound: {:?}", error);
390 }
391
392 self.set_factory_reset_state(FactoryResetState::Resetting);
394 log::info!("Calling {}.Reset", FactoryResetMarker::PROTOCOL_NAME);
395 let factory_reset = fuchsia_component::client::connect_to_protocol::<FactoryResetMarker>()
396 .with_context(|| {
397 format!("failed to connect to {}", FactoryResetMarker::PROTOCOL_NAME)
398 })?;
399 factory_reset.reset().await.context("failed while calling Reset")?;
400 Ok(())
401 }
402}
403
404impl Handler for FactoryResetHandler {
405 fn set_handler_healthy(self: std::rc::Rc<Self>) {
406 self.inspect_status.health_node.borrow_mut().set_ok();
407 }
408
409 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
410 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
411 }
412
413 fn get_name(&self) -> &'static str {
414 "FactoryResetHandler"
415 }
416
417 fn interest(&self) -> Vec<input_device::InputEventType> {
418 vec![input_device::InputEventType::ConsumerControls]
419 }
420}
421
422#[async_trait(?Send)]
423impl UnhandledInputHandler for FactoryResetHandler {
424 async fn handle_unhandled_input_event(
428 self: Rc<Self>,
429 unhandled_input_event: input_device::UnhandledInputEvent,
430 ) -> Vec<input_device::InputEvent> {
431 fuchsia_trace::duration!("input", "factory_reset_handler");
432 match unhandled_input_event {
433 input_device::UnhandledInputEvent {
434 device_event: input_device::InputDeviceEvent::ConsumerControls(ref event),
435 device_descriptor: input_device::InputDeviceDescriptor::ConsumerControls(_),
436 event_time,
437 trace_id: _,
438 } => {
439 fuchsia_trace::duration!("input", "factory_reset_handler[processing]");
440 self.inspect_status.count_received_event(&event_time);
441 match self.factory_reset_state() {
442 FactoryResetState::Idle => {
443 let event_clone = event.clone();
444 Task::local(async move { self.handle_allowed_event(&event_clone).await })
445 .detach()
446 }
447 FactoryResetState::Disallowed => self.handle_disallowed_event(event),
448 FactoryResetState::ButtonCountdown { deadline: _ } => {
449 self.handle_button_countdown_event(event)
450 }
451 FactoryResetState::ResetCountdown { deadline: _ } => {
452 self.handle_reset_countdown_event(event)
453 }
454 FactoryResetState::Resetting => {
455 log::warn!("Recieved an input event while factory resetting the device")
456 }
457 };
458 }
459 _ => {
460 self.metrics_logger.log_error(
461 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
462 std::format!(
463 "uninterested input event: {:?}",
464 unhandled_input_event.get_event_type()
465 ),
466 );
467 }
468 };
469
470 vec![input_device::InputEvent::from(unhandled_input_event)]
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477 use crate::consumer_controls_binding::ConsumerControlsDeviceDescriptor;
478 use crate::input_handler::InputHandler;
479 use assert_matches::assert_matches;
480 use fidl::endpoints::create_proxy_and_stream;
481 use fidl_fuchsia_recovery_policy::{DeviceMarker, DeviceProxy};
482 use fidl_fuchsia_recovery_ui::{FactoryResetCountdownMarker, FactoryResetCountdownProxy};
483 use fuchsia_async::TestExecutor;
484 use pretty_assertions::assert_eq;
485 use std::pin::pin;
486 use std::task::Poll;
487
488 fn create_factory_reset_countdown_proxy(
489 reset_handler: Rc<FactoryResetHandler>,
490 ) -> FactoryResetCountdownProxy {
491 let (countdown_proxy, countdown_stream) =
492 create_proxy_and_stream::<FactoryResetCountdownMarker>();
493
494 let stream_fut =
495 reset_handler.clone().handle_factory_reset_countdown_request_stream(countdown_stream);
496
497 Task::local(async move {
498 if stream_fut.await.is_err() {
499 log::warn!("Failed to handle factory reset countdown request stream");
500 }
501 })
502 .detach();
503
504 countdown_proxy
505 }
506
507 fn create_recovery_policy_proxy(reset_handler: Rc<FactoryResetHandler>) -> DeviceProxy {
508 let (device_proxy, device_stream) = create_proxy_and_stream::<DeviceMarker>();
509
510 Task::local(async move {
511 if reset_handler
512 .handle_recovery_policy_device_request_stream(device_stream)
513 .await
514 .is_err()
515 {
516 log::warn!("Failed to handle recovery policy device request stream");
517 }
518 })
519 .detach();
520
521 device_proxy
522 }
523
524 fn create_input_device_descriptor() -> input_device::InputDeviceDescriptor {
525 input_device::InputDeviceDescriptor::ConsumerControls(ConsumerControlsDeviceDescriptor {
526 buttons: vec![
527 fidl_input_report::ConsumerControlButton::CameraDisable,
528 fidl_input_report::ConsumerControlButton::FactoryReset,
529 fidl_input_report::ConsumerControlButton::MicMute,
530 fidl_input_report::ConsumerControlButton::Pause,
531 fidl_input_report::ConsumerControlButton::VolumeDown,
532 fidl_input_report::ConsumerControlButton::VolumeUp,
533 ],
534 device_id: 0,
535 })
536 }
537
538 fn create_reset_consumer_controls_event() -> ConsumerControlsEvent {
539 ConsumerControlsEvent::new(
540 vec![fidl_input_report::ConsumerControlButton::FactoryReset],
541 None,
542 )
543 }
544
545 fn create_non_reset_consumer_controls_event() -> ConsumerControlsEvent {
546 ConsumerControlsEvent::new(
547 vec![
548 fidl_input_report::ConsumerControlButton::CameraDisable,
549 fidl_input_report::ConsumerControlButton::MicMute,
550 fidl_input_report::ConsumerControlButton::Pause,
551 fidl_input_report::ConsumerControlButton::VolumeDown,
552 fidl_input_report::ConsumerControlButton::VolumeUp,
553 ],
554 None,
555 )
556 }
557
558 fn create_non_reset_input_event() -> input_device::UnhandledInputEvent {
559 let device_event = input_device::InputDeviceEvent::ConsumerControls(
560 create_non_reset_consumer_controls_event(),
561 );
562
563 input_device::UnhandledInputEvent {
564 device_event,
565 device_descriptor: create_input_device_descriptor(),
566 event_time: zx::MonotonicInstant::get(),
567 trace_id: None,
568 }
569 }
570
571 fn create_reset_input_event() -> input_device::UnhandledInputEvent {
572 let device_event = input_device::InputDeviceEvent::ConsumerControls(
573 create_reset_consumer_controls_event(),
574 );
575
576 input_device::UnhandledInputEvent {
577 device_event,
578 device_descriptor: create_input_device_descriptor(),
579 event_time: zx::MonotonicInstant::get(),
580 trace_id: None,
581 }
582 }
583
584 #[fuchsia::test]
585 async fn is_reset_requested_looks_for_reset_signal() {
586 let reset_event = create_reset_consumer_controls_event();
587 let non_reset_event = create_non_reset_consumer_controls_event();
588
589 assert!(
590 is_reset_requested(&reset_event),
591 "Should reset when the reset signal is received."
592 );
593 assert!(
594 !is_reset_requested(&non_reset_event),
595 "Should only reset when the reset signal is received."
596 );
597 }
598
599 #[fuchsia::test]
600 async fn factory_reset_countdown_listener_gets_initial_state() {
601 let inspector = fuchsia_inspect::Inspector::default();
602 let test_node = inspector.root().create_child("test_node");
603 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
604 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
605 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
606 assert_eq!(reset_state.scheduled_reset_time, None);
607 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
608 }
609
610 #[fuchsia::test]
611 fn factory_reset_countdown_listener_is_notified_on_state_change() -> Result<(), Error> {
612 let mut executor = TestExecutor::new_with_fake_time();
613 let inspector = fuchsia_inspect::Inspector::default();
614 let test_node = inspector.root().create_child("test_node");
615 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
616 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
617
618 let get_countdown_state = |executor: &mut TestExecutor| {
619 let mut fut = countdown_proxy.watch();
620 loop {
621 match executor.run_until_stalled(&mut fut) {
623 Poll::Pending => continue,
624 Poll::Ready(state) => {
625 return state.expect("Failed to get countdown state");
626 }
627 }
628 }
629 };
630
631 let countdown_state = get_countdown_state(&mut executor);
634 let handler_state = reset_handler.factory_reset_state();
635 assert_eq!(countdown_state.scheduled_reset_time, None);
636 assert_eq!(handler_state, FactoryResetState::Idle);
637
638 let reset_event = create_reset_input_event();
640 let mut handle_input_event_fut =
641 pin!(reset_handler.clone().handle_unhandled_input_event(reset_event));
642 assert_matches!(executor.run_until_stalled(&mut handle_input_event_fut), Poll::Ready(events) => {
643 assert_matches!(events.as_slice(), [input_device::InputEvent { .. }]);
644 });
645
646 let countdown_state = get_countdown_state(&mut executor);
648 let handler_state = reset_handler.factory_reset_state();
649 assert_eq!(countdown_state.scheduled_reset_time, None);
650 assert_matches!(handler_state, FactoryResetState::ButtonCountdown { deadline: _ });
651
652 executor.set_fake_time(MonotonicInstant::after(MonotonicDuration::from_millis(500)));
654 executor.wake_expired_timers();
655
656 let countdown_state = get_countdown_state(&mut executor);
659 let handler_state = reset_handler.factory_reset_state();
660 assert_matches!(countdown_state.scheduled_reset_time, Some(_));
661 assert_matches!(handler_state, FactoryResetState::ResetCountdown { deadline: _ });
662
663 executor.set_fake_time(MonotonicInstant::after(MonotonicDuration::from_seconds(10)));
665 executor.wake_expired_timers();
666
667 let countdown_state = get_countdown_state(&mut executor);
670 let handler_state = reset_handler.factory_reset_state();
671 assert_eq!(countdown_state.scheduled_reset_time, None);
672 assert_eq!(handler_state, FactoryResetState::Resetting);
673
674 Ok(())
675 }
676
677 #[fuchsia::test]
678 async fn recovery_policy_requests_update_reset_handler_state() {
679 let inspector = fuchsia_inspect::Inspector::default();
680 let test_node = inspector.root().create_child("test_node");
681 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
682 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
683
684 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
686 assert_eq!(reset_state.scheduled_reset_time, None);
687 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
688
689 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
691 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
692
693 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
695 assert_eq!(reset_state.scheduled_reset_time, None);
696 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
697
698 let reset_event = create_reset_input_event();
700 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
701
702 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
704
705 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
707 device_proxy.set_is_local_reset_allowed(true).expect("Failed to set recovery policy");
708
709 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
711 assert_eq!(reset_state.scheduled_reset_time, None);
712 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
713 }
714
715 #[fuchsia::test]
716 fn handle_allowed_event_changes_state_with_reset() {
717 let mut executor = TestExecutor::new();
718
719 let reset_event = create_reset_consumer_controls_event();
720 let inspector = fuchsia_inspect::Inspector::default();
721 let test_node = inspector.root().create_child("test_node");
722 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
723 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
724
725 let reset_state = executor
727 .run_singlethreaded(countdown_proxy.watch())
728 .expect("Failed to get countdown state");
729 assert_eq!(reset_state.scheduled_reset_time, None);
730 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
731
732 let handle_allowed_event_fut = reset_handler.handle_allowed_event(&reset_event);
733 futures::pin_mut!(handle_allowed_event_fut);
734 assert_eq!(executor.run_until_stalled(&mut handle_allowed_event_fut), Poll::Pending);
735
736 assert_matches!(
738 executor.run_singlethreaded(countdown_proxy.watch()),
739 Ok(FactoryResetCountdownState { scheduled_reset_time: None, .. })
740 );
741 assert_matches!(
742 reset_handler.factory_reset_state(),
743 FactoryResetState::ButtonCountdown { deadline: _ }
744 );
745 }
746
747 #[fuchsia::test]
748 async fn handle_allowed_event_wont_change_state_without_reset() {
749 let inspector = fuchsia_inspect::Inspector::default();
750 let test_node = inspector.root().create_child("test_node");
751 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
752 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
753
754 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
756 assert_eq!(reset_state.scheduled_reset_time, None);
757 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
758
759 let non_reset_event = create_non_reset_consumer_controls_event();
760 reset_handler.clone().handle_allowed_event(&non_reset_event).await;
761
762 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
764 }
765
766 #[fuchsia::test]
767 async fn handle_disallowed_event_wont_change_state() {
768 let inspector = fuchsia_inspect::Inspector::default();
769 let test_node = inspector.root().create_child("test_node");
770 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
771 *reset_handler.factory_reset_state.borrow_mut() = FactoryResetState::Disallowed;
772
773 let reset_event = create_reset_consumer_controls_event();
776 reset_handler.handle_disallowed_event(&reset_event);
777 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
778
779 let non_reset_event = create_non_reset_consumer_controls_event();
780 reset_handler.handle_disallowed_event(&non_reset_event);
781 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
782 }
783
784 #[fuchsia::test]
785 async fn handle_button_countdown_event_changes_state_when_reset_no_longer_requested() {
786 let inspector = fuchsia_inspect::Inspector::default();
787 let test_node = inspector.root().create_child("test_node");
788 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
789
790 let deadline = MonotonicInstant::after(BUTTON_TIMEOUT);
791 *reset_handler.factory_reset_state.borrow_mut() =
792 FactoryResetState::ButtonCountdown { deadline };
793
794 let non_reset_event = create_non_reset_consumer_controls_event();
797 reset_handler.handle_button_countdown_event(&non_reset_event);
798 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
799 }
800
801 #[fuchsia::test]
802 async fn handle_reset_countdown_event_changes_state_when_reset_no_longer_requested() {
803 let inspector = fuchsia_inspect::Inspector::default();
804 let test_node = inspector.root().create_child("test_node");
805 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
806
807 *reset_handler.factory_reset_state.borrow_mut() =
808 FactoryResetState::ResetCountdown { deadline: MonotonicInstant::now() };
809
810 let non_reset_event = create_non_reset_consumer_controls_event();
813 reset_handler.handle_reset_countdown_event(&non_reset_event);
814 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
815 }
816
817 #[fuchsia::test]
818 async fn factory_reset_disallowed_during_button_countdown() {
819 let inspector = fuchsia_inspect::Inspector::default();
820 let test_node = inspector.root().create_child("test_node");
821 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
822 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
823
824 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
826 assert_eq!(reset_state.scheduled_reset_time, None);
827 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
828
829 let reset_event = create_reset_input_event();
831 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
832
833 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
835 assert_eq!(reset_state.scheduled_reset_time, None);
836 assert_matches!(
837 reset_handler.factory_reset_state(),
838 FactoryResetState::ButtonCountdown { deadline: _ }
839 );
840
841 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
843 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
844
845 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
847 assert_eq!(reset_state.scheduled_reset_time, None);
848 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
849 }
850
851 #[fuchsia::test]
852 async fn factory_reset_disallowed_during_reset_countdown() {
853 let inspector = fuchsia_inspect::Inspector::default();
854 let test_node = inspector.root().create_child("test_node");
855 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
856 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
857
858 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
860 assert_eq!(reset_state.scheduled_reset_time, None);
861 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
862
863 let reset_event = create_reset_input_event();
865 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
866
867 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
869 assert_eq!(reset_state.scheduled_reset_time, None);
870 assert_matches!(
871 reset_handler.factory_reset_state(),
872 FactoryResetState::ButtonCountdown { deadline: _ }
873 );
874
875 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
877 assert_matches!(reset_state.scheduled_reset_time, Some(_));
878 assert_matches!(
879 reset_handler.factory_reset_state(),
880 FactoryResetState::ResetCountdown { deadline: _ }
881 );
882
883 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
885 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
886
887 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
889 assert_eq!(reset_state.scheduled_reset_time, None);
890 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
891 }
892
893 #[fuchsia::test]
894 async fn factory_reset_cancelled_during_button_countdown() {
895 let inspector = fuchsia_inspect::Inspector::default();
896 let test_node = inspector.root().create_child("test_node");
897 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
898 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
899
900 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
902 assert_eq!(reset_state.scheduled_reset_time, None);
903 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
904
905 let reset_event = create_reset_input_event();
907 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
908
909 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
911 assert_eq!(reset_state.scheduled_reset_time, None);
912 assert_matches!(
913 reset_handler.factory_reset_state(),
914 FactoryResetState::ButtonCountdown { deadline: _ }
915 );
916
917 let non_reset_event = create_non_reset_input_event();
919 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
920
921 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
923 assert_eq!(reset_state.scheduled_reset_time, None);
924 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
925 }
926
927 #[fuchsia::test]
928 async fn factory_reset_cancelled_during_reset_countdown() {
929 let inspector = fuchsia_inspect::Inspector::default();
930 let test_node = inspector.root().create_child("test_node");
931 let reset_handler = FactoryResetHandler::new(&test_node, metrics::MetricsLogger::default());
932 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
933
934 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
936 assert_eq!(reset_state.scheduled_reset_time, None);
937 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
938
939 let reset_event = create_reset_input_event();
941 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
942
943 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
945 assert_eq!(reset_state.scheduled_reset_time, None);
946 assert_matches!(
947 reset_handler.factory_reset_state(),
948 FactoryResetState::ButtonCountdown { deadline: _ }
949 );
950
951 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
953 assert_matches!(reset_state.scheduled_reset_time, Some(_));
954 assert_matches!(
955 reset_handler.factory_reset_state(),
956 FactoryResetState::ResetCountdown { deadline: _ }
957 );
958
959 let non_reset_event = create_non_reset_input_event();
961 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
962
963 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
965 assert_eq!(reset_state.scheduled_reset_time, None);
966 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
967 }
968
969 #[fuchsia::test]
970 async fn factory_reset_handler_initialized_with_inspect_node() {
971 let inspector = fuchsia_inspect::Inspector::default();
972 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
973 let _handler =
974 FactoryResetHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
975 diagnostics_assertions::assert_data_tree!(inspector, root: {
976 input_handlers_node: {
977 factory_reset_handler: {
978 events_received_count: 0u64,
979 events_handled_count: 0u64,
980 last_received_timestamp_ns: 0u64,
981 "fuchsia.inspect.Health": {
982 status: "STARTING_UP",
983 start_timestamp_nanos: diagnostics_assertions::AnyProperty
986 },
987 }
988 }
989 });
990 }
991
992 #[fuchsia::test]
993 async fn factory_reset_handler_inspect_counts_events() {
994 let inspector = fuchsia_inspect::Inspector::default();
995 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
996 let reset_handler =
997 FactoryResetHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
998
999 let reset_event = create_reset_input_event();
1001 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
1002
1003 let mut handled_event = input_device::InputEvent::from(create_reset_input_event());
1005 handled_event.handled = input_device::Handled::Yes;
1006 reset_handler.clone().handle_input_event(handled_event).await;
1007
1008 let non_reset_event = create_non_reset_input_event();
1010 let last_event_timestamp: u64 =
1011 non_reset_event.clone().event_time.into_nanos().try_into().unwrap();
1012 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
1013
1014 diagnostics_assertions::assert_data_tree!(inspector, root: {
1015 input_handlers_node: {
1016 factory_reset_handler: {
1017 events_received_count: 2u64,
1018 events_handled_count: 0u64,
1019 last_received_timestamp_ns: last_event_timestamp,
1020 "fuchsia.inspect.Health": {
1021 status: "STARTING_UP",
1022 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1025 },
1026 }
1027 }
1028 });
1029 }
1030}