Skip to main content

settings_input/
input_controller.rs

1// Copyright 2020 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.
4
5use 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
33// The MAX_INPUT_DEVICES, in conjunction with the FIDL API's constraint of 128-byte names, ensures
34// that we don't exceed the 64 KiB default transport limit for the API (with some padding). It also
35// lessens the chance that we OOM while loading the device list from disk to memory.
36pub(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        // Convert the old states into an input device.
94        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    /// Persistent storage.
164    store: Rc<DeviceStorage>,
165
166    /// Local tracking of the input device states.
167    input_device_state: InputState,
168
169    /// Configuration for this device.
170    input_device_config: InputConfiguration,
171    publisher: Option<Publisher>,
172    setting_value_publisher: SettingValuePublisher<InputInfo>,
173    external_publisher: ExternalEventPublisher,
174    /// Tracks whether the last attempt to push camera software mute state failed,
175    /// ensuring retries still push state to all cameras even if `CameraWatcherAgent`
176    /// updated `input_device_state` in the interim.
177    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    /// Alternate constructor that allows specifying a configuration.
213    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    // Whether the configuration for this device contains a specific |device_type|.
236    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    // Wrapper around client.read() that fills in the config
324    // as the default value if the read value is empty. It may be empty
325    // after a migration from a previous InputInfoSources version
326    // or on pave.
327    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    /// Restores the input state.
336    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                    // Camera setup failure should not prevent start of service. This also allows
345                    // clients to see that the camera may not be usable.
346                    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    /// Sets the hardware mic/cam state from the muted states in `media_buttons`.
384    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            // Fetch current state.
400            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                // Unset available and set muted.
414                hw_state &= !DeviceState::AVAILABLE;
415                hw_state |= DeviceState::MUTED;
416            } else {
417                // Set available and unset muted.
418                hw_state |= DeviceState::AVAILABLE;
419                hw_state &= !DeviceState::MUTED;
420            }
421
422            // Set the updated state.
423            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    /// Sets state for the given input devices.
446    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        // Firstly, do a validation pass to make sure the input_devices contain only valid devices,
456        // and any newly added devices are not going to exceed our maximum device limit.
457        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        // Abort if applying these new devices would exceed the limit.
477        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        // Update the function-scoped storage cache with the requested changes. This will allow us
488        // to determine if any camera state has changed, necessary for the logic which follows.
489        for input_device in &input_devices {
490            input_info.input_device_state.insert_device(input_device.clone(), source);
491        }
492
493        // If the device has a camera, it should successfully get the sw state, and
494        // push the state if it has changed. If the device does not have a camera,
495        // it should be None both here and above, and thus not detect a change.
496        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        // All prerequisites are done, and camera changes are committed to the hardware. We can now
504        // finalize the update by updating our cache, and queue the write to storage.
505        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    /// Pulls the software state of the camera from the given `input_state`.
518    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    /// Pulls the current software state of the camera from the device state.
533    fn get_cam_sw_state(&self) -> Result<DeviceState, InputError> {
534        Self::get_cam_sw_state_from(&self.input_device_state)
535    }
536
537    /// Set the camera state into an error condition.
538    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    /// Forwards the given software state to the camera3 api. Will first establish
549    /// a connection to the camera3.DeviceWatcher api. This function should only be called
550    /// when there is a camera included in the config. The config is used to populate the
551    /// stored input_info, so the input_info's input_device_state can be checked whether its
552    /// device_types contains Camera prior to calling this function.
553    ///
554    /// Multiple cameras are not yet supported individually, and individual camera mute/unmute is
555    /// not exposed, so the software mute state is applied across all reported camera devices.
556    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        // Start up a connection to the camera device watcher and connect to the
561        // camera proxies using the ids that are returned. The connections will drop out
562        // of scope after the mute state is sent.
563        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        // Restore should pass.
729        let result = controller.restore().await;
730        assert!(result.is_ok());
731
732        // But the camera state should show an error.
733        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    // This tests the bug documented in https://fxbug.dev/521503847. This test validates that
866    // the in-memory cached state of the Settings service doesn't commit changes prior to
867    // updating the hardware state. If that were to happen, the camera hardware could get "stuck"
868    // in its current state, while further updates are rejected due to the internal cache believing
869    // that the update is already applied.
870    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        // Mock the camera service, rejecting the first call (count == 1) to simulate a failure.
882        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        // Initialize the mocked "persistent" storage.
906        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, // AVAILABLE
925                    }],
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        // Verify initial state is AVAILABLE (not muted).
938        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        // Create a request to MUTE the camera. The hardware API call will fail on this request,
945        // due to the previously configured mock camera service.
946        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        // Attempt to mute again. If the bug this test tracks is present, this will skip
961        // push_cam_sw_state and return Ok, because the service thinks the state is already MUTED
962        // due to the in-memory state being updated despite the hardware update failing.
963        let res2 =
964            controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
965
966        // If the bug is fixed, we expect that the camera service will have been called again, the
967        // result was okay and the mute state was properly updated.
968        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 // AVAILABLE
1065        };
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        // Fail camera 1 on attempt 1 (the first set_input_states call after restore()).
1117        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        // Even though camera 1 failed, camera 2 must still have been muted.
1131        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        // Both cameras start muted. Fail camera 1 on attempt 1 (the first unmute attempt).
1139        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        // First unmute attempt fails on camera 1 while camera 2 unmutes.
1152        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        // Simulate CameraWatcherAgent reporting that the aggregate mute state is now unmuted
1160        // (`false`) because camera 2 unmuted.
1161        let watcher_res = controller.handle_camera_event(false).await;
1162        assert!(watcher_res.is_ok());
1163
1164        // Retrying set_input_states(AVAILABLE) must still push SetSoftwareMuteState(false) to
1165        // camera 1 even though handle_camera_event already updated the cached state to AVAILABLE.
1166        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}