settings/agent/
camera_watcher.rs1use 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 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 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 #[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 let result = agent.spawn(&service_context).await;
91 assert!(matches!(result, Err(_)));
92 }
93
94 #[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 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 {
114 futures::select! {
115 event = next_rx1 => {
116 let Some(muted) = event else {
117 continue;
118 };
119 assert!(muted);
120 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 tx2.close_channel();
131 channel_received += 1;
132 }
133 complete => break,
134 }
135 }
136
137 assert_eq!(channel_received, 2);
138 }
139}