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/// Retrieves the id of a camera device given the camera device watcher proxy.
22async fn get_camera_id(
23    camera_watcher_proxy: &ExternalServiceProxy<Camera3DeviceWatcherProxy, ExternalEventPublisher>,
24) -> Result<u64, Error> {
25    // Get a list of id structs containing existing, new, and removed ids.
26
27    // Sets a timer and watches for changes from the camera api. If the first response is empty,
28    // continue to watch for an update to the devices. If we receive a nonempty response,
29    // we extract the id and return. If the timeout is reached, then it is assumed to be an error.
30    let timer =
31        fasync::Timer::new(MonotonicDuration::from_millis(CAMERA_WATCHER_TIMEOUT).after_now())
32            .fuse();
33    let camera_ids = call_async!(camera_watcher_proxy => watch_devices()).fuse();
34
35    // Used to add the second watch call if the first comes back with empty devices.
36    let unfulfilled_future = Fuse::terminated();
37
38    futures::pin_mut!(timer, camera_ids, unfulfilled_future);
39    loop {
40        futures::select! {
41            ids_result = camera_ids => {
42                let ids = ids_result?;
43                if ids.is_empty() {
44                    // The camera list might not be initialized yet, make another watch call and
45                    // keep waiting.
46                    let next_camera_ids = call_async!(camera_watcher_proxy => watch_devices()).fuse();
47                    unfulfilled_future.set(next_camera_ids);
48                } else {
49                    // Nonempty response, extract id.
50                    return extract_cam_id(ids);
51                }
52            }
53            ids_result_second = unfulfilled_future => {
54                let ids = ids_result_second?;
55                return extract_cam_id(ids);
56            }
57            _ = timer => {
58                return Err(format_err!("Could not find a camera"));
59            }
60        }
61    }
62}
63
64/// Extract the camera id from the list of ids. Assumes there is only one camera.
65fn extract_cam_id(ids: Vec<WatchDevicesEvent>) -> Result<u64, Error> {
66    let first_cam = ids.first();
67    if let Some(WatchDevicesEvent::Existing(id)) | Some(WatchDevicesEvent::Added(id)) = first_cam {
68        Ok(*id)
69    } else {
70        Err(format_err!("Could not find a camera"))
71    }
72}
73
74/// Establishes a connection to the fuchsia.camera3.Device api by watching
75/// the camera id and using it to connect to the device.
76pub async fn connect_to_camera(
77    service_context: &ServiceContext,
78    external_publisher: ExternalEventPublisher,
79) -> Result<Camera3DeviceProxy, Error> {
80    // Connect to the camera device watcher to get camera ids. This will
81    // be used to connect to the camera.
82    let camera_watcher_proxy = service_context
83        .connect_with_publisher::<DeviceWatcherMarker, _>(external_publisher)
84        .await?;
85    let camera_id = get_camera_id(&camera_watcher_proxy).await?;
86
87    // Connect to the camera device with the found id.
88    let (camera_proxy, device_server) = create_proxy::<DeviceMarker>();
89    if call!(camera_watcher_proxy => connect_to_device(camera_id, device_server)).is_err() {
90        return Err(format_err!("Could not connect to fuchsia.camera3.DeviceWatcher device"));
91    }
92    Ok(camera_proxy)
93}