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