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