settings_camera_watcher_agent/
lib.rs1use 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 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 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 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 #[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 let result = agent.spawn(&service_context).await;
189 assert!(result.is_err());
190 }
191
192 #[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 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 {
211 futures::select! {
212 event = next_rx1 => {
213 let Some(muted) = event else {
214 continue;
215 };
216 assert!(muted);
217 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 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 event_handler.handle_event(0, true);
244 assert!(rx.try_recv().is_err());
245
246 event_handler.handle_event(1, true);
248 assert_eq!(rx.next().await, Some(true));
249
250 event_handler.handle_event(0, true);
252 assert!(rx.try_recv().is_err());
253
254 event_handler.handle_event(1, false);
256 assert_eq!(rx.next().await, Some(false));
257
258 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 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 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 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 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 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 assert!(matches!(res, Ok(())), "agent spawn failed");
341
342 let res = rx.next().await;
344 assert_eq!(res, Some(true));
345 }
346
347 #[fuchsia::test]
350 fn test_camera_devices_watcher_timeout() {
351 let mut executor = TestExecutor::new_with_fake_time();
354 executor.set_fake_time(MonotonicInstant::from_nanos(0));
355
356 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 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 let spawn_future = agent.spawn(&service_context);
380
381 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 assert!(res.is_err(), "spawn did not hit timeout");
389 }
390
391 #[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 assert!(matches!(res, Ok(())), "spawn failed");
409 let muted = rx.next().await;
410 assert_eq!(muted, Some(true));
411 }
412}