Skip to main content

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