settings/agent/
camera_watcher.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::channel::mpsc::UnboundedSender;
8use settings_camera::connect_to_camera;
9use settings_common::inspect::event::ExternalEventPublisher;
10use settings_common::service_context::ServiceContext;
11use settings_common::trace;
12
13pub struct CameraWatcherAgent {
14    /// Sends an event whenever camera muted state changes. The `bool`
15    /// represents whether the camera is muted or not.
16    muted_txs: Vec<UnboundedSender<bool>>,
17    external_publisher: ExternalEventPublisher,
18}
19
20impl CameraWatcherAgent {
21    pub fn new(
22        muted_txs: Vec<UnboundedSender<bool>>,
23        external_publisher: ExternalEventPublisher,
24    ) -> Self {
25        Self { muted_txs, external_publisher }
26    }
27
28    pub async fn spawn(self, service_context: &ServiceContext) -> Result<(), Error> {
29        let camera_device_client =
30            connect_to_camera(service_context, self.external_publisher.clone())
31                .await
32                .context("connecting to camera")?;
33        let mut event_handler = EventHandler { muted_txs: self.muted_txs.clone(), sw_muted: false };
34        fasync::Task::local(async move {
35            let id = fuchsia_trace::Id::new();
36            // Here we don't care about hw_muted state because the input service would pick
37            // up mute changes directly from the switch. We care about sw changes because
38            // other clients of the camera3 service could change the sw mute state but not
39            // notify the settings service.
40            trace!(id, c"camera_watcher_agent_handler");
41            while let Ok((sw_muted, _hw_muted)) = camera_device_client.watch_mute_state().await {
42                trace!(id, c"event");
43                event_handler.handle_event(sw_muted);
44            }
45        })
46        .detach();
47
48        Ok(())
49    }
50}
51
52struct EventHandler {
53    muted_txs: Vec<UnboundedSender<bool>>,
54    sw_muted: bool,
55}
56
57impl EventHandler {
58    fn handle_event(&mut self, sw_muted: bool) {
59        if self.sw_muted != sw_muted {
60            self.sw_muted = sw_muted;
61            self.send_event(sw_muted);
62        }
63    }
64
65    fn send_event(&self, muted: bool) {
66        for muted_tx in &self.muted_txs {
67            let _ = muted_tx.unbounded_send(muted);
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use futures::StreamExt;
76    use futures::channel::mpsc;
77    use settings_test_common::fakes::service::ServiceRegistry;
78
79    // Tests that the agent cannot start without a camera3 service.
80    #[fuchsia::test(allow_stalls = false)]
81    async fn when_camera3_inaccessible_returns_err() {
82        let (event_tx, _event_rx) = mpsc::unbounded();
83        let external_publisher = ExternalEventPublisher::new(event_tx);
84
85        let agent = CameraWatcherAgent { muted_txs: vec![], external_publisher };
86        let service_context =
87            ServiceContext::new(Some(ServiceRegistry::serve(ServiceRegistry::create())));
88
89        // Try to initiate the Service lifespan without providing the camera3 fidl interface.
90        let result = agent.spawn(&service_context).await;
91        assert!(matches!(result, Err(_)));
92    }
93
94    // Tests that events can be sent to the intended recipients.
95    #[fuchsia::test(allow_stalls = false)]
96    async fn event_handler_proxies_event() {
97        let (tx1, mut rx1) = mpsc::unbounded();
98        let (tx2, mut rx2) = mpsc::unbounded();
99
100        let mut event_handler =
101            EventHandler { muted_txs: vec![tx1.clone(), tx2.clone()], sw_muted: false };
102
103        // Send the events.
104        event_handler.handle_event(true);
105
106        let mut channel_received = 0;
107
108        let mut next_rx1 = rx1.next();
109        let mut next_rx2 = rx2.next();
110
111        // Loop over the select so we can handle the messages as they come in. When all messages
112        // have been handled, the senders a closed to ensure the complete case can be hit below.
113        loop {
114            futures::select! {
115                event = next_rx1 => {
116                    let Some(muted) = event else {
117                        continue;
118                    };
119                    assert!(muted);
120                    // Close channel so we can exit select loop.
121                    tx1.close_channel();
122                    channel_received += 1;
123                }
124                event = next_rx2 => {
125                    let Some(muted) = event else {
126                        continue;
127                    };
128                    assert!(muted);
129                    // Close channel so we can exit select loop.
130                    tx2.close_channel();
131                    channel_received += 1;
132                }
133                complete => break,
134            }
135        }
136
137        assert_eq!(channel_received, 2);
138    }
139}