1use anyhow::{Error, format_err};
5use fidl::endpoints::create_proxy;
6use fidl_fuchsia_camera3::{
7 DeviceMarker, DeviceProxy as Camera3DeviceProxy, DeviceWatcherMarker,
8 DeviceWatcherProxy as Camera3DeviceWatcherProxy, WatchDevicesEvent,
9};
10use fuchsia_async::{self as fasync, DurationExt};
11use futures::FutureExt;
12use futures::future::Fuse;
13use settings_common::inspect::event::ExternalEventPublisher;
14use settings_common::service_context::{ExternalServiceProxy, ServiceContext};
15use settings_common::{call, call_async};
16use zx::MonotonicDuration;
17
18pub const CAMERA_WATCHER_TIMEOUT: i64 = 30_000;
20
21pub type CameraWatcherProxy =
23 ExternalServiceProxy<Camera3DeviceWatcherProxy, ExternalEventPublisher>;
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum CameraWatcherEvent {
28 SoftwareMuted(bool),
31 DeviceAdded,
35}
36
37async fn get_camera_ids(camera_watcher_proxy: &CameraWatcherProxy) -> Result<Vec<u64>, Error> {
39 let timer =
45 fasync::Timer::new(MonotonicDuration::from_millis(CAMERA_WATCHER_TIMEOUT).after_now())
46 .fuse();
47 let camera_ids = call_async!(camera_watcher_proxy => watch_devices()).fuse();
48
49 let unfulfilled_future = Fuse::terminated();
51
52 futures::pin_mut!(timer, camera_ids, unfulfilled_future);
53 loop {
54 futures::select! {
55 ids_result = camera_ids => {
56 let ids = ids_result?;
57 match extract_cam_ids(ids) {
58 Ok(cam_ids) => return Ok(cam_ids),
59 Err(_) => {
60 let next_camera_ids =
63 call_async!(camera_watcher_proxy => watch_devices()).fuse();
64 unfulfilled_future.set(next_camera_ids);
65 }
66 }
67 }
68 ids_result_second = unfulfilled_future => {
69 let ids = ids_result_second?;
70 return extract_cam_ids(ids);
71 }
72 _ = timer => {
73 return Err(format_err!("Could not find a camera"));
74 }
75 }
76 }
77}
78
79fn extract_cam_ids(events: Vec<WatchDevicesEvent>) -> Result<Vec<u64>, Error> {
84 let ids: Vec<u64> = events
85 .into_iter()
86 .filter_map(|event| match event {
87 WatchDevicesEvent::Existing(id) | WatchDevicesEvent::Added(id) => Some(id),
88 WatchDevicesEvent::Removed(_) => None,
89 })
90 .collect();
91 if ids.is_empty() { Err(format_err!("Could not find a camera")) } else { Ok(ids) }
92}
93
94pub async fn connect_to_cameras(
100 service_context: &ServiceContext,
101 external_publisher: ExternalEventPublisher,
102) -> Result<Vec<Camera3DeviceProxy>, Error> {
103 let (_camera_watcher_proxy, cameras) =
104 connect_to_cameras_with_watcher(service_context, external_publisher).await?;
105 Ok(cameras.into_iter().map(|(_, camera_proxy)| camera_proxy).collect())
106}
107
108pub async fn connect_to_cameras_with_watcher(
111 service_context: &ServiceContext,
112 external_publisher: ExternalEventPublisher,
113) -> Result<(CameraWatcherProxy, Vec<(u64, Camera3DeviceProxy)>), Error> {
114 let camera_watcher_proxy = service_context
117 .connect_with_publisher::<DeviceWatcherMarker, _>(external_publisher)
118 .await?;
119 let camera_ids = get_camera_ids(&camera_watcher_proxy).await?;
120
121 let mut cameras = Vec::with_capacity(camera_ids.len());
123 for camera_id in camera_ids {
124 cameras.push((camera_id, connect_to_camera(&camera_watcher_proxy, camera_id)?));
125 }
126 Ok((camera_watcher_proxy, cameras))
127}
128
129pub fn connect_to_camera(
131 camera_watcher_proxy: &CameraWatcherProxy,
132 camera_id: u64,
133) -> Result<Camera3DeviceProxy, Error> {
134 let (camera_proxy, device_server) = create_proxy::<DeviceMarker>();
135 call!(camera_watcher_proxy => connect_to_device(camera_id, device_server)).map_err(|e| {
136 format_err!("Could not connect to fuchsia.camera3.DeviceWatcher device {camera_id}: {e:?}")
137 })?;
138 Ok(camera_proxy)
139}
140
141pub async fn watch_camera_changes(
143 camera_watcher_proxy: &CameraWatcherProxy,
144) -> Result<Vec<WatchDevicesEvent>, Error> {
145 call_async!(camera_watcher_proxy => watch_devices())
146 .await
147 .map_err(|e| format_err!("Could not watch camera devices: {e:?}"))
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[fuchsia::test]
155 fn test_extract_cam_ids_single_camera() {
156 assert_eq!(extract_cam_ids(vec![WatchDevicesEvent::Added(1)]).unwrap(), vec![1]);
157 assert_eq!(extract_cam_ids(vec![WatchDevicesEvent::Existing(42)]).unwrap(), vec![42]);
158 }
159
160 #[fuchsia::test]
161 fn test_extract_cam_ids_multiple_cameras() {
162 let events = vec![
163 WatchDevicesEvent::Existing(1),
164 WatchDevicesEvent::Added(2),
165 WatchDevicesEvent::Removed(3),
166 WatchDevicesEvent::Added(4),
167 ];
168 assert_eq!(extract_cam_ids(events).unwrap(), vec![1, 2, 4]);
169 }
170
171 #[fuchsia::test]
172 fn test_extract_cam_ids_no_active_cameras_returns_err() {
173 assert!(extract_cam_ids(vec![]).is_err());
174 assert!(extract_cam_ids(vec![WatchDevicesEvent::Removed(1)]).is_err());
175 }
176}