Skip to main content

input_pipeline_dso/
media_buttons_handler.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::dispatcher::TaskHandle;
6use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
7use crate::{Dispatcher, consumer_controls_binding, input_device, metrics};
8use async_trait::async_trait;
9use fidl::endpoints::Proxy;
10use fidl_fuchsia_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, fasync::Task<()>) {
333        let (device_listener_proxy, mut device_listener_stream) =
334            create_proxy_and_stream::<fidl_ui_policy::DeviceListenerRegistryMarker>();
335
336        let task = 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
359        (device_listener_proxy, task)
360    }
361
362    fn create_ui_input_media_buttons_event(
363        volume: Option<i8>,
364        mic_mute: Option<bool>,
365        pause: Option<bool>,
366        camera_disable: Option<bool>,
367        power: Option<bool>,
368        function: Option<bool>,
369    ) -> fidl_ui_input::MediaButtonsEvent {
370        fidl_ui_input::MediaButtonsEvent {
371            volume,
372            mic_mute,
373            pause,
374            camera_disable,
375            power,
376            function,
377            device_id: Some(0),
378            ..Default::default()
379        }
380    }
381
382    /// Makes a `Task` that waits for a `oneshot`'s value to be set, and then forwards that value to
383    /// a reference-counted container that can be observed outside the task.
384    fn make_signalable_task<T: Default + 'static>()
385    -> (oneshot::Sender<T>, TaskHandle<()>, Rc<RefCell<T>>) {
386        let (sender, receiver) = oneshot::channel();
387        let task_completed = Rc::new(RefCell::new(<T as Default>::default()));
388        let task_completed_ = task_completed.clone();
389        let task = fasync::Task::local(async move {
390            if let Ok(value) = receiver.await {
391                *task_completed_.borrow_mut() = value;
392            }
393        });
394        (sender, task.into(), task_completed)
395    }
396
397    /// Tests that a media button listener can be registered and is sent the latest event upon
398    /// registration.
399    #[fasync::run_singlethreaded(test)]
400    async fn register_media_buttons_listener() {
401        let inspector = fuchsia_inspect::Inspector::default();
402        let test_node = inspector.root().create_child("test_node");
403        let inspect_status = InputHandlerStatus::new(
404            &test_node,
405            "media_buttons_handler",
406            /* generates_events */ false,
407        );
408
409        let media_buttons_handler = Rc::new(MediaButtonsHandler {
410            inner: RefCell::new(MediaButtonsHandlerInner {
411                listeners: SortedVecMap::new(),
412                last_event: Some(create_ui_input_media_buttons_event(
413                    Some(1),
414                    None,
415                    None,
416                    None,
417                    None,
418                    None,
419                )),
420                send_event_task_tracker: LocalTaskTracker::new(),
421            }),
422            inspect_status,
423            metrics_logger: metrics::MetricsLogger::default(),
424        });
425        let (device_listener_proxy, _server_task) =
426            spawn_device_listener_registry_server(media_buttons_handler.clone());
427
428        // Register a listener.
429        let (listener, mut listener_stream) =
430            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
431        let register_listener_fut = async {
432            let res = device_listener_proxy.register_listener(listener).await;
433            assert!(res.is_ok());
434        };
435
436        // Assert listener was registered and received last event.
437        let expected_event =
438            create_ui_input_media_buttons_event(Some(1), None, None, None, None, None);
439        let assert_fut = async {
440            match listener_stream.next().await {
441                Some(Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
442                    mut event,
443                    responder,
444                })) => {
445                    event.trace_flow_id = None;
446                    assert_eq!(event, expected_event);
447                    responder.send().expect("responder failed.");
448                }
449                _ => assert!(false),
450            }
451        };
452        futures::join!(register_listener_fut, assert_fut);
453        assert_eq!(media_buttons_handler.inner.borrow().listeners.len(), 1);
454    }
455
456    /// Tests that all supported buttons are sent.
457    #[fasync::run_singlethreaded(test)]
458    async fn listener_receives_all_buttons() {
459        let event_time = zx::MonotonicInstant::get();
460        let inspector = fuchsia_inspect::Inspector::default();
461        let test_node = inspector.root().create_child("test_node");
462        let inspect_status = InputHandlerStatus::new(
463            &test_node,
464            "media_buttons_handler",
465            /* generates_events */ false,
466        );
467        let media_buttons_handler =
468            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
469        let (device_listener_proxy, _server_task) =
470            spawn_device_listener_registry_server(media_buttons_handler.clone());
471
472        // Register a listener.
473        let (listener, listener_stream) =
474            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
475        let _ = device_listener_proxy.register_listener(listener).await;
476
477        // Setup events and expectations.
478        let descriptor = testing_utilities::consumer_controls_device_descriptor();
479        let input_events = vec![testing_utilities::create_consumer_controls_event(
480            vec![
481                fidl_input_report::ConsumerControlButton::VolumeUp,
482                fidl_input_report::ConsumerControlButton::VolumeDown,
483                fidl_input_report::ConsumerControlButton::Pause,
484                fidl_input_report::ConsumerControlButton::MicMute,
485                fidl_input_report::ConsumerControlButton::CameraDisable,
486                fidl_input_report::ConsumerControlButton::Function,
487                fidl_input_report::ConsumerControlButton::Power,
488            ],
489            event_time,
490            &descriptor,
491        )];
492        let expected_events = vec![create_ui_input_media_buttons_event(
493            Some(0),
494            Some(true),
495            Some(true),
496            Some(true),
497            Some(true),
498            Some(true),
499        )];
500
501        // Assert registered listener receives event.
502        use crate::input_handler::InputHandler as _; // Adapt UnhandledInputHandler to InputHandler
503        assert_input_event_sequence_generates_media_buttons_events!(
504            input_handler: media_buttons_handler,
505            input_events: input_events,
506            expected_events: expected_events,
507            media_buttons_listener_request_stream: vec![listener_stream],
508        );
509    }
510
511    /// Tests that multiple listeners are supported.
512    #[fasync::run_singlethreaded(test)]
513    async fn multiple_listeners_receive_event() {
514        let event_time = zx::MonotonicInstant::get();
515        let inspector = fuchsia_inspect::Inspector::default();
516        let test_node = inspector.root().create_child("test_node");
517        let inspect_status = InputHandlerStatus::new(
518            &test_node,
519            "media_buttons_handler",
520            /* generates_events */ false,
521        );
522        let media_buttons_handler =
523            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
524        let (device_listener_proxy, _server_task) =
525            spawn_device_listener_registry_server(media_buttons_handler.clone());
526
527        // Register two listeners.
528        let (first_listener, first_listener_stream) =
529            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
530        let (second_listener, second_listener_stream) =
531            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
532        let _ = device_listener_proxy.register_listener(first_listener).await;
533        let _ = device_listener_proxy.register_listener(second_listener).await;
534
535        // Setup events and expectations.
536        let descriptor = testing_utilities::consumer_controls_device_descriptor();
537        let input_events = vec![testing_utilities::create_consumer_controls_event(
538            vec![fidl_input_report::ConsumerControlButton::VolumeUp],
539            event_time,
540            &descriptor,
541        )];
542        let expected_events = vec![create_ui_input_media_buttons_event(
543            Some(1),
544            Some(false),
545            Some(false),
546            Some(false),
547            Some(false),
548            Some(false),
549        )];
550
551        // Assert registered listeners receives event.
552        use crate::input_handler::InputHandler as _; // Adapt UnhandledInputHandler to InputHandler
553        assert_input_event_sequence_generates_media_buttons_events!(
554            input_handler: media_buttons_handler,
555            input_events: input_events,
556            expected_events: expected_events,
557            media_buttons_listener_request_stream:
558                vec![first_listener_stream, second_listener_stream],
559        );
560    }
561
562    /// Tests that listener is unregistered if channel is closed and we try to send input event to listener
563    #[fuchsia::test]
564    fn unregister_listener_if_channel_closed() {
565        let mut exec = fasync::TestExecutor::new();
566
567        let event_time = zx::MonotonicInstant::get();
568        let inspector = fuchsia_inspect::Inspector::default();
569        let test_node = inspector.root().create_child("test_node");
570        let inspect_status = InputHandlerStatus::new(
571            &test_node,
572            "media_buttons_handler",
573            /* generates_events */ false,
574        );
575        let media_buttons_handler =
576            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
577        let media_buttons_handler_clone = media_buttons_handler.clone();
578
579        let mut task = fasync::Task::local(async move {
580            let (device_listener_proxy, _server_task) =
581                spawn_device_listener_registry_server(media_buttons_handler.clone());
582
583            // Register three listeners.
584            let (first_listener, mut first_listener_stream) =
585                fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>(
586                );
587            let (second_listener, mut second_listener_stream) =
588                fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>(
589                );
590            let (third_listener, third_listener_stream) = fidl::endpoints::create_request_stream::<
591                fidl_ui_policy::MediaButtonsListenerMarker,
592            >();
593            let _ = device_listener_proxy.register_listener(first_listener).await;
594            let _ = device_listener_proxy.register_listener(second_listener).await;
595            let _ = device_listener_proxy.register_listener(third_listener).await;
596            assert_eq!(media_buttons_handler.inner.borrow().listeners.len(), 3);
597
598            // Generate input event to be handled by MediaButtonsHandler.
599            let descriptor = testing_utilities::consumer_controls_device_descriptor();
600            let input_event = testing_utilities::create_consumer_controls_event(
601                vec![fidl_input_report::ConsumerControlButton::VolumeUp],
602                event_time,
603                &descriptor,
604            );
605
606            let expected_media_buttons_event = create_ui_input_media_buttons_event(
607                Some(1),
608                Some(false),
609                Some(false),
610                Some(false),
611                Some(false),
612                Some(false),
613            );
614
615            // Drop third registered listener.
616            std::mem::drop(third_listener_stream);
617
618            let _ = media_buttons_handler.clone().handle_input_event(input_event).await;
619            // First listener stalls, responder doesn't send response - subsequent listeners should still be able receive event.
620            if let Some(request) = first_listener_stream.next().await {
621                match request {
622                    Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
623                        mut event,
624                        responder: _,
625                    }) => {
626                        event.trace_flow_id = None;
627                        pretty_assertions::assert_eq!(event, expected_media_buttons_event);
628
629                        // No need to send response because we want to simulate reader getting stuck.
630                    }
631                    _ => assert!(false),
632                }
633            } else {
634                assert!(false);
635            }
636
637            // Send response from responder on second listener stream
638            if let Some(request) = second_listener_stream.next().await {
639                match request {
640                    Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
641                        mut event,
642                        responder,
643                    }) => {
644                        event.trace_flow_id = None;
645                        pretty_assertions::assert_eq!(event, expected_media_buttons_event);
646                        let _ = responder.send();
647                    }
648                    _ => assert!(false),
649                }
650            } else {
651                assert!(false);
652            }
653        });
654
655        // Must manually run tasks with executor to ensure all tasks in LocalTaskTracker complete/stall before we call final assertion.
656        let _ = exec.run_until_stalled(&mut task);
657
658        // Should only be two listeners still registered in 'inner' after we unregister the listener with closed channel.
659        let _ = exec.run_singlethreaded(async {
660            assert_eq!(media_buttons_handler_clone.inner.borrow().listeners.len(), 2);
661        });
662    }
663
664    /// Tests that handle_input_event returns even if reader gets stuck while sending event to listener
665    #[fasync::run_singlethreaded(test)]
666    async fn stuck_reader_wont_block_input_pipeline() {
667        let event_time = zx::MonotonicInstant::get();
668        let inspector = fuchsia_inspect::Inspector::default();
669        let test_node = inspector.root().create_child("test_node");
670        let inspect_status = InputHandlerStatus::new(
671            &test_node,
672            "media_buttons_handler",
673            /* generates_events */ false,
674        );
675        let media_buttons_handler =
676            MediaButtonsHandler::new_internal(inspect_status, metrics::MetricsLogger::default());
677        let (device_listener_proxy, _server_task) =
678            spawn_device_listener_registry_server(media_buttons_handler.clone());
679
680        let (first_listener, mut first_listener_stream) =
681            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
682        let (second_listener, mut second_listener_stream) =
683            fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>();
684        let _ = device_listener_proxy.register_listener(first_listener).await;
685        let _ = device_listener_proxy.register_listener(second_listener).await;
686
687        // Setup events and expectations.
688        let descriptor = testing_utilities::consumer_controls_device_descriptor();
689        let first_unhandled_input_event = input_device::UnhandledInputEvent {
690            device_event: input_device::InputDeviceEvent::ConsumerControls(
691                consumer_controls_binding::ConsumerControlsEvent::new(
692                    vec![fidl_input_report::ConsumerControlButton::VolumeUp],
693                    None,
694                ),
695            ),
696            device_descriptor: descriptor.clone(),
697            event_time,
698            trace_id: None,
699        };
700        let first_expected_media_buttons_event = create_ui_input_media_buttons_event(
701            Some(1),
702            Some(false),
703            Some(false),
704            Some(false),
705            Some(false),
706            Some(false),
707        );
708
709        assert_matches!(
710            media_buttons_handler
711                .clone()
712                .handle_unhandled_input_event(first_unhandled_input_event)
713                .await
714                .as_slice(),
715            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
716        );
717
718        let mut save_responder = None;
719
720        // Ensure handle_input_event attempts to send event to first listener.
721        if let Some(request) = first_listener_stream.next().await {
722            match request {
723                Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
724                    mut event,
725                    responder,
726                }) => {
727                    event.trace_flow_id = None;
728                    pretty_assertions::assert_eq!(event, first_expected_media_buttons_event);
729
730                    // No need to send response because we want to simulate reader getting stuck.
731
732                    // Save responder to send response later
733                    save_responder = Some(responder);
734                }
735                _ => assert!(false),
736            }
737        } else {
738            assert!(false)
739        }
740
741        // Ensure handle_input_event still sends event to second listener when reader for first listener is stuck.
742        if let Some(request) = second_listener_stream.next().await {
743            match request {
744                Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
745                    mut event,
746                    responder,
747                }) => {
748                    event.trace_flow_id = None;
749                    pretty_assertions::assert_eq!(event, first_expected_media_buttons_event);
750                    let _ = responder.send();
751                }
752                _ => assert!(false),
753            }
754        } else {
755            assert!(false)
756        }
757
758        // Setup second event to handle
759        let second_unhandled_input_event = input_device::UnhandledInputEvent {
760            device_event: input_device::InputDeviceEvent::ConsumerControls(
761                consumer_controls_binding::ConsumerControlsEvent::new(
762                    vec![fidl_input_report::ConsumerControlButton::MicMute],
763                    None,
764                ),
765            ),
766            device_descriptor: descriptor.clone(),
767            event_time,
768            trace_id: None,
769        };
770        let second_expected_media_buttons_event = create_ui_input_media_buttons_event(
771            Some(0),
772            Some(true),
773            Some(false),
774            Some(false),
775            Some(false),
776            Some(false),
777        );
778
779        // Ensure we can handle a subsequent event if listener stalls on first event.
780        assert_matches!(
781            media_buttons_handler
782                .clone()
783                .handle_unhandled_input_event(second_unhandled_input_event)
784                .await
785                .as_slice(),
786            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
787        );
788
789        // Ensure events are still sent to listeners if a listener stalls on a previous event.
790        if let Some(request) = second_listener_stream.next().await {
791            match request {
792                Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
793                    mut event,
794                    responder,
795                }) => {
796                    event.trace_flow_id = None;
797                    pretty_assertions::assert_eq!(event, second_expected_media_buttons_event);
798                    let _ = responder.send();
799                }
800                _ => assert!(false),
801            }
802        } else {
803            assert!(false)
804        }
805
806        match save_responder {
807            Some(save_responder) => {
808                // Simulate delayed response to first listener for first event
809                let _ = save_responder.send();
810                // First listener should now receive second event after delayed response for first event
811                if let Some(request) = first_listener_stream.next().await {
812                    match request {
813                        Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
814                            mut event,
815                            responder: _,
816                        }) => {
817                            event.trace_flow_id = None;
818                            pretty_assertions::assert_eq!(
819                                event,
820                                second_expected_media_buttons_event
821                            );
822
823                            // No need to send response
824                        }
825                        _ => assert!(false),
826                    }
827                } else {
828                    assert!(false)
829                }
830            }
831            None => {
832                assert!(false)
833            }
834        }
835    }
836
837    // Test for LocalTaskTracker
838    #[fuchsia::test]
839    fn local_task_tracker_test() -> Result<(), Error> {
840        let mut exec = fasync::TestExecutor::new();
841
842        let (mut sender_1, task_1, completed_1) = make_signalable_task::<bool>();
843        let (sender_2, task_2, completed_2) = make_signalable_task::<bool>();
844
845        let mut tracker = LocalTaskTracker::new();
846
847        tracker.track(metrics::MetricsLogger::default(), task_1);
848        tracker.track(metrics::MetricsLogger::default(), task_2);
849
850        assert_matches!(exec.run_until_stalled(&mut tracker._receiver_task), Poll::Pending);
851        assert_eq!(Rc::strong_count(&completed_1), 2);
852        assert_eq!(Rc::strong_count(&completed_2), 2);
853        assert!(!sender_1.is_canceled());
854        assert!(!sender_2.is_canceled());
855
856        assert!(sender_2.send(true).is_ok());
857        assert_matches!(exec.run_until_stalled(&mut tracker._receiver_task), Poll::Pending);
858
859        assert_eq!(Rc::strong_count(&completed_1), 2);
860        assert_eq!(Rc::strong_count(&completed_2), 1);
861        assert_eq!(*completed_1.borrow(), false);
862        assert_eq!(*completed_2.borrow(), true);
863        assert!(!sender_1.is_canceled());
864
865        drop(tracker);
866        let mut sender_1_cancellation = sender_1.cancellation();
867        assert_matches!(exec.run_until_stalled(&mut sender_1_cancellation), Poll::Ready(()));
868        assert_eq!(Rc::strong_count(&completed_1), 1);
869        assert!(sender_1.is_canceled());
870
871        Ok(())
872    }
873
874    #[fasync::run_singlethreaded(test)]
875    async fn media_buttons_handler_initialized_with_inspect_node() {
876        let inspector = fuchsia_inspect::Inspector::default();
877        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
878        let _handler =
879            MediaButtonsHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
880        diagnostics_assertions::assert_data_tree!(inspector, root: {
881            input_handlers_node: {
882                media_buttons_handler: {
883                    events_received_count: 0u64,
884                    events_handled_count: 0u64,
885                    last_received_timestamp_ns: 0u64,
886                    "fuchsia.inspect.Health": {
887                        status: "STARTING_UP",
888                        // Timestamp value is unpredictable and not relevant in this context,
889                        // so we only assert that the property is present.
890                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
891                    },
892                }
893            }
894        });
895    }
896
897    #[fasync::run_singlethreaded(test)]
898    async fn media_buttons_handler_inspect_counts_events() {
899        let inspector = fuchsia_inspect::Inspector::default();
900        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
901        let media_buttons_handler =
902            MediaButtonsHandler::new(&fake_handlers_node, metrics::MetricsLogger::default());
903
904        // Unhandled input event should be counted by inspect.
905        let descriptor = testing_utilities::consumer_controls_device_descriptor();
906        let events = vec![
907            input_device::InputEvent {
908                device_event: input_device::InputDeviceEvent::ConsumerControls(
909                    consumer_controls_binding::ConsumerControlsEvent::new(
910                        vec![fidl_input_report::ConsumerControlButton::VolumeUp],
911                        None,
912                    ),
913                ),
914                device_descriptor: descriptor.clone(),
915                event_time: zx::MonotonicInstant::get(),
916                handled: input_device::Handled::No,
917                trace_id: None,
918            },
919            // Handled input event should be ignored.
920            input_device::InputEvent {
921                device_event: input_device::InputDeviceEvent::ConsumerControls(
922                    consumer_controls_binding::ConsumerControlsEvent::new(
923                        vec![fidl_input_report::ConsumerControlButton::VolumeUp],
924                        None,
925                    ),
926                ),
927                device_descriptor: descriptor.clone(),
928                event_time: zx::MonotonicInstant::get(),
929                handled: input_device::Handled::Yes,
930                trace_id: None,
931            },
932            input_device::InputEvent {
933                device_event: input_device::InputDeviceEvent::ConsumerControls(
934                    consumer_controls_binding::ConsumerControlsEvent::new(
935                        vec![fidl_input_report::ConsumerControlButton::VolumeDown],
936                        None,
937                    ),
938                ),
939                device_descriptor: descriptor.clone(),
940                event_time: zx::MonotonicInstant::get(),
941                handled: input_device::Handled::No,
942                trace_id: None,
943            },
944        ];
945
946        let last_event_timestamp: u64 =
947            events[2].clone().event_time.into_nanos().try_into().unwrap();
948
949        for event in events {
950            media_buttons_handler.clone().handle_input_event(event).await;
951        }
952
953        diagnostics_assertions::assert_data_tree!(inspector, root: {
954            input_handlers_node: {
955                media_buttons_handler: {
956                    events_received_count: 2u64,
957                    events_handled_count: 2u64,
958                    last_received_timestamp_ns: last_event_timestamp,
959                    "fuchsia.inspect.Health": {
960                        status: "STARTING_UP",
961                        // Timestamp value is unpredictable and not relevant in this context,
962                        // so we only assert that the property is present.
963                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
964                    },
965                }
966            }
967        });
968    }
969
970    #[fasync::run_singlethreaded(test)]
971    async fn clone_event_with_lease_duplicates_lease() {
972        let (event_pair, _) = fidl::EventPair::create();
973        let event_with_lease = fidl_ui_input::MediaButtonsEvent {
974            volume: Some(1),
975            mic_mute: Some(true),
976            pause: Some(true),
977            camera_disable: Some(true),
978            power: Some(true),
979            function: Some(true),
980            device_id: Some(1),
981            wake_lease: Some(event_pair),
982            ..Default::default()
983        };
984
985        // Test cloning an event that has a wake lease.
986        // With wake lease argument should duplicate the handle.
987        let cloned_event = MediaButtonsHandler::clone_event(&event_with_lease);
988        assert_eq!(event_with_lease.volume, cloned_event.volume);
989        assert_eq!(event_with_lease.mic_mute, cloned_event.mic_mute);
990        assert_eq!(event_with_lease.pause, cloned_event.pause);
991        assert_eq!(event_with_lease.camera_disable, cloned_event.camera_disable);
992        assert_eq!(event_with_lease.power, cloned_event.power);
993        assert_eq!(event_with_lease.function, cloned_event.function);
994        assert_eq!(event_with_lease.device_id, cloned_event.device_id);
995        assert!(event_with_lease.wake_lease.is_some());
996        assert!(cloned_event.wake_lease.is_some());
997        assert_ne!(
998            event_with_lease.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle(),
999            cloned_event.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle()
1000        );
1001        assert_eq!(
1002            event_with_lease.wake_lease.as_ref().unwrap().koid(),
1003            cloned_event.wake_lease.as_ref().unwrap().koid()
1004        );
1005    }
1006
1007    #[fasync::run_singlethreaded(test)]
1008    async fn clone_event_without_lease_has_no_lease() {
1009        // Test cloning an event that does not have a wake lease.
1010        let event_without_lease = fidl_ui_input::MediaButtonsEvent {
1011            volume: Some(1),
1012            mic_mute: Some(true),
1013            pause: Some(true),
1014            camera_disable: Some(true),
1015            power: Some(true),
1016            function: Some(true),
1017            device_id: Some(1),
1018            ..Default::default()
1019        };
1020
1021        // With wake lease argument should result in no wake lease.
1022        let cloned_event = MediaButtonsHandler::clone_event(&event_without_lease);
1023        assert_eq!(event_without_lease.volume, cloned_event.volume);
1024        assert_eq!(event_without_lease.mic_mute, cloned_event.mic_mute);
1025        assert_eq!(event_without_lease.pause, cloned_event.pause);
1026        assert_eq!(event_without_lease.camera_disable, cloned_event.camera_disable);
1027        assert_eq!(event_without_lease.power, cloned_event.power);
1028        assert_eq!(event_without_lease.function, cloned_event.function);
1029        assert_eq!(event_without_lease.device_id, cloned_event.device_id);
1030        assert!(cloned_event.wake_lease.is_none());
1031    }
1032
1033    #[fasync::run_singlethreaded(test)]
1034    async fn clone_event_sets_trace_flow_id() {
1035        let event = fidl_ui_input::MediaButtonsEvent { volume: Some(1), ..Default::default() };
1036
1037        let cloned_event_1 = MediaButtonsHandler::clone_event(&event);
1038        let cloned_event_2 = MediaButtonsHandler::clone_event(&event);
1039
1040        assert!(cloned_event_1.trace_flow_id.is_some());
1041        assert!(cloned_event_2.trace_flow_id.is_some());
1042        assert_ne!(cloned_event_1.trace_flow_id, cloned_event_2.trace_flow_id);
1043    }
1044}