Skip to main content

settings_camera/
lib.rs

1// Copyright 2019 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.
4use 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
18/// The amount of time in milliseconds to wait for a camera device to be detected.
19pub const CAMERA_WATCHER_TIMEOUT: i64 = 30_000;
20
21/// A proxy to `fuchsia.camera3.DeviceWatcher`.
22pub type CameraWatcherProxy =
23    ExternalServiceProxy<Camera3DeviceWatcherProxy, ExternalEventPublisher>;
24
25/// Events reported by the camera watcher agent to clients of the camera software mute state.
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum CameraWatcherEvent {
28    /// The aggregate software mute state of the watched cameras. The cameras are muted only if
29    /// all connected cameras are muted.
30    SoftwareMuted(bool),
31    /// A camera was reported as added after initial enumeration. Its software mute state starts
32    /// unmuted in the camera driver, so the stored state may need to be applied to it. The
33    /// camera's mute state is reported afterwards.
34    DeviceAdded,
35}
36
37/// Retrieves the ids of all camera devices given the camera device watcher proxy.
38async fn get_camera_ids(camera_watcher_proxy: &CameraWatcherProxy) -> Result<Vec<u64>, Error> {
39    // Get a list of id structs containing existing, new, and removed ids.
40
41    // Sets a timer and watches for changes from the camera api. If the first response is empty,
42    // continue to watch for an update to the devices. If we receive a nonempty response,
43    // we extract the ids and return. If the timeout is reached, then it is assumed to be an error.
44    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    // Used to add the second watch call if the first comes back with empty devices.
50    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                        // The camera list might not have active cameras yet; make another watch
61                        // call and keep waiting.
62                        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
79/// Extracts all present camera ids from the list of device watcher events.
80///
81/// Multiple cameras are not yet supported individually, and individual camera mute/unmute is not
82/// exposed, so all active camera ids are returned to be muted and watched together.
83fn 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
94/// Establishes connections to all `fuchsia.camera3.Device` instances by watching
95/// the camera ids and using them to connect to the devices.
96///
97/// Multiple cameras are not yet supported individually, and individual camera mute/unmute is not
98/// exposed, so all reported cameras are connected.
99pub 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
108/// Like `connect_to_cameras`, but also returns the camera device watcher connection, which can
109/// be used to watch for cameras that are added or removed later, and the id of each camera.
110pub async fn connect_to_cameras_with_watcher(
111    service_context: &ServiceContext,
112    external_publisher: ExternalEventPublisher,
113) -> Result<(CameraWatcherProxy, Vec<(u64, Camera3DeviceProxy)>), Error> {
114    // Connect to the camera device watcher to get camera ids. This will
115    // be used to connect to the cameras.
116    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    // Connect to each camera device with the found ids.
122    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
129/// Connects to the camera device with the given id.
130pub 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
141/// Waits for the next change to the set of camera devices reported by `camera_watcher_proxy`.
142pub 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}