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