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