settings_media_buttons_agent/
lib.rs1use 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
19pub(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 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 #[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 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 #[fuchsia::test(allow_stalls = false)]
132 async fn when_media_buttons_inaccessible_returns_err() {
133 let (event_tx, _) = mpsc::unbounded();
135 let external_publisher = ExternalEventPublisher::new(event_tx);
136
137 let agent = MediaButtonsAgent { event_txs: vec![], external_publisher };
139 let service_context = ServiceContext::new(
140 Some(ServiceRegistry::serve(ServiceRegistry::create())),
142 );
143
144 let result = agent.spawn(&service_context).await;
146 assert!(matches!(result, Err(_)));
147 }
148
149 #[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 let event_handler = EventHandler { event_txs: vec![tx1, tx2] };
157
158 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 {
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 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 if let Some(event) = input_rx.next().await {
227 assert_eq!(initial_event, event);
228 }
229
230 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 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 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 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 assert!(matches!(res, Ok(())), "spawn failed");
310
311 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}