Skip to main content

settings_camera_watcher_agent/
lib.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 anyhow::{Context, Error};
6use fuchsia_async as fasync;
7use futures::StreamExt;
8use futures::channel::mpsc::UnboundedSender;
9use settings_camera::connect_to_cameras;
10use settings_common::inspect::event::ExternalEventPublisher;
11use settings_common::service_context::ServiceContext;
12use settings_common::trace;
13
14pub struct CameraWatcherAgent {
15    /// Sends an event whenever camera muted state changes. The `bool`
16    /// represents whether the camera is muted or not.
17    muted_txs: Vec<UnboundedSender<bool>>,
18    external_publisher: ExternalEventPublisher,
19}
20
21impl CameraWatcherAgent {
22    pub fn new(
23        muted_txs: Vec<UnboundedSender<bool>>,
24        external_publisher: ExternalEventPublisher,
25    ) -> Self {
26        Self { muted_txs, external_publisher }
27    }
28
29    pub async fn spawn(self, service_context: &ServiceContext) -> Result<(), Error> {
30        // Multiple cameras are not yet supported individually, and individual camera mute/unmute
31        // is not exposed, so all reported cameras are watched and aggregated together.
32        let camera_device_clients =
33            connect_to_cameras(service_context, self.external_publisher.clone())
34                .await
35                .context("connecting to camera")?;
36        let mut event_handler =
37            EventHandler::new(self.muted_txs.clone(), camera_device_clients.len());
38        fasync::Task::local(async move {
39            let id = fuchsia_trace::Id::new();
40            // Here we don't care about hw_muted state because the input service would pick
41            // up mute changes directly from the switch. We care about sw changes because
42            // other clients of the camera3 service could change the sw mute state but not
43            // notify the settings service.
44            trace!(id, "camera_watcher_agent_handler");
45            let mut mute_streams = futures::stream::select_all(
46                camera_device_clients.into_iter().enumerate().map(|(index, client)| {
47                    futures::stream::unfold(Some(client), move |client_opt| async move {
48                        let client = client_opt?;
49                        match client.watch_mute_state().await {
50                            Ok((sw_muted, _hw_muted)) => {
51                                Some(((index, CameraEvent::MuteState(sw_muted)), Some(client)))
52                            }
53                            Err(e) => {
54                                log::warn!("Failed to watch mute state for camera {index}: {e:?}");
55                                Some(((index, CameraEvent::Disconnected), None))
56                            }
57                        }
58                    })
59                    .boxed_local()
60                }),
61            );
62            while let Some((index, event)) = mute_streams.next().await {
63                trace!(id, "event");
64                match event {
65                    CameraEvent::MuteState(sw_muted) => {
66                        event_handler.handle_event(index, sw_muted);
67                    }
68                    CameraEvent::Disconnected => {
69                        event_handler.handle_disconnect(index);
70                    }
71                }
72            }
73            log::warn!("All camera mute state streams have closed");
74        })
75        .detach();
76
77        Ok(())
78    }
79}
80
81enum CameraEvent {
82    MuteState(bool),
83    Disconnected,
84}
85
86#[derive(Clone, Copy, PartialEq, Eq)]
87enum CameraState {
88    Unknown,
89    Active(bool),
90    Disconnected,
91}
92
93struct EventHandler {
94    muted_txs: Vec<UnboundedSender<bool>>,
95    sw_muted: Option<bool>,
96    camera_states: Vec<CameraState>,
97}
98
99impl EventHandler {
100    fn new(muted_txs: Vec<UnboundedSender<bool>>, num_cameras: usize) -> Self {
101        Self { muted_txs, sw_muted: None, camera_states: vec![CameraState::Unknown; num_cameras] }
102    }
103
104    fn handle_event(&mut self, index: usize, sw_muted: bool) {
105        if let Some(state) = self.camera_states.get_mut(index) {
106            *state = CameraState::Active(sw_muted);
107        }
108        self.evaluate_and_send();
109    }
110
111    fn handle_disconnect(&mut self, index: usize) {
112        if let Some(state) = self.camera_states.get_mut(index) {
113            *state = CameraState::Disconnected;
114        }
115        self.evaluate_and_send();
116    }
117
118    fn evaluate_and_send(&mut self) {
119        let mut has_connected = false;
120        let mut has_unknown = false;
121        let mut any_unmuted = false;
122
123        for state in &self.camera_states {
124            match state {
125                CameraState::Active(false) => {
126                    has_connected = true;
127                    any_unmuted = true;
128                }
129                CameraState::Active(true) => {
130                    has_connected = true;
131                }
132                CameraState::Unknown => {
133                    has_connected = true;
134                    has_unknown = true;
135                }
136                CameraState::Disconnected => {}
137            }
138        }
139
140        let resolved_muted = if !has_connected {
141            None
142        } else if any_unmuted {
143            Some(false)
144        } else if !has_unknown {
145            Some(true)
146        } else {
147            None
148        };
149
150        if let Some(all_muted) = resolved_muted
151            && self.sw_muted != Some(all_muted)
152        {
153            self.sw_muted = Some(all_muted);
154            self.send_event(all_muted);
155        }
156    }
157
158    fn send_event(&self, muted: bool) {
159        for muted_tx in &self.muted_txs {
160            let _ = muted_tx.unbounded_send(muted);
161        }
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use fuchsia_async::{MonotonicInstant, TestExecutor};
169    use futures::channel::mpsc;
170    use futures::lock::Mutex;
171    use settings_camera::CAMERA_WATCHER_TIMEOUT;
172    use settings_test_common::fakes::camera3_service::Camera3Service;
173    use settings_test_common::fakes::service::ServiceRegistry;
174    use settings_test_common::helpers::move_executor_forward_and_get;
175    use std::rc::Rc;
176
177    // Tests that the agent cannot start without a camera3 service.
178    #[fuchsia::test(allow_stalls = false)]
179    async fn when_camera3_inaccessible_returns_err() {
180        let (event_tx, _event_rx) = mpsc::unbounded();
181        let external_publisher = ExternalEventPublisher::new(event_tx);
182
183        let agent = CameraWatcherAgent { muted_txs: vec![], external_publisher };
184        let service_context =
185            ServiceContext::new(Some(ServiceRegistry::serve(ServiceRegistry::create())));
186
187        // Try to initiate the Service lifespan without providing the camera3 fidl interface.
188        let result = agent.spawn(&service_context).await;
189        assert!(result.is_err());
190    }
191
192    // Tests that events can be sent to the intended recipients.
193    #[fuchsia::test(allow_stalls = false)]
194    async fn event_handler_proxies_event() {
195        let (tx1, mut rx1) = mpsc::unbounded();
196        let (tx2, mut rx2) = mpsc::unbounded();
197
198        let mut event_handler = EventHandler::new(vec![tx1.clone(), tx2.clone()], 1);
199
200        // Send the events.
201        event_handler.handle_event(0, true);
202
203        let mut channel_received = 0;
204
205        let mut next_rx1 = rx1.next();
206        let mut next_rx2 = rx2.next();
207
208        // Loop over the select so we can handle the messages as they come in. When all messages
209        // have been handled, the senders a closed to ensure the complete case can be hit below.
210        loop {
211            futures::select! {
212                event = next_rx1 => {
213                    let Some(muted) = event else {
214                        continue;
215                    };
216                    assert!(muted);
217                    // Close channel so we can exit select loop.
218                    tx1.close_channel();
219                    channel_received += 1;
220                }
221                event = next_rx2 => {
222                    let Some(muted) = event else {
223                        continue;
224                    };
225                    assert!(muted);
226                    // Close channel so we can exit select loop.
227                    tx2.close_channel();
228                    channel_received += 1;
229                }
230                complete => break,
231            }
232        }
233
234        assert_eq!(channel_received, 2);
235    }
236
237    #[fuchsia::test(allow_stalls = false)]
238    async fn event_handler_aggregates_multiple_cameras() {
239        let (tx, mut rx) = mpsc::unbounded();
240        let mut event_handler = EventHandler::new(vec![tx], 2);
241
242        // Muting only camera 0 should not emit a muted event because camera 1 is still unmuted.
243        event_handler.handle_event(0, true);
244        assert!(rx.try_recv().is_err());
245
246        // Muting camera 1 makes all cameras muted, emitting `true` once.
247        event_handler.handle_event(1, true);
248        assert_eq!(rx.next().await, Some(true));
249
250        // Duplicate `true` on camera 0 does not emit another event.
251        event_handler.handle_event(0, true);
252        assert!(rx.try_recv().is_err());
253
254        // Unmuting camera 1 immediately emits `false`.
255        event_handler.handle_event(1, false);
256        assert_eq!(rx.next().await, Some(false));
257
258        // Unmuting camera 0 afterwards does not emit a duplicate `false`.
259        event_handler.handle_event(0, false);
260        assert!(rx.try_recv().is_err());
261    }
262
263    #[fuchsia::test(allow_stalls = false)]
264    async fn event_handler_handles_camera_disconnect() {
265        let (tx, mut rx) = mpsc::unbounded();
266        let mut event_handler = EventHandler::new(vec![tx], 2);
267
268        // Initial state: both cameras unmuted (`false`), emitting initial `false`.
269        event_handler.handle_event(0, false);
270        assert_eq!(rx.next().await, Some(false));
271        event_handler.handle_event(1, false);
272        assert!(rx.try_recv().is_err());
273
274        // Disconnecting camera 0 while unmuted does not prevent camera 1 from muting the aggregate.
275        event_handler.handle_disconnect(0);
276        assert!(rx.try_recv().is_err());
277
278        event_handler.handle_event(1, true);
279        assert_eq!(rx.next().await, Some(true));
280
281        // Disconnecting the last remaining camera does not emit a state change.
282        event_handler.handle_disconnect(1);
283        assert!(rx.try_recv().is_err());
284    }
285
286    struct FakeServices {
287        camera3_service: Rc<Mutex<Camera3Service>>,
288    }
289
290    #[derive(PartialEq)]
291    enum CameraDevice {
292        With,
293        Without,
294    }
295
296    #[derive(PartialEq)]
297    enum DelayCamera {
298        Yes,
299        No,
300    }
301
302    // Returns a registry and input related services with which it is populated. If delay_camera_device
303    // is true, then has_camera_device is ignored. It sends back the camera device after a delay.
304    // Otherwise, if has_camera_device is true, it will immediately respond with the populated camera
305    // device. If has_camera_device is false, it will immediately respond with an empty device list.
306    async fn create_services(
307        has_camera_device: CameraDevice,
308        delay_camera_device: DelayCamera,
309    ) -> (Rc<Mutex<ServiceRegistry>>, FakeServices) {
310        let service_registry = ServiceRegistry::create();
311
312        let camera3_service_handle =
313            Rc::new(Mutex::new(if DelayCamera::Yes == delay_camera_device {
314                Camera3Service::new_delayed_devices(delay_camera_device == DelayCamera::Yes)
315            } else {
316                Camera3Service::new(has_camera_device == CameraDevice::With)
317            }));
318        service_registry.lock().await.register_service(camera3_service_handle.clone());
319
320        (service_registry, FakeServices { camera3_service: camera3_service_handle })
321    }
322
323    #[fuchsia::test(allow_stalls = false)]
324    async fn test_camera_agent_proxy() {
325        // Setup the fake services.
326        let (service_registry, fake_services) =
327            create_services(CameraDevice::With, DelayCamera::No).await;
328
329        let expected_camera_state = true;
330        fake_services.camera3_service.lock().await.set_camera_sw_muted(expected_camera_state);
331        let (event_tx, _event_rx) = mpsc::unbounded();
332        let external_publisher = ExternalEventPublisher::new(event_tx);
333        let (tx, mut rx) = mpsc::unbounded();
334
335        let agent = CameraWatcherAgent::new(vec![tx], external_publisher);
336        let service_context = ServiceContext::new(Some(ServiceRegistry::serve(service_registry)));
337        let res = agent.spawn(&service_context).await;
338
339        // Validate that the setup is complete.
340        assert!(matches!(res, Ok(())), "agent spawn failed");
341
342        // Track the events to make sure they came in.
343        let res = rx.next().await;
344        assert_eq!(res, Some(true));
345    }
346
347    // Tests that an error is returned if the camera watcher cannot find a camera device
348    // after the timeout is reached.
349    #[fuchsia::test]
350    fn test_camera_devices_watcher_timeout() {
351        // Custom executor for this test so that we can advance the clock arbitrarily and verify the
352        // state of the executor at any given point.
353        let mut executor = TestExecutor::new_with_fake_time();
354        executor.set_fake_time(MonotonicInstant::from_nanos(0));
355
356        // Setup the fake services.
357        let services_future = create_services(CameraDevice::Without, DelayCamera::No);
358        let (service_registry, fake_services) = move_executor_forward_and_get(
359            &mut executor,
360            services_future,
361            "Could not create services",
362        );
363
364        // Mute the camera via software.
365        let camera_service_future = fake_services.camera3_service.lock();
366        move_executor_forward_and_get(
367            &mut executor,
368            camera_service_future,
369            "Unable to get camera service",
370        )
371        .set_camera_sw_muted(true);
372
373        let (event_tx, _event_rx) = mpsc::unbounded();
374        let external_publisher = ExternalEventPublisher::new(event_tx);
375        let agent = CameraWatcherAgent::new(vec![], external_publisher);
376        let service_context = ServiceContext::new(Some(ServiceRegistry::serve(service_registry)));
377
378        // Create and send the invocation with faked services.
379        let spawn_future = agent.spawn(&service_context);
380
381        // Advance time past the timeout.
382        executor.set_fake_time(MonotonicInstant::from_nanos(CAMERA_WATCHER_TIMEOUT + 1));
383
384        let res =
385            move_executor_forward_and_get(&mut executor, spawn_future, "Could not complete spawn");
386
387        // Validate that the setup is complete.
388        assert!(res.is_err(), "spawn did not hit timeout");
389    }
390
391    // Tests that the camera agent is able to handle an empty device list first, and then
392    // a second update with the device in it that comes in before the timeout.
393    #[fuchsia::test]
394    async fn test_camera_agent_delayed_devices() {
395        let (service_registry, fake_services) =
396            create_services(CameraDevice::Without, DelayCamera::Yes).await;
397
398        let expected_camera_state = true;
399        fake_services.camera3_service.lock().await.set_camera_sw_muted(expected_camera_state);
400        let (tx, mut rx) = mpsc::unbounded();
401        let (event_tx, _event_rx) = mpsc::unbounded();
402        let external_publisher = ExternalEventPublisher::new(event_tx);
403        let agent = CameraWatcherAgent::new(vec![tx], external_publisher);
404        let service_context = ServiceContext::new(Some(ServiceRegistry::serve(service_registry)));
405        let res = agent.spawn(&service_context).await;
406
407        // Validate that the setup is complete.
408        assert!(matches!(res, Ok(())), "spawn failed");
409        let muted = rx.next().await;
410        assert_eq!(muted, Some(true));
411    }
412}