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