1use crate::consumer_controls_binding::ConsumerControlsEvent;
6use crate::dispatcher::TimeoutExt;
7use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
8use crate::{Dispatcher, Incoming, MonotonicInstant, 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::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
116fn is_reset_requested(event: &ConsumerControlsEvent) -> bool {
121 event.pressed_buttons.iter().any(|button| match button {
122 fidl_input_report::ConsumerControlButton::FactoryReset => true,
123 _ => false,
124 })
125}
126
127impl FactoryResetHandler {
128 pub fn new(
131 incoming: Incoming,
132 input_handlers_node: &fuchsia_inspect::Node,
133 metrics_logger: metrics::MetricsLogger,
134 ) -> Rc<Self> {
135 let initial_state = if Path::new(FACTORY_RESET_DISALLOWED_PATH).exists() {
136 FactoryResetState::Disallowed
137 } else {
138 FactoryResetState::Idle
139 };
140
141 let countdown_hanging_get =
142 FactoryResetHandler::init_hanging_get(initial_state, metrics_logger.clone());
143 let inspect_status = InputHandlerStatus::new(
144 input_handlers_node,
145 "factory_reset_handler",
146 false,
147 );
148
149 Rc::new(Self {
150 incoming,
151 factory_reset_state: RefCell::new(initial_state),
152 countdown_hanging_get: RefCell::new(countdown_hanging_get),
153 inspect_status,
154 metrics_logger,
155 })
156 }
157
158 pub fn handle_factory_reset_countdown_request_stream(
163 self: Rc<Self>,
164 mut stream: FactoryResetCountdownRequestStream,
165 ) -> impl futures::Future<Output = Result<(), Error>> {
166 let subscriber = self.countdown_hanging_get.borrow_mut().new_subscriber();
167
168 async move {
169 while let Some(request_result) = stream.next().await {
170 let watcher = request_result?
171 .into_watch()
172 .ok_or_else(|| anyhow!("Failed to get FactoryResetCoundown Watcher"))?;
173 subscriber.register(watcher)?;
174 }
175
176 Ok(())
177 }
178 }
179
180 pub fn handle_recovery_policy_device_request_stream(
185 self: Rc<Self>,
186 mut stream: DeviceRequestStream,
187 ) -> impl futures::Future<Output = Result<(), Error>> {
188 async move {
189 while let Some(request_result) = stream.next().await {
190 let DeviceRequest::SetIsLocalResetAllowed { allowed, .. } = request_result?;
191 match self.factory_reset_state() {
192 FactoryResetState::Disallowed if allowed => {
193 self.set_factory_reset_state(FactoryResetState::Idle);
195 fs::remove_file(FACTORY_RESET_DISALLOWED_PATH).with_context(|| {
196 format!("failed to remove {}", FACTORY_RESET_DISALLOWED_PATH)
197 })?
198 }
199 _ if !allowed => {
200 self.set_factory_reset_state(FactoryResetState::Disallowed);
202 let _: File =
203 File::create(FACTORY_RESET_DISALLOWED_PATH).with_context(|| {
204 format!("failed to create {}", FACTORY_RESET_DISALLOWED_PATH)
205 })?;
206 }
207 _ => (),
208 }
209 }
210
211 Ok(())
212 }
213 }
214
215 async fn handle_allowed_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
218 if is_reset_requested(event) {
219 if let Err(error) = self.start_button_countdown().await {
220 self.metrics_logger.log_error(
221 InputPipelineErrorMetricDimensionEvent::FactoryResetFailedToReset,
222 std::format!("Failed to factory reset device: {:?}", error),
223 );
224 }
225 }
226 }
227
228 fn handle_disallowed_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
231 if is_reset_requested(event) {
232 self.metrics_logger.log_error(
233 InputPipelineErrorMetricDimensionEvent::FactoryResetNotAllowedReset,
234 "Attempted to factory reset a device that is not allowed to reset",
235 );
236 }
237 }
238
239 fn handle_button_countdown_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
242 if !is_reset_requested(event) {
243 self.set_factory_reset_state(FactoryResetState::Idle);
245 }
246 }
247
248 fn handle_reset_countdown_event(self: &Rc<Self>, event: &ConsumerControlsEvent) {
251 if !is_reset_requested(event) {
252 self.set_factory_reset_state(FactoryResetState::Idle);
254 }
255 }
256
257 fn init_hanging_get(
258 initial_state: FactoryResetState,
259 metrics_logger: metrics::MetricsLogger,
260 ) -> ResetCountdownHangingGet {
261 let notify_fn: NotifyFn = Box::new(|(state, metrics_logger), responder| {
262 let deadline = match state {
263 FactoryResetState::ResetCountdown { deadline } => {
264 Some(deadline.into_nanos() as i64)
265 }
266 _ => None,
267 };
268
269 let countdown_state =
270 FactoryResetCountdownState { scheduled_reset_time: deadline, ..Default::default() };
271
272 if responder.send(&countdown_state).is_err() {
273 metrics_logger.log_error(
274 InputPipelineErrorMetricDimensionEvent::FactoryResetFailedToSendCountdown,
275 "Failed to send factory reset countdown state",
276 );
277 }
278
279 true
280 });
281
282 ResetCountdownHangingGet::new((initial_state, metrics_logger), notify_fn)
283 }
284
285 fn set_factory_reset_state(self: &Rc<Self>, state: FactoryResetState) {
287 *self.factory_reset_state.borrow_mut() = state;
288 self.countdown_hanging_get
289 .borrow_mut()
290 .new_publisher()
291 .set((state, self.metrics_logger.clone()));
292 }
293
294 fn factory_reset_state(self: &Rc<Self>) -> FactoryResetState {
295 *self.factory_reset_state.borrow()
296 }
297
298 async fn start_button_countdown(self: &Rc<Self>) -> Result<(), Error> {
301 let deadline = MonotonicInstant::after(BUTTON_TIMEOUT);
302 self.set_factory_reset_state(FactoryResetState::ButtonCountdown { deadline });
303
304 Dispatcher::after_deadline(MonotonicInstant::after(BUTTON_TIMEOUT)).await;
306
307 match self.factory_reset_state() {
309 FactoryResetState::ButtonCountdown { deadline: state_deadline }
310 if state_deadline == deadline =>
311 {
312 self.start_reset_countdown().await?;
314 }
315 _ => {
316 log::info!("Factory reset request cancelled");
317 }
318 }
319
320 Ok(())
321 }
322
323 async fn start_reset_countdown(self: &Rc<Self>) -> Result<(), Error> {
326 let deadline = MonotonicInstant::after(RESET_TIMEOUT);
327 self.set_factory_reset_state(FactoryResetState::ResetCountdown { deadline });
328
329 Dispatcher::after_deadline(MonotonicInstant::after(RESET_TIMEOUT)).await;
331
332 match self.factory_reset_state() {
334 FactoryResetState::ResetCountdown { deadline: state_deadline }
335 if state_deadline == deadline =>
336 {
337 self.reset().await?;
339 }
340 _ => {
341 log::info!("Factory reset request cancelled");
342 }
343 }
344
345 Ok(())
346 }
347
348 async fn play_reset_sound(self: &Rc<Self>) -> Result<(), Error> {
350 log::debug!("Getting sound");
351 let sound_endpoint = fuchsia_component::directory::open_file_async(
353 &self.incoming,
354 FACTORY_RESET_SOUND_PATH,
355 fio::R_STAR_DIR,
356 )
357 .context("Failed to open factory reset sound file")?;
358
359 log::debug!("Playing sound");
360 let sound_player = self
362 .incoming
363 .connect_protocol::<PlayerProxy>()
364 .with_context(|| format!("failed to connect to {}", PlayerMarker::PROTOCOL_NAME))?;
365
366 log::debug!("Connected to player");
367 let sound_id = 0;
368 let _duration: i64 = sound_player
369 .add_sound_from_file(sound_id, sound_endpoint.into_client_end().unwrap())
370 .await
371 .context("AddSoundFromFile error")?
372 .map_err(zx::Status::from_raw)
373 .context("AddSoundFromFile failed")?;
374 log::debug!("Added sound from file");
375
376 sound_player
377 .play_sound2(sound_id, AudioRenderUsage2::Media)
378 .await
379 .context("PlaySound2 error")?
380 .map_err(|err: PlaySoundError| anyhow!("PlaySound2 failed: {:?}", err))?;
381
382 log::debug!("Played sound");
383
384 Ok(())
385 }
386
387 async fn reset(self: &Rc<Self>) -> Result<(), Error> {
389 log::info!("Beginning reset sequence");
390 if let Err(error) = self
391 .play_reset_sound()
392 .on_timeout(Dispatcher::after_deadline(MonotonicInstant::after(EARCON_TIMEOUT)), || {
393 Err(anyhow!("play_reset_sound took too long"))
394 })
395 .await
396 {
397 log::warn!("Failed to play reset sound: {:?}", error);
398 }
399
400 self.set_factory_reset_state(FactoryResetState::Resetting);
402 log::info!("Calling {}.Reset", FactoryResetMarker::PROTOCOL_NAME);
403 let factory_reset =
404 self.incoming.connect_protocol::<FactoryResetProxy>().with_context(|| {
405 format!("failed to connect to {}", FactoryResetMarker::PROTOCOL_NAME)
406 })?;
407 factory_reset.reset().await.context("failed while calling Reset")?;
408 Ok(())
409 }
410}
411
412impl Handler for FactoryResetHandler {
413 fn set_handler_healthy(self: std::rc::Rc<Self>) {
414 self.inspect_status.health_node.borrow_mut().set_ok();
415 }
416
417 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
418 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
419 }
420
421 fn get_name(&self) -> &'static str {
422 "FactoryResetHandler"
423 }
424
425 fn interest(&self) -> Vec<input_device::InputEventType> {
426 vec![input_device::InputEventType::ConsumerControls]
427 }
428}
429
430#[async_trait(?Send)]
431impl UnhandledInputHandler for FactoryResetHandler {
432 async fn handle_unhandled_input_event(
436 self: Rc<Self>,
437 unhandled_input_event: input_device::UnhandledInputEvent,
438 ) -> Vec<input_device::InputEvent> {
439 fuchsia_trace::duration!("input", "factory_reset_handler");
440 match unhandled_input_event {
441 input_device::UnhandledInputEvent {
442 device_event: input_device::InputDeviceEvent::ConsumerControls(ref event),
443 device_descriptor: input_device::InputDeviceDescriptor::ConsumerControls(_),
444 event_time,
445 trace_id: _,
446 } => {
447 fuchsia_trace::duration!("input", "factory_reset_handler[processing]");
448 self.inspect_status.count_received_event(&event_time);
449 match self.factory_reset_state() {
450 FactoryResetState::Idle => {
451 let event_clone = event.clone();
452 Dispatcher::spawn_local(async move {
453 self.handle_allowed_event(&event_clone).await
454 })
455 .detach();
456 }
457 FactoryResetState::Disallowed => self.handle_disallowed_event(event),
458 FactoryResetState::ButtonCountdown { deadline: _ } => {
459 self.handle_button_countdown_event(event)
460 }
461 FactoryResetState::ResetCountdown { deadline: _ } => {
462 self.handle_reset_countdown_event(event)
463 }
464 FactoryResetState::Resetting => {
465 log::warn!("Recieved an input event while factory resetting the device")
466 }
467 };
468 }
469 _ => {
470 self.metrics_logger.log_error(
471 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
472 std::format!(
473 "{} uninterested input event: {:?}",
474 self.get_name(),
475 unhandled_input_event.get_event_type()
476 ),
477 );
478 }
479 };
480
481 vec![input_device::InputEvent::from(unhandled_input_event)]
482 }
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488 use crate::consumer_controls_binding::ConsumerControlsDeviceDescriptor;
489 use crate::input_handler::InputHandler;
490 use assert_matches::assert_matches;
491 use fidl::endpoints::create_proxy_and_stream;
492 use fidl_fuchsia_recovery_policy::{DeviceMarker, DeviceProxy};
493 use fidl_fuchsia_recovery_ui::{FactoryResetCountdownMarker, FactoryResetCountdownProxy};
494 use fuchsia_async::{Task, TestExecutor};
495 use pretty_assertions::assert_eq;
496 use std::pin::pin;
497 use std::task::Poll;
498
499 fn create_factory_reset_countdown_proxy(
500 reset_handler: Rc<FactoryResetHandler>,
501 ) -> FactoryResetCountdownProxy {
502 let (countdown_proxy, countdown_stream) =
503 create_proxy_and_stream::<FactoryResetCountdownMarker>();
504
505 let stream_fut =
506 reset_handler.clone().handle_factory_reset_countdown_request_stream(countdown_stream);
507
508 Task::local(async move {
509 if stream_fut.await.is_err() {
510 log::warn!("Failed to handle factory reset countdown request stream");
511 }
512 })
513 .detach();
514
515 countdown_proxy
516 }
517
518 fn create_recovery_policy_proxy(reset_handler: Rc<FactoryResetHandler>) -> DeviceProxy {
519 let (device_proxy, device_stream) = create_proxy_and_stream::<DeviceMarker>();
520
521 Task::local(async move {
522 if reset_handler
523 .handle_recovery_policy_device_request_stream(device_stream)
524 .await
525 .is_err()
526 {
527 log::warn!("Failed to handle recovery policy device request stream");
528 }
529 })
530 .detach();
531
532 device_proxy
533 }
534
535 fn create_input_device_descriptor() -> input_device::InputDeviceDescriptor {
536 input_device::InputDeviceDescriptor::ConsumerControls(ConsumerControlsDeviceDescriptor {
537 buttons: vec![
538 fidl_input_report::ConsumerControlButton::CameraDisable,
539 fidl_input_report::ConsumerControlButton::FactoryReset,
540 fidl_input_report::ConsumerControlButton::MicMute,
541 fidl_input_report::ConsumerControlButton::Pause,
542 fidl_input_report::ConsumerControlButton::VolumeDown,
543 fidl_input_report::ConsumerControlButton::VolumeUp,
544 ],
545 device_id: 0,
546 })
547 }
548
549 fn create_reset_consumer_controls_event() -> ConsumerControlsEvent {
550 ConsumerControlsEvent::new(
551 vec![fidl_input_report::ConsumerControlButton::FactoryReset],
552 None,
553 )
554 }
555
556 fn create_non_reset_consumer_controls_event() -> ConsumerControlsEvent {
557 ConsumerControlsEvent::new(
558 vec![
559 fidl_input_report::ConsumerControlButton::CameraDisable,
560 fidl_input_report::ConsumerControlButton::MicMute,
561 fidl_input_report::ConsumerControlButton::Pause,
562 fidl_input_report::ConsumerControlButton::VolumeDown,
563 fidl_input_report::ConsumerControlButton::VolumeUp,
564 ],
565 None,
566 )
567 }
568
569 fn create_non_reset_input_event() -> input_device::UnhandledInputEvent {
570 let device_event = input_device::InputDeviceEvent::ConsumerControls(
571 create_non_reset_consumer_controls_event(),
572 );
573
574 input_device::UnhandledInputEvent {
575 device_event,
576 device_descriptor: create_input_device_descriptor(),
577 event_time: zx::MonotonicInstant::get(),
578 trace_id: None,
579 }
580 }
581
582 fn create_reset_input_event() -> input_device::UnhandledInputEvent {
583 let device_event = input_device::InputDeviceEvent::ConsumerControls(
584 create_reset_consumer_controls_event(),
585 );
586
587 input_device::UnhandledInputEvent {
588 device_event,
589 device_descriptor: create_input_device_descriptor(),
590 event_time: zx::MonotonicInstant::get(),
591 trace_id: None,
592 }
593 }
594
595 #[fuchsia::test]
596 async fn is_reset_requested_looks_for_reset_signal() {
597 let reset_event = create_reset_consumer_controls_event();
598 let non_reset_event = create_non_reset_consumer_controls_event();
599
600 assert!(
601 is_reset_requested(&reset_event),
602 "Should reset when the reset signal is received."
603 );
604 assert!(
605 !is_reset_requested(&non_reset_event),
606 "Should only reset when the reset signal is received."
607 );
608 }
609
610 #[fuchsia::test]
611 async fn factory_reset_countdown_listener_gets_initial_state() {
612 let inspector = fuchsia_inspect::Inspector::default();
613 let test_node = inspector.root().create_child("test_node");
614 let reset_handler = FactoryResetHandler::new(
615 Incoming::new(),
616 &test_node,
617 metrics::MetricsLogger::default(),
618 );
619 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
620 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
621 assert_eq!(reset_state.scheduled_reset_time, None);
622 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
623 }
624
625 #[fuchsia::test]
626 fn factory_reset_countdown_listener_is_notified_on_state_change() -> Result<(), Error> {
627 let mut executor = TestExecutor::new_with_fake_time();
628 let inspector = fuchsia_inspect::Inspector::default();
629 let test_node = inspector.root().create_child("test_node");
630 let reset_handler = FactoryResetHandler::new(
631 Incoming::new(),
632 &test_node,
633 metrics::MetricsLogger::default(),
634 );
635 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
636
637 let get_countdown_state = |executor: &mut TestExecutor| {
638 let mut fut = countdown_proxy.watch();
639 loop {
640 match executor.run_until_stalled(&mut fut) {
642 Poll::Pending => continue,
643 Poll::Ready(state) => {
644 return state.expect("Failed to get countdown state");
645 }
646 }
647 }
648 };
649
650 let countdown_state = get_countdown_state(&mut executor);
653 let handler_state = reset_handler.factory_reset_state();
654 assert_eq!(countdown_state.scheduled_reset_time, None);
655 assert_eq!(handler_state, FactoryResetState::Idle);
656
657 let reset_event = create_reset_input_event();
659 let mut handle_input_event_fut =
660 pin!(reset_handler.clone().handle_unhandled_input_event(reset_event));
661 assert_matches!(executor.run_until_stalled(&mut handle_input_event_fut), Poll::Ready(events) => {
662 assert_matches!(events.as_slice(), [input_device::InputEvent { .. }]);
663 });
664
665 let countdown_state = get_countdown_state(&mut executor);
667 let handler_state = reset_handler.factory_reset_state();
668 assert_eq!(countdown_state.scheduled_reset_time, None);
669 assert_matches!(handler_state, FactoryResetState::ButtonCountdown { deadline: _ });
670
671 executor.set_fake_time(MonotonicInstant::after(MonotonicDuration::from_millis(500)));
673 executor.wake_expired_timers();
674
675 let countdown_state = get_countdown_state(&mut executor);
678 let handler_state = reset_handler.factory_reset_state();
679 assert_matches!(countdown_state.scheduled_reset_time, Some(_));
680 assert_matches!(handler_state, FactoryResetState::ResetCountdown { deadline: _ });
681
682 executor.set_fake_time(MonotonicInstant::after(MonotonicDuration::from_seconds(10)));
684 executor.wake_expired_timers();
685
686 let countdown_state = get_countdown_state(&mut executor);
689 let handler_state = reset_handler.factory_reset_state();
690 assert_eq!(countdown_state.scheduled_reset_time, None);
691 assert_eq!(handler_state, FactoryResetState::Resetting);
692
693 Ok(())
694 }
695
696 #[fuchsia::test]
697 async fn recovery_policy_requests_update_reset_handler_state() {
698 let inspector = fuchsia_inspect::Inspector::default();
699 let test_node = inspector.root().create_child("test_node");
700 let reset_handler = FactoryResetHandler::new(
701 Incoming::new(),
702 &test_node,
703 metrics::MetricsLogger::default(),
704 );
705 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
706
707 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
709 assert_eq!(reset_state.scheduled_reset_time, None);
710 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
711
712 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
714 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
715
716 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
718 assert_eq!(reset_state.scheduled_reset_time, None);
719 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
720
721 let reset_event = create_reset_input_event();
723 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
724
725 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
727
728 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
730 device_proxy.set_is_local_reset_allowed(true).expect("Failed to set recovery policy");
731
732 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
734 assert_eq!(reset_state.scheduled_reset_time, None);
735 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
736 }
737
738 #[fuchsia::test]
739 fn handle_allowed_event_changes_state_with_reset() {
740 let mut executor = TestExecutor::new();
741
742 let reset_event = create_reset_consumer_controls_event();
743 let inspector = fuchsia_inspect::Inspector::default();
744 let test_node = inspector.root().create_child("test_node");
745 let reset_handler = FactoryResetHandler::new(
746 Incoming::new(),
747 &test_node,
748 metrics::MetricsLogger::default(),
749 );
750 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
751
752 let reset_state = executor
754 .run_singlethreaded(countdown_proxy.watch())
755 .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 handle_allowed_event_fut = reset_handler.handle_allowed_event(&reset_event);
760 futures::pin_mut!(handle_allowed_event_fut);
761 assert_eq!(executor.run_until_stalled(&mut handle_allowed_event_fut), Poll::Pending);
762
763 assert_matches!(
765 executor.run_singlethreaded(countdown_proxy.watch()),
766 Ok(FactoryResetCountdownState { scheduled_reset_time: None, .. })
767 );
768 assert_matches!(
769 reset_handler.factory_reset_state(),
770 FactoryResetState::ButtonCountdown { deadline: _ }
771 );
772 }
773
774 #[fuchsia::test]
775 async fn handle_allowed_event_wont_change_state_without_reset() {
776 let inspector = fuchsia_inspect::Inspector::default();
777 let test_node = inspector.root().create_child("test_node");
778 let reset_handler = FactoryResetHandler::new(
779 Incoming::new(),
780 &test_node,
781 metrics::MetricsLogger::default(),
782 );
783 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
784
785 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
787 assert_eq!(reset_state.scheduled_reset_time, None);
788 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
789
790 let non_reset_event = create_non_reset_consumer_controls_event();
791 reset_handler.clone().handle_allowed_event(&non_reset_event).await;
792
793 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
795 }
796
797 #[fuchsia::test]
798 async fn handle_disallowed_event_wont_change_state() {
799 let inspector = fuchsia_inspect::Inspector::default();
800 let test_node = inspector.root().create_child("test_node");
801 let reset_handler = FactoryResetHandler::new(
802 Incoming::new(),
803 &test_node,
804 metrics::MetricsLogger::default(),
805 );
806 *reset_handler.factory_reset_state.borrow_mut() = FactoryResetState::Disallowed;
807
808 let reset_event = create_reset_consumer_controls_event();
811 reset_handler.handle_disallowed_event(&reset_event);
812 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
813
814 let non_reset_event = create_non_reset_consumer_controls_event();
815 reset_handler.handle_disallowed_event(&non_reset_event);
816 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
817 }
818
819 #[fuchsia::test]
820 async fn handle_button_countdown_event_changes_state_when_reset_no_longer_requested() {
821 let inspector = fuchsia_inspect::Inspector::default();
822 let test_node = inspector.root().create_child("test_node");
823 let reset_handler = FactoryResetHandler::new(
824 Incoming::new(),
825 &test_node,
826 metrics::MetricsLogger::default(),
827 );
828
829 let deadline = MonotonicInstant::after(BUTTON_TIMEOUT);
830 *reset_handler.factory_reset_state.borrow_mut() =
831 FactoryResetState::ButtonCountdown { deadline };
832
833 let non_reset_event = create_non_reset_consumer_controls_event();
836 reset_handler.handle_button_countdown_event(&non_reset_event);
837 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
838 }
839
840 #[fuchsia::test]
841 async fn handle_reset_countdown_event_changes_state_when_reset_no_longer_requested() {
842 let inspector = fuchsia_inspect::Inspector::default();
843 let test_node = inspector.root().create_child("test_node");
844 let reset_handler = FactoryResetHandler::new(
845 Incoming::new(),
846 &test_node,
847 metrics::MetricsLogger::default(),
848 );
849
850 *reset_handler.factory_reset_state.borrow_mut() =
851 FactoryResetState::ResetCountdown { deadline: MonotonicInstant::now() };
852
853 let non_reset_event = create_non_reset_consumer_controls_event();
856 reset_handler.handle_reset_countdown_event(&non_reset_event);
857 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
858 }
859
860 #[fuchsia::test]
861 async fn factory_reset_disallowed_during_button_countdown() {
862 let inspector = fuchsia_inspect::Inspector::default();
863 let test_node = inspector.root().create_child("test_node");
864 let reset_handler = FactoryResetHandler::new(
865 Incoming::new(),
866 &test_node,
867 metrics::MetricsLogger::default(),
868 );
869 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
870
871 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
873 assert_eq!(reset_state.scheduled_reset_time, None);
874 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
875
876 let reset_event = create_reset_input_event();
878 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
879
880 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
882 assert_eq!(reset_state.scheduled_reset_time, None);
883 assert_matches!(
884 reset_handler.factory_reset_state(),
885 FactoryResetState::ButtonCountdown { deadline: _ }
886 );
887
888 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
890 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
891
892 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
894 assert_eq!(reset_state.scheduled_reset_time, None);
895 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
896 }
897
898 #[fuchsia::test]
899 async fn factory_reset_disallowed_during_reset_countdown() {
900 let inspector = fuchsia_inspect::Inspector::default();
901 let test_node = inspector.root().create_child("test_node");
902 let reset_handler = FactoryResetHandler::new(
903 Incoming::new(),
904 &test_node,
905 metrics::MetricsLogger::default(),
906 );
907 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
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_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
913
914 let reset_event = create_reset_input_event();
916 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
917
918 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
920 assert_eq!(reset_state.scheduled_reset_time, None);
921 assert_matches!(
922 reset_handler.factory_reset_state(),
923 FactoryResetState::ButtonCountdown { deadline: _ }
924 );
925
926 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
928 assert_matches!(reset_state.scheduled_reset_time, Some(_));
929 assert_matches!(
930 reset_handler.factory_reset_state(),
931 FactoryResetState::ResetCountdown { deadline: _ }
932 );
933
934 let device_proxy = create_recovery_policy_proxy(reset_handler.clone());
936 device_proxy.set_is_local_reset_allowed(false).expect("Failed to set recovery policy");
937
938 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
940 assert_eq!(reset_state.scheduled_reset_time, None);
941 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Disallowed);
942 }
943
944 #[fuchsia::test]
945 async fn factory_reset_cancelled_during_button_countdown() {
946 let inspector = fuchsia_inspect::Inspector::default();
947 let test_node = inspector.root().create_child("test_node");
948 let reset_handler = FactoryResetHandler::new(
949 Incoming::new(),
950 &test_node,
951 metrics::MetricsLogger::default(),
952 );
953 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
954
955 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
957 assert_eq!(reset_state.scheduled_reset_time, None);
958 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
959
960 let reset_event = create_reset_input_event();
962 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
963
964 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
966 assert_eq!(reset_state.scheduled_reset_time, None);
967 assert_matches!(
968 reset_handler.factory_reset_state(),
969 FactoryResetState::ButtonCountdown { deadline: _ }
970 );
971
972 let non_reset_event = create_non_reset_input_event();
974 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
975
976 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
978 assert_eq!(reset_state.scheduled_reset_time, None);
979 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
980 }
981
982 #[fuchsia::test]
983 async fn factory_reset_cancelled_during_reset_countdown() {
984 let inspector = fuchsia_inspect::Inspector::default();
985 let test_node = inspector.root().create_child("test_node");
986 let reset_handler = FactoryResetHandler::new(
987 Incoming::new(),
988 &test_node,
989 metrics::MetricsLogger::default(),
990 );
991 let countdown_proxy = create_factory_reset_countdown_proxy(reset_handler.clone());
992
993 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
995 assert_eq!(reset_state.scheduled_reset_time, None);
996 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
997
998 let reset_event = create_reset_input_event();
1000 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
1001
1002 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
1004 assert_eq!(reset_state.scheduled_reset_time, None);
1005 assert_matches!(
1006 reset_handler.factory_reset_state(),
1007 FactoryResetState::ButtonCountdown { deadline: _ }
1008 );
1009
1010 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
1012 assert_matches!(reset_state.scheduled_reset_time, Some(_));
1013 assert_matches!(
1014 reset_handler.factory_reset_state(),
1015 FactoryResetState::ResetCountdown { deadline: _ }
1016 );
1017
1018 let non_reset_event = create_non_reset_input_event();
1020 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
1021
1022 let reset_state = countdown_proxy.watch().await.expect("Failed to get countdown state");
1024 assert_eq!(reset_state.scheduled_reset_time, None);
1025 assert_eq!(reset_handler.factory_reset_state(), FactoryResetState::Idle);
1026 }
1027
1028 #[fuchsia::test]
1029 async fn factory_reset_handler_initialized_with_inspect_node() {
1030 let inspector = fuchsia_inspect::Inspector::default();
1031 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1032 let _handler = FactoryResetHandler::new(
1033 Incoming::new(),
1034 &fake_handlers_node,
1035 metrics::MetricsLogger::default(),
1036 );
1037 diagnostics_assertions::assert_data_tree!(inspector, root: {
1038 input_handlers_node: {
1039 factory_reset_handler: {
1040 events_received_count: 0u64,
1041 events_handled_count: 0u64,
1042 last_received_timestamp_ns: 0u64,
1043 "fuchsia.inspect.Health": {
1044 status: "STARTING_UP",
1045 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1048 },
1049 }
1050 }
1051 });
1052 }
1053
1054 #[fuchsia::test]
1055 async fn factory_reset_handler_inspect_counts_events() {
1056 let inspector = fuchsia_inspect::Inspector::default();
1057 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1058 let reset_handler = FactoryResetHandler::new(
1059 Incoming::new(),
1060 &fake_handlers_node,
1061 metrics::MetricsLogger::default(),
1062 );
1063
1064 let reset_event = create_reset_input_event();
1066 reset_handler.clone().handle_unhandled_input_event(reset_event).await;
1067
1068 let mut handled_event = input_device::InputEvent::from(create_reset_input_event());
1070 handled_event.handled = input_device::Handled::Yes;
1071 reset_handler.clone().handle_input_event(handled_event).await;
1072
1073 let non_reset_event = create_non_reset_input_event();
1075 let last_event_timestamp: u64 =
1076 non_reset_event.clone().event_time.into_nanos().try_into().unwrap();
1077 reset_handler.clone().handle_unhandled_input_event(non_reset_event).await;
1078
1079 diagnostics_assertions::assert_data_tree!(inspector, root: {
1080 input_handlers_node: {
1081 factory_reset_handler: {
1082 events_received_count: 2u64,
1083 events_handled_count: 0u64,
1084 last_received_timestamp_ns: last_event_timestamp,
1085 "fuchsia.inspect.Health": {
1086 status: "STARTING_UP",
1087 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1090 },
1091 }
1092 }
1093 });
1094 }
1095}