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