Skip to main content

input_pipeline/
media_buttons_handler.rs

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