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