1use crate::input_device_configuration::InputConfiguration;
6use crate::input_fidl_handler::Publisher;
7use crate::types::{
8 DeviceState, DeviceStateSource, InputDevice, InputDeviceType, InputInfo, InputInfoSources,
9 InputState, Microphone,
10};
11use anyhow::{Context, Error};
12use fuchsia_async as fasync;
13use futures::StreamExt;
14use futures::channel::mpsc::UnboundedReceiver;
15use futures::channel::oneshot::Sender;
16use serde::{Deserialize, Serialize};
17use settings_camera::connect_to_cameras;
18use settings_common::config::default_settings::DefaultSetting;
19use settings_common::inspect::event::{
20 ExternalEventPublisher, ResponseType, SettingValuePublisher,
21};
22use settings_common::service_context::ServiceContext;
23use settings_media_buttons::{Event, MediaButtons};
24use settings_storage::UpdateState;
25use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
26use settings_storage::storage_factory::{NoneT, StorageAccess, StorageFactory};
27use std::borrow::Cow;
28use std::rc::Rc;
29
30pub(crate) const DEFAULT_CAMERA_NAME: &str = "camera";
31pub(crate) const DEFAULT_MIC_NAME: &str = "microphone";
32
33pub(crate) const MAX_INPUT_DEVICES: usize = fidl_fuchsia_settings::MAX_INPUT_DEVICES as usize;
37
38type UpdateInputResult = Result<Option<InputInfo>, InputError>;
39fn check_publish(
40 result: UpdateInputResult,
41 publish: impl Fn(InputInfo),
42) -> Result<Option<()>, InputError> {
43 result.map(|info| info.map(publish))
44}
45
46#[derive(thiserror::Error, Debug)]
47pub(crate) enum InputError {
48 #[error("Failed to initialize controller: {0:?}")]
49 InitFailure(Error),
50 #[error("Unsupported device type: {0:?}")]
51 Unsupported(InputDeviceType),
52 #[error("External failure for Input dependency: {0:?} request:{1:?} error:{2}")]
53 ExternalFailure(Cow<'static, str>, Cow<'static, str>, Cow<'static, str>),
54 #[error("Write failed for Input: {0:?}")]
55 WriteFailure(Error),
56 #[error("The maximum number of input devices has been reached.")]
57 MaximumInputDeviceLimitReached(Cow<'static, str>),
58 #[error("Unexpected error: {0}")]
59 UnexpectedError(Cow<'static, str>),
60}
61
62impl From<&InputError> for ResponseType {
63 fn from(error: &InputError) -> Self {
64 match error {
65 InputError::InitFailure(..) => ResponseType::InitFailure,
66 InputError::Unsupported(..) => ResponseType::UnsupportedError,
67 InputError::ExternalFailure(..) => ResponseType::ExternalFailure,
68 InputError::WriteFailure(..) => ResponseType::StorageFailure,
69 InputError::MaximumInputDeviceLimitReached(..) => {
70 ResponseType::MaximumInputDevicesReached
71 }
72 InputError::UnexpectedError(..) => ResponseType::UnexpectedError,
73 }
74 }
75}
76
77impl DeviceStorageCompatible for InputInfoSources {
78 type Loader = NoneT;
79 const KEY: &'static str = "input_info";
80
81 fn try_deserialize_from(value: &str) -> Result<Self, Error> {
82 Self::extract(value).or_else(|e| {
83 log::info!("Failed to deserialize InputInfoSources. Falling back to V2: {e:?}");
84 InputInfoSourcesV2::try_deserialize_from(value).map(Self::from)
85 })
86 }
87}
88
89impl From<InputInfoSourcesV2> for InputInfoSources {
90 fn from(v2: InputInfoSourcesV2) -> Self {
91 let mut input_state = v2.input_device_state;
92
93 input_state.set_source_state(
95 InputDeviceType::MICROPHONE,
96 DEFAULT_MIC_NAME.to_string(),
97 DeviceStateSource::HARDWARE,
98 if v2.hw_microphone.muted { DeviceState::MUTED } else { DeviceState::AVAILABLE },
99 );
100 input_state.set_source_state(
101 InputDeviceType::MICROPHONE,
102 DEFAULT_MIC_NAME.to_string(),
103 DeviceStateSource::SOFTWARE,
104 if v2.sw_microphone.muted { DeviceState::MUTED } else { DeviceState::AVAILABLE },
105 );
106
107 InputInfoSources { input_device_state: input_state }
108 }
109}
110
111impl From<InputInfoSources> for InputInfo {
112 fn from(info: InputInfoSources) -> InputInfo {
113 InputInfo { input_device_state: info.input_device_state }
114 }
115}
116
117#[derive(PartialEq, Default, Debug, Clone, Serialize, Deserialize)]
118pub struct InputInfoSourcesV2 {
119 hw_microphone: Microphone,
120 sw_microphone: Microphone,
121 input_device_state: InputState,
122}
123
124impl DeviceStorageCompatible for InputInfoSourcesV2 {
125 type Loader = NoneT;
126 const KEY: &'static str = "input_info_sources_v2";
127
128 fn try_deserialize_from(value: &str) -> Result<Self, Error> {
129 Self::extract(value).or_else(|e| {
130 log::info!("Failed to deserialize InputInfoSourcesV2. Falling back to V1: {e:?}");
131 InputInfoSourcesV1::try_deserialize_from(value).map(Self::from)
132 })
133 }
134}
135
136impl From<InputInfoSourcesV1> for InputInfoSourcesV2 {
137 fn from(v1: InputInfoSourcesV1) -> Self {
138 InputInfoSourcesV2 {
139 hw_microphone: v1.hw_microphone,
140 sw_microphone: v1.sw_microphone,
141 input_device_state: InputState::new(),
142 }
143 }
144}
145
146#[derive(PartialEq, Default, Debug, Clone, Copy, Serialize, Deserialize)]
147pub struct InputInfoSourcesV1 {
148 pub hw_microphone: Microphone,
149 pub sw_microphone: Microphone,
150}
151
152impl DeviceStorageCompatible for InputInfoSourcesV1 {
153 type Loader = NoneT;
154 const KEY: &'static str = "input_info_sources_v1";
155}
156
157pub(crate) enum Request {
158 Set(Vec<InputDevice>, Sender<Result<(), InputError>>),
159}
160
161pub struct InputController {
162 service_context: Rc<ServiceContext>,
163 store: Rc<DeviceStorage>,
165
166 input_device_state: InputState,
168
169 input_device_config: InputConfiguration,
171 publisher: Option<Publisher>,
172 setting_value_publisher: SettingValuePublisher<InputInfo>,
173 external_publisher: ExternalEventPublisher,
174 cam_sw_state_dirty: bool,
178}
179
180impl StorageAccess for InputController {
181 type Storage = DeviceStorage;
182 type Data = InputInfoSources;
183 const STORAGE_KEY: &'static str = InputInfoSources::KEY;
184}
185
186impl InputController {
187 pub(super) async fn new<F>(
188 service_context: Rc<ServiceContext>,
189 default_setting: &mut DefaultSetting<InputConfiguration, &'static str>,
190 storage_factory: Rc<F>,
191 setting_value_publisher: SettingValuePublisher<InputInfo>,
192 external_publisher: ExternalEventPublisher,
193 ) -> Result<Self, InputError>
194 where
195 F: StorageFactory<Storage = DeviceStorage>,
196 {
197 let input_device_config = default_setting
198 .load_default_value()
199 .context("Unable to load input device config")
200 .map_err(InputError::InitFailure)?
201 .expect("Input requires a configuration");
202 Ok(InputController::create_with_config(
203 service_context,
204 input_device_config,
205 &*storage_factory,
206 setting_value_publisher,
207 external_publisher,
208 )
209 .await)
210 }
211
212 pub(crate) async fn create_with_config<F>(
214 service_context: Rc<ServiceContext>,
215 input_device_config: InputConfiguration,
216 storage_factory: &F,
217 setting_value_publisher: SettingValuePublisher<InputInfo>,
218 external_publisher: ExternalEventPublisher,
219 ) -> Self
220 where
221 F: StorageFactory<Storage = DeviceStorage>,
222 {
223 Self {
224 service_context,
225 store: storage_factory.get_store().await,
226 input_device_state: InputState::new(),
227 input_device_config,
228 publisher: None,
229 setting_value_publisher,
230 external_publisher,
231 cam_sw_state_dirty: false,
232 }
233 }
234
235 async fn has_input_device(&self, device_type: InputDeviceType) -> bool {
237 let input_device_config_state: InputState = self.input_device_config.clone().into();
238 input_device_config_state.device_types().contains(&device_type)
239 }
240
241 pub(super) fn register_publisher(&mut self, publisher: Publisher) {
242 self.publisher = Some(publisher);
243 }
244
245 fn publish(&self, info: InputInfo) {
246 let _ = self.setting_value_publisher.publish(&info);
247 if let Some(publisher) = self.publisher.as_ref() {
248 publisher.set(info);
249 }
250 }
251
252 pub(super) async fn handle(
253 mut self,
254 mut camera_event_rx: UnboundedReceiver<(bool, super::ResultSender)>,
255 mut media_buttons_event_rx: UnboundedReceiver<(Event, super::ResultSender)>,
256 mut request_rx: UnboundedReceiver<Request>,
257 ) -> fasync::Task<()> {
258 fasync::Task::local(async move {
259 let mut next_camera_event = camera_event_rx.next();
260 let mut next_media_buttons_event = media_buttons_event_rx.next();
261 let mut next_request = request_rx.next();
262 loop {
263 futures::select! {
264 event = next_camera_event => {
265 let Some((is_muted, response_tx)) = event else {
266 continue;
267 };
268 next_camera_event = camera_event_rx.next();
269 let res = self.handle_camera_event(is_muted).await;
270 let _ = response_tx.send(res);
271 }
272 event = next_media_buttons_event => {
273 let Some((Event::OnButton(buttons), response_tx)) = event else {
274 continue;
275 };
276 next_media_buttons_event = media_buttons_event_rx.next();
277 let res = self.handle_media_buttons_event(buttons).await;
278 let _ = response_tx.send(res);
279 }
280 request = next_request => {
281 let Some(request) = request else {
282 continue;
283 };
284 next_request = request_rx.next();
285 let Request::Set(input_devices, tx) = request;
286 let res = check_publish(
287 self.set_input_states(input_devices, DeviceStateSource::SOFTWARE).await,
288 |info| self.publish(info)).map(|_|{});
289 let _ = tx.send(res);
290 }
291 }
292 }
293 })
294 }
295
296 async fn handle_camera_event(&mut self, is_muted: bool) -> Result<Option<()>, InputError> {
297 let stored_info = self.get_stored_info().await;
298 let old_state = Self::get_cam_sw_state_from(&stored_info.input_device_state)?;
299 if old_state.has_state(DeviceState::MUTED) != is_muted {
300 check_publish(
301 self.set_sw_camera_mute(is_muted, DEFAULT_CAMERA_NAME.to_string()).await,
302 |info| self.publish(info),
303 )
304 } else {
305 Ok(None)
306 }
307 }
308
309 async fn handle_media_buttons_event(
310 &mut self,
311 mut buttons: MediaButtons,
312 ) -> Result<Option<()>, InputError> {
313 if buttons.mic_mute.is_some() && !self.has_input_device(InputDeviceType::MICROPHONE).await {
314 buttons.set_mic_mute(None);
315 }
316 if buttons.camera_disable.is_some() && !self.has_input_device(InputDeviceType::CAMERA).await
317 {
318 buttons.set_camera_disable(None);
319 }
320 check_publish(self.set_hw_media_buttons_state(buttons).await, |info| self.publish(info))
321 }
322
323 async fn get_stored_info(&self) -> InputInfo {
328 let mut input_info = InputInfo::from(self.store.get::<InputInfo>().await);
329 if input_info.input_device_state.is_empty() {
330 input_info.input_device_state = self.input_device_config.clone().into();
331 }
332 input_info
333 }
334
335 pub(super) async fn restore(&mut self) -> Result<InputInfo, InputError> {
337 let input_info = self.get_stored_info().await;
338 self.input_device_state = input_info.input_device_state.clone();
339
340 if self.input_device_config.devices.iter().any(|d| d.device_type == InputDeviceType::CAMERA)
341 {
342 match self.get_cam_sw_state() {
343 Ok(state) => {
344 if let Err(e) = self.push_cam_sw_state(state).await {
347 log::error!("Unable to restore camera state: {e:?}");
348 self.set_cam_err_state(state);
349 }
350 }
351 Err(e) => {
352 log::error!("Unable to load cam sw state: {e:?}");
353 self.set_cam_err_state(DeviceState::ERROR);
354 }
355 }
356 }
357 Ok(input_info)
358 }
359
360 async fn set_sw_camera_mute(&mut self, disabled: bool, name: String) -> UpdateInputResult {
361 let mut input_info = self.get_stored_info().await;
362 input_info.input_device_state.set_source_state(
363 InputDeviceType::CAMERA,
364 name.clone(),
365 DeviceStateSource::SOFTWARE,
366 if disabled { DeviceState::MUTED } else { DeviceState::AVAILABLE },
367 );
368
369 self.input_device_state.set_source_state(
370 InputDeviceType::CAMERA,
371 name.clone(),
372 DeviceStateSource::SOFTWARE,
373 if disabled { DeviceState::MUTED } else { DeviceState::AVAILABLE },
374 );
375 self.store
376 .write(&input_info)
377 .await
378 .map(|state| (UpdateState::Updated == state).then_some(input_info))
379 .context("writing sw camera info")
380 .map_err(InputError::WriteFailure)
381 }
382
383 async fn set_hw_media_buttons_state(
385 &mut self,
386 media_buttons: MediaButtons,
387 ) -> UpdateInputResult {
388 let mut states_to_process = Vec::new();
389 if let Some(mic_mute) = media_buttons.mic_mute {
390 states_to_process.push((InputDeviceType::MICROPHONE, mic_mute));
391 }
392 if let Some(camera_disable) = media_buttons.camera_disable {
393 states_to_process.push((InputDeviceType::CAMERA, camera_disable));
394 }
395
396 let mut input_info = self.get_stored_info().await;
397
398 for (device_type, muted) in states_to_process.into_iter() {
399 let hw_state_res = input_info.input_device_state.get_source_state(
401 device_type,
402 device_type.to_string(),
403 DeviceStateSource::HARDWARE,
404 );
405
406 let mut hw_state = hw_state_res.map_err(|err| {
407 InputError::UnexpectedError(
408 format!("Could not fetch current hw mute state: {err:?}").into(),
409 )
410 })?;
411
412 if muted {
413 hw_state &= !DeviceState::AVAILABLE;
415 hw_state |= DeviceState::MUTED;
416 } else {
417 hw_state |= DeviceState::AVAILABLE;
419 hw_state &= !DeviceState::MUTED;
420 }
421
422 input_info.input_device_state.set_source_state(
424 device_type,
425 device_type.to_string(),
426 DeviceStateSource::HARDWARE,
427 hw_state,
428 );
429 self.input_device_state.set_source_state(
430 device_type,
431 device_type.to_string(),
432 DeviceStateSource::HARDWARE,
433 hw_state,
434 );
435 }
436
437 self.store
438 .write(&input_info)
439 .await
440 .map(|state| (UpdateState::Updated == state).then_some(input_info))
441 .context("writing hw media buttons")
442 .map_err(InputError::WriteFailure)
443 }
444
445 async fn set_input_states(
447 &mut self,
448 input_devices: Vec<InputDevice>,
449 source: DeviceStateSource,
450 ) -> UpdateInputResult {
451 let mut input_info = self.get_stored_info().await;
452 let device_types = input_info.input_device_state.device_types();
453 let cam_state = self.get_cam_sw_state().ok();
454
455 let mut new_devices = Vec::new();
458 for input_device in input_devices.iter() {
459 if !device_types.contains(&input_device.device_type) {
460 return Err(InputError::Unsupported(input_device.device_type));
461 }
462
463 let already_exists = input_info
464 .input_device_state
465 .contains_device(input_device.device_type, &input_device.name);
466
467 let already_counted = new_devices
468 .iter()
469 .any(|(dt, name)| *dt == input_device.device_type && *name == &input_device.name);
470
471 if !already_exists && !already_counted {
472 new_devices.push((input_device.device_type, &input_device.name));
473 }
474 }
475
476 if input_info.input_device_state.total_devices() + new_devices.len() > MAX_INPUT_DEVICES {
478 log::error!(
479 "Maximum number of supported input devices ({MAX_INPUT_DEVICES}) has been reached."
480 );
481 return Err(InputError::MaximumInputDeviceLimitReached(
482 format!("Maximum limit of {MAX_INPUT_DEVICES} input devices has been reached.")
483 .into(),
484 ));
485 }
486
487 for input_device in &input_devices {
490 input_info.input_device_state.insert_device(input_device.clone(), source);
491 }
492
493 let modified_cam_state = Self::get_cam_sw_state_from(&input_info.input_device_state).ok();
497 if (cam_state != modified_cam_state || self.cam_sw_state_dirty)
498 && let Some(state) = modified_cam_state
499 {
500 self.push_cam_sw_state(state).await?;
501 }
502
503 for input_device in input_devices {
506 self.input_device_state.insert_device(input_device, source);
507 }
508
509 self.store
510 .write(&input_info)
511 .await
512 .map(|state| (UpdateState::Updated == state).then_some(input_info))
513 .context("writing input states")
514 .map_err(InputError::WriteFailure)
515 }
516
517 fn get_cam_sw_state_from(input_state: &InputState) -> Result<DeviceState, InputError> {
519 input_state
520 .get_source_state(
521 InputDeviceType::CAMERA,
522 DEFAULT_CAMERA_NAME.to_string(),
523 DeviceStateSource::SOFTWARE,
524 )
525 .map_err(|e| {
526 InputError::UnexpectedError(
527 format!("Could not find camera software state: {e:?}").into(),
528 )
529 })
530 }
531
532 fn get_cam_sw_state(&self) -> Result<DeviceState, InputError> {
534 Self::get_cam_sw_state_from(&self.input_device_state)
535 }
536
537 fn set_cam_err_state(&mut self, mut state: DeviceState) {
539 state.set(DeviceState::ERROR, true);
540 self.input_device_state.set_source_state(
541 InputDeviceType::CAMERA,
542 DEFAULT_CAMERA_NAME.to_string(),
543 DeviceStateSource::SOFTWARE,
544 state,
545 )
546 }
547
548 async fn push_cam_sw_state(&mut self, cam_state: DeviceState) -> Result<(), InputError> {
557 let is_muted = cam_state.has_state(DeviceState::MUTED);
558 self.cam_sw_state_dirty = true;
559
560 let camera_proxies =
564 connect_to_cameras(&self.service_context, self.external_publisher.clone())
565 .await
566 .map_err(|e| {
567 InputError::UnexpectedError(
568 format!("Could not connect to camera device: {e:?}").into(),
569 )
570 })?;
571
572 let results = futures::future::join_all(
573 camera_proxies.iter().map(|proxy| proxy.set_software_mute_state(is_muted)),
574 )
575 .await;
576
577 let mut mute_error = None;
578 for result in results {
579 if let Err(e) = result {
580 log::warn!("Failed to set software mute state on camera: {e:?}");
581 mute_error = Some(InputError::ExternalFailure(
582 "fuchsia.camera3.Device".into(),
583 "SetSoftwareMuteState".into(),
584 format!("{e:?}").into(),
585 ));
586 }
587 }
588 if let Some(err) = mute_error {
589 return Err(err);
590 }
591 self.cam_sw_state_dirty = false;
592 Ok(())
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use super::*;
599 use crate::input_device_configuration::{InputDeviceConfiguration, SourceState};
600 use fuchsia_async as fasync;
601 use fuchsia_inspect::component;
602 use futures::channel::mpsc;
603 use settings_common::inspect::config_logger::InspectConfigLogger;
604 use settings_common::service_context::ServiceContext;
605 use settings_test_common::storage::InMemoryStorageFactory;
606
607 #[fuchsia::test]
608 fn test_input_migration_v1_to_current() {
609 const MUTED_MIC: Microphone = Microphone { muted: true };
610 let v1 = InputInfoSourcesV1 { sw_microphone: MUTED_MIC, ..Default::default() };
611
612 let serialized_v1 = v1.serialize_to();
613 let current = InputInfoSources::try_deserialize_from(&serialized_v1)
614 .expect("deserialization should succeed");
615 let mut expected_input_state = InputState::new();
616 expected_input_state.set_source_state(
617 InputDeviceType::MICROPHONE,
618 DEFAULT_MIC_NAME.to_string(),
619 DeviceStateSource::SOFTWARE,
620 DeviceState::MUTED,
621 );
622 expected_input_state.set_source_state(
623 InputDeviceType::MICROPHONE,
624 DEFAULT_MIC_NAME.to_string(),
625 DeviceStateSource::HARDWARE,
626 DeviceState::AVAILABLE,
627 );
628 assert_eq!(current.input_device_state, expected_input_state);
629 }
630
631 #[fuchsia::test]
632 fn test_input_migration_v1_to_v2() {
633 const MUTED_MIC: Microphone = Microphone { muted: true };
634 let v1 = InputInfoSourcesV1 { sw_microphone: MUTED_MIC, ..Default::default() };
635
636 let serialized_v1 = v1.serialize_to();
637 let v2 = InputInfoSourcesV2::try_deserialize_from(&serialized_v1)
638 .expect("deserialization should succeed");
639
640 assert_eq!(v2.hw_microphone, Microphone { muted: false });
641 assert_eq!(v2.sw_microphone, MUTED_MIC);
642 assert_eq!(v2.input_device_state, InputState::new());
643 }
644
645 #[fuchsia::test]
646 fn test_input_migration_v2_to_current() {
647 const DEFAULT_CAMERA_NAME: &str = "camera";
648 const MUTED_MIC: Microphone = Microphone { muted: true };
649 let mut v2 = InputInfoSourcesV2::default();
650 v2.input_device_state.set_source_state(
651 InputDeviceType::CAMERA,
652 DEFAULT_CAMERA_NAME.to_string(),
653 DeviceStateSource::SOFTWARE,
654 DeviceState::AVAILABLE,
655 );
656 v2.input_device_state.set_source_state(
657 InputDeviceType::CAMERA,
658 DEFAULT_CAMERA_NAME.to_string(),
659 DeviceStateSource::HARDWARE,
660 DeviceState::MUTED,
661 );
662 v2.sw_microphone = MUTED_MIC;
663
664 let serialized_v2 = v2.serialize_to();
665 let current = InputInfoSources::try_deserialize_from(&serialized_v2)
666 .expect("deserialization should succeed");
667 let mut expected_input_state = InputState::new();
668
669 expected_input_state.set_source_state(
670 InputDeviceType::MICROPHONE,
671 DEFAULT_MIC_NAME.to_string(),
672 DeviceStateSource::SOFTWARE,
673 DeviceState::MUTED,
674 );
675 expected_input_state.set_source_state(
676 InputDeviceType::MICROPHONE,
677 DEFAULT_MIC_NAME.to_string(),
678 DeviceStateSource::HARDWARE,
679 DeviceState::AVAILABLE,
680 );
681 expected_input_state.set_source_state(
682 InputDeviceType::CAMERA,
683 DEFAULT_CAMERA_NAME.to_string(),
684 DeviceStateSource::SOFTWARE,
685 DeviceState::AVAILABLE,
686 );
687 expected_input_state.set_source_state(
688 InputDeviceType::CAMERA,
689 DEFAULT_CAMERA_NAME.to_string(),
690 DeviceStateSource::HARDWARE,
691 DeviceState::MUTED,
692 );
693
694 assert_eq!(current.input_device_state, expected_input_state);
695 }
696
697 #[fuchsia::test]
698 async fn test_camera_error_on_restore() {
699 let (event_tx, _event_rx) = mpsc::unbounded();
700 let external_publisher = ExternalEventPublisher::new(event_tx);
701 let storage_factory = InMemoryStorageFactory::new();
702 storage_factory
703 .initialize::<InputController>()
704 .await
705 .expect("controller should have impls");
706 let (value_tx, _value_rx) = mpsc::unbounded();
707 let setting_value_publisher = SettingValuePublisher::new(value_tx);
708 let mut controller: InputController =
709 InputController::create_with_config::<InMemoryStorageFactory>(
710 Rc::new(ServiceContext::new(None)),
711 InputConfiguration {
712 devices: vec![InputDeviceConfiguration {
713 device_name: DEFAULT_CAMERA_NAME.to_string(),
714 device_type: InputDeviceType::CAMERA,
715 source_states: vec![SourceState {
716 source: DeviceStateSource::SOFTWARE,
717 state: 0,
718 }],
719 mutable_toggle_state: 0,
720 }],
721 },
722 &storage_factory,
723 setting_value_publisher,
724 external_publisher,
725 )
726 .await;
727
728 let result = controller.restore().await;
730 assert!(result.is_ok());
731
732 let camera_state = controller
734 .input_device_state
735 .get_state(InputDeviceType::CAMERA, DEFAULT_CAMERA_NAME.to_string())
736 .unwrap();
737 assert!(camera_state.has_state(DeviceState::ERROR));
738 }
739
740 #[fuchsia::test(allow_stalls = false)]
741 async fn test_controller_creation_with_default_config() {
742 let config_logger = InspectConfigLogger::new(component::inspector().root());
743 let mut default_setting = DefaultSetting::new(
744 Some(InputConfiguration::default()),
745 "/config/data/input_device_config.json",
746 Rc::new(std::sync::Mutex::new(config_logger)),
747 );
748
749 let (event_tx, _) = mpsc::unbounded();
750 let external_publisher = ExternalEventPublisher::new(event_tx);
751
752 let storage_factory = InMemoryStorageFactory::new();
753 storage_factory
754 .initialize::<InputController>()
755 .await
756 .expect("controller should have impls");
757 let (value_tx, _value_rx) = mpsc::unbounded();
758 let setting_value_publisher = SettingValuePublisher::new(value_tx);
759 let _controller = InputController::new(
760 Rc::new(ServiceContext::new(None)),
761 &mut default_setting,
762 Rc::new(storage_factory),
763 setting_value_publisher,
764 external_publisher,
765 )
766 .await
767 .expect("Should have controller");
768 }
769
770 #[fuchsia::test]
771 async fn test_set_input_states_limit() {
772 let (event_tx, _event_rx) = mpsc::unbounded();
773 let external_publisher = ExternalEventPublisher::new(event_tx);
774 let storage_factory = InMemoryStorageFactory::new();
775 storage_factory
776 .initialize::<InputController>()
777 .await
778 .expect("controller should have impls");
779 let (value_tx, _value_rx) = mpsc::unbounded();
780 let setting_value_publisher = SettingValuePublisher::new(value_tx);
781
782 let mut device_configs = Vec::new();
783 for i in 0..MAX_INPUT_DEVICES {
784 device_configs.push(InputDeviceConfiguration {
785 device_name: format!("mic{i}"),
786 device_type: InputDeviceType::MICROPHONE,
787 source_states: vec![SourceState { source: DeviceStateSource::SOFTWARE, state: 0 }],
788 mutable_toggle_state: 0,
789 });
790 }
791
792 let mut controller: InputController =
793 InputController::create_with_config::<InMemoryStorageFactory>(
794 Rc::new(ServiceContext::new(None)),
795 InputConfiguration { devices: device_configs },
796 &storage_factory,
797 setting_value_publisher,
798 external_publisher,
799 )
800 .await;
801
802 let _ = controller.restore().await;
803
804 let overflow_dev = InputDevice {
805 name: "mic_max_exceeded".to_string(),
806 device_type: InputDeviceType::MICROPHONE,
807 source_states: [(DeviceStateSource::SOFTWARE, DeviceState::AVAILABLE)].into(),
808 state: DeviceState::AVAILABLE,
809 };
810 let res =
811 controller.set_input_states(vec![overflow_dev], DeviceStateSource::SOFTWARE).await;
812 match res {
813 Err(InputError::MaximumInputDeviceLimitReached(msg)) => {
814 assert_eq!(
815 msg,
816 format!("Maximum limit of {MAX_INPUT_DEVICES} input devices has been reached.")
817 );
818 }
819 _ => panic!("Expected MaximumInputDeviceLimitReached, got {res:?}"),
820 }
821 }
822
823 async fn handle_camera3_device(
824 mut stream: fidl_fuchsia_camera3::DeviceRequestStream,
825 camera_muted: std::sync::Arc<std::sync::atomic::AtomicBool>,
826 ) {
827 use futures::StreamExt;
828 use std::sync::atomic::Ordering;
829 while let Some(Ok(req)) = stream.next().await {
830 if let fidl_fuchsia_camera3::DeviceRequest::SetSoftwareMuteState { muted, responder } =
831 req
832 {
833 camera_muted.store(muted, Ordering::Relaxed);
834 let _ = responder.send();
835 }
836 }
837 }
838
839 async fn handle_camera3_device_watcher(
840 mut stream: fidl_fuchsia_camera3::DeviceWatcherRequestStream,
841 camera_muted: std::sync::Arc<std::sync::atomic::AtomicBool>,
842 ) {
843 use futures::StreamExt;
844 while let Some(Ok(req)) = stream.next().await {
845 #[allow(unreachable_patterns)]
846 match req {
847 fidl_fuchsia_camera3::DeviceWatcherRequest::WatchDevices { responder } => {
848 let _ = responder.send(&[fidl_fuchsia_camera3::WatchDevicesEvent::Added(1)]);
849 }
850 fidl_fuchsia_camera3::DeviceWatcherRequest::ConnectToDevice {
851 id: _,
852 request,
853 control_handle: _,
854 } => {
855 let camera_muted = camera_muted.clone();
856 fasync::Task::local(handle_camera3_device(request.into_stream(), camera_muted))
857 .detach();
858 }
859 _ => {}
860 }
861 }
862 }
863
864 #[fuchsia::test]
865 async fn test_set_input_states_retry_on_failure() {
871 use fidl::endpoints::DiscoverableProtocolMarker;
872 use std::sync::Arc;
873 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
874
875 let call_count = Arc::new(AtomicUsize::new(0));
876 let call_count_clone = call_count.clone();
877
878 let camera_muted = Arc::new(AtomicBool::new(false));
879 let camera_muted_clone = camera_muted.clone();
880
881 let service_context = Rc::new(ServiceContext::new(Some(Box::new(
883 move |service_name: &str, channel: zx::Channel| {
884 let count = call_count_clone.fetch_add(1, Ordering::Relaxed);
885 let camera_muted_clone = camera_muted_clone.clone();
886 let service_name = service_name.to_string();
887 Box::pin(async move {
888 if count == 1 {
889 return Err(anyhow::Error::msg("injected failure"));
890 }
891 if service_name != fidl_fuchsia_camera3::DeviceWatcherMarker::PROTOCOL_NAME {
892 return Err(anyhow::Error::msg("unsupported service"));
893 }
894 let stream = fidl::endpoints::ServerEnd::<
895 fidl_fuchsia_camera3::DeviceWatcherMarker,
896 >::new(channel)
897 .into_stream();
898 fasync::Task::local(handle_camera3_device_watcher(stream, camera_muted_clone))
899 .detach();
900 Ok(())
901 })
902 },
903 ))));
904
905 let storage_factory = InMemoryStorageFactory::new();
907 storage_factory
908 .initialize::<InputController>()
909 .await
910 .expect("controller should have impls");
911 let (value_tx, _value_rx) = mpsc::unbounded();
912 let setting_value_publisher = SettingValuePublisher::new(value_tx);
913 let (event_tx, _event_rx) = mpsc::unbounded();
914 let external_publisher = ExternalEventPublisher::new(event_tx);
915
916 let mut controller = InputController::create_with_config::<InMemoryStorageFactory>(
917 service_context,
918 InputConfiguration {
919 devices: vec![InputDeviceConfiguration {
920 device_name: DEFAULT_CAMERA_NAME.to_string(),
921 device_type: InputDeviceType::CAMERA,
922 source_states: vec![SourceState {
923 source: DeviceStateSource::SOFTWARE,
924 state: 0, }],
926 mutable_toggle_state: 0,
927 }],
928 },
929 &storage_factory,
930 setting_value_publisher,
931 external_publisher,
932 )
933 .await;
934
935 let _ = controller.restore().await;
936
937 let camera_state = controller
939 .input_device_state
940 .get_state(InputDeviceType::CAMERA, DEFAULT_CAMERA_NAME.to_string())
941 .unwrap();
942 assert!(!camera_state.has_state(DeviceState::MUTED));
943
944 let mute_device = InputDevice {
947 name: DEFAULT_CAMERA_NAME.to_string(),
948 device_type: InputDeviceType::CAMERA,
949 source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
950 state: DeviceState::MUTED,
951 };
952
953 let res = controller
954 .set_input_states(vec![mute_device.clone()], DeviceStateSource::SOFTWARE)
955 .await;
956 assert!(res.is_err());
957 assert_eq!(call_count.load(Ordering::Relaxed), 2);
958 assert!(!camera_muted.load(Ordering::Relaxed));
959
960 let res2 =
964 controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
965
966 assert_eq!(call_count.load(Ordering::Relaxed), 3);
969 assert!(res2.is_ok());
970 assert!(camera_muted.load(Ordering::Relaxed));
971 }
972
973 async fn setup_multi_camera_test(
974 initial_muted: bool,
975 fail_camera_1_on_attempt: Option<usize>,
976 ) -> (
977 InputController,
978 std::sync::Arc<std::sync::atomic::AtomicBool>,
979 std::sync::Arc<std::sync::atomic::AtomicBool>,
980 ) {
981 use fidl::endpoints::DiscoverableProtocolMarker;
982 use std::sync::Arc;
983 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
984
985 let camera1_muted = Arc::new(AtomicBool::new(initial_muted));
986 let camera2_muted = Arc::new(AtomicBool::new(initial_muted));
987 let camera1_clone = camera1_muted.clone();
988 let camera2_clone = camera2_muted.clone();
989 let camera1_attempts = Arc::new(AtomicUsize::new(0));
990
991 let service_context = Rc::new(ServiceContext::new(Some(Box::new(
992 move |service_name: &str, channel: zx::Channel| {
993 let camera1 = camera1_clone.clone();
994 let camera2 = camera2_clone.clone();
995 let camera1_attempts = camera1_attempts.clone();
996 let service_name = service_name.to_string();
997 Box::pin(async move {
998 if service_name != fidl_fuchsia_camera3::DeviceWatcherMarker::PROTOCOL_NAME {
999 return Err(anyhow::Error::msg("unsupported service"));
1000 }
1001 let mut stream = fidl::endpoints::ServerEnd::<
1002 fidl_fuchsia_camera3::DeviceWatcherMarker,
1003 >::new(channel)
1004 .into_stream();
1005 fasync::Task::local(async move {
1006 while let Some(Ok(req)) = stream.next().await {
1007 #[allow(unreachable_patterns)]
1008 match req {
1009 fidl_fuchsia_camera3::DeviceWatcherRequest::WatchDevices {
1010 responder,
1011 } => {
1012 let _ = responder.send(&[
1013 fidl_fuchsia_camera3::WatchDevicesEvent::Existing(1),
1014 fidl_fuchsia_camera3::WatchDevicesEvent::Added(2),
1015 ]);
1016 }
1017 fidl_fuchsia_camera3::DeviceWatcherRequest::ConnectToDevice {
1018 id,
1019 request,
1020 control_handle: _,
1021 } => {
1022 let target = match id {
1023 1 => {
1024 let attempt =
1025 camera1_attempts.fetch_add(1, Ordering::Relaxed);
1026 if fail_camera_1_on_attempt == Some(attempt) {
1027 drop(request);
1028 continue;
1029 }
1030 camera1.clone()
1031 }
1032 2 => camera2.clone(),
1033 _ => panic!("unexpected camera id {id}"),
1034 };
1035 fasync::Task::local(handle_camera3_device(
1036 request.into_stream(),
1037 target,
1038 ))
1039 .detach();
1040 }
1041 _ => {}
1042 }
1043 }
1044 })
1045 .detach();
1046 Ok(())
1047 })
1048 },
1049 ))));
1050
1051 let storage_factory = InMemoryStorageFactory::new();
1052 storage_factory
1053 .initialize::<InputController>()
1054 .await
1055 .expect("controller should have impls");
1056 let (value_tx, _value_rx) = mpsc::unbounded();
1057 let setting_value_publisher = SettingValuePublisher::new(value_tx);
1058 let (event_tx, _event_rx) = mpsc::unbounded();
1059 let external_publisher = ExternalEventPublisher::new(event_tx);
1060
1061 let initial_state = if initial_muted {
1062 DeviceState::MUTED.bits()
1063 } else {
1064 0 };
1066
1067 let mut controller = InputController::create_with_config::<InMemoryStorageFactory>(
1068 service_context,
1069 InputConfiguration {
1070 devices: vec![InputDeviceConfiguration {
1071 device_name: DEFAULT_CAMERA_NAME.to_string(),
1072 device_type: InputDeviceType::CAMERA,
1073 source_states: vec![SourceState {
1074 source: DeviceStateSource::SOFTWARE,
1075 state: initial_state,
1076 }],
1077 mutable_toggle_state: 0,
1078 }],
1079 },
1080 &storage_factory,
1081 setting_value_publisher,
1082 external_publisher,
1083 )
1084 .await;
1085
1086 let _ = controller.restore().await;
1087 (controller, camera1_muted, camera2_muted)
1088 }
1089
1090 #[fuchsia::test]
1091 async fn test_set_input_states_multiple_cameras() {
1092 use std::sync::atomic::Ordering;
1093
1094 let (mut controller, camera1_muted, camera2_muted) =
1095 setup_multi_camera_test(false, None).await;
1096 assert!(!camera1_muted.load(Ordering::Relaxed));
1097 assert!(!camera2_muted.load(Ordering::Relaxed));
1098
1099 let mute_device = InputDevice {
1100 name: DEFAULT_CAMERA_NAME.to_string(),
1101 device_type: InputDeviceType::CAMERA,
1102 source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1103 state: DeviceState::MUTED,
1104 };
1105
1106 let res = controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
1107 assert!(res.is_ok());
1108 assert!(camera1_muted.load(Ordering::Relaxed));
1109 assert!(camera2_muted.load(Ordering::Relaxed));
1110 }
1111
1112 #[fuchsia::test]
1113 async fn test_set_input_states_multiple_cameras_partial_failure() {
1114 use std::sync::atomic::Ordering;
1115
1116 let (mut controller, camera1_muted, camera2_muted) =
1118 setup_multi_camera_test(false, Some(1)).await;
1119
1120 let mute_device = InputDevice {
1121 name: DEFAULT_CAMERA_NAME.to_string(),
1122 device_type: InputDeviceType::CAMERA,
1123 source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1124 state: DeviceState::MUTED,
1125 };
1126
1127 let res = controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
1128 assert!(res.is_err());
1129 assert!(!camera1_muted.load(Ordering::Relaxed));
1130 assert!(camera2_muted.load(Ordering::Relaxed));
1132 }
1133
1134 #[fuchsia::test]
1135 async fn test_set_input_states_multiple_cameras_partial_unmute_retry() {
1136 use std::sync::atomic::Ordering;
1137
1138 let (mut controller, camera1_muted, camera2_muted) =
1140 setup_multi_camera_test(true, Some(1)).await;
1141 assert!(camera1_muted.load(Ordering::Relaxed));
1142 assert!(camera2_muted.load(Ordering::Relaxed));
1143
1144 let unmute_device = InputDevice {
1145 name: DEFAULT_CAMERA_NAME.to_string(),
1146 device_type: InputDeviceType::CAMERA,
1147 source_states: [(DeviceStateSource::SOFTWARE, DeviceState::AVAILABLE)].into(),
1148 state: DeviceState::AVAILABLE,
1149 };
1150
1151 let res = controller
1153 .set_input_states(vec![unmute_device.clone()], DeviceStateSource::SOFTWARE)
1154 .await;
1155 assert!(res.is_err());
1156 assert!(camera1_muted.load(Ordering::Relaxed));
1157 assert!(!camera2_muted.load(Ordering::Relaxed));
1158
1159 let watcher_res = controller.handle_camera_event(false).await;
1162 assert!(watcher_res.is_ok());
1163
1164 let retry_res =
1167 controller.set_input_states(vec![unmute_device], DeviceStateSource::SOFTWARE).await;
1168 assert!(retry_res.is_ok());
1169 assert!(!camera1_muted.load(Ordering::Relaxed));
1170 assert!(!camera2_muted.load(Ordering::Relaxed));
1171 }
1172}