Skip to main content

settings_media_buttons_agent/
lib.rs

1// Copyright 2020 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 anyhow::{Context, Error};
6use fidl::endpoints::create_request_stream;
7use fidl_fuchsia_ui_input::MediaButtonsEvent;
8use fidl_fuchsia_ui_policy::{
9    DeviceListenerRegistryMarker, MediaButtonsListenerMarker, MediaButtonsListenerRequest,
10};
11use fuchsia_async as fasync;
12use futures::StreamExt;
13use futures::channel::mpsc::UnboundedSender;
14use settings_common::call_async;
15use settings_common::inspect::event::ExternalEventPublisher;
16use settings_common::service_context::ServiceContext;
17use settings_media_buttons::{self as media_buttons, MediaButtons};
18
19/// Method for listening to media button changes. Changes will be reported back
20/// on the supplied sender.
21pub(crate) async fn monitor_media_buttons(
22    service_context: &ServiceContext,
23    sender: futures::channel::mpsc::UnboundedSender<MediaButtonsEvent>,
24    external_publisher: ExternalEventPublisher,
25) -> Result<(), Error> {
26    let presenter_service = service_context
27        .connect_with_publisher::<DeviceListenerRegistryMarker, _>(external_publisher)
28        .await?;
29    let (client_end, mut stream) = create_request_stream::<MediaButtonsListenerMarker>();
30
31    // TODO(https://fxbug.dev/42058092) This independent spawn is necessary! For some reason removing this or
32    // merging it with the spawn below causes devices to lock up on input button events. Figure out
33    // whether this can be removed or left as-is as part of the linked bug.
34    fasync::Task::local(async move {
35        if let Err(error) = call_async!(presenter_service => register_listener(client_end)).await {
36            log::error!(
37                "Registering media button listener with presenter service failed {:?}",
38                error
39            );
40        }
41    })
42    .detach();
43
44    fasync::Task::local(async move {
45        while let Some(Ok(media_request)) = stream.next().await {
46            // Support future expansion of FIDL
47            #[allow(clippy::single_match)]
48            #[allow(unreachable_patterns)]
49            match media_request {
50                MediaButtonsListenerRequest::OnEvent { event, responder } => {
51                    sender
52                        .unbounded_send(event)
53                        .expect("Media buttons sender failed to send event");
54                    // Acknowledge the event.
55                    responder
56                        .send()
57                        .unwrap_or_else(|_| log::error!("Failed to ack media buttons event"));
58                }
59                _ => {}
60            }
61        }
62    })
63    .detach();
64
65    Ok(())
66}
67
68pub struct MediaButtonsAgent {
69    event_txs: Vec<UnboundedSender<media_buttons::Event>>,
70    external_publisher: ExternalEventPublisher,
71}
72
73impl MediaButtonsAgent {
74    pub fn new(
75        event_txs: Vec<UnboundedSender<media_buttons::Event>>,
76        external_publisher: ExternalEventPublisher,
77    ) -> Self {
78        Self { event_txs, external_publisher }
79    }
80
81    pub async fn spawn(self, service_context: &ServiceContext) -> Result<(), Error> {
82        let (input_tx, mut input_rx) = futures::channel::mpsc::unbounded::<MediaButtonsEvent>();
83        monitor_media_buttons(service_context, input_tx, self.external_publisher.clone())
84            .await
85            .context("monitoring media buttons")?;
86
87        let event_handler = EventHandler { event_txs: self.event_txs.clone() };
88        fasync::Task::local(async move {
89            while let Some(event) = input_rx.next().await {
90                event_handler.handle_event(event);
91            }
92        })
93        .detach();
94
95        Ok(())
96    }
97}
98
99struct EventHandler {
100    event_txs: Vec<UnboundedSender<media_buttons::Event>>,
101}
102
103impl EventHandler {
104    fn handle_event(&self, event: MediaButtonsEvent) {
105        if event.mic_mute.is_some() || event.camera_disable.is_some() {
106            let media_buttons: MediaButtons = event.into();
107            self.send_event(media_buttons);
108        }
109    }
110
111    fn send_event(&self, event: MediaButtons) {
112        for tx in &self.event_txs {
113            let _ = tx.unbounded_send(media_buttons::Event::from(event));
114        }
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use fidl_fuchsia_ui_input::MediaButtonsEvent;
122    use futures::channel::mpsc;
123    use futures::lock::Mutex;
124    use settings_media_buttons::{Event, MediaButtonsEventBuilder};
125    use settings_test_common::fakes::input_device_registry_service::InputDeviceRegistryService;
126    use settings_test_common::fakes::service::ServiceRegistry;
127    use settings_test_common::helpers::clone_media_buttons_event_without_wake_lease;
128    use std::rc::Rc;
129
130    // Tests that the agent cannot start without a media buttons service.
131    #[fuchsia::test(allow_stalls = false)]
132    async fn when_media_buttons_inaccessible_returns_err() {
133        // Setup messengers needed to construct the agent.
134        let (event_tx, _) = mpsc::unbounded();
135        let external_publisher = ExternalEventPublisher::new(event_tx);
136
137        // Construct the agent.
138        let agent = MediaButtonsAgent { event_txs: vec![], external_publisher };
139        let service_context = ServiceContext::new(
140            // Create a service registry without a media buttons interface.
141            Some(ServiceRegistry::serve(ServiceRegistry::create())),
142        );
143
144        // Try to spawn the agent without a media buttons interface.
145        let result = agent.spawn(&service_context).await;
146        assert!(matches!(result, Err(_)));
147    }
148
149    // Tests that events can be sent to the intended recipients.
150    #[fuchsia::test(allow_stalls = false)]
151    async fn event_handler_proxies_event() {
152        let (tx1, rx1) = mpsc::unbounded();
153        let (tx2, rx2) = mpsc::unbounded();
154
155        // Make all setting types available.
156        let event_handler = EventHandler { event_txs: vec![tx1, tx2] };
157
158        // Send the events.
159        event_handler.handle_event(
160            MediaButtonsEventBuilder::new().set_mic_mute(true).set_camera_disable(true).build(),
161        );
162
163        let mut received_channel_events: usize = 0;
164
165        let fused_rx1 = rx1.fuse();
166        let fused_rx2 = rx2.fuse();
167        futures::pin_mut!(fused_rx1, fused_rx2);
168
169        drop(event_handler);
170        // Loop over the select so we can handle the messages as they come in. When all messages
171        // have been handled, due to the messengers being deleted above, the complete branch should
172        // be hit to break out of the loop.
173        loop {
174            futures::select! {
175                message = fused_rx1.select_next_some() => {
176                    match message {
177                        settings_media_buttons::Event::OnButton(
178                            MediaButtons{..}
179                        ) => {
180                            received_channel_events += 1;
181                        }
182                    }
183                }
184                message = fused_rx2.select_next_some() => {
185                    match message {
186                        settings_media_buttons::Event::OnButton(
187                            MediaButtons{..}
188                        ) => {
189                            received_channel_events += 1;
190                        }
191                    }
192                }
193                complete => break,
194            }
195        }
196
197        // channels should have received one event each for both mic and camera.
198        assert_eq!(received_channel_events, 2);
199    }
200
201    #[fuchsia::test(allow_stalls = false)]
202    async fn test_media_buttons() {
203        let service_registry = ServiceRegistry::create();
204        let input_device_registry_service = Rc::new(Mutex::new(InputDeviceRegistryService::new()));
205
206        let initial_event = MediaButtonsEventBuilder::new().set_mic_mute(true).build();
207        input_device_registry_service
208            .lock()
209            .await
210            .send_media_button_event(clone_media_buttons_event_without_wake_lease(&initial_event))
211            .await;
212
213        service_registry.lock().await.register_service(input_device_registry_service.clone());
214
215        let service_context =
216            ServiceContext::new(Some(ServiceRegistry::serve(service_registry.clone())));
217        let (event_tx, _) = mpsc::unbounded();
218        let external_publisher = ExternalEventPublisher::new(event_tx);
219
220        let (input_tx, mut input_rx) = futures::channel::mpsc::unbounded::<MediaButtonsEvent>();
221        assert!(
222            monitor_media_buttons(&service_context, input_tx, external_publisher).await.is_ok()
223        );
224
225        // Listener receives an event immediately upon listening.
226        if let Some(event) = input_rx.next().await {
227            assert_eq!(initial_event, event);
228        }
229
230        // Disable the camera.
231        let second_event = MediaButtonsEventBuilder::new().set_camera_disable(true).build();
232        input_device_registry_service
233            .lock()
234            .await
235            .send_media_button_event(clone_media_buttons_event_without_wake_lease(&second_event))
236            .await;
237
238        // Listener receives the camera disable event.
239        if let Some(event) = input_rx.next().await {
240            assert_eq!(second_event, event);
241        }
242    }
243
244    #[fuchsia::test(allow_stalls = false)]
245    async fn test_device_listener_failure() {
246        let service_registry = ServiceRegistry::create();
247        let input_device_registry_service = Rc::new(Mutex::new(InputDeviceRegistryService::new()));
248        input_device_registry_service.lock().await.set_fail(true);
249
250        let initial_event = MediaButtonsEventBuilder::new().set_mic_mute(true).build();
251
252        input_device_registry_service
253            .lock()
254            .await
255            .send_media_button_event(clone_media_buttons_event_without_wake_lease(&initial_event))
256            .await;
257
258        service_registry.lock().await.register_service(input_device_registry_service.clone());
259
260        let service_context =
261            &ServiceContext::new(Some(ServiceRegistry::serve(service_registry.clone())));
262        let (event_tx, _) = mpsc::unbounded();
263        let external_publisher = ExternalEventPublisher::new(event_tx);
264
265        let (input_tx, _input_rx) = futures::channel::mpsc::unbounded::<MediaButtonsEvent>();
266        #[allow(clippy::bool_assert_comparison)]
267        {
268            assert_eq!(
269                monitor_media_buttons(service_context, input_tx, external_publisher).await.is_ok(),
270                false
271            );
272        }
273    }
274
275    struct FakeServices {
276        input_device_registry: Rc<Mutex<InputDeviceRegistryService>>,
277    }
278
279    // Returns a registry and input related services with which it is populated.
280    async fn create_services() -> (Rc<Mutex<ServiceRegistry>>, FakeServices) {
281        let service_registry = ServiceRegistry::create();
282
283        let input_device_registry_service_handle =
284            Rc::new(Mutex::new(InputDeviceRegistryService::new()));
285        service_registry
286            .lock()
287            .await
288            .register_service(input_device_registry_service_handle.clone());
289
290        (
291            service_registry,
292            FakeServices { input_device_registry: input_device_registry_service_handle },
293        )
294    }
295
296    #[fuchsia::test(allow_stalls = false)]
297    async fn test_media_buttons_proxied() {
298        let (event_tx, _) = mpsc::unbounded();
299        let external_publisher = ExternalEventPublisher::new(event_tx);
300        let (tx, mut rx) = mpsc::unbounded();
301        let agent = MediaButtonsAgent::new(vec![tx], external_publisher);
302
303        // Setup the fake services.
304        let (service_registry, fake_services) = create_services().await;
305        let service_context = ServiceContext::new(Some(ServiceRegistry::serve(service_registry)));
306
307        let res = agent.spawn(&service_context).await;
308        // Validate that the setup is complete.
309        assert!(matches!(res, Ok(())), "spawn failed");
310
311        // The agent should now be initialized. Send a media button event.
312        fake_services
313            .input_device_registry
314            .lock()
315            .await
316            .send_media_button_event(MediaButtonsEvent {
317                volume: Some(1),
318                mic_mute: Some(true),
319                pause: None,
320                camera_disable: None,
321                ..Default::default()
322            })
323            .await;
324
325        let mic_mute = rx.next().await;
326        assert_eq!(
327            mic_mute,
328            Some(Event::OnButton(MediaButtons { mic_mute: Some(true), camera_disable: None }))
329        );
330    }
331}