input_pipeline/
media_buttons_handler.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
6use crate::{consumer_controls_binding, input_device, metrics};
7use async_trait::async_trait;
8use fidl::endpoints::Proxy;
9use fuchsia_inspect::health::Reporter;
10use futures::channel::mpsc;
11use futures::StreamExt;
12use metrics_registry::*;
13use std::cell::RefCell;
14use std::collections::HashMap;
15use std::rc::Rc;
16use zx::AsHandleRef;
17use {
18    fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
19    fidl_fuchsia_ui_policy as fidl_ui_policy, fuchsia_async as fasync,
20};
21
22/// A [`MediaButtonsHandler`] tracks MediaButtonListeners and sends media button events to them.
23pub struct MediaButtonsHandler {
24    /// The mutable fields of this handler.
25    inner: RefCell<MediaButtonsHandlerInner>,
26
27    /// The inventory of this handler's Inspect status.
28    pub inspect_status: InputHandlerStatus,
29
30    metrics_logger: metrics::MetricsLogger,
31}
32
33#[derive(Debug)]
34struct MediaButtonsHandlerInner {
35    /// The media button listeners, key referenced by proxy channel's raw handle.
36    pub listeners: HashMap<u32, fidl_ui_policy::MediaButtonsListenerProxy>,
37
38    /// The last MediaButtonsEvent sent to all listeners.
39    /// This is used to send new listeners the state of the media buttons.
40    pub last_event: Option<fidl_ui_input::MediaButtonsEvent>,
41
42    pub send_event_task_tracker: LocalTaskTracker,
43}
44
45#[async_trait(?Send)]
46impl UnhandledInputHandler for MediaButtonsHandler {
47    async fn handle_unhandled_input_event(
48        self: Rc<Self>,
49        unhandled_input_event: input_device::UnhandledInputEvent,
50    ) -> Vec<input_device::InputEvent> {
51        match unhandled_input_event {
52            input_device::UnhandledInputEvent {
53                device_event:
54                    input_device::InputDeviceEvent::ConsumerControls(ref media_buttons_event),
55                device_descriptor:
56                    input_device::InputDeviceDescriptor::ConsumerControls(ref device_descriptor),
57                event_time: _,
58                trace_id,
59            } => {
60                fuchsia_trace::duration!(c"input", c"media_buttons_handler");
61                if let Some(trace_id) = trace_id {
62                    fuchsia_trace::flow_end!(c"input", c"event_in_input_pipeline", trace_id.into());
63                }
64
65                self.inspect_status.count_received_event(input_device::InputEvent::from(
66                    unhandled_input_event.clone(),
67                ));
68                let media_buttons_event = Self::create_media_buttons_event(
69                    media_buttons_event,
70                    device_descriptor.device_id,
71                );
72
73                // Send the event if the media buttons are supported.
74                self.send_event_to_listeners(&media_buttons_event).await;
75
76                // Store the sent event.
77                self.inner.borrow_mut().last_event = Some(media_buttons_event);
78
79                // Consume the input event.
80                self.inspect_status.count_handled_event();
81                vec![input_device::InputEvent::from(unhandled_input_event).into_handled()]
82            }
83            _ => vec![input_device::InputEvent::from(unhandled_input_event)],
84        }
85    }
86
87    fn set_handler_healthy(self: std::rc::Rc<Self>) {
88        self.inspect_status.health_node.borrow_mut().set_ok();
89    }
90
91    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
92        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
93    }
94}
95
96impl MediaButtonsHandler {
97    /// Creates a new [`MediaButtonsHandler`] that sends media button events to listeners.
98    pub fn new(
99        input_handlers_node: &fuchsia_inspect::Node,
100        metrics_logger: metrics::MetricsLogger,
101    ) -> Rc<Self> {
102        let inspect_status =
103            InputHandlerStatus::new(input_handlers_node, "media_buttons_handler", false);
104        Self::new_internal(inspect_status, metrics_logger)
105    }
106
107    fn new_internal(
108        inspect_status: InputHandlerStatus,
109        metrics_logger: metrics::MetricsLogger,
110    ) -> Rc<Self> {
111        let media_buttons_handler = Self {
112            inner: RefCell::new(MediaButtonsHandlerInner {
113                listeners: HashMap::new(),
114                last_event: None,
115                send_event_task_tracker: LocalTaskTracker::new(),
116            }),
117            inspect_status,
118            metrics_logger,
119        };
120        Rc::new(media_buttons_handler)
121    }
122
123    /// Creates a fidl_ui_input::MediaButtonsEvent from a media_buttons::MediaButtonEvent.
124    ///
125    /// # Parameters
126    /// -  `event`: The MediaButtonEvent to create a MediaButtonsEvent from.
127    fn create_media_buttons_event(
128        event: &consumer_controls_binding::ConsumerControlsEvent,
129        device_id: u32,
130    ) -> fidl_ui_input::MediaButtonsEvent {
131        let mut new_event = fidl_ui_input::MediaButtonsEvent {
132            volume: Some(0),
133            mic_mute: Some(false),
134            pause: Some(false),
135            camera_disable: Some(false),
136            power: Some(false),
137            function: Some(false),
138            device_id: Some(device_id),
139            ..Default::default()
140        };
141        for button in &event.pressed_buttons {
142            match button {
143                fidl_input_report::ConsumerControlButton::VolumeUp => {
144                    new_event.volume = Some(new_event.volume.unwrap().saturating_add(1));
145                }
146                fidl_input_report::ConsumerControlButton::VolumeDown => {
147                    new_event.volume = Some(new_event.volume.unwrap().saturating_sub(1));
148                }
149                fidl_input_report::ConsumerControlButton::MicMute => {
150                    new_event.mic_mute = Some(true);
151                }
152                fidl_input_report::ConsumerControlButton::Pause => {
153                    new_event.pause = Some(true);
154                }
155                fidl_input_report::ConsumerControlButton::CameraDisable => {
156                    new_event.camera_disable = Some(true);
157                }
158                fidl_input_report::ConsumerControlButton::Function => {
159                    new_event.function = Some(true);
160                }
161                fidl_input_report::ConsumerControlButton::Power => {
162                    new_event.power = Some(true);
163                }
164                _ => {}
165            }
166        }
167
168        new_event
169    }
170
171    /// Sends media button events to media button listeners.
172    ///
173    /// # Parameters
174    /// - `event`: The event to send to the listeners.
175    async fn send_event_to_listeners(self: &Rc<Self>, event: &fidl_ui_input::MediaButtonsEvent) {
176        let tracker = &self.inner.borrow().send_event_task_tracker;
177
178        for (handle, listener) in &self.inner.borrow().listeners {
179            let weak_handler = Rc::downgrade(&self);
180            let listener_clone = listener.clone();
181            let handle_clone = handle.clone();
182            let event_to_send = event.clone();
183            let fut = async move {
184                match listener_clone.on_event(&event_to_send).await {
185                    Ok(_) => {}
186                    Err(e) => {
187                        if let Some(handler) = weak_handler.upgrade() {
188                            handler.inner.borrow_mut().listeners.remove(&handle_clone);
189                            log::info!(
190                                "Unregistering listener; unable to send MediaButtonsEvent: {:?}",
191                                e
192                            )
193                        }
194                    }
195                }
196            };
197
198            let metrics_logger_clone = self.metrics_logger.clone();
199            tracker.track(metrics_logger_clone, fasync::Task::local(fut));
200        }
201    }
202
203    // Add the listener to the registry.
204    ///
205    /// # Parameters
206    /// - `proxy`: A new listener proxy to send events to.
207    pub async fn register_listener_proxy(
208        self: &Rc<Self>,
209        proxy: fidl_ui_policy::MediaButtonsListenerProxy,
210    ) {
211        self.inner.borrow_mut().listeners.insert(proxy.as_channel().raw_handle(), proxy.clone());
212
213        // Send the listener the last media button event.
214        if let Some(event) = &self.inner.borrow().last_event {
215            let event_to_send = event.clone();
216            let fut = async move {
217                match proxy.on_event(&event_to_send).await {
218                    Ok(_) => {}
219                    Err(e) => {
220                        log::info!("Failed to send media buttons event to listener {:?}", e)
221                    }
222                }
223            };
224            let metrics_logger_clone = self.metrics_logger.clone();
225            self.inner
226                .borrow()
227                .send_event_task_tracker
228                .track(metrics_logger_clone, fasync::Task::local(fut));
229        }
230    }
231}
232
233/// Maintains a collection of pending local [`Task`]s, allowing them to be dropped (and cancelled)
234/// en masse.
235#[derive(Debug)]
236pub struct LocalTaskTracker {
237    sender: mpsc::UnboundedSender<fasync::Task<()>>,
238    _receiver_task: fasync::Task<()>,
239}
240
241impl LocalTaskTracker {
242    pub fn new() -> Self {
243        let (sender, receiver) = mpsc::unbounded();
244        let receiver_task = fasync::Task::local(async move {
245            // Drop the tasks as they are completed.
246            receiver.for_each_concurrent(None, |task: fasync::Task<()>| task).await
247        });
248
249        Self { sender, _receiver_task: receiver_task }
250    }
251
252    /// Submits a new task to track.
253    pub fn track(&self, metrics_logger: metrics::MetricsLogger, task: fasync::Task<()>) {
254        match self.sender.unbounded_send(task) {
255            Ok(_) => {}
256            // `Full` should never happen because this is unbounded.
257            // `Disconnected` might happen if the `Service` was dropped. However, it's not clear how
258            // to create such a race condition.
259            Err(e) => {
260                metrics_logger.log_error(
261                    InputPipelineErrorMetricDimensionEvent::MediaButtonErrorWhilePushingTask,
262                    std::format!("Unexpected {e:?} while pushing task"),
263                );
264            }
265        };
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272    use crate::input_handler::InputHandler;
273    use crate::testing_utilities;
274    use anyhow::Error;
275    use assert_matches::assert_matches;
276    use fidl::endpoints::create_proxy_and_stream;
277    use futures::channel::oneshot;
278    use futures::TryStreamExt;
279    use pretty_assertions::assert_eq;
280    use std::task::Poll;
281    use {fidl_fuchsia_input_report as fidl_input_report, fuchsia_async as fasync};
282
283    fn spawn_device_listener_registry_server(
284        handler: Rc<MediaButtonsHandler>,
285    ) -> fidl_ui_policy::DeviceListenerRegistryProxy {
286        let (device_listener_proxy, mut device_listener_stream) =
287            create_proxy_and_stream::<fidl_ui_policy::DeviceListenerRegistryMarker>();
288
289        fasync::Task::local(async move {
290            loop {
291                match device_listener_stream.try_next().await {
292                    Ok(Some(fidl_ui_policy::DeviceListenerRegistryRequest::RegisterListener {
293                        listener,
294                        responder,
295                    })) => {
296                        handler.register_listener_proxy(listener.into_proxy()).await;
297                        let _ = responder.send();
298                    }
299                    Ok(Some(_)) => {
300                        panic!("Unexpected registration");
301                    }
302                    Ok(None) => {
303                        break;
304                    }
305                    Err(e) => {
306                        panic!("Error handling device listener registry request stream: {}", e);
307                    }
308                }
309            }
310        })
311        .detach();
312
313        device_listener_proxy
314    }
315
316    fn create_ui_input_media_buttons_event(
317        volume: Option<i8>,
318        mic_mute: Option<bool>,
319        pause: Option<bool>,
320        camera_disable: Option<bool>,
321        power: Option<bool>,
322        function: Option<bool>,
323    ) -> fidl_ui_input::MediaButtonsEvent {
324        fidl_ui_input::MediaButtonsEvent {
325            volume,
326            mic_mute,
327            pause,
328            camera_disable,
329            power,
330            function,
331            device_id: Some(0),
332            ..Default::default()
333        }
334    }
335
336    /// Makes a `Task` that waits for a `oneshot`'s value to be set, and then forwards that value to
337    /// a reference-counted container that can be observed outside the task.
338    fn make_signalable_task<T: Default + 'static>(
339    ) -> (oneshot::Sender<T>, fasync::Task<()>, Rc<RefCell<T>>) {
340        let (sender, receiver) = oneshot::channel();
341        let task_completed = Rc::new(RefCell::new(<T as Default>::default()));
342        let task_completed_ = task_completed.clone();
343        let task = fasync::Task::local(async move {
344            if let Ok(value) = receiver.await {
345                *task_completed_.borrow_mut() = value;
346            }
347        });
348        (sender, task, task_completed)
349    }
350
351    /// Tests that a media button listener can be registered and is sent the latest event upon
352    /// registration.
353    #[fasync::run_singlethreaded(test)]
354    async fn register_media_buttons_listener() {
355        let inspector = fuchsia_inspect::Inspector::default();
356        let test_node = inspector.root().create_child("test_node");
357        let inspect_status = InputHandlerStatus::new(
358            &test_node,
359            "media_buttons_handler",
360            /* generates_events */ false,
361        );
362
363        let media_buttons_handler = Rc::new(MediaButtonsHandler {
364            inner: RefCell::new(MediaButtonsHandlerInner {
365                listeners: HashMap::new(),
366                last_event: Some(create_ui_input_media_buttons_event(
367                    Some(1),
368                    None,
369                    None,
370                    None,
371                    None,
372                    None,
373                )),
374                send_event_task_tracker: LocalTaskTracker::new(),
375            }),
376            inspect_status,
377            metrics_logger: metrics::MetricsLogger::default(),
378        });
379        let device_listener_proxy =
380            spawn_device_listener_registry_server(media_buttons_handler.clone());
381
382        // Register a listener.
383        let (listener, mut listener_stream) =
384            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
385        let register_listener_fut = async {
386            let res = device_listener_proxy.register_listener(listener).await;
387            assert!(res.is_ok());
388        };
389
390        // Assert listener was registered and received last event.
391        let expected_event =
392            create_ui_input_media_buttons_event(Some(1), None, None, None, None, None);
393        let assert_fut = async {
394            match listener_stream.next().await {
395                Some(Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
396                    event,
397                    responder,
398                })) => {
399                    assert_eq!(event, expected_event);
400                    responder.send().expect("responder failed.");
401                }
402                _ => assert!(false),
403            }
404        };
405        futures::join!(register_listener_fut, assert_fut);
406        assert_eq!(media_buttons_handler.inner.borrow().listeners.len(), 1);
407    }
408
409    /// Tests that all supported buttons are sent.
410    #[fasync::run_singlethreaded(test)]
411    async fn listener_receives_all_buttons() {
412        let event_time = zx::MonotonicInstant::get();
413        let inspector = fuchsia_inspect::Inspector::default();
414        let test_node = inspector.root().create_child("test_node");
415        let inspect_status = InputHandlerStatus::new(
416            &test_node,
417            "media_buttons_handler",
418            /* generates_events */ false,
419        );
420        let media_buttons_handler =
421            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
422        let device_listener_proxy =
423            spawn_device_listener_registry_server(media_buttons_handler.clone());
424
425        // Register a listener.
426        let (listener, listener_stream) =
427            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
428        let _ = device_listener_proxy.register_listener(listener).await;
429
430        // Setup events and expectations.
431        let descriptor = testing_utilities::consumer_controls_device_descriptor();
432        let input_events = vec![testing_utilities::create_consumer_controls_event(
433            vec![
434                fidl_input_report::ConsumerControlButton::VolumeUp,
435                fidl_input_report::ConsumerControlButton::VolumeDown,
436                fidl_input_report::ConsumerControlButton::Pause,
437                fidl_input_report::ConsumerControlButton::MicMute,
438                fidl_input_report::ConsumerControlButton::CameraDisable,
439                fidl_input_report::ConsumerControlButton::Function,
440                fidl_input_report::ConsumerControlButton::Power,
441            ],
442            event_time,
443            &descriptor,
444        )];
445        let expected_events = vec![create_ui_input_media_buttons_event(
446            Some(0),
447            Some(true),
448            Some(true),
449            Some(true),
450            Some(true),
451            Some(true),
452        )];
453
454        // Assert registered listener receives event.
455        use crate::input_handler::InputHandler as _; // Adapt UnhandledInputHandler to InputHandler
456        assert_input_event_sequence_generates_media_buttons_events!(
457            input_handler: media_buttons_handler,
458            input_events: input_events,
459            expected_events: expected_events,
460            media_buttons_listener_request_stream: vec![listener_stream],
461        );
462    }
463
464    /// Tests that multiple listeners are supported.
465    #[fasync::run_singlethreaded(test)]
466    async fn multiple_listeners_receive_event() {
467        let event_time = zx::MonotonicInstant::get();
468        let inspector = fuchsia_inspect::Inspector::default();
469        let test_node = inspector.root().create_child("test_node");
470        let inspect_status = InputHandlerStatus::new(
471            &test_node,
472            "media_buttons_handler",
473            /* generates_events */ false,
474        );
475        let media_buttons_handler =
476            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
477        let device_listener_proxy =
478            spawn_device_listener_registry_server(media_buttons_handler.clone());
479
480        // Register two listeners.
481        let (first_listener, first_listener_stream) =
482            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
483        let (second_listener, second_listener_stream) =
484            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
485        let _ = device_listener_proxy.register_listener(first_listener).await;
486        let _ = device_listener_proxy.register_listener(second_listener).await;
487
488        // Setup events and expectations.
489        let descriptor = testing_utilities::consumer_controls_device_descriptor();
490        let input_events = vec![testing_utilities::create_consumer_controls_event(
491            vec![fidl_input_report::ConsumerControlButton::VolumeUp],
492            event_time,
493            &descriptor,
494        )];
495        let expected_events = vec![create_ui_input_media_buttons_event(
496            Some(1),
497            Some(false),
498            Some(false),
499            Some(false),
500            Some(false),
501            Some(false),
502        )];
503
504        // Assert registered listeners receives event.
505        use crate::input_handler::InputHandler as _; // Adapt UnhandledInputHandler to InputHandler
506        assert_input_event_sequence_generates_media_buttons_events!(
507            input_handler: media_buttons_handler,
508            input_events: input_events,
509            expected_events: expected_events,
510            media_buttons_listener_request_stream:
511                vec![first_listener_stream, second_listener_stream],
512        );
513    }
514
515    /// Tests that listener is unregistered if channel is closed and we try to send input event to listener
516    #[fuchsia::test]
517    fn unregister_listener_if_channel_closed() {
518        let mut exec = fasync::TestExecutor::new();
519
520        let event_time = zx::MonotonicInstant::get();
521        let inspector = fuchsia_inspect::Inspector::default();
522        let test_node = inspector.root().create_child("test_node");
523        let inspect_status = InputHandlerStatus::new(
524            &test_node,
525            "media_buttons_handler",
526            /* generates_events */ false,
527        );
528        let media_buttons_handler =
529            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
530        let media_buttons_handler_clone = media_buttons_handler.clone();
531
532        let mut task = fasync::Task::local(async move {
533            let device_listener_proxy =
534                spawn_device_listener_registry_server(media_buttons_handler.clone());
535
536            // Register three listeners.
537            let (first_listener, mut first_listener_stream) =
538                fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>(
539                );
540            let (second_listener, mut second_listener_stream) =
541                fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>(
542                );
543            let (third_listener, third_listener_stream) = fidl::endpoints::create_request_stream::<
544                fidl_ui_policy::MediaButtonsListenerMarker,
545            >();
546            let _ = device_listener_proxy.register_listener(first_listener).await;
547            let _ = device_listener_proxy.register_listener(second_listener).await;
548            let _ = device_listener_proxy.register_listener(third_listener).await;
549            assert_eq!(media_buttons_handler.inner.borrow().listeners.len(), 3);
550
551            // Generate input event to be handled by MediaButtonsHandler.
552            let descriptor = testing_utilities::consumer_controls_device_descriptor();
553            let input_event = testing_utilities::create_consumer_controls_event(
554                vec![fidl_input_report::ConsumerControlButton::VolumeUp],
555                event_time,
556                &descriptor,
557            );
558
559            let expected_media_buttons_event = create_ui_input_media_buttons_event(
560                Some(1),
561                Some(false),
562                Some(false),
563                Some(false),
564                Some(false),
565                Some(false),
566            );
567
568            // Drop third registered listener.
569            std::mem::drop(third_listener_stream);
570
571            let _ = media_buttons_handler.clone().handle_input_event(input_event).await;
572            // First listener stalls, responder doesn't send response - subsequent listeners should still be able receive event.
573            if let Some(request) = first_listener_stream.next().await {
574                match request {
575                    Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
576                        event,
577                        responder: _,
578                    }) => {
579                        pretty_assertions::assert_eq!(event, expected_media_buttons_event);
580
581                        // No need to send response because we want to simulate reader getting stuck.
582                    }
583                    _ => assert!(false),
584                }
585            } else {
586                assert!(false);
587            }
588
589            // Send response from responder on second listener stream
590            if let Some(request) = second_listener_stream.next().await {
591                match request {
592                    Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
593                        event,
594                        responder,
595                    }) => {
596                        pretty_assertions::assert_eq!(event, expected_media_buttons_event);
597                        let _ = responder.send();
598                    }
599                    _ => assert!(false),
600                }
601            } else {
602                assert!(false);
603            }
604        });
605
606        // Must manually run tasks with executor to ensure all tasks in LocalTaskTracker complete/stall before we call final assertion.
607        let _ = exec.run_until_stalled(&mut task);
608
609        // Should only be two listeners still registered in 'inner' after we unregister the listener with closed channel.
610        let _ = exec.run_singlethreaded(async {
611            assert_eq!(media_buttons_handler_clone.inner.borrow().listeners.len(), 2);
612        });
613    }
614
615    /// Tests that handle_input_event returns even if reader gets stuck while sending event to listener
616    #[fasync::run_singlethreaded(test)]
617    async fn stuck_reader_wont_block_input_pipeline() {
618        let event_time = zx::MonotonicInstant::get();
619        let inspector = fuchsia_inspect::Inspector::default();
620        let test_node = inspector.root().create_child("test_node");
621        let inspect_status = InputHandlerStatus::new(
622            &test_node,
623            "media_buttons_handler",
624            /* generates_events */ false,
625        );
626        let media_buttons_handler =
627            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
628        let device_listener_proxy =
629            spawn_device_listener_registry_server(media_buttons_handler.clone());
630
631        let (first_listener, mut first_listener_stream) =
632            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
633        let (second_listener, mut second_listener_stream) =
634            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
635        let _ = device_listener_proxy.register_listener(first_listener).await;
636        let _ = device_listener_proxy.register_listener(second_listener).await;
637
638        // Setup events and expectations.
639        let descriptor = testing_utilities::consumer_controls_device_descriptor();
640        let first_unhandled_input_event = input_device::UnhandledInputEvent {
641            device_event: input_device::InputDeviceEvent::ConsumerControls(
642                consumer_controls_binding::ConsumerControlsEvent::new(vec![
643                    fidl_input_report::ConsumerControlButton::VolumeUp,
644                ]),
645            ),
646            device_descriptor: descriptor.clone(),
647            event_time,
648            trace_id: None,
649        };
650        let first_expected_media_buttons_event = create_ui_input_media_buttons_event(
651            Some(1),
652            Some(false),
653            Some(false),
654            Some(false),
655            Some(false),
656            Some(false),
657        );
658
659        assert_matches!(
660            media_buttons_handler
661                .clone()
662                .handle_unhandled_input_event(first_unhandled_input_event)
663                .await
664                .as_slice(),
665            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
666        );
667
668        let mut save_responder = None;
669
670        // Ensure handle_input_event attempts to send event to first listener.
671        if let Some(request) = first_listener_stream.next().await {
672            match request {
673                Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent { event, responder }) => {
674                    pretty_assertions::assert_eq!(event, first_expected_media_buttons_event);
675
676                    // No need to send response because we want to simulate reader getting stuck.
677
678                    // Save responder to send response later
679                    save_responder = Some(responder);
680                }
681                _ => assert!(false),
682            }
683        } else {
684            assert!(false)
685        }
686
687        // Ensure handle_input_event still sends event to second listener when reader for first listener is stuck.
688        if let Some(request) = second_listener_stream.next().await {
689            match request {
690                Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent { event, responder }) => {
691                    pretty_assertions::assert_eq!(event, first_expected_media_buttons_event);
692                    let _ = responder.send();
693                }
694                _ => assert!(false),
695            }
696        } else {
697            assert!(false)
698        }
699
700        // Setup second event to handle
701        let second_unhandled_input_event = input_device::UnhandledInputEvent {
702            device_event: input_device::InputDeviceEvent::ConsumerControls(
703                consumer_controls_binding::ConsumerControlsEvent::new(vec![
704                    fidl_input_report::ConsumerControlButton::MicMute,
705                ]),
706            ),
707            device_descriptor: descriptor.clone(),
708            event_time,
709            trace_id: None,
710        };
711        let second_expected_media_buttons_event = create_ui_input_media_buttons_event(
712            Some(0),
713            Some(true),
714            Some(false),
715            Some(false),
716            Some(false),
717            Some(false),
718        );
719
720        // Ensure we can handle a subsequent event if listener stalls on first event.
721        assert_matches!(
722            media_buttons_handler
723                .clone()
724                .handle_unhandled_input_event(second_unhandled_input_event)
725                .await
726                .as_slice(),
727            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
728        );
729
730        // Ensure events are still sent to listeners if a listener stalls on a previous event.
731        if let Some(request) = second_listener_stream.next().await {
732            match request {
733                Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent { event, responder }) => {
734                    pretty_assertions::assert_eq!(event, second_expected_media_buttons_event);
735                    let _ = responder.send();
736                }
737                _ => assert!(false),
738            }
739        } else {
740            assert!(false)
741        }
742
743        match save_responder {
744            Some(save_responder) => {
745                // Simulate delayed response to first listener for first event
746                let _ = save_responder.send();
747                // First listener should now receive second event after delayed response for first event
748                if let Some(request) = first_listener_stream.next().await {
749                    match request {
750                        Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
751                            event,
752                            responder: _,
753                        }) => {
754                            pretty_assertions::assert_eq!(
755                                event,
756                                second_expected_media_buttons_event
757                            );
758
759                            // No need to send response
760                        }
761                        _ => assert!(false),
762                    }
763                } else {
764                    assert!(false)
765                }
766            }
767            None => {
768                assert!(false)
769            }
770        }
771    }
772
773    // Test for LocalTaskTracker
774    #[fuchsia::test]
775    fn local_task_tracker_test() -> Result<(), Error> {
776        let mut exec = fasync::TestExecutor::new();
777
778        let (mut sender_1, task_1, completed_1) = make_signalable_task::<bool>();
779        let (sender_2, task_2, completed_2) = make_signalable_task::<bool>();
780
781        let mut tracker = LocalTaskTracker::new();
782
783        tracker.track(metrics::MetricsLogger::default(), task_1);
784        tracker.track(metrics::MetricsLogger::default(), task_2);
785
786        assert_matches!(exec.run_until_stalled(&mut tracker._receiver_task), Poll::Pending);
787        assert_eq!(Rc::strong_count(&completed_1), 2);
788        assert_eq!(Rc::strong_count(&completed_2), 2);
789        assert!(!sender_1.is_canceled());
790        assert!(!sender_2.is_canceled());
791
792        assert!(sender_2.send(true).is_ok());
793        assert_matches!(exec.run_until_stalled(&mut tracker._receiver_task), Poll::Pending);
794
795        assert_eq!(Rc::strong_count(&completed_1), 2);
796        assert_eq!(Rc::strong_count(&completed_2), 1);
797        assert_eq!(*completed_1.borrow(), false);
798        assert_eq!(*completed_2.borrow(), true);
799        assert!(!sender_1.is_canceled());
800
801        drop(tracker);
802        let mut sender_1_cancellation = sender_1.cancellation();
803        assert_matches!(exec.run_until_stalled(&mut sender_1_cancellation), Poll::Ready(()));
804        assert_eq!(Rc::strong_count(&completed_1), 1);
805        assert!(sender_1.is_canceled());
806
807        Ok(())
808    }
809
810    #[fasync::run_singlethreaded(test)]
811    async fn media_buttons_handler_initialized_with_inspect_node() {
812        let inspector = fuchsia_inspect::Inspector::default();
813        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
814        let _handler =
815            MediaButtonsHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
816        diagnostics_assertions::assert_data_tree!(inspector, root: {
817            input_handlers_node: {
818                media_buttons_handler: {
819                    events_received_count: 0u64,
820                    events_handled_count: 0u64,
821                    last_received_timestamp_ns: 0u64,
822                    "fuchsia.inspect.Health": {
823                        status: "STARTING_UP",
824                        // Timestamp value is unpredictable and not relevant in this context,
825                        // so we only assert that the property is present.
826                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
827                    },
828                }
829            }
830        });
831    }
832
833    #[fasync::run_singlethreaded(test)]
834    async fn media_buttons_handler_inspect_counts_events() {
835        let inspector = fuchsia_inspect::Inspector::default();
836        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
837        let media_buttons_handler =
838            MediaButtonsHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
839
840        // Unhandled input event should be counted by inspect.
841        let descriptor = testing_utilities::consumer_controls_device_descriptor();
842        let events = vec![
843            input_device::InputEvent {
844                device_event: input_device::InputDeviceEvent::ConsumerControls(
845                    consumer_controls_binding::ConsumerControlsEvent::new(vec![
846                        fidl_input_report::ConsumerControlButton::VolumeUp,
847                    ]),
848                ),
849                device_descriptor: descriptor.clone(),
850                event_time: zx::MonotonicInstant::get(),
851                handled: input_device::Handled::No,
852                trace_id: None,
853            },
854            // Handled input event should be ignored.
855            input_device::InputEvent {
856                device_event: input_device::InputDeviceEvent::ConsumerControls(
857                    consumer_controls_binding::ConsumerControlsEvent::new(vec![
858                        fidl_input_report::ConsumerControlButton::VolumeUp,
859                    ]),
860                ),
861                device_descriptor: descriptor.clone(),
862                event_time: zx::MonotonicInstant::get(),
863                handled: input_device::Handled::Yes,
864                trace_id: None,
865            },
866            input_device::InputEvent {
867                device_event: input_device::InputDeviceEvent::ConsumerControls(
868                    consumer_controls_binding::ConsumerControlsEvent::new(vec![
869                        fidl_input_report::ConsumerControlButton::VolumeDown,
870                    ]),
871                ),
872                device_descriptor: descriptor.clone(),
873                event_time: zx::MonotonicInstant::get(),
874                handled: input_device::Handled::No,
875                trace_id: None,
876            },
877        ];
878
879        let last_event_timestamp: u64 =
880            events[2].clone().event_time.into_nanos().try_into().unwrap();
881
882        for event in events {
883            media_buttons_handler.clone().handle_input_event(event).await;
884        }
885
886        diagnostics_assertions::assert_data_tree!(inspector, root: {
887            input_handlers_node: {
888                media_buttons_handler: {
889                    events_received_count: 2u64,
890                    events_handled_count: 2u64,
891                    last_received_timestamp_ns: last_event_timestamp,
892                    "fuchsia.inspect.Health": {
893                        status: "STARTING_UP",
894                        // Timestamp value is unpredictable and not relevant in this context,
895                        // so we only assert that the property is present.
896                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
897                    },
898                }
899            }
900        });
901    }
902}