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 crate::handler::base::Request;
5use crate::service_context::{ExternalServiceProxy, ServiceContext};
6use crate::{call, call_async};
7use anyhow::{format_err, Error};
8use fidl::endpoints::{create_proxy, create_request_stream};
9use fidl_fuchsia_camera3::{
10 DeviceMarker, DeviceProxy as Camera3DeviceProxy, DeviceWatcherMarker,
11 DeviceWatcherProxy as Camera3DeviceWatcherProxy, WatchDevicesEvent,
12};
13use fidl_fuchsia_ui_input::MediaButtonsEvent;
14use fidl_fuchsia_ui_policy::{
15 DeviceListenerRegistryMarker, MediaButtonsListenerMarker, MediaButtonsListenerRequest,
16};
17use fuchsia_async::{self as fasync, DurationExt};
18use futures::future::Fuse;
19use futures::{FutureExt, StreamExt};
20use std::rc::Rc;
21use zx::MonotonicDuration;
2223/// The amount of time in milliseconds to wait for a camera device to be detected.
24pub const CAMERA_WATCHER_TIMEOUT: i64 = 30_000;
2526/// Builder to simplify construction of fidl_fuchsia_ui_input::MediaButtonsEvent.
27/// # Example usage:
28/// ```
29/// MediaButtonsEventBuilder::new().set_mic_mute(true).build();
30/// ```
31#[cfg(test)]
32pub(crate) struct MediaButtonsEventBuilder {
33 mic_mute: bool,
34 camera_disable: bool,
35}
3637#[cfg(test)]
38impl MediaButtonsEventBuilder {
39pub(crate) fn new() -> Self {
40// Create with defaults.
41Self { mic_mute: false, camera_disable: false }
42 }
4344pub(crate) fn build(self) -> MediaButtonsEvent {
45 MediaButtonsEvent {
46 mic_mute: Some(self.mic_mute),
47 pause: Some(false),
48 camera_disable: Some(self.camera_disable),
49 ..Default::default()
50 }
51 }
5253pub(crate) fn set_mic_mute(mut self, mic_mute: bool) -> Self {
54self.mic_mute = mic_mute;
55self
56}
5758pub(crate) fn set_camera_disable(mut self, camera_disable: bool) -> Self {
59self.camera_disable = camera_disable;
60self
61}
62}
6364/// Setting service internal representation of hw media buttons. Used to send
65/// OnButton events in the service.
66#[derive(PartialEq, Eq, Copy, Clone, Debug)]
67pub struct MediaButtons {
68pub mic_mute: Option<bool>,
69pub camera_disable: Option<bool>,
70}
7172impl MediaButtons {
73fn new() -> Self {
74Self { mic_mute: None, camera_disable: None }
75 }
7677pub(crate) fn set_mic_mute(&mut self, mic_mute: Option<bool>) {
78self.mic_mute = mic_mute;
79 }
8081pub(crate) fn set_camera_disable(&mut self, camera_disable: Option<bool>) {
82self.camera_disable = camera_disable;
83 }
84}
8586impl From<MediaButtonsEvent> for MediaButtons {
87fn from(event: MediaButtonsEvent) -> Self {
88let mut buttons = MediaButtons::new();
8990if let Some(mic_mute) = event.mic_mute {
91 buttons.set_mic_mute(Some(mic_mute));
92 }
93if let Some(camera_disable) = event.camera_disable {
94 buttons.set_camera_disable(Some(camera_disable));
95 }
9697 buttons
98 }
99}
100101impl From<MediaButtons> for Request {
102fn from(event: MediaButtons) -> Self {
103 Request::OnButton(event)
104 }
105}
106107/// Method for listening to media button changes. Changes will be reported back
108/// on the supplied sender.
109pub(crate) async fn monitor_media_buttons(
110 service_context_handle: Rc<ServiceContext>,
111 sender: futures::channel::mpsc::UnboundedSender<MediaButtonsEvent>,
112) -> Result<(), Error> {
113let presenter_service =
114 service_context_handle.connect::<DeviceListenerRegistryMarker>().await?;
115let (client_end, mut stream) = create_request_stream::<MediaButtonsListenerMarker>();
116117// TODO(https://fxbug.dev/42058092) This independent spawn is necessary! For some reason removing this or
118 // merging it with the spawn below causes devices to lock up on input button events. Figure out
119 // whether this can be removed or left as-is as part of the linked bug.
120fasync::Task::local(async move {
121if let Err(error) = call_async!(presenter_service => register_listener(client_end)).await {
122log::error!(
123"Registering media button listener with presenter service failed {:?}",
124 error
125 );
126 }
127 })
128 .detach();
129130 fasync::Task::local(async move {
131while let Some(Ok(media_request)) = stream.next().await {
132// Support future expansion of FIDL
133#[allow(clippy::single_match)]
134 #[allow(unreachable_patterns)]
135match media_request {
136 MediaButtonsListenerRequest::OnEvent { event, responder } => {
137 sender
138 .unbounded_send(event)
139 .expect("Media buttons sender failed to send event");
140// Acknowledge the event.
141responder
142 .send()
143 .unwrap_or_else(|_| log::error!("Failed to ack media buttons event"));
144 }
145_ => {}
146 }
147 }
148 })
149 .detach();
150151Ok(())
152}
153154/// Connects to the fuchsia.camera3.DeviceWatcher api.
155async fn connect_to_camera_watcher(
156 service_context_handle: Rc<ServiceContext>,
157) -> Result<ExternalServiceProxy<Camera3DeviceWatcherProxy>, Error> {
158 service_context_handle.connect::<DeviceWatcherMarker>().await
159}
160161/// Retrieves the id of a camera device given the camera device watcher proxy.
162async fn get_camera_id(
163 camera_watcher_proxy: &ExternalServiceProxy<Camera3DeviceWatcherProxy>,
164) -> Result<u64, Error> {
165// Get a list of id structs containing existing, new, and removed ids.
166167 // Sets a timer and watches for changes from the camera api. If the first response is empty,
168 // continue to watch for an update to the devices. If we receive a nonempty response,
169 // we extract the id and return. If the timeout is reached, then it is assumed to be an error.
170let timer =
171 fasync::Timer::new(MonotonicDuration::from_millis(CAMERA_WATCHER_TIMEOUT).after_now())
172 .fuse();
173let camera_ids = call_async!(camera_watcher_proxy => watch_devices()).fuse();
174175// Used to add the second watch call if the first comes back with empty devices.
176let unfulfilled_future = Fuse::terminated();
177178futures::pin_mut!(timer, camera_ids, unfulfilled_future);
179loop {
180futures::select! {
181 ids_result = camera_ids => {
182let ids = ids_result?;
183if ids.is_empty() {
184// The camera list might not be initialized yet, make another watch call and
185 // keep waiting.
186let next_camera_ids = call_async!(camera_watcher_proxy => watch_devices()).fuse();
187 unfulfilled_future.set(next_camera_ids);
188 } else {
189// Nonempty response, extract id.
190return extract_cam_id(ids);
191 }
192 }
193 ids_result_second = unfulfilled_future => {
194let ids = ids_result_second?;
195return extract_cam_id(ids);
196 }
197_ = timer => {
198return Err(format_err!("Could not find a camera"));
199 }
200 }
201 }
202}
203204/// Extract the camera id from the list of ids. Assumes there is only one camera.
205fn extract_cam_id(ids: Vec<WatchDevicesEvent>) -> Result<u64, Error> {
206let first_cam = ids.first();
207if let Some(WatchDevicesEvent::Existing(id)) | Some(WatchDevicesEvent::Added(id)) = first_cam {
208Ok(*id)
209 } else {
210Err(format_err!("Could not find a camera"))
211 }
212}
213214/// Establishes a connection to the fuchsia.camera3.Device api by watching
215/// the camera id and using it to connect to the device.
216pub(crate) async fn connect_to_camera(
217 service_context_handle: Rc<ServiceContext>,
218) -> Result<Camera3DeviceProxy, Error> {
219// Connect to the camera device watcher to get camera ids. This will
220 // be used to connect to the camera.
221let camera_watcher_proxy = connect_to_camera_watcher(service_context_handle).await?;
222let camera_id = get_camera_id(&camera_watcher_proxy).await?;
223224// Connect to the camera device with the found id.
225let (camera_proxy, device_server) = create_proxy::<DeviceMarker>();
226if call!(camera_watcher_proxy => connect_to_device(camera_id, device_server)).is_err() {
227return Err(format_err!("Could not connect to fuchsia.camera3.DeviceWatcher device"));
228 }
229Ok(camera_proxy)
230}