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