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::{CameraWatcherEvent, 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    /// Whether the stored camera software state has been applied to the cameras. Unless
175    /// `Synced`, the next `set_input_states` pushes the camera state to all cameras even if it
176    /// did not change, since `CameraWatcherAgent` may have updated `input_device_state` in the
177    /// interim.
178    cam_sw_sync: CamSwSync,
179}
180
181/// Whether the stored camera software mute state has been applied to the cameras.
182///
183/// The camera software state is tracked in three places:
184/// - Storage holds the user's preference, which `restore()` applies on startup.
185/// - `input_device_state` holds the state reported to clients.
186/// - `cam_sw_sync` records how the two relate to the cameras.
187///
188/// Storage and `input_device_state` agree, except that `input_device_state` may additionally
189/// carry `DeviceState::ERROR`, and in `Adopted` (or `Pending` after a camera is added from
190/// `Adopted` before its mute state event arrives) it shows the cameras' reported state instead of
191/// the stored one.
192///
193/// A camera event never causes the cameras to be unmuted: the stored state is only pushed from a
194/// camera event when it is MUTED and the cameras report unmuted. Because such pushes only ever
195/// mute, they cannot make camera events alternate between muted and unmuted.
196#[derive(Clone, Copy, Debug, PartialEq, Eq)]
197enum CamSwSync {
198    /// The stored state has been applied to all cameras.
199    Synced,
200    /// The stored state may not be applied to every camera, either because the last push failed or
201    /// because a camera was added after startup. A camera event reporting unmuted cameras while
202    /// the stored state is MUTED pushes the stored state again.
203    Pending,
204    /// Pushing the stored MUTED state from a camera event failed. Camera events update the
205    /// reported state to match the cameras without pushing again or changing storage, so the
206    /// user's preference is still applied on the next startup. The stored state is always MUTED
207    /// in this state: it is only entered when the stored state is MUTED, camera events do not
208    /// write storage, and `set_input_states` only writes storage after a successful push, which
209    /// moves to `Synced`.
210    Adopted,
211}
212
213impl StorageAccess for InputController {
214    type Storage = DeviceStorage;
215    type Data = InputInfoSources;
216    const STORAGE_KEY: &'static str = InputInfoSources::KEY;
217}
218
219impl InputController {
220    pub(super) async fn new<F>(
221        service_context: Rc<ServiceContext>,
222        default_setting: &mut DefaultSetting<InputConfiguration, &'static str>,
223        storage_factory: Rc<F>,
224        setting_value_publisher: SettingValuePublisher<InputInfo>,
225        external_publisher: ExternalEventPublisher,
226    ) -> Result<Self, InputError>
227    where
228        F: StorageFactory<Storage = DeviceStorage>,
229    {
230        let input_device_config = default_setting
231            .load_default_value()
232            .context("Unable to load input device config")
233            .map_err(InputError::InitFailure)?
234            .expect("Input requires a configuration");
235        Ok(InputController::create_with_config(
236            service_context,
237            input_device_config,
238            &*storage_factory,
239            setting_value_publisher,
240            external_publisher,
241        )
242        .await)
243    }
244
245    /// Alternate constructor that allows specifying a configuration.
246    pub(crate) async fn create_with_config<F>(
247        service_context: Rc<ServiceContext>,
248        input_device_config: InputConfiguration,
249        storage_factory: &F,
250        setting_value_publisher: SettingValuePublisher<InputInfo>,
251        external_publisher: ExternalEventPublisher,
252    ) -> Self
253    where
254        F: StorageFactory<Storage = DeviceStorage>,
255    {
256        Self {
257            service_context,
258            store: storage_factory.get_store().await,
259            input_device_state: InputState::new(),
260            input_device_config,
261            publisher: None,
262            setting_value_publisher,
263            external_publisher,
264            cam_sw_sync: CamSwSync::Synced,
265        }
266    }
267
268    // Whether the configuration for this device contains a specific |device_type|.
269    async fn has_input_device(&self, device_type: InputDeviceType) -> bool {
270        let input_device_config_state: InputState = self.input_device_config.clone().into();
271        input_device_config_state.device_types().contains(&device_type)
272    }
273
274    pub(super) fn register_publisher(&mut self, publisher: Publisher) {
275        self.publisher = Some(publisher);
276    }
277
278    fn publish(&self, info: InputInfo) {
279        let _ = self.setting_value_publisher.publish(&info);
280        if let Some(publisher) = self.publisher.as_ref() {
281            publisher.set(info);
282        }
283    }
284
285    pub(super) async fn handle(
286        mut self,
287        mut camera_event_rx: UnboundedReceiver<(CameraWatcherEvent, super::ResultSender)>,
288        mut media_buttons_event_rx: UnboundedReceiver<(Event, super::ResultSender)>,
289        mut request_rx: UnboundedReceiver<Request>,
290    ) -> fasync::Task<()> {
291        fasync::Task::local(async move {
292            let mut next_camera_event = camera_event_rx.next();
293            let mut next_media_buttons_event = media_buttons_event_rx.next();
294            let mut next_request = request_rx.next();
295            loop {
296                futures::select! {
297                    event = next_camera_event => {
298                        let Some((event, response_tx)) = event else {
299                            continue;
300                        };
301                        next_camera_event = camera_event_rx.next();
302                        let res = match event {
303                            CameraWatcherEvent::SoftwareMuted(is_muted) => {
304                                self.handle_camera_event(is_muted).await
305                            }
306                            CameraWatcherEvent::DeviceAdded => Ok(self.handle_camera_added()),
307                        };
308                        let _ = response_tx.send(res);
309                    }
310                    event = next_media_buttons_event => {
311                        let Some((Event::OnButton(buttons), response_tx)) = event else {
312                            continue;
313                        };
314                        next_media_buttons_event = media_buttons_event_rx.next();
315                        let res = self.handle_media_buttons_event(buttons).await;
316                        let _ = response_tx.send(res);
317                    }
318                    request = next_request => {
319                        let Some(request) = request else {
320                            continue;
321                        };
322                        next_request = request_rx.next();
323                        let Request::Set(input_devices, tx) = request;
324                        let res = check_publish(
325                            self.set_input_states(input_devices, DeviceStateSource::SOFTWARE).await,
326                            |info| self.publish(info)).map(|_|{});
327                        let _ = tx.send(res);
328                    }
329                }
330            }
331        })
332    }
333
334    /// Handles a camera that was connected after startup. The camera driver resets its software
335    /// mute state, so the stored state may no longer be applied to every camera. The watcher
336    /// reports the cameras' mute state afterwards, which re-applies a stored MUTED state if the
337    /// new camera is unmuted.
338    fn handle_camera_added(&mut self) -> Option<()> {
339        if self.cam_sw_sync != CamSwSync::Pending {
340            log::info!("Camera added, re-applying stored camera state on the next camera event");
341            self.cam_sw_sync = CamSwSync::Pending;
342        }
343        None
344    }
345
346    async fn handle_camera_event(&mut self, is_muted: bool) -> Result<Option<()>, InputError> {
347        let stored_info = self.get_stored_info().await;
348        let stored_state = Self::get_cam_sw_state_from(&stored_info.input_device_state)?;
349        let stored_muted = stored_state.has_state(DeviceState::MUTED);
350        match self.cam_sw_sync {
351            CamSwSync::Pending if stored_muted && !is_muted => {
352                self.reconcile_cam_sw_state(stored_state, is_muted).await
353            }
354            CamSwSync::Adopted => {
355                if self.show_cam_sw_state(is_muted) {
356                    self.publish(InputInfo { input_device_state: self.input_device_state.clone() });
357                    Ok(Some(()))
358                } else {
359                    Ok(None)
360                }
361            }
362            CamSwSync::Synced | CamSwSync::Pending => {
363                if stored_muted != is_muted {
364                    let res =
365                        self.set_sw_camera_mute(is_muted, DEFAULT_CAMERA_NAME.to_string()).await;
366                    if res.is_ok() && is_muted {
367                        self.cam_sw_sync = CamSwSync::Synced;
368                    }
369                    if res.is_err() {
370                        // The reported state already matches the cameras, so publish it even
371                        // though it could not be stored, keeping a cleared ERROR state visible.
372                        self.publish(InputInfo {
373                            input_device_state: self.input_device_state.clone(),
374                        });
375                    }
376                    check_publish(res, |info| self.publish(info))
377                } else {
378                    if is_muted {
379                        self.cam_sw_sync = CamSwSync::Synced;
380                    }
381                    if self.show_cam_sw_state(is_muted) {
382                        // The cameras report the stored state, so a camera ERROR state is cleared.
383                        self.publish(InputInfo {
384                            input_device_state: self.input_device_state.clone(),
385                        });
386                        Ok(Some(()))
387                    } else {
388                        Ok(None)
389                    }
390                }
391            }
392        }
393    }
394
395    /// Re-applies the stored MUTED camera software state after the cameras reported being
396    /// unmuted while the stored state had not been applied (`CamSwSync::Pending`). If the push
397    /// succeeds, the stored state is shown. Otherwise, the cameras' reported state is shown
398    /// without changing storage, and the controller moves to `CamSwSync::Adopted`. The reported
399    /// state was observed before the push, so it may lag behind the cameras until the next camera
400    /// event. In both cases the camera ERROR state is cleared and the result is published.
401    async fn reconcile_cam_sw_state(
402        &mut self,
403        mut stored_state: DeviceState,
404        is_muted: bool,
405    ) -> Result<Option<()>, InputError> {
406        stored_state.remove(DeviceState::ERROR);
407        match self.push_cam_sw_state(stored_state).await {
408            Ok(()) => {
409                self.input_device_state.set_source_state(
410                    InputDeviceType::CAMERA,
411                    DEFAULT_CAMERA_NAME.to_string(),
412                    DeviceStateSource::SOFTWARE,
413                    stored_state,
414                );
415            }
416            Err(e) => {
417                log::warn!(
418                    "Unable to apply stored camera state, reporting camera state \
419                    (muted: {is_muted}): {e:?}"
420                );
421                self.cam_sw_sync = CamSwSync::Adopted;
422                let _ = self.show_cam_sw_state(is_muted);
423            }
424        }
425        self.publish(InputInfo { input_device_state: self.input_device_state.clone() });
426        Ok(Some(()))
427    }
428
429    /// Sets the reported camera software state to `is_muted` without changing storage, clearing
430    /// any camera ERROR state. Returns whether the reported state changed.
431    fn show_cam_sw_state(&mut self, is_muted: bool) -> bool {
432        if self.get_cam_sw_state().is_ok_and(|current| {
433            !current.has_state(DeviceState::ERROR)
434                && current.has_state(DeviceState::MUTED) == is_muted
435        }) {
436            return false;
437        }
438        let state = if is_muted { DeviceState::MUTED } else { DeviceState::AVAILABLE };
439        self.input_device_state.set_source_state(
440            InputDeviceType::CAMERA,
441            DEFAULT_CAMERA_NAME.to_string(),
442            DeviceStateSource::SOFTWARE,
443            state,
444        );
445        true
446    }
447
448    async fn handle_media_buttons_event(
449        &mut self,
450        mut buttons: MediaButtons,
451    ) -> Result<Option<()>, InputError> {
452        if buttons.mic_mute.is_some() && !self.has_input_device(InputDeviceType::MICROPHONE).await {
453            buttons.set_mic_mute(None);
454        }
455        if buttons.camera_disable.is_some() && !self.has_input_device(InputDeviceType::CAMERA).await
456        {
457            buttons.set_camera_disable(None);
458        }
459        check_publish(self.set_hw_media_buttons_state(buttons).await, |info| self.publish(info))
460    }
461
462    // Wrapper around client.read() that fills in the config
463    // as the default value if the read value is empty. It may be empty
464    // after a migration from a previous InputInfoSources version
465    // or on pave.
466    async fn get_stored_info(&self) -> InputInfo {
467        let mut input_info = InputInfo::from(self.store.get::<InputInfo>().await);
468        if input_info.input_device_state.is_empty() {
469            input_info.input_device_state = self.input_device_config.clone().into();
470        }
471        input_info
472    }
473
474    /// Restores the input state and returns the resulting in-memory state, which reports
475    /// `DeviceState::ERROR` for the camera if its software state could not be restored.
476    pub(super) async fn restore(&mut self) -> Result<InputInfo, InputError> {
477        self.input_device_state = self.get_stored_info().await.input_device_state;
478
479        if self.input_device_config.devices.iter().any(|d| d.device_type == InputDeviceType::CAMERA)
480        {
481            match self.get_cam_sw_state() {
482                Ok(state) => {
483                    // Camera setup failure should not prevent start of service. This also allows
484                    // clients to see that the camera may not be usable.
485                    if let Err(e) = self.push_cam_sw_state(state).await {
486                        log::error!("Unable to restore camera state: {e:?}");
487                        self.set_cam_err_state(state);
488                    }
489                }
490                Err(e) => {
491                    log::error!("Unable to load cam sw state: {e:?}");
492                    self.set_cam_err_state(DeviceState::ERROR);
493                }
494            }
495        }
496        Ok(InputInfo { input_device_state: self.input_device_state.clone() })
497    }
498
499    async fn set_sw_camera_mute(&mut self, disabled: bool, name: String) -> UpdateInputResult {
500        let mut input_info = self.get_stored_info().await;
501        input_info.input_device_state.set_source_state(
502            InputDeviceType::CAMERA,
503            name.clone(),
504            DeviceStateSource::SOFTWARE,
505            if disabled { DeviceState::MUTED } else { DeviceState::AVAILABLE },
506        );
507
508        self.input_device_state.set_source_state(
509            InputDeviceType::CAMERA,
510            name.clone(),
511            DeviceStateSource::SOFTWARE,
512            if disabled { DeviceState::MUTED } else { DeviceState::AVAILABLE },
513        );
514        self.store
515            .write(&input_info)
516            .await
517            .map(|state| (UpdateState::Updated == state).then_some(input_info))
518            .context("writing sw camera info")
519            .map_err(InputError::WriteFailure)
520    }
521
522    /// Sets the hardware mic/cam state from the muted states in `media_buttons`.
523    async fn set_hw_media_buttons_state(
524        &mut self,
525        media_buttons: MediaButtons,
526    ) -> UpdateInputResult {
527        let mut states_to_process = Vec::new();
528        if let Some(mic_mute) = media_buttons.mic_mute {
529            states_to_process.push((InputDeviceType::MICROPHONE, mic_mute));
530        }
531        if let Some(camera_disable) = media_buttons.camera_disable {
532            states_to_process.push((InputDeviceType::CAMERA, camera_disable));
533        }
534
535        let mut input_info = self.get_stored_info().await;
536
537        for (device_type, muted) in states_to_process.into_iter() {
538            // Fetch current state.
539            let hw_state_res = input_info.input_device_state.get_source_state(
540                device_type,
541                device_type.to_string(),
542                DeviceStateSource::HARDWARE,
543            );
544
545            let mut hw_state = hw_state_res.map_err(|err| {
546                InputError::UnexpectedError(
547                    format!("Could not fetch current hw mute state: {err:?}").into(),
548                )
549            })?;
550
551            if muted {
552                // Unset available and set muted.
553                hw_state &= !DeviceState::AVAILABLE;
554                hw_state |= DeviceState::MUTED;
555            } else {
556                // Set available and unset muted.
557                hw_state |= DeviceState::AVAILABLE;
558                hw_state &= !DeviceState::MUTED;
559            }
560
561            // Set the updated state.
562            input_info.input_device_state.set_source_state(
563                device_type,
564                device_type.to_string(),
565                DeviceStateSource::HARDWARE,
566                hw_state,
567            );
568            self.input_device_state.set_source_state(
569                device_type,
570                device_type.to_string(),
571                DeviceStateSource::HARDWARE,
572                hw_state,
573            );
574        }
575
576        // The in-memory state is reported so that a camera ERROR state remains visible.
577        self.store
578            .write(&input_info)
579            .await
580            .map(|state| {
581                (UpdateState::Updated == state)
582                    .then(|| InputInfo { input_device_state: self.input_device_state.clone() })
583            })
584            .context("writing hw media buttons")
585            .map_err(InputError::WriteFailure)
586    }
587
588    /// Sets state for the given input devices.
589    async fn set_input_states(
590        &mut self,
591        input_devices: Vec<InputDevice>,
592        source: DeviceStateSource,
593    ) -> UpdateInputResult {
594        let mut input_info = self.get_stored_info().await;
595        let device_types = input_info.input_device_state.device_types();
596        let cam_state = self.get_cam_sw_state().ok();
597
598        // Firstly, do a validation pass to make sure the input_devices contain only valid devices,
599        // and any newly added devices are not going to exceed our maximum device limit.
600        let mut new_devices = Vec::new();
601        for input_device in input_devices.iter() {
602            if !device_types.contains(&input_device.device_type) {
603                return Err(InputError::Unsupported(input_device.device_type));
604            }
605
606            let already_exists = input_info
607                .input_device_state
608                .contains_device(input_device.device_type, &input_device.name);
609
610            let already_counted = new_devices
611                .iter()
612                .any(|(dt, name)| *dt == input_device.device_type && *name == &input_device.name);
613
614            if !already_exists && !already_counted {
615                new_devices.push((input_device.device_type, &input_device.name));
616            }
617        }
618
619        // Abort if applying these new devices would exceed the limit.
620        if input_info.input_device_state.total_devices() + new_devices.len() > MAX_INPUT_DEVICES {
621            log::error!(
622                "Maximum number of supported input devices ({MAX_INPUT_DEVICES}) has been reached."
623            );
624            return Err(InputError::MaximumInputDeviceLimitReached(
625                format!("Maximum limit of {MAX_INPUT_DEVICES} input devices has been reached.")
626                    .into(),
627            ));
628        }
629
630        // Update the function-scoped storage cache with the requested changes. This will allow us
631        // to determine if any camera state has changed, necessary for the logic which follows.
632        for input_device in &input_devices {
633            input_info.input_device_state.insert_device(input_device.clone(), source);
634        }
635
636        // If the device has a camera, it should successfully get the sw state, and
637        // push the state if it has changed. If the device does not have a camera,
638        // it should be None both here and above, and thus not detect a change.
639        //
640        // If the stored camera state has not been applied to every camera, it is pushed again.
641        // When the request does not include the camera, that push only happens for a MUTED state
642        // and is best-effort, so a camera failure neither unmutes a camera nor blocks other
643        // changes such as muting the microphone.
644        //
645        // Once a push succeeds, the reported camera state matches the pushed state. If that
646        // changes the reported state (for example, clearing a camera ERROR state), the result is
647        // reported even if storage is unchanged so that watchers see the change.
648        let modified_cam_state = Self::get_cam_sw_state_from(&input_info.input_device_state).ok();
649        let camera_requested =
650            input_devices.iter().any(|device| device.device_type == InputDeviceType::CAMERA);
651        let push_cam_state = if camera_requested {
652            cam_state != modified_cam_state || self.cam_sw_sync != CamSwSync::Synced
653        } else {
654            self.cam_sw_sync != CamSwSync::Synced
655                && modified_cam_state.is_some_and(|state| state.has_state(DeviceState::MUTED))
656        };
657        let mut reported_cam_state_changed = false;
658        if push_cam_state && let Some(state) = modified_cam_state {
659            let pushed = match self.push_cam_sw_state(state).await {
660                Ok(()) => true,
661                Err(e) if !camera_requested => {
662                    log::warn!("Unable to re-apply stored camera state: {e:?}");
663                    false
664                }
665                Err(e) => return Err(e),
666            };
667            if pushed && cam_state != Some(state) {
668                self.input_device_state.set_source_state(
669                    InputDeviceType::CAMERA,
670                    DEFAULT_CAMERA_NAME.to_string(),
671                    DeviceStateSource::SOFTWARE,
672                    state,
673                );
674                reported_cam_state_changed = true;
675            }
676        }
677
678        // All prerequisites are done, and camera changes are committed to the hardware. We can now
679        // finalize the update by updating our cache, and queue the write to storage.
680        for input_device in input_devices {
681            self.input_device_state.insert_device(input_device, source);
682        }
683
684        let published_info = InputInfo { input_device_state: self.input_device_state.clone() };
685        self.store
686            .write(&input_info)
687            .await
688            .map(|state| {
689                (UpdateState::Updated == state || reported_cam_state_changed)
690                    .then_some(published_info)
691            })
692            .context("writing input states")
693            .map_err(InputError::WriteFailure)
694    }
695
696    /// Pulls the software state of the camera from the given `input_state`.
697    fn get_cam_sw_state_from(input_state: &InputState) -> Result<DeviceState, InputError> {
698        input_state
699            .get_source_state(
700                InputDeviceType::CAMERA,
701                DEFAULT_CAMERA_NAME.to_string(),
702                DeviceStateSource::SOFTWARE,
703            )
704            .map_err(|e| {
705                InputError::UnexpectedError(
706                    format!("Could not find camera software state: {e:?}").into(),
707                )
708            })
709    }
710
711    /// Pulls the current software state of the camera from the device state.
712    fn get_cam_sw_state(&self) -> Result<DeviceState, InputError> {
713        Self::get_cam_sw_state_from(&self.input_device_state)
714    }
715
716    /// Set the camera state into an error condition.
717    fn set_cam_err_state(&mut self, mut state: DeviceState) {
718        state.set(DeviceState::ERROR, true);
719        self.input_device_state.set_source_state(
720            InputDeviceType::CAMERA,
721            DEFAULT_CAMERA_NAME.to_string(),
722            DeviceStateSource::SOFTWARE,
723            state,
724        )
725    }
726
727    /// Forwards the given software state to the camera3 api. Will first establish
728    /// a connection to the camera3.DeviceWatcher api. This function should only be called
729    /// when there is a camera included in the config. The config is used to populate the
730    /// stored input_info, so the input_info's input_device_state can be checked whether its
731    /// device_types contains Camera prior to calling this function.
732    ///
733    /// Multiple cameras are not yet supported individually, and individual camera mute/unmute is
734    /// not exposed, so the software mute state is applied across all reported camera devices.
735    async fn push_cam_sw_state(&mut self, cam_state: DeviceState) -> Result<(), InputError> {
736        let result = self.try_push_cam_sw_state(cam_state).await;
737        self.cam_sw_sync = match (&result, self.cam_sw_sync) {
738            (Ok(()), _) => CamSwSync::Synced,
739            (Err(_), CamSwSync::Synced) => CamSwSync::Pending,
740            (Err(_), sync) => sync,
741        };
742        result
743    }
744
745    /// Sends the software mute state in `cam_state` to all reported camera devices, returning an
746    /// error if any of them could not be updated.
747    async fn try_push_cam_sw_state(&self, cam_state: DeviceState) -> Result<(), InputError> {
748        let is_muted = cam_state.has_state(DeviceState::MUTED);
749
750        // Start up a connection to the camera device watcher and connect to the
751        // camera proxies using the ids that are returned. The connections will drop out
752        // of scope after the mute state is sent.
753        let camera_proxies =
754            connect_to_cameras(&self.service_context, self.external_publisher.clone())
755                .await
756                .map_err(|e| {
757                    InputError::UnexpectedError(
758                        format!("Could not connect to camera device: {e:?}").into(),
759                    )
760                })?;
761
762        let results = futures::future::join_all(
763            camera_proxies.iter().map(|proxy| proxy.set_software_mute_state(is_muted)),
764        )
765        .await;
766
767        let mut mute_error = None;
768        for result in results {
769            if let Err(e) = result {
770                log::warn!("Failed to set software mute state on camera: {e:?}");
771                mute_error = Some(InputError::ExternalFailure(
772                    "fuchsia.camera3.Device".into(),
773                    "SetSoftwareMuteState".into(),
774                    format!("{e:?}").into(),
775                ));
776            }
777        }
778        if let Some(err) = mute_error {
779            return Err(err);
780        }
781        Ok(())
782    }
783}
784
785#[cfg(test)]
786mod tests {
787    use super::*;
788    use crate::input_device_configuration::{InputDeviceConfiguration, SourceState};
789    use fuchsia_async as fasync;
790    use fuchsia_inspect::component;
791    use futures::channel::mpsc;
792    use settings_common::inspect::config_logger::InspectConfigLogger;
793    use settings_common::service_context::ServiceContext;
794    use settings_test_common::storage::InMemoryStorageFactory;
795
796    #[fuchsia::test]
797    fn test_input_migration_v1_to_current() {
798        const MUTED_MIC: Microphone = Microphone { muted: true };
799        let v1 = InputInfoSourcesV1 { sw_microphone: MUTED_MIC, ..Default::default() };
800
801        let serialized_v1 = v1.serialize_to();
802        let current = InputInfoSources::try_deserialize_from(&serialized_v1)
803            .expect("deserialization should succeed");
804        let mut expected_input_state = InputState::new();
805        expected_input_state.set_source_state(
806            InputDeviceType::MICROPHONE,
807            DEFAULT_MIC_NAME.to_string(),
808            DeviceStateSource::SOFTWARE,
809            DeviceState::MUTED,
810        );
811        expected_input_state.set_source_state(
812            InputDeviceType::MICROPHONE,
813            DEFAULT_MIC_NAME.to_string(),
814            DeviceStateSource::HARDWARE,
815            DeviceState::AVAILABLE,
816        );
817        assert_eq!(current.input_device_state, expected_input_state);
818    }
819
820    #[fuchsia::test]
821    fn test_input_migration_v1_to_v2() {
822        const MUTED_MIC: Microphone = Microphone { muted: true };
823        let v1 = InputInfoSourcesV1 { sw_microphone: MUTED_MIC, ..Default::default() };
824
825        let serialized_v1 = v1.serialize_to();
826        let v2 = InputInfoSourcesV2::try_deserialize_from(&serialized_v1)
827            .expect("deserialization should succeed");
828
829        assert_eq!(v2.hw_microphone, Microphone { muted: false });
830        assert_eq!(v2.sw_microphone, MUTED_MIC);
831        assert_eq!(v2.input_device_state, InputState::new());
832    }
833
834    #[fuchsia::test]
835    fn test_input_migration_v2_to_current() {
836        const DEFAULT_CAMERA_NAME: &str = "camera";
837        const MUTED_MIC: Microphone = Microphone { muted: true };
838        let mut v2 = InputInfoSourcesV2::default();
839        v2.input_device_state.set_source_state(
840            InputDeviceType::CAMERA,
841            DEFAULT_CAMERA_NAME.to_string(),
842            DeviceStateSource::SOFTWARE,
843            DeviceState::AVAILABLE,
844        );
845        v2.input_device_state.set_source_state(
846            InputDeviceType::CAMERA,
847            DEFAULT_CAMERA_NAME.to_string(),
848            DeviceStateSource::HARDWARE,
849            DeviceState::MUTED,
850        );
851        v2.sw_microphone = MUTED_MIC;
852
853        let serialized_v2 = v2.serialize_to();
854        let current = InputInfoSources::try_deserialize_from(&serialized_v2)
855            .expect("deserialization should succeed");
856        let mut expected_input_state = InputState::new();
857
858        expected_input_state.set_source_state(
859            InputDeviceType::MICROPHONE,
860            DEFAULT_MIC_NAME.to_string(),
861            DeviceStateSource::SOFTWARE,
862            DeviceState::MUTED,
863        );
864        expected_input_state.set_source_state(
865            InputDeviceType::MICROPHONE,
866            DEFAULT_MIC_NAME.to_string(),
867            DeviceStateSource::HARDWARE,
868            DeviceState::AVAILABLE,
869        );
870        expected_input_state.set_source_state(
871            InputDeviceType::CAMERA,
872            DEFAULT_CAMERA_NAME.to_string(),
873            DeviceStateSource::SOFTWARE,
874            DeviceState::AVAILABLE,
875        );
876        expected_input_state.set_source_state(
877            InputDeviceType::CAMERA,
878            DEFAULT_CAMERA_NAME.to_string(),
879            DeviceStateSource::HARDWARE,
880            DeviceState::MUTED,
881        );
882
883        assert_eq!(current.input_device_state, expected_input_state);
884    }
885
886    #[fuchsia::test]
887    async fn test_camera_error_on_restore() {
888        let (event_tx, _event_rx) = mpsc::unbounded();
889        let external_publisher = ExternalEventPublisher::new(event_tx);
890        let storage_factory = InMemoryStorageFactory::new();
891        storage_factory
892            .initialize::<InputController>()
893            .await
894            .expect("controller should have impls");
895        let (value_tx, _value_rx) = mpsc::unbounded();
896        let setting_value_publisher = SettingValuePublisher::new(value_tx);
897        let mut controller: InputController =
898            InputController::create_with_config::<InMemoryStorageFactory>(
899                Rc::new(ServiceContext::new(None)),
900                InputConfiguration {
901                    devices: vec![InputDeviceConfiguration {
902                        device_name: DEFAULT_CAMERA_NAME.to_string(),
903                        device_type: InputDeviceType::CAMERA,
904                        source_states: vec![SourceState {
905                            source: DeviceStateSource::SOFTWARE,
906                            state: 0,
907                        }],
908                        mutable_toggle_state: 0,
909                    }],
910                },
911                &storage_factory,
912                setting_value_publisher,
913                external_publisher,
914            )
915            .await;
916
917        // Restore should pass.
918        let result = controller.restore().await;
919        assert!(result.is_ok());
920
921        // But the camera state should show an error.
922        let camera_state = controller
923            .input_device_state
924            .get_state(InputDeviceType::CAMERA, DEFAULT_CAMERA_NAME.to_string())
925            .unwrap();
926        assert!(camera_state.has_state(DeviceState::ERROR));
927    }
928
929    #[fuchsia::test(allow_stalls = false)]
930    async fn test_controller_creation_with_default_config() {
931        let config_logger = InspectConfigLogger::new(component::inspector().root());
932        let mut default_setting = DefaultSetting::new(
933            Some(InputConfiguration::default()),
934            "/config/data/input_device_config.json",
935            Rc::new(std::sync::Mutex::new(config_logger)),
936        );
937
938        let (event_tx, _) = mpsc::unbounded();
939        let external_publisher = ExternalEventPublisher::new(event_tx);
940
941        let storage_factory = InMemoryStorageFactory::new();
942        storage_factory
943            .initialize::<InputController>()
944            .await
945            .expect("controller should have impls");
946        let (value_tx, _value_rx) = mpsc::unbounded();
947        let setting_value_publisher = SettingValuePublisher::new(value_tx);
948        let _controller = InputController::new(
949            Rc::new(ServiceContext::new(None)),
950            &mut default_setting,
951            Rc::new(storage_factory),
952            setting_value_publisher,
953            external_publisher,
954        )
955        .await
956        .expect("Should have controller");
957    }
958
959    #[fuchsia::test]
960    async fn test_set_input_states_limit() {
961        let (event_tx, _event_rx) = mpsc::unbounded();
962        let external_publisher = ExternalEventPublisher::new(event_tx);
963        let storage_factory = InMemoryStorageFactory::new();
964        storage_factory
965            .initialize::<InputController>()
966            .await
967            .expect("controller should have impls");
968        let (value_tx, _value_rx) = mpsc::unbounded();
969        let setting_value_publisher = SettingValuePublisher::new(value_tx);
970
971        let mut device_configs = Vec::new();
972        for i in 0..MAX_INPUT_DEVICES {
973            device_configs.push(InputDeviceConfiguration {
974                device_name: format!("mic{i}"),
975                device_type: InputDeviceType::MICROPHONE,
976                source_states: vec![SourceState { source: DeviceStateSource::SOFTWARE, state: 0 }],
977                mutable_toggle_state: 0,
978            });
979        }
980
981        let mut controller: InputController =
982            InputController::create_with_config::<InMemoryStorageFactory>(
983                Rc::new(ServiceContext::new(None)),
984                InputConfiguration { devices: device_configs },
985                &storage_factory,
986                setting_value_publisher,
987                external_publisher,
988            )
989            .await;
990
991        let _ = controller.restore().await;
992
993        let overflow_dev = InputDevice {
994            name: "mic_max_exceeded".to_string(),
995            device_type: InputDeviceType::MICROPHONE,
996            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::AVAILABLE)].into(),
997            state: DeviceState::AVAILABLE,
998        };
999        let res =
1000            controller.set_input_states(vec![overflow_dev], DeviceStateSource::SOFTWARE).await;
1001        match res {
1002            Err(InputError::MaximumInputDeviceLimitReached(msg)) => {
1003                assert_eq!(
1004                    msg,
1005                    format!("Maximum limit of {MAX_INPUT_DEVICES} input devices has been reached.")
1006                );
1007            }
1008            _ => panic!("Expected MaximumInputDeviceLimitReached, got {res:?}"),
1009        }
1010    }
1011
1012    async fn handle_camera3_device(
1013        mut stream: fidl_fuchsia_camera3::DeviceRequestStream,
1014        camera_muted: std::sync::Arc<std::sync::atomic::AtomicBool>,
1015    ) {
1016        use futures::StreamExt;
1017        use std::sync::atomic::Ordering;
1018        while let Some(Ok(req)) = stream.next().await {
1019            if let fidl_fuchsia_camera3::DeviceRequest::SetSoftwareMuteState { muted, responder } =
1020                req
1021            {
1022                camera_muted.store(muted, Ordering::Relaxed);
1023                let _ = responder.send();
1024            }
1025        }
1026    }
1027
1028    async fn handle_camera3_device_watcher(
1029        mut stream: fidl_fuchsia_camera3::DeviceWatcherRequestStream,
1030        camera_muted: std::sync::Arc<std::sync::atomic::AtomicBool>,
1031    ) {
1032        use futures::StreamExt;
1033        while let Some(Ok(req)) = stream.next().await {
1034            #[allow(unreachable_patterns)]
1035            match req {
1036                fidl_fuchsia_camera3::DeviceWatcherRequest::WatchDevices { responder } => {
1037                    let _ = responder.send(&[fidl_fuchsia_camera3::WatchDevicesEvent::Added(1)]);
1038                }
1039                fidl_fuchsia_camera3::DeviceWatcherRequest::ConnectToDevice {
1040                    id: _,
1041                    request,
1042                    control_handle: _,
1043                } => {
1044                    let camera_muted = camera_muted.clone();
1045                    fasync::Task::local(handle_camera3_device(request.into_stream(), camera_muted))
1046                        .detach();
1047                }
1048                _ => {}
1049            }
1050        }
1051    }
1052
1053    #[fuchsia::test]
1054    // This tests the bug documented in https://fxbug.dev/521503847. This test validates that
1055    // the in-memory cached state of the Settings service doesn't commit changes prior to
1056    // updating the hardware state. If that were to happen, the camera hardware could get "stuck"
1057    // in its current state, while further updates are rejected due to the internal cache believing
1058    // that the update is already applied.
1059    async fn test_set_input_states_retry_on_failure() {
1060        use fidl::endpoints::DiscoverableProtocolMarker;
1061        use std::sync::Arc;
1062        use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1063
1064        let call_count = Arc::new(AtomicUsize::new(0));
1065        let call_count_clone = call_count.clone();
1066
1067        let camera_muted = Arc::new(AtomicBool::new(false));
1068        let camera_muted_clone = camera_muted.clone();
1069
1070        // Mock the camera service, rejecting the first call (count == 1) to simulate a failure.
1071        let service_context = Rc::new(ServiceContext::new(Some(Box::new(
1072            move |service_name: &str, channel: zx::Channel| {
1073                let count = call_count_clone.fetch_add(1, Ordering::Relaxed);
1074                let camera_muted_clone = camera_muted_clone.clone();
1075                let service_name = service_name.to_string();
1076                Box::pin(async move {
1077                    if count == 1 {
1078                        return Err(anyhow::Error::msg("injected failure"));
1079                    }
1080                    if service_name != fidl_fuchsia_camera3::DeviceWatcherMarker::PROTOCOL_NAME {
1081                        return Err(anyhow::Error::msg("unsupported service"));
1082                    }
1083                    let stream = fidl::endpoints::ServerEnd::<
1084                        fidl_fuchsia_camera3::DeviceWatcherMarker,
1085                    >::new(channel)
1086                    .into_stream();
1087                    fasync::Task::local(handle_camera3_device_watcher(stream, camera_muted_clone))
1088                        .detach();
1089                    Ok(())
1090                })
1091            },
1092        ))));
1093
1094        // Initialize the mocked "persistent" storage.
1095        let storage_factory = InMemoryStorageFactory::new();
1096        storage_factory
1097            .initialize::<InputController>()
1098            .await
1099            .expect("controller should have impls");
1100        let (value_tx, _value_rx) = mpsc::unbounded();
1101        let setting_value_publisher = SettingValuePublisher::new(value_tx);
1102        let (event_tx, _event_rx) = mpsc::unbounded();
1103        let external_publisher = ExternalEventPublisher::new(event_tx);
1104
1105        let mut controller = InputController::create_with_config::<InMemoryStorageFactory>(
1106            service_context,
1107            InputConfiguration {
1108                devices: vec![InputDeviceConfiguration {
1109                    device_name: DEFAULT_CAMERA_NAME.to_string(),
1110                    device_type: InputDeviceType::CAMERA,
1111                    source_states: vec![SourceState {
1112                        source: DeviceStateSource::SOFTWARE,
1113                        state: 0, // AVAILABLE
1114                    }],
1115                    mutable_toggle_state: 0,
1116                }],
1117            },
1118            &storage_factory,
1119            setting_value_publisher,
1120            external_publisher,
1121        )
1122        .await;
1123
1124        let _ = controller.restore().await;
1125
1126        // Verify initial state is AVAILABLE (not muted).
1127        let camera_state = controller
1128            .input_device_state
1129            .get_state(InputDeviceType::CAMERA, DEFAULT_CAMERA_NAME.to_string())
1130            .unwrap();
1131        assert!(!camera_state.has_state(DeviceState::MUTED));
1132
1133        // Create a request to MUTE the camera. The hardware API call will fail on this request,
1134        // due to the previously configured mock camera service.
1135        let mute_device = InputDevice {
1136            name: DEFAULT_CAMERA_NAME.to_string(),
1137            device_type: InputDeviceType::CAMERA,
1138            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1139            state: DeviceState::MUTED,
1140        };
1141
1142        let res = controller
1143            .set_input_states(vec![mute_device.clone()], DeviceStateSource::SOFTWARE)
1144            .await;
1145        assert!(res.is_err());
1146        assert_eq!(call_count.load(Ordering::Relaxed), 2);
1147        assert!(!camera_muted.load(Ordering::Relaxed));
1148
1149        // Attempt to mute again. If the bug this test tracks is present, this will skip
1150        // push_cam_sw_state and return Ok, because the service thinks the state is already MUTED
1151        // due to the in-memory state being updated despite the hardware update failing.
1152        let res2 =
1153            controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
1154
1155        // If the bug is fixed, we expect that the camera service will have been called again, the
1156        // result was okay and the mute state was properly updated.
1157        assert_eq!(call_count.load(Ordering::Relaxed), 3);
1158        assert!(res2.is_ok());
1159        assert!(camera_muted.load(Ordering::Relaxed));
1160    }
1161
1162    async fn setup_multi_camera_test(
1163        initial_muted: bool,
1164        fail_camera_1_on_attempt: Option<usize>,
1165    ) -> (
1166        InputController,
1167        std::sync::Arc<std::sync::atomic::AtomicBool>,
1168        std::sync::Arc<std::sync::atomic::AtomicBool>,
1169    ) {
1170        let (controller, camera1_muted, camera2_muted, _value_rx) =
1171            setup_multi_camera_test_with_failures(
1172                initial_muted,
1173                fail_camera_1_on_attempt.into_iter().collect(),
1174            )
1175            .await;
1176        (controller, camera1_muted, camera2_muted)
1177    }
1178
1179    /// Creates a controller with two cameras and runs `restore()`. Connecting to camera 1 fails on
1180    /// each 0-based attempt listed in `fail_camera_1_on_attempts`, where attempt 0 is the push made
1181    /// by `restore()`. Also returns the receiver for values published by the controller.
1182    async fn setup_multi_camera_test_with_failures(
1183        initial_muted: bool,
1184        fail_camera_1_on_attempts: Vec<usize>,
1185    ) -> (
1186        InputController,
1187        std::sync::Arc<std::sync::atomic::AtomicBool>,
1188        std::sync::Arc<std::sync::atomic::AtomicBool>,
1189        mpsc::UnboundedReceiver<(&'static str, String)>,
1190    ) {
1191        use fidl::endpoints::DiscoverableProtocolMarker;
1192        use std::sync::Arc;
1193        use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1194
1195        let camera1_muted = Arc::new(AtomicBool::new(initial_muted));
1196        let camera2_muted = Arc::new(AtomicBool::new(initial_muted));
1197        let camera1_clone = camera1_muted.clone();
1198        let camera2_clone = camera2_muted.clone();
1199        let camera1_attempts = Arc::new(AtomicUsize::new(0));
1200
1201        let service_context = Rc::new(ServiceContext::new(Some(Box::new(
1202            move |service_name: &str, channel: zx::Channel| {
1203                let camera1 = camera1_clone.clone();
1204                let camera2 = camera2_clone.clone();
1205                let camera1_attempts = camera1_attempts.clone();
1206                let fail_camera_1_on_attempts = fail_camera_1_on_attempts.clone();
1207                let service_name = service_name.to_string();
1208                Box::pin(async move {
1209                    if service_name != fidl_fuchsia_camera3::DeviceWatcherMarker::PROTOCOL_NAME {
1210                        return Err(anyhow::Error::msg("unsupported service"));
1211                    }
1212                    let mut stream = fidl::endpoints::ServerEnd::<
1213                        fidl_fuchsia_camera3::DeviceWatcherMarker,
1214                    >::new(channel)
1215                    .into_stream();
1216                    fasync::Task::local(async move {
1217                        while let Some(Ok(req)) = stream.next().await {
1218                            #[allow(unreachable_patterns)]
1219                            match req {
1220                                fidl_fuchsia_camera3::DeviceWatcherRequest::WatchDevices {
1221                                    responder,
1222                                } => {
1223                                    let _ = responder.send(&[
1224                                        fidl_fuchsia_camera3::WatchDevicesEvent::Existing(1),
1225                                        fidl_fuchsia_camera3::WatchDevicesEvent::Added(2),
1226                                    ]);
1227                                }
1228                                fidl_fuchsia_camera3::DeviceWatcherRequest::ConnectToDevice {
1229                                    id,
1230                                    request,
1231                                    control_handle: _,
1232                                } => {
1233                                    let target = match id {
1234                                        1 => {
1235                                            let attempt =
1236                                                camera1_attempts.fetch_add(1, Ordering::Relaxed);
1237                                            if fail_camera_1_on_attempts.contains(&attempt) {
1238                                                drop(request);
1239                                                continue;
1240                                            }
1241                                            camera1.clone()
1242                                        }
1243                                        2 => camera2.clone(),
1244                                        _ => panic!("unexpected camera id {id}"),
1245                                    };
1246                                    fasync::Task::local(handle_camera3_device(
1247                                        request.into_stream(),
1248                                        target,
1249                                    ))
1250                                    .detach();
1251                                }
1252                                _ => {}
1253                            }
1254                        }
1255                    })
1256                    .detach();
1257                    Ok(())
1258                })
1259            },
1260        ))));
1261
1262        let storage_factory = InMemoryStorageFactory::new();
1263        storage_factory
1264            .initialize::<InputController>()
1265            .await
1266            .expect("controller should have impls");
1267        let (value_tx, value_rx) = mpsc::unbounded();
1268        let setting_value_publisher = SettingValuePublisher::new(value_tx);
1269        let (event_tx, _event_rx) = mpsc::unbounded();
1270        let external_publisher = ExternalEventPublisher::new(event_tx);
1271
1272        let initial_state = if initial_muted {
1273            DeviceState::MUTED.bits()
1274        } else {
1275            0 // AVAILABLE
1276        };
1277
1278        let mut controller = InputController::create_with_config::<InMemoryStorageFactory>(
1279            service_context,
1280            InputConfiguration {
1281                devices: vec![
1282                    InputDeviceConfiguration {
1283                        device_name: DEFAULT_CAMERA_NAME.to_string(),
1284                        device_type: InputDeviceType::CAMERA,
1285                        source_states: vec![SourceState {
1286                            source: DeviceStateSource::SOFTWARE,
1287                            state: initial_state,
1288                        }],
1289                        mutable_toggle_state: 0,
1290                    },
1291                    InputDeviceConfiguration {
1292                        device_name: DEFAULT_MIC_NAME.to_string(),
1293                        device_type: InputDeviceType::MICROPHONE,
1294                        source_states: vec![SourceState {
1295                            source: DeviceStateSource::SOFTWARE,
1296                            state: 0, // AVAILABLE
1297                        }],
1298                        mutable_toggle_state: 0,
1299                    },
1300                ],
1301            },
1302            &storage_factory,
1303            setting_value_publisher,
1304            external_publisher,
1305        )
1306        .await;
1307
1308        let _ = controller.restore().await;
1309        (controller, camera1_muted, camera2_muted, value_rx)
1310    }
1311
1312    #[fuchsia::test]
1313    async fn test_set_input_states_multiple_cameras() {
1314        use std::sync::atomic::Ordering;
1315
1316        let (mut controller, camera1_muted, camera2_muted) =
1317            setup_multi_camera_test(false, None).await;
1318        assert!(!camera1_muted.load(Ordering::Relaxed));
1319        assert!(!camera2_muted.load(Ordering::Relaxed));
1320
1321        let mute_device = InputDevice {
1322            name: DEFAULT_CAMERA_NAME.to_string(),
1323            device_type: InputDeviceType::CAMERA,
1324            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1325            state: DeviceState::MUTED,
1326        };
1327
1328        let res = controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
1329        assert!(res.is_ok());
1330        assert!(camera1_muted.load(Ordering::Relaxed));
1331        assert!(camera2_muted.load(Ordering::Relaxed));
1332    }
1333
1334    #[fuchsia::test]
1335    async fn test_set_input_states_multiple_cameras_partial_failure() {
1336        use std::sync::atomic::Ordering;
1337
1338        // Fail camera 1 on attempt 1 (the first set_input_states call after restore()).
1339        let (mut controller, camera1_muted, camera2_muted) =
1340            setup_multi_camera_test(false, Some(1)).await;
1341
1342        let mute_device = InputDevice {
1343            name: DEFAULT_CAMERA_NAME.to_string(),
1344            device_type: InputDeviceType::CAMERA,
1345            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1346            state: DeviceState::MUTED,
1347        };
1348
1349        let res = controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
1350        assert!(res.is_err());
1351        assert!(!camera1_muted.load(Ordering::Relaxed));
1352        // Even though camera 1 failed, camera 2 must still have been muted.
1353        assert!(camera2_muted.load(Ordering::Relaxed));
1354    }
1355
1356    #[fuchsia::test]
1357    async fn test_set_input_states_multiple_cameras_partial_unmute_retry() {
1358        use std::sync::atomic::Ordering;
1359
1360        // Both cameras start muted. Fail camera 1 on attempt 1 (the first unmute attempt).
1361        let (mut controller, camera1_muted, camera2_muted) =
1362            setup_multi_camera_test(true, Some(1)).await;
1363        assert!(camera1_muted.load(Ordering::Relaxed));
1364        assert!(camera2_muted.load(Ordering::Relaxed));
1365
1366        let unmute_device = InputDevice {
1367            name: DEFAULT_CAMERA_NAME.to_string(),
1368            device_type: InputDeviceType::CAMERA,
1369            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::AVAILABLE)].into(),
1370            state: DeviceState::AVAILABLE,
1371        };
1372
1373        // First unmute attempt fails on camera 1 while camera 2 unmutes.
1374        let res = controller
1375            .set_input_states(vec![unmute_device.clone()], DeviceStateSource::SOFTWARE)
1376            .await;
1377        assert!(res.is_err());
1378        assert!(camera1_muted.load(Ordering::Relaxed));
1379        assert!(!camera2_muted.load(Ordering::Relaxed));
1380
1381        // Simulate CameraWatcherAgent reporting that the aggregate mute state is now unmuted
1382        // (`false`) because camera 2 unmuted. The stored MUTED state has not been fully applied,
1383        // so it is pushed to both cameras again.
1384        let watcher_res = controller.handle_camera_event(false).await;
1385        assert!(watcher_res.is_ok());
1386        assert!(camera1_muted.load(Ordering::Relaxed));
1387        assert!(camera2_muted.load(Ordering::Relaxed));
1388
1389        // Retrying set_input_states(AVAILABLE) pushes SetSoftwareMuteState(false) to both cameras.
1390        let retry_res =
1391            controller.set_input_states(vec![unmute_device], DeviceStateSource::SOFTWARE).await;
1392        assert!(retry_res.is_ok());
1393        assert!(!camera1_muted.load(Ordering::Relaxed));
1394        assert!(!camera2_muted.load(Ordering::Relaxed));
1395    }
1396
1397    async fn create_camera_controller_without_camera_service(
1398        source_states: Vec<SourceState>,
1399    ) -> InputController {
1400        let (event_tx, _event_rx) = mpsc::unbounded();
1401        let external_publisher = ExternalEventPublisher::new(event_tx);
1402        let storage_factory = InMemoryStorageFactory::new();
1403        storage_factory
1404            .initialize::<InputController>()
1405            .await
1406            .expect("controller should have impls");
1407        let (value_tx, _value_rx) = mpsc::unbounded();
1408        let setting_value_publisher = SettingValuePublisher::new(value_tx);
1409        InputController::create_with_config::<InMemoryStorageFactory>(
1410            Rc::new(ServiceContext::new(None)),
1411            InputConfiguration {
1412                devices: vec![InputDeviceConfiguration {
1413                    device_name: DEFAULT_CAMERA_NAME.to_string(),
1414                    device_type: InputDeviceType::CAMERA,
1415                    source_states,
1416                    mutable_toggle_state: 0,
1417                }],
1418            },
1419            &storage_factory,
1420            setting_value_publisher,
1421            external_publisher,
1422        )
1423        .await
1424    }
1425
1426    #[fuchsia::test]
1427    async fn test_restore_returns_camera_error_on_push_failure() {
1428        let mut controller = create_camera_controller_without_camera_service(vec![SourceState {
1429            source: DeviceStateSource::SOFTWARE,
1430            state: DeviceState::MUTED.bits(),
1431        }])
1432        .await;
1433
1434        let info = controller.restore().await.expect("restore should succeed");
1435
1436        let camera_state = info
1437            .input_device_state
1438            .get_state(InputDeviceType::CAMERA, DEFAULT_CAMERA_NAME.to_string())
1439            .unwrap();
1440        assert!(camera_state.has_state(DeviceState::ERROR));
1441        let sw_state = InputController::get_cam_sw_state_from(&info.input_device_state).unwrap();
1442        assert_eq!(sw_state, DeviceState::MUTED | DeviceState::ERROR);
1443        assert_eq!(info.input_device_state, controller.input_device_state);
1444    }
1445
1446    #[fuchsia::test]
1447    async fn test_restore_returns_camera_error_on_missing_sw_state() {
1448        let mut controller = create_camera_controller_without_camera_service(vec![SourceState {
1449            source: DeviceStateSource::HARDWARE,
1450            state: DeviceState::AVAILABLE.bits(),
1451        }])
1452        .await;
1453
1454        let info = controller.restore().await.expect("restore should succeed");
1455
1456        let sw_state = InputController::get_cam_sw_state_from(&info.input_device_state).unwrap();
1457        assert_eq!(sw_state, DeviceState::ERROR);
1458        assert_eq!(info.input_device_state, controller.input_device_state);
1459    }
1460
1461    #[fuchsia::test]
1462    async fn test_media_buttons_keep_camera_error() {
1463        let mut controller = create_camera_controller_without_camera_service(vec![
1464            SourceState {
1465                source: DeviceStateSource::HARDWARE,
1466                state: DeviceState::AVAILABLE.bits(),
1467            },
1468            SourceState { source: DeviceStateSource::SOFTWARE, state: DeviceState::MUTED.bits() },
1469        ])
1470        .await;
1471        let _ = controller.restore().await.expect("restore should succeed");
1472
1473        // A hardware camera button press while the camera software state could not be restored
1474        // reports the new hardware state together with the camera ERROR state.
1475        let info = controller
1476            .set_hw_media_buttons_state(MediaButtons { mic_mute: None, camera_disable: Some(true) })
1477            .await
1478            .expect("media buttons should succeed")
1479            .expect("hardware state should be published");
1480        assert_eq!(
1481            InputController::get_cam_sw_state_from(&info.input_device_state).unwrap(),
1482            DeviceState::MUTED | DeviceState::ERROR
1483        );
1484        assert_eq!(
1485            info.input_device_state
1486                .get_source_state(
1487                    InputDeviceType::CAMERA,
1488                    DEFAULT_CAMERA_NAME.to_string(),
1489                    DeviceStateSource::HARDWARE
1490                )
1491                .unwrap(),
1492            DeviceState::MUTED
1493        );
1494        assert_eq!(info.input_device_state, controller.input_device_state);
1495    }
1496
1497    fn expected_published_value(controller: &InputController) -> String {
1498        format!("{:?}", InputInfo { input_device_state: controller.input_device_state.clone() })
1499    }
1500
1501    #[fuchsia::test]
1502    async fn test_camera_event_while_pending_repush_succeeds() {
1503        use std::sync::atomic::Ordering;
1504
1505        // The stored state is MUTED, and the push made by restore() fails on camera 1, which is
1506        // left unmuted.
1507        let (mut controller, camera1_muted, camera2_muted, mut value_rx) =
1508            setup_multi_camera_test_with_failures(true, vec![0]).await;
1509        camera1_muted.store(false, Ordering::Relaxed);
1510        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1511        assert!(
1512            InputController::get_cam_sw_state_from(&controller.input_device_state)
1513                .unwrap()
1514                .has_state(DeviceState::ERROR)
1515        );
1516
1517        // The watcher reports the cameras as unmuted.
1518        let res = controller.handle_camera_event(false).await;
1519        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1520
1521        // The stored MUTED state is pushed to the cameras again and kept.
1522        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1523        assert!(camera1_muted.load(Ordering::Relaxed));
1524        assert!(camera2_muted.load(Ordering::Relaxed));
1525        assert_eq!(
1526            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1527            DeviceState::MUTED
1528        );
1529        assert_eq!(
1530            InputController::get_cam_sw_state_from(
1531                &controller.get_stored_info().await.input_device_state
1532            )
1533            .unwrap(),
1534            DeviceState::MUTED
1535        );
1536
1537        // The reconciled state is published even though storage did not change.
1538        let (_, published) = value_rx.try_recv().expect("reconciled state should be published");
1539        assert_eq!(published, expected_published_value(&controller));
1540        assert!(value_rx.try_recv().is_err());
1541    }
1542
1543    #[fuchsia::test]
1544    async fn test_camera_event_while_pending_repush_fails() {
1545        use std::sync::atomic::Ordering;
1546
1547        // The stored state is MUTED. The push made by restore() and the following re-push both
1548        // fail on camera 1, which is left unmuted.
1549        let (mut controller, camera1_muted, camera2_muted, mut value_rx) =
1550            setup_multi_camera_test_with_failures(true, vec![0, 1]).await;
1551        camera1_muted.store(false, Ordering::Relaxed);
1552        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1553
1554        // The watcher reports the cameras as unmuted.
1555        let res = controller.handle_camera_event(false).await;
1556        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1557        assert!(!camera1_muted.load(Ordering::Relaxed));
1558        assert!(camera2_muted.load(Ordering::Relaxed));
1559        assert_eq!(controller.cam_sw_sync, CamSwSync::Adopted);
1560
1561        // The cameras' reported state is shown without ERROR, and the stored MUTED preference is
1562        // kept so that it is applied again on the next startup.
1563        assert_eq!(
1564            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1565            DeviceState::AVAILABLE
1566        );
1567        assert_eq!(
1568            InputController::get_cam_sw_state_from(
1569                &controller.get_stored_info().await.input_device_state
1570            )
1571            .unwrap(),
1572            DeviceState::MUTED
1573        );
1574
1575        let (_, published) = value_rx.try_recv().expect("reported state should be published");
1576        assert_eq!(published, expected_published_value(&controller));
1577        assert!(value_rx.try_recv().is_err());
1578
1579        // Another client mutes camera 1, so the watcher reports the cameras as muted. Later camera
1580        // events update the reported state without pushing or changing storage.
1581        camera1_muted.store(true, Ordering::Relaxed);
1582        let res = controller.handle_camera_event(true).await;
1583        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1584        assert_eq!(
1585            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1586            DeviceState::MUTED
1587        );
1588        let res = controller.handle_camera_event(true).await;
1589        assert!(matches!(res, Ok(None)), "unexpected result {res:?}");
1590
1591        // Another client unmutes camera 1. The event is reported without pushing MUTED again.
1592        camera1_muted.store(false, Ordering::Relaxed);
1593        let res = controller.handle_camera_event(false).await;
1594        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1595        assert!(!camera1_muted.load(Ordering::Relaxed));
1596        assert_eq!(controller.cam_sw_sync, CamSwSync::Adopted);
1597        assert_eq!(
1598            InputController::get_cam_sw_state_from(
1599                &controller.get_stored_info().await.input_device_state
1600            )
1601            .unwrap(),
1602            DeviceState::MUTED
1603        );
1604    }
1605
1606    #[fuchsia::test]
1607    async fn test_camera_event_while_pending_never_unmutes() {
1608        use std::sync::atomic::Ordering;
1609
1610        // The stored state is AVAILABLE, and the push made by restore() fails on camera 1.
1611        let (mut controller, camera1_muted, camera2_muted, mut value_rx) =
1612            setup_multi_camera_test_with_failures(false, vec![0]).await;
1613        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1614
1615        // Another client mutes both cameras and the watcher reports the cameras as muted.
1616        camera1_muted.store(true, Ordering::Relaxed);
1617        camera2_muted.store(true, Ordering::Relaxed);
1618        let res = controller.handle_camera_event(true).await;
1619        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1620
1621        // The cameras are not unmuted, and their muted state is adopted.
1622        assert!(camera1_muted.load(Ordering::Relaxed));
1623        assert!(camera2_muted.load(Ordering::Relaxed));
1624        assert_eq!(
1625            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1626            DeviceState::MUTED
1627        );
1628        assert_eq!(
1629            InputController::get_cam_sw_state_from(
1630                &controller.get_stored_info().await.input_device_state
1631            )
1632            .unwrap(),
1633            DeviceState::MUTED
1634        );
1635        assert!(value_rx.try_recv().is_ok());
1636    }
1637
1638    #[fuchsia::test]
1639    async fn test_camera_event_while_pending_matching_storage_clears_error() {
1640        // The push made by restore() fails on camera 1.
1641        let (mut controller, _camera1_muted, _camera2_muted, mut value_rx) =
1642            setup_multi_camera_test_with_failures(true, vec![0]).await;
1643        let stored_info = controller.get_stored_info().await;
1644        let _ = controller.store.write(&stored_info).await.expect("write should succeed");
1645
1646        // The camera reports the same value as storage, so storage is unchanged, the stored
1647        // MUTED state is now applied to all cameras, and the ERROR state is cleared and published.
1648        let res = controller.handle_camera_event(true).await;
1649        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1650        assert_eq!(controller.get_stored_info().await, stored_info);
1651        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1652        assert_eq!(
1653            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1654            DeviceState::MUTED
1655        );
1656
1657        let (_, published) = value_rx.try_recv().expect("cleared error should be published");
1658        assert_eq!(published, expected_published_value(&controller));
1659    }
1660
1661    #[fuchsia::test]
1662    async fn test_camera_event_while_synced() {
1663        let (mut controller, _camera1_muted, _camera2_muted, mut value_rx) =
1664            setup_multi_camera_test_with_failures(false, vec![]).await;
1665        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1666
1667        // A camera event matching the stored state is not published.
1668        let res = controller.handle_camera_event(false).await;
1669        assert!(matches!(res, Ok(None)), "unexpected result {res:?}");
1670        assert!(value_rx.try_recv().is_err());
1671
1672        // A camera event that differs from the stored state is adopted and published.
1673        let res = controller.handle_camera_event(true).await;
1674        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1675        assert_eq!(
1676            InputController::get_cam_sw_state_from(
1677                &controller.get_stored_info().await.input_device_state
1678            )
1679            .unwrap(),
1680            DeviceState::MUTED
1681        );
1682        assert!(value_rx.try_recv().is_ok());
1683    }
1684
1685    #[fuchsia::test]
1686    async fn test_set_input_states_clears_camera_error_with_unchanged_storage() {
1687        use std::sync::atomic::Ordering;
1688
1689        // The push made by restore() fails on camera 1.
1690        let (mut controller, camera1_muted, camera2_muted, _value_rx) =
1691            setup_multi_camera_test_with_failures(true, vec![0]).await;
1692        let stored_info = controller.get_stored_info().await;
1693        let _ = controller.store.write(&stored_info).await.expect("write should succeed");
1694        assert!(
1695            InputController::get_cam_sw_state_from(&controller.input_device_state)
1696                .unwrap()
1697                .has_state(DeviceState::ERROR)
1698        );
1699
1700        // Setting the stored state again applies it to the cameras. Storage is unchanged, but the
1701        // result must still be reported so that the ERROR state is cleared for watchers.
1702        let mute_device = InputDevice {
1703            name: DEFAULT_CAMERA_NAME.to_string(),
1704            device_type: InputDeviceType::CAMERA,
1705            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1706            state: DeviceState::MUTED,
1707        };
1708        let res = controller.set_input_states(vec![mute_device], DeviceStateSource::SOFTWARE).await;
1709        let info = res.expect("set should succeed").expect("cleared error should be published");
1710        assert_eq!(controller.get_stored_info().await, stored_info);
1711        assert!(camera1_muted.load(Ordering::Relaxed));
1712        assert!(camera2_muted.load(Ordering::Relaxed));
1713        assert_eq!(
1714            InputController::get_cam_sw_state_from(&info.input_device_state).unwrap(),
1715            DeviceState::MUTED
1716        );
1717        assert_eq!(
1718            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1719            DeviceState::MUTED
1720        );
1721    }
1722    #[fuchsia::test]
1723    async fn test_set_mic_clears_camera_error() {
1724        use std::sync::atomic::Ordering;
1725
1726        // The push made by restore() fails on camera 1.
1727        let (mut controller, camera1_muted, camera2_muted, _value_rx) =
1728            setup_multi_camera_test_with_failures(true, vec![0]).await;
1729        assert!(
1730            InputController::get_cam_sw_state_from(&controller.input_device_state)
1731                .unwrap()
1732                .has_state(DeviceState::ERROR)
1733        );
1734
1735        // Updating only the microphone retries pushing the dirty camera software state and clears
1736        // the camera ERROR state even though the request does not include a camera device.
1737        let mute_mic = InputDevice {
1738            name: DEFAULT_MIC_NAME.to_string(),
1739            device_type: InputDeviceType::MICROPHONE,
1740            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1741            state: DeviceState::MUTED,
1742        };
1743        let res = controller.set_input_states(vec![mute_mic], DeviceStateSource::SOFTWARE).await;
1744        let info = res.expect("set should succeed").expect("updated mic should be published");
1745        assert!(camera1_muted.load(Ordering::Relaxed));
1746        assert!(camera2_muted.load(Ordering::Relaxed));
1747        assert_eq!(
1748            InputController::get_cam_sw_state_from(&info.input_device_state).unwrap(),
1749            DeviceState::MUTED
1750        );
1751        assert_eq!(
1752            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1753            DeviceState::MUTED
1754        );
1755    }
1756
1757    #[fuchsia::test]
1758    async fn test_set_input_states_retry_after_adopting_camera_state() {
1759        use std::sync::atomic::Ordering;
1760
1761        // Both cameras start muted. Camera 1 fails the first unmute attempt and the re-push made
1762        // while handling the following camera event.
1763        let (mut controller, camera1_muted, camera2_muted, _value_rx) =
1764            setup_multi_camera_test_with_failures(true, vec![1, 2]).await;
1765
1766        let unmute_device = InputDevice {
1767            name: DEFAULT_CAMERA_NAME.to_string(),
1768            device_type: InputDeviceType::CAMERA,
1769            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::AVAILABLE)].into(),
1770            state: DeviceState::AVAILABLE,
1771        };
1772        let res = controller
1773            .set_input_states(vec![unmute_device.clone()], DeviceStateSource::SOFTWARE)
1774            .await;
1775        assert!(res.is_err());
1776        assert!(camera1_muted.load(Ordering::Relaxed));
1777        assert!(!camera2_muted.load(Ordering::Relaxed));
1778
1779        // The watcher reports `false` because camera 2 unmuted. The re-push of the stored MUTED
1780        // state fails on camera 1, so the reported AVAILABLE state is shown while storage keeps
1781        // MUTED.
1782        let watcher_res = controller.handle_camera_event(false).await;
1783        assert!(matches!(watcher_res, Ok(Some(()))), "unexpected result {watcher_res:?}");
1784        assert_eq!(controller.cam_sw_sync, CamSwSync::Adopted);
1785        assert_eq!(
1786            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1787            DeviceState::AVAILABLE
1788        );
1789        assert_eq!(
1790            InputController::get_cam_sw_state_from(
1791                &controller.get_stored_info().await.input_device_state
1792            )
1793            .unwrap(),
1794            DeviceState::MUTED
1795        );
1796
1797        // Retrying set_input_states(AVAILABLE) still pushes to camera 1 even though the reported
1798        // state is already AVAILABLE.
1799        let retry_res =
1800            controller.set_input_states(vec![unmute_device], DeviceStateSource::SOFTWARE).await;
1801        assert!(retry_res.is_ok());
1802        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1803        assert!(!camera1_muted.load(Ordering::Relaxed));
1804        assert!(!camera2_muted.load(Ordering::Relaxed));
1805    }
1806
1807    #[fuchsia::test]
1808    async fn test_set_input_states_camera_resync_does_not_block_other_devices() {
1809        use std::sync::atomic::Ordering;
1810
1811        // Stored state is MUTED. Every push (including restore) fails on camera 1.
1812        let (mut controller, camera1_muted, camera2_muted, _value_rx) =
1813            setup_multi_camera_test_with_failures(true, (0..100).collect()).await;
1814        camera1_muted.store(false, Ordering::Relaxed);
1815        camera2_muted.store(false, Ordering::Relaxed);
1816        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1817        assert_eq!(
1818            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1819            DeviceState::MUTED | DeviceState::ERROR
1820        );
1821
1822        // Muting the microphone while in-memory camera state carries ERROR re-applies the stored
1823        // MUTED camera state where possible, succeeds even though camera 1 fails, and keeps the
1824        // reported camera ERROR state in the published InputInfo.
1825        let mute_mic = InputDevice {
1826            name: DEFAULT_MIC_NAME.to_string(),
1827            device_type: InputDeviceType::MICROPHONE,
1828            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1829            state: DeviceState::MUTED,
1830        };
1831        let info = controller
1832            .set_input_states(vec![mute_mic], DeviceStateSource::SOFTWARE)
1833            .await
1834            .expect("mic mute should succeed")
1835            .expect("mic mute should be published");
1836        assert!(!camera1_muted.load(Ordering::Relaxed));
1837        assert!(camera2_muted.load(Ordering::Relaxed));
1838        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1839        assert_eq!(
1840            InputController::get_cam_sw_state_from(&info.input_device_state).unwrap(),
1841            DeviceState::MUTED | DeviceState::ERROR
1842        );
1843        assert_eq!(
1844            InputController::get_cam_sw_state_from(
1845                &controller.get_stored_info().await.input_device_state
1846            )
1847            .unwrap(),
1848            DeviceState::MUTED
1849        );
1850
1851        // A camera event fails to re-push MUTED to camera 1 and moves to Adopted, where the
1852        // reported camera state is AVAILABLE while storage keeps MUTED.
1853        let watcher_res = controller.handle_camera_event(false).await;
1854        assert!(matches!(watcher_res, Ok(Some(()))), "unexpected result {watcher_res:?}");
1855        assert_eq!(controller.cam_sw_sync, CamSwSync::Adopted);
1856        assert_eq!(
1857            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1858            DeviceState::AVAILABLE
1859        );
1860
1861        // Changing the microphone while Adopted still succeeds even though the reported and
1862        // stored camera states differ and camera 1 keeps failing, and keeps reporting the camera
1863        // as AVAILABLE.
1864        let unmute_mic = InputDevice {
1865            name: DEFAULT_MIC_NAME.to_string(),
1866            device_type: InputDeviceType::MICROPHONE,
1867            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::AVAILABLE)].into(),
1868            state: DeviceState::AVAILABLE,
1869        };
1870        let info = controller
1871            .set_input_states(vec![unmute_mic], DeviceStateSource::SOFTWARE)
1872            .await
1873            .expect("mic unmute should succeed")
1874            .expect("mic unmute should be published");
1875        assert_eq!(controller.cam_sw_sync, CamSwSync::Adopted);
1876        assert_eq!(
1877            InputController::get_cam_sw_state_from(&info.input_device_state).unwrap(),
1878            DeviceState::AVAILABLE
1879        );
1880        assert_eq!(
1881            InputController::get_cam_sw_state_from(
1882                &controller.get_stored_info().await.input_device_state
1883            )
1884            .unwrap(),
1885            DeviceState::MUTED
1886        );
1887    }
1888
1889    #[fuchsia::test]
1890    async fn test_set_input_states_other_device_does_not_unmute_camera() {
1891        use std::sync::atomic::Ordering;
1892
1893        // Both cameras start unmuted. The first mute fails on camera 1, so the stored state stays
1894        // AVAILABLE without being fully applied.
1895        let (mut controller, camera1_muted, camera2_muted, _value_rx) =
1896            setup_multi_camera_test_with_failures(false, vec![1]).await;
1897        let mute_camera = InputDevice {
1898            name: DEFAULT_CAMERA_NAME.to_string(),
1899            device_type: InputDeviceType::CAMERA,
1900            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1901            state: DeviceState::MUTED,
1902        };
1903        let res = controller.set_input_states(vec![mute_camera], DeviceStateSource::SOFTWARE).await;
1904        assert!(res.is_err());
1905        assert!(camera2_muted.load(Ordering::Relaxed));
1906        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1907
1908        // Another client mutes camera 1. Muting the microphone does not push the stored
1909        // AVAILABLE state, so the cameras stay muted.
1910        camera1_muted.store(true, Ordering::Relaxed);
1911        let mute_mic = InputDevice {
1912            name: DEFAULT_MIC_NAME.to_string(),
1913            device_type: InputDeviceType::MICROPHONE,
1914            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::MUTED)].into(),
1915            state: DeviceState::MUTED,
1916        };
1917        let res = controller.set_input_states(vec![mute_mic], DeviceStateSource::SOFTWARE).await;
1918        assert!(res.is_ok(), "unexpected result {res:?}");
1919        assert!(camera1_muted.load(Ordering::Relaxed));
1920        assert!(camera2_muted.load(Ordering::Relaxed));
1921    }
1922
1923    #[fuchsia::test]
1924    async fn test_camera_added_reapplies_stored_mute() {
1925        use std::sync::atomic::Ordering;
1926
1927        // The stored MUTED state is applied to both cameras at startup.
1928        let (mut controller, camera1_muted, camera2_muted, mut value_rx) =
1929            setup_multi_camera_test_with_failures(true, vec![]).await;
1930        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1931        assert!(camera1_muted.load(Ordering::Relaxed));
1932
1933        // Camera 1 restarts, which resets its software mute state, and is reported as added.
1934        // The watcher then reports the cameras as unmuted.
1935        camera1_muted.store(false, Ordering::Relaxed);
1936        assert_eq!(controller.handle_camera_added(), None);
1937        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1938        let res = controller.handle_camera_event(false).await;
1939        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1940
1941        // The stored MUTED state is applied to the cameras again instead of being replaced.
1942        assert!(camera1_muted.load(Ordering::Relaxed));
1943        assert!(camera2_muted.load(Ordering::Relaxed));
1944        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1945        assert_eq!(
1946            InputController::get_cam_sw_state_from(
1947                &controller.get_stored_info().await.input_device_state
1948            )
1949            .unwrap(),
1950            DeviceState::MUTED
1951        );
1952        assert_eq!(
1953            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap(),
1954            DeviceState::MUTED
1955        );
1956        assert!(value_rx.try_recv().is_ok());
1957    }
1958
1959    #[fuchsia::test]
1960    async fn test_camera_added_while_unmuted_does_not_revert_later_external_unmute() {
1961        use std::sync::atomic::Ordering;
1962
1963        // Both cameras start unmuted with stored AVAILABLE.
1964        let (mut controller, camera1_muted, camera2_muted, _value_rx) =
1965            setup_multi_camera_test_with_failures(false, vec![]).await;
1966        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1967
1968        // A camera is reported as added, and the watcher reports the cameras as unmuted.
1969        assert_eq!(controller.handle_camera_added(), None);
1970        assert_eq!(controller.cam_sw_sync, CamSwSync::Pending);
1971        let res = controller.handle_camera_event(false).await;
1972        assert!(matches!(res, Ok(None)), "unexpected result {res:?}");
1973
1974        // Another client later mutes all cameras, then unmutes camera 1. The unmute is followed
1975        // rather than reverted.
1976        camera1_muted.store(true, Ordering::Relaxed);
1977        camera2_muted.store(true, Ordering::Relaxed);
1978        let res = controller.handle_camera_event(true).await;
1979        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1980        assert_eq!(controller.cam_sw_sync, CamSwSync::Synced);
1981
1982        camera1_muted.store(false, Ordering::Relaxed);
1983        let res = controller.handle_camera_event(false).await;
1984        assert!(matches!(res, Ok(Some(()))), "unexpected result {res:?}");
1985        assert!(!camera1_muted.load(Ordering::Relaxed));
1986        assert_eq!(
1987            InputController::get_cam_sw_state_from(
1988                &controller.get_stored_info().await.input_device_state
1989            )
1990            .unwrap(),
1991            DeviceState::AVAILABLE
1992        );
1993    }
1994
1995    #[fuchsia::test]
1996    async fn test_camera_events_converge_when_one_camera_keeps_failing() {
1997        use std::sync::atomic::Ordering;
1998
1999        // Both cameras start muted. Every push after restore() fails on camera 1.
2000        let (mut controller, camera1_muted, camera2_muted, _value_rx) =
2001            setup_multi_camera_test_with_failures(true, (1..100).collect()).await;
2002
2003        let unmute_device = InputDevice {
2004            name: DEFAULT_CAMERA_NAME.to_string(),
2005            device_type: InputDeviceType::CAMERA,
2006            source_states: [(DeviceStateSource::SOFTWARE, DeviceState::AVAILABLE)].into(),
2007            state: DeviceState::AVAILABLE,
2008        };
2009        let res =
2010            controller.set_input_states(vec![unmute_device], DeviceStateSource::SOFTWARE).await;
2011        assert!(res.is_err());
2012
2013        // Act as CameraWatcherAgent: report the aggregate mute state (muted only if all cameras
2014        // are muted) whenever it differs from the last reported value.
2015        let mut last_reported = true;
2016        let mut events = 0;
2017        loop {
2018            let aggregate =
2019                camera1_muted.load(Ordering::Relaxed) && camera2_muted.load(Ordering::Relaxed);
2020            if aggregate == last_reported {
2021                break;
2022            }
2023            assert!(events < 10, "camera events did not converge");
2024            last_reported = aggregate;
2025            events += 1;
2026            let res = controller.handle_camera_event(aggregate).await;
2027            assert!(res.is_ok(), "unexpected result {res:?}");
2028        }
2029
2030        // The reported state matches the state reported by the cameras, storage keeps the user's
2031        // MUTED preference, and the next set_input_states call still retries the push.
2032        assert_eq!(controller.cam_sw_sync, CamSwSync::Adopted);
2033        let reported_state =
2034            InputController::get_cam_sw_state_from(&controller.input_device_state).unwrap();
2035        assert_eq!(reported_state.has_state(DeviceState::MUTED), last_reported);
2036        assert_eq!(
2037            InputController::get_cam_sw_state_from(
2038                &controller.get_stored_info().await.input_device_state
2039            )
2040            .unwrap(),
2041            DeviceState::MUTED
2042        );
2043    }
2044}