1use crate::consumer_controls_binding::ConsumerControlsEvent;
6use crate::dispatcher::TimeoutExt;
7use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
8use crate::{Dispatcher, Incoming, MonotonicInstant, dispatcher, input_device, metrics};
9use anyhow::{Context as _, Error, anyhow};
10use async_trait::async_trait;
11use async_utils::hanging_get::server::HangingGet;
12use fidl::endpoints::{DiscoverableProtocolMarker as _, Proxy};
13use fidl_fuchsia_input_report as fidl_input_report;
14use fidl_fuchsia_io as fio;
15use fidl_fuchsia_media::AudioRenderUsage2;
16use fidl_fuchsia_media_sounds::{PlaySoundError, PlayerMarker, PlayerProxy};
17use fidl_fuchsia_recovery::{FactoryResetMarker, FactoryResetProxy};
18use fidl_fuchsia_recovery_policy::{DeviceRequest, DeviceRequestStream};
19use fidl_fuchsia_recovery_ui::{
20 FactoryResetCountdownRequestStream, FactoryResetCountdownState,
21 FactoryResetCountdownWatchResponder,
22};
23use fuchsia_async::MonotonicDuration;
24use fuchsia_inspect::health::Reporter;
25use futures::StreamExt;
26use metrics_registry::*;
27use std::cell::{Cell, RefCell};
28use std::fs::{self, File};
29use std::path::Path;
30use std::rc::Rc;
31
32#[derive(Clone, Copy, Debug, PartialEq)]
74enum FactoryResetState {
75 Disallowed,
76 Idle,
77 ButtonCountdown { deadline: MonotonicInstant },
78 ResetCountdown { deadline: MonotonicInstant },
79 Resetting,
80}
81
82const FACTORY_RESET_DISALLOWED_PATH: &'static str = "/data/factory_reset_disallowed";
83const FACTORY_RESET_SOUND_PATH: &'static str = "/config/data/chirp-start-tone.wav";
84
85const BUTTON_TIMEOUT: MonotonicDuration = MonotonicDuration::from_millis(500);
86const RESET_TIMEOUT: MonotonicDuration = MonotonicDuration::from_seconds(10);
87const EARCON_TIMEOUT: MonotonicDuration = MonotonicDuration::from_millis(2000);
89
90type NotifyFn = Box<
91 dyn Fn(
92 &(FactoryResetState, metrics::MetricsLogger),
93 FactoryResetCountdownWatchResponder,
94 ) -> bool
95 + Send,
96>;
97type ResetCountdownHangingGet = HangingGet<
98 (FactoryResetState, metrics::MetricsLogger),
99 FactoryResetCountdownWatchResponder,
100 NotifyFn,
101>;
102
103pub struct FactoryResetHandler {
106 incoming: Incoming,
107 factory_reset_state: RefCell<FactoryResetState>,
108 countdown_hanging_get: RefCell<ResetCountdownHangingGet>,
109
110 pub inspect_status: InputHandlerStatus,
112
113 metrics_logger: metrics::MetricsLogger,
114
115 _tasks: RefCell<Vec<(dispatcher::TaskHandle<()>, Rc<Cell<bool>>)>>,
117}
118
119fn is_reset_requested(event: &ConsumerControlsEvent) -> bool {
124 event.pressed_buttons.iter().any(|button| match button {
125 fidl_input_report::ConsumerControlButton::FactoryReset => true,
126 _ => false,
127 })
128}
129
130impl FactoryResetHandler {
131 pub fn new(
134 incoming: Incoming,
135 input_handlers_node: &fuchsia_inspect::Node,
136 metrics_logger: metrics::MetricsLogger,
137 ) -> Rc<Self> {
138 let initial_state = if Path::new(FACTORY_RESET_DISALLOWED_PATH).exists() {
139 FactoryResetState::Disallowed
140 } else {
141 FactoryResetState::Idle
142 };
143
144 let countdown_hanging_get =
145 FactoryResetHandler::init_hanging_get(initial_state, metrics_logger.clone());
146 let inspect_status = InputHandlerStatus::new(
147 input_handlers_node,
148 "factory_reset_handler",
149 false,
150 );
151
152 Rc::new(Self {
153 incoming,
154 factory_reset_state: RefCell::new(initial_state),
155 countdown_hanging_get: RefCell::new(countdown_hanging_get),
156 inspect_status,
157 metrics_logger,
158 _tasks: RefCell::new(vec![]),
159 })
160 }
161
162 pub fn handle_factory_reset_countdown_request_stream(
167 self: Rc<Self>,
168 mut stream: FactoryResetCountdownRequestStream,
169 ) -> impl futures::Future<Output = Result<(), Error>> {
170 let subscriber = self.countdown_hanging_get.borrow_mut().new_subscriber();
171
172 async move {
173 while let Some(request_result) = stream.next().await {
174 let watcher = request_result?
175 .into_watch()
176 .ok_or_else(|| anyhow!("Failed to get FactoryResetCoundown Watcher"))?;
177 subscriber.register(watcher)?;
178 }
179
180 Ok(())
181 }
182 }
183
184 pub fn handle_recovery_policy_device_request_stream(
189 self: Rc<Self>,
190 mut stream: DeviceRequestStream,
191 ) -> impl futures::Future<Output = Result<(), Error>> {
192 async move {
193 while let Some(request_result) = stream.next().await {
194 let DeviceRequest::SetIsLocalResetAllowed { allowed, .. } = request_result?;
195 match self.factory_reset_state() {
196 FactoryResetState::Disallowed if allowed => {
197 self.set_factory_reset_state(FactoryResetState::Idle);
199 fs::remove_file(FACTORY_RESET_DISALLOWED_PATH).with_context(|| {
200 format!("failed to remove {}", FACTORY_RESET_DISALLOWED_PATH)
201 })?
202 }
203 _ if !allowed => {
204 self.set_factory_reset_state(FactoryResetState::Disallowed);
206 let _: File =
207 File::create(FACTORY_RESET_DISALLOWED_PATH).with_context(|| {
208 format!("failed to create {}", FACTORY_RESET_DISALLOWED_PATH)
209 })?;
210 }
211 _ => (),
212 }
213 }
214
215 Ok(())
216 }
217 }
218
219 async fn handle_allowed_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
222 if is_reset_requested(event) {
223 if let Err(error) = self.start_button_countdown().await {
224 self.metrics_logger.log_error(
225 InputPipelineErrorMetricDimensionEvent::FactoryResetFailedToReset,
226 std::format!("Failed to factory reset device: {:?}", error),
227 );
228 }
229 }
230 }
231
232 fn handle_disallowed_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
235 if is_reset_requested(event) {
236 self.metrics_logger.log_error(
237 InputPipelineErrorMetricDimensionEvent::FactoryResetNotAllowedReset,
238 "Attempted to factory reset a device that is not allowed to reset",
239 );
240 }
241 }
242
243 fn handle_button_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 handle_reset_countdown_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
255 if !is_reset_requested(event) {
256 self.set_factory_reset_state(FactoryResetState::Idle);
258 }
259 }
260
261 fn init_hanging_get(
262 initial_state: FactoryResetState,
263 metrics_logger: metrics::MetricsLogger,
264 ) -> ResetCountdownHangingGet {
265 let notify_fn: NotifyFn = Box::new(|(state, metrics_logger), responder| {
266 let deadline = match state {
267 FactoryResetState::ResetCountdown { deadline } => {
268 Some(deadline.into_nanos() as i64)
269 }
270 _ => None,
271 };
272
273 let countdown_state =
274 FactoryResetCountdownState { scheduled_reset_time: deadline, ..Default::default() };
275
276 if responder.send(&countdown_state).is_err() {
277 metrics_logger.log_error(
278 InputPipelineErrorMetricDimensionEvent::FactoryResetFailedToSendCountdown,
279 "Failed to send factory reset countdown state",
280 );
281 }
282
283 true
284 });
285
286 ResetCountdownHangingGet::new((initial_state, metrics_logger), notify_fn)
287 }
288
289 fn set_factory_reset_state(self: &Rc<Self>, state: FactoryResetState) {
291 *self.factory_reset_state.borrow_mut() = state;
292 self.countdown_hanging_get
293 .borrow_mut()
294 .new_publisher()
295 .set((state, self.metrics_logger.clone()));
296 }
297
298 fn factory_reset_state(self: &Rc<Self>) -> FactoryResetState {
299 *self.factory_reset_state.borrow()
300 }
301
302 async fn start_button_countdown(self: &Rc<Self>) -> Result<(), Error> {
305 let deadline = MonotonicInstant::after(BUTTON_TIMEOUT);
306 self.set_factory_reset_state(FactoryResetState::ButtonCountdown { deadline });
307
308 Dispatcher::after_deadline(MonotonicInstant::after(BUTTON_TIMEOUT)).await;
310
311 match self.factory_reset_state() {
313 FactoryResetState::ButtonCountdown { deadline: state_deadline }
314 if state_deadline == deadline =>
315 {
316 self.start_reset_countdown().await?;
318 }
319 _ => {
320 log::info!("Factory reset request cancelled");
321 }
322 }
323
324 Ok(())
325 }
326
327 async fn start_reset_countdown(self: &Rc<Self>) -> Result<(), Error> {
330 let deadline = MonotonicInstant::after(RESET_TIMEOUT);
331 self.set_factory_reset_state(FactoryResetState::ResetCountdown { deadline });
332
333 Dispatcher::after_deadline(MonotonicInstant::after(RESET_TIMEOUT)).await;
335
336 match self.factory_reset_state() {
338 FactoryResetState::ResetCountdown { deadline: state_deadline }
339 if state_deadline == deadline =>
340 {
341 self.reset().await?;
343 }
344 _ => {
345 log::info!("Factory reset request cancelled");
346 }
347 }
348
349 Ok(())
350 }
351
352 async fn play_reset_sound(self: &Rc<Self>) -> Result<(), Error> {
354 log::debug!("Getting sound");
355 let sound_endpoint = fuchsia_component::directory::open_file_async(
357 &self.incoming,
358 FACTORY_RESET_SOUND_PATH,
359 fio::R_STAR_DIR,
360 )
361 .context("Failed to open factory reset sound file")?;
362
363 log::debug!("Playing sound");
364 let sound_player = self
366 .incoming
367 .connect_protocol::<PlayerProxy>()
368 .with_context(|| format!("failed to connect to {}", PlayerMarker::PROTOCOL_NAME))?;
369
370 log::debug!("Connected to player");
371 let sound_id = 0;
372 let _duration: i64 = sound_player
373 .add_sound_from_file(sound_id, sound_endpoint.into_client_end().unwrap())
374 .await
375 .context("AddSoundFromFile error")?
376 .map_err(zx::Status::from_raw)
377 .context("AddSoundFromFile failed")?;
378 log::debug!("Added sound from file");
379
380 sound_player
381 .play_sound2(sound_id, AudioRenderUsage2::Media)
382 .await
383 .context("PlaySound2 error")?
384 .map_err(|err: PlaySoundError| anyhow!("PlaySound2 failed: {:?}", err))?;
385
386 log::debug!("Played sound");
387
388 Ok(())
389 }
390
391 async fn reset(self: &Rc<Self>) -> Result<(), Error> {
393 log::info!("Beginning reset sequence");
394 if let Err(error) = self
395 .play_reset_sound()
396 .on_timeout(Dispatcher::after_deadline(MonotonicInstant::after(EARCON_TIMEOUT)), || {
397 Err(anyhow!("play_reset_sound took too long"))
398 })
399 .await
400 {
401 log::warn!("Failed to play reset sound: {:?}", error);
402 }
403
404 self.set_factory_reset_state(FactoryResetState::Resetting);
406 log::info!("Calling {}.Reset", FactoryResetMarker::PROTOCOL_NAME);
407 let factory_reset =
408 self.incoming.connect_protocol::<FactoryResetProxy>().with_context(|| {
409 format!("failed to connect to {}", FactoryResetMarker::PROTOCOL_NAME)
410 })?;
411 factory_reset.reset().await.context("failed while calling Reset")?;
412 Ok(())
413 }
414}
415
416impl Handler for FactoryResetHandler {
417 fn set_handler_healthy(self: std::rc::Rc<Self>) {
418 self.inspect_status.health_node.borrow_mut().set_ok();
419 }
420
421 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
422 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
423 }
424
425 fn get_name(&self) -> &'static str {
426 "FactoryResetHandler"
427 }
428
429 fn interest(&self) -> Vec<input_device::InputEventType> {
430 vec![input_device::InputEventType::ConsumerControls]
431 }
432}
433
434#[async_trait(?Send)]
435impl UnhandledInputHandler for FactoryResetHandler {
436 async fn handle_unhandled_input_event(
440 self: Rc<Self>,
441 unhandled_input_event: input_device::UnhandledInputEvent,
442 ) -> Vec<input_device::InputEvent> {
443 fuchsia_trace::duration!("input", "factory_reset_handler");
444 match unhandled_input_event {
445 input_device::UnhandledInputEvent {
446 device_event: input_device::InputDeviceEvent::ConsumerControls(ref event),
447 device_descriptor: input_device::InputDeviceDescriptor::ConsumerControls(_),
448 event_time,
449 trace_id: _,
450 } => {
451 fuchsia_trace::duration!("input", "factory_reset_handler[processing]");
452 self.inspect_status.count_received_event(&event_time);
453 match self.factory_reset_state() {
454 FactoryResetState::Idle => {
455 let event_clone = event.clone();
456 let self_weak = Rc::downgrade(&self);
457 let completed = Rc::new(Cell::new(false));
458 let completed_clone = completed.clone();
459 let task = Dispatcher::spawn_local(async move {
460 if let Some(self_tg) = self_weak.upgrade() {
461 self_tg.handle_allowed_event(&event_clone).await;
462 }
463 completed_clone.set(true);
464 });
465 let mut tasks = self._tasks.borrow_mut();
466 tasks.retain(|(_, completed)| !completed.get());
467 tasks.push((task, completed));
468 }
469 FactoryResetState::Disallowed => self.handle_disallowed_event(event),
470 FactoryResetState::ButtonCountdown { deadline: _ } => {
471 self.handle_button_countdown_event(event)
472 }
473 FactoryResetState::ResetCountdown { deadline: _ } => {
474 self.handle_reset_countdown_event(event)
475 }
476 FactoryResetState::Resetting => {
477 log::warn!("Recieved an input event while factory resetting the device")
478 }
479 };
480 }
481 _ => {
482 self.metrics_logger.log_error(
483 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
484 std::format!(
485 "{} uninterested input event: {:?}",
486 self.get_name(),
487 unhandled_input_event.get_event_type()
488 ),
489 );
490 }
491 };
492
493 vec![input_device::InputEvent::from(unhandled_input_event)]
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500 use crate::consumer_controls_binding::ConsumerControlsDeviceDescriptor;
501 use crate::input_handler::InputHandler;
502 use assert_matches::assert_matches;
503 use fidl::endpoints::create_proxy_and_stream;
504 use fidl_fuchsia_recovery_policy::{DeviceMarker, DeviceProxy};
505 use fidl_fuchsia_recovery_ui::{FactoryResetCountdownMarker, FactoryResetCountdownProxy};
506 use fuchsia_async::TestExecutor;
507 use pretty_assertions::assert_eq;
508 use std::pin::pin;
509 use std::task::Poll;
510
511 fn create_factory_reset_countdown_proxy(
512 reset_handler: Rc<FactoryResetHandler>,
513 ) -> FactoryResetCountdownProxy {
514 let (countdown_proxy, countdown_stream) =
515 create_proxy_and_stream::<FactoryResetCountdownMarker>();
516
517 let stream_fut =
518 reset_handler.clone().handle_factory_reset_countdown_request_stream(countdown_stream);
519
520 let completed = Rc::new(Cell::new(false));
521 let completed_clone = completed.clone();
522 let task = Dispatcher::spawn_local(async move {
523 if stream_fut.await.is_err() {
524 log::warn!("Failed to handle factory reset countdown request stream");
525 }
526 completed_clone.set(true);
527 });
528 reset_handler._tasks.borrow_mut().push((task, completed));
529
530 countdown_proxy
531 }
532
533 fn create_recovery_policy_proxy(reset_handler: Rc<FactoryResetHandler>) -> DeviceProxy {
534 let (device_proxy, device_stream) = create_proxy_and_stream::<DeviceMarker>();
535
536 let reset_handler_clone = reset_handler.clone();
537 let completed = Rc::new(Cell::new(false));
538 let completed_clone = completed.clone();
539 let task = Dispatcher::spawn_local(async move {
540 if reset_handler_clone
541 .handle_recovery_policy_device_request_stream(device_stream)
542 .await
543 .is_err()
544 {
545 log::warn!("Failed to handle recovery policy device request stream");
546 }
547 completed_clone.set(true);
548 });
549 reset_handler._tasks.borrow_mut().push((task, completed));
550
551 device_proxy
552 }
553
554 fn create_input_device_descriptor() -> input_device::InputDeviceDescriptor {
555 input_device::InputDeviceDescriptor::ConsumerControls(ConsumerControlsDeviceDescriptor {
556 buttons: vec![
557 fidl_input_report::ConsumerControlButton::CameraDisable,
558 fidl_input_report::ConsumerControlButton::FactoryReset,
559 fidl_input_report::ConsumerControlButton::MicMute,
560 fidl_input_report::ConsumerControlButton::Pause,
561 fidl_input_report::ConsumerControlButton::VolumeDown,
562 fidl_input_report::ConsumerControlButton::VolumeUp,
563 ],
564 device_id: 0,
565 })
566 }
567
568 fn create_reset_consumer_controls_event() -> ConsumerControlsEvent {
569 ConsumerControlsEvent::new(
570 vec![fidl_input_report::ConsumerControlButton::FactoryReset],
571 None,
572 )
573 }
574
575 fn create_non_reset_consumer_controls_event() -> ConsumerControlsEvent {
576 ConsumerControlsEvent::new(
577 vec![
578 fidl_input_report::ConsumerControlButton::CameraDisable,
579 fidl_input_report::ConsumerControlButton::MicMute,
580 fidl_input_report::ConsumerControlButton::Pause,
581 fidl_input_report::ConsumerControlButton::VolumeDown,
582 fidl_input_report::ConsumerControlButton::VolumeUp,
583 ],
584 None,
585 )
586 }
587
588 fn create_non_reset_input_event() -> input_device::UnhandledInputEvent {
589 let device_event = input_device::InputDeviceEvent::ConsumerControls(
590 create_non_reset_consumer_controls_event(),
591 );
592
593 input_device::UnhandledInputEvent {
594 device_event,
595 device_descriptor: create_input_device_descriptor(),
596 event_time: zx::MonotonicInstant::get(),
597 trace_id: None,
598 }
599 }
600
601 fn create_reset_input_event() -> input_device::UnhandledInputEvent {
602 let device_event = input_device::InputDeviceEvent::ConsumerControls(
603 create_reset_consumer_controls_event(),
604 );
605
606 input_device::UnhandledInputEvent {
607 device_event,
608 device_descriptor: create_input_device_descriptor(),
609 event_time: zx::MonotonicInstant::get(),
610 trace_id: None,
611 }
612 }
613
614 #[fuchsia::test]
615 async fn is_reset_requested_looks_for_reset_signal() {
616 let reset_event = create_reset_consumer_controls_event();
617 let non_reset_event = create_non_reset_consumer_controls_event();
618
619 assert!(
620 is_reset_requested(&reset_event),
621 "Should reset when the reset signal is received."
622 );
623 assert!(
624 !is_reset_requested(&non_reset_event),
625 "Should only reset when the reset signal is received."
626 );
627 }
628
629 #[fuchsia::test]
630 async fn factory_reset_countdown_listener_gets_initial_state() {
631 let inspector = fuchsia_inspect::Inspector::default();
632 let test_node = inspector.root().create_child("test_node");
633 let reset_handler = FactoryResetHandler::new(
634 Incoming::new(),
635 &test_node,
636 metrics::MetricsLogger::default(),
637 );
638 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
639 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
640 assert_eq!(reset_state.scheduled_reset_time, None);
641 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
642 }
643
644 #[fuchsia::test]
645 fn factory_reset_countdown_listener_is_notified_on_state_change() -> Result<(), Error> {
646 let mut executor = TestExecutor::new_with_fake_time();
647 let inspector = fuchsia_inspect::Inspector::default();
648 let test_node = inspector.root().create_child("test_node");
649 let reset_handler = FactoryResetHandler::new(
650 Incoming::new(),
651 &test_node,
652 metrics::MetricsLogger::default(),
653 );
654 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
655
656 let get_countdown_state = |executor: &mut TestExecutor| {
657 let mut fut = countdown_proxy.watch();
658 loop {
659 match executor.run_until_stalled(&mut fut) {
661 Poll::Pending => continue,
662 Poll::Ready(state) => {
663 return state.expect("Failed to get countdown state");
664 }
665 }
666 }
667 };
668
669 let countdown_state = get_countdown_state(&mut executor);
672 let handler_state = reset_handler.factory_reset_state();
673 assert_eq!(countdown_state.scheduled_reset_time, None);
674 assert_eq!(handler_state, FactoryResetState::Idle);
675
676 let reset_event = create_reset_input_event();
678 let mut handle_input_event_fut =
679 pin!(reset_handler.clone().handle_unhandled_input_event(reset_event));
680 assert_matches!(executor.run_until_stalled(&mut handle_input_event_fut), Poll::Ready(events) => {
681 assert_matches!(events.as_slice(), [input_device::InputEvent { .. }]);
682 });
683
684 let countdown_state = get_countdown_state(&mut executor);
686 let handler_state = reset_handler.factory_reset_state();
687 assert_eq!(countdown_state.scheduled_reset_time, None);
688 assert_matches!(handler_state, FactoryResetState::ButtonCountdown { deadline: _ });
689
690 executor.set_fake_time(MonotonicInstant::after(MonotonicDuration::from_millis(500)));
692 executor.wake_expired_timers();
693
694 let countdown_state = get_countdown_state(&mut executor);
697 let handler_state = reset_handler.factory_reset_state();
698 assert_matches!(countdown_state.scheduled_reset_time, Some(_));
699 assert_matches!(handler_state, FactoryResetState::ResetCountdown { deadline: _ });
700
701 executor.set_fake_time(MonotonicInstant::after(MonotonicDuration::from_seconds(10)));
703 executor.wake_expired_timers();
704
705 let countdown_state = get_countdown_state(&mut executor);
708 let handler_state = reset_handler.factory_reset_state();
709 assert_eq!(countdown_state.scheduled_reset_time, None);
710 assert_eq!(handler_state, FactoryResetState::Resetting);
711
712 Ok(())
713 }
714
715 #[fuchsia::test]
716 async fn recovery_policy_requests_update_reset_handler_state() {
717 let inspector = fuchsia_inspect::Inspector::default();
718 let test_node = inspector.root().create_child("test_node");
719 let reset_handler = FactoryResetHandler::new(
720 Incoming::new(),
721 &test_node,
722 metrics::MetricsLogger::default(),
723 );
724 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
725
726 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
728 assert_eq!(reset_state.scheduled_reset_time, None);
729 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
730
731 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
733 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
734
735 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
737 assert_eq!(reset_state.scheduled_reset_time, None);
738 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
739
740 let reset_event = create_reset_input_event();
742 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
743
744 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
746
747 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
749 device_proxy.set_is_local_reset_allowed(true).expect("Failed to set recovery policy");
750
751 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
753 assert_eq!(reset_state.scheduled_reset_time, None);
754 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
755 }
756
757 #[fuchsia::test]
758 fn handle_allowed_event_changes_state_with_reset() {
759 let mut executor = TestExecutor::new();
760
761 let reset_event = create_reset_consumer_controls_event();
762 let inspector = fuchsia_inspect::Inspector::default();
763 let test_node = inspector.root().create_child("test_node");
764 let reset_handler = FactoryResetHandler::new(
765 Incoming::new(),
766 &test_node,
767 metrics::MetricsLogger::default(),
768 );
769 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
770
771 let reset_state = executor
773 .run_singlethreaded(countdown_proxy.watch())
774 .expect("Failed to get countdown state");
775 assert_eq!(reset_state.scheduled_reset_time, None);
776 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
777
778 let handle_allowed_event_fut = reset_handler.handle_allowed_event(&reset_event);
779 futures::pin_mut!(handle_allowed_event_fut);
780 assert_eq!(executor.run_until_stalled(&mut handle_allowed_event_fut), Poll::Pending);
781
782 assert_matches!(
784 executor.run_singlethreaded(countdown_proxy.watch()),
785 Ok(FactoryResetCountdownState { scheduled_reset_time: None, .. })
786 );
787 assert_matches!(
788 reset_handler.factory_reset_state(),
789 FactoryResetState::ButtonCountdown { deadline: _ }
790 );
791 }
792
793 #[fuchsia::test]
794 async fn handle_allowed_event_wont_change_state_without_reset() {
795 let inspector = fuchsia_inspect::Inspector::default();
796 let test_node = inspector.root().create_child("test_node");
797 let reset_handler = FactoryResetHandler::new(
798 Incoming::new(),
799 &test_node,
800 metrics::MetricsLogger::default(),
801 );
802 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
803
804 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
806 assert_eq!(reset_state.scheduled_reset_time, None);
807 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
808
809 let non_reset_event = create_non_reset_consumer_controls_event();
810 reset_handler.clone().handle_allowed_event(&non_reset_event).await;
811
812 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
814 }
815
816 #[fuchsia::test]
817 async fn handle_disallowed_event_wont_change_state() {
818 let inspector = fuchsia_inspect::Inspector::default();
819 let test_node = inspector.root().create_child("test_node");
820 let reset_handler = FactoryResetHandler::new(
821 Incoming::new(),
822 &test_node,
823 metrics::MetricsLogger::default(),
824 );
825 *reset_handler.factory_reset_state.borrow_mut() = FactoryResetState::Disallowed;
826
827 let reset_event = create_reset_consumer_controls_event();
830 reset_handler.handle_disallowed_event(&reset_event);
831 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
832
833 let non_reset_event = create_non_reset_consumer_controls_event();
834 reset_handler.handle_disallowed_event(&non_reset_event);
835 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
836 }
837
838 #[fuchsia::test]
839 async fn handle_button_countdown_event_changes_state_when_reset_no_longer_requested() {
840 let inspector = fuchsia_inspect::Inspector::default();
841 let test_node = inspector.root().create_child("test_node");
842 let reset_handler = FactoryResetHandler::new(
843 Incoming::new(),
844 &test_node,
845 metrics::MetricsLogger::default(),
846 );
847
848 let deadline = MonotonicInstant::after(BUTTON_TIMEOUT);
849 *reset_handler.factory_reset_state.borrow_mut() =
850 FactoryResetState::ButtonCountdown { deadline };
851
852 let non_reset_event = create_non_reset_consumer_controls_event();
855 reset_handler.handle_button_countdown_event(&non_reset_event);
856 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
857 }
858
859 #[fuchsia::test]
860 async fn handle_reset_countdown_event_changes_state_when_reset_no_longer_requested() {
861 let inspector = fuchsia_inspect::Inspector::default();
862 let test_node = inspector.root().create_child("test_node");
863 let reset_handler = FactoryResetHandler::new(
864 Incoming::new(),
865 &test_node,
866 metrics::MetricsLogger::default(),
867 );
868
869 *reset_handler.factory_reset_state.borrow_mut() =
870 FactoryResetState::ResetCountdown { deadline: MonotonicInstant::now() };
871
872 let non_reset_event = create_non_reset_consumer_controls_event();
875 reset_handler.handle_reset_countdown_event(&non_reset_event);
876 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
877 }
878
879 #[fuchsia::test]
880 async fn factory_reset_disallowed_during_button_countdown() {
881 let inspector = fuchsia_inspect::Inspector::default();
882 let test_node = inspector.root().create_child("test_node");
883 let reset_handler = FactoryResetHandler::new(
884 Incoming::new(),
885 &test_node,
886 metrics::MetricsLogger::default(),
887 );
888 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
889
890 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
892 assert_eq!(reset_state.scheduled_reset_time, None);
893 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
894
895 let reset_event = create_reset_input_event();
897 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
898
899 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
901 assert_eq!(reset_state.scheduled_reset_time, None);
902 assert_matches!(
903 reset_handler.factory_reset_state(),
904 FactoryResetState::ButtonCountdown { deadline: _ }
905 );
906
907 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
909 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
910
911 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
913 assert_eq!(reset_state.scheduled_reset_time, None);
914 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
915 }
916
917 #[fuchsia::test]
918 async fn factory_reset_disallowed_during_reset_countdown() {
919 let inspector = fuchsia_inspect::Inspector::default();
920 let test_node = inspector.root().create_child("test_node");
921 let reset_handler = FactoryResetHandler::new(
922 Incoming::new(),
923 &test_node,
924 metrics::MetricsLogger::default(),
925 );
926 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
927
928 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
930 assert_eq!(reset_state.scheduled_reset_time, None);
931 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
932
933 let reset_event = create_reset_input_event();
935 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
936
937 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
939 assert_eq!(reset_state.scheduled_reset_time, None);
940 assert_matches!(
941 reset_handler.factory_reset_state(),
942 FactoryResetState::ButtonCountdown { deadline: _ }
943 );
944
945 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
947 assert_matches!(reset_state.scheduled_reset_time, Some(_));
948 assert_matches!(
949 reset_handler.factory_reset_state(),
950 FactoryResetState::ResetCountdown { deadline: _ }
951 );
952
953 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
955 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
956
957 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
959 assert_eq!(reset_state.scheduled_reset_time, None);
960 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
961 }
962
963 #[fuchsia::test]
964 async fn factory_reset_cancelled_during_button_countdown() {
965 let inspector = fuchsia_inspect::Inspector::default();
966 let test_node = inspector.root().create_child("test_node");
967 let reset_handler = FactoryResetHandler::new(
968 Incoming::new(),
969 &test_node,
970 metrics::MetricsLogger::default(),
971 );
972 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
973
974 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
976 assert_eq!(reset_state.scheduled_reset_time, None);
977 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
978
979 let reset_event = create_reset_input_event();
981 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
982
983 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
985 assert_eq!(reset_state.scheduled_reset_time, None);
986 assert_matches!(
987 reset_handler.factory_reset_state(),
988 FactoryResetState::ButtonCountdown { deadline: _ }
989 );
990
991 let non_reset_event = create_non_reset_input_event();
993 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
994
995 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
997 assert_eq!(reset_state.scheduled_reset_time, None);
998 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
999 }
1000
1001 #[fuchsia::test]
1002 async fn factory_reset_cancelled_during_reset_countdown() {
1003 let inspector = fuchsia_inspect::Inspector::default();
1004 let test_node = inspector.root().create_child("test_node");
1005 let reset_handler = FactoryResetHandler::new(
1006 Incoming::new(),
1007 &test_node,
1008 metrics::MetricsLogger::default(),
1009 );
1010 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
1011
1012 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
1014 assert_eq!(reset_state.scheduled_reset_time, None);
1015 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
1016
1017 let reset_event = create_reset_input_event();
1019 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
1020
1021 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
1023 assert_eq!(reset_state.scheduled_reset_time, None);
1024 assert_matches!(
1025 reset_handler.factory_reset_state(),
1026 FactoryResetState::ButtonCountdown { deadline: _ }
1027 );
1028
1029 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
1031 assert_matches!(reset_state.scheduled_reset_time, Some(_));
1032 assert_matches!(
1033 reset_handler.factory_reset_state(),
1034 FactoryResetState::ResetCountdown { deadline: _ }
1035 );
1036
1037 let non_reset_event = create_non_reset_input_event();
1039 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
1040
1041 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
1043 assert_eq!(reset_state.scheduled_reset_time, None);
1044 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
1045 }
1046
1047 #[fuchsia::test]
1048 async fn factory_reset_handler_initialized_with_inspect_node() {
1049 let inspector = fuchsia_inspect::Inspector::default();
1050 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1051 let _handler = FactoryResetHandler::new(
1052 Incoming::new(),
1053 &fake_handlers_node,
1054 metrics::MetricsLogger::default(),
1055 );
1056 diagnostics_assertions::assert_data_tree!(inspector, root: {
1057 input_handlers_node: {
1058 factory_reset_handler: {
1059 events_received_count: 0u64,
1060 events_handled_count: 0u64,
1061 last_received_timestamp_ns: 0u64,
1062 "fuchsia.inspect.Health": {
1063 status: "STARTING_UP",
1064 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1067 },
1068 }
1069 }
1070 });
1071 }
1072
1073 #[fuchsia::test]
1074 async fn factory_reset_handler_inspect_counts_events() {
1075 let inspector = fuchsia_inspect::Inspector::default();
1076 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1077 let reset_handler = FactoryResetHandler::new(
1078 Incoming::new(),
1079 &fake_handlers_node,
1080 metrics::MetricsLogger::default(),
1081 );
1082
1083 let reset_event = create_reset_input_event();
1085 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
1086
1087 let mut handled_event = input_device::InputEvent::from(create_reset_input_event());
1089 handled_event.handled = input_device::Handled::Yes;
1090 reset_handler.clone().handle_input_event(handled_event).await;
1091
1092 let non_reset_event = create_non_reset_input_event();
1094 let last_event_timestamp: u64 =
1095 non_reset_event.clone().event_time.into_nanos().try_into().unwrap();
1096 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
1097
1098 diagnostics_assertions::assert_data_tree!(inspector, root: {
1099 input_handlers_node: {
1100 factory_reset_handler: {
1101 events_received_count: 2u64,
1102 events_handled_count: 0u64,
1103 last_received_timestamp_ns: last_event_timestamp,
1104 "fuchsia.inspect.Health": {
1105 status: "STARTING_UP",
1106 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1109 },
1110 }
1111 }
1112 });
1113 }
1114}