settings/input/
types.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::input_device_configuration::InputConfiguration;
6use settings_common::inspect::event::Nameable;
7use settings_storage::device_storage::DeviceStorageConvertible;
8
9use anyhow::Error;
10use bitflags::bitflags;
11use fidl_fuchsia_settings::{
12    DeviceState as FidlDeviceState, DeviceStateSource as FidlDeviceStateSource,
13    DeviceType as FidlDeviceType, InputDevice as FidlInputDevice,
14    InputSettings as FidlInputSettings, SourceState as FidlSourceState,
15    ToggleStateFlags as FidlToggleFlags,
16};
17use serde::{Deserialize, Serialize};
18use std::borrow::Cow;
19use std::collections::{HashMap, HashSet};
20use std::fmt;
21
22use super::input_controller::InputError;
23
24impl From<&InputInfo> for FidlInputSettings {
25    fn from(info: &InputInfo) -> Self {
26        FidlInputSettings {
27            devices: Some(
28                info.input_device_state
29                    .input_categories
30                    .iter()
31                    .flat_map(|(_, category)| {
32                        category.devices.iter().map(|(_, device)| device.clone().into())
33                    })
34                    .collect(),
35            ),
36            ..Default::default()
37        }
38    }
39}
40
41#[derive(PartialEq, Debug, Clone)]
42pub struct InputInfo {
43    pub input_device_state: InputState,
44}
45
46impl Nameable for InputInfo {
47    const NAME: &str = "Input";
48}
49
50impl DeviceStorageConvertible for InputInfo {
51    type Storable = InputInfoSources;
52
53    fn get_storable(&self) -> Cow<'_, Self::Storable> {
54        Cow::Owned(InputInfoSources { input_device_state: self.input_device_state.clone() })
55    }
56}
57
58#[derive(PartialEq, Default, Debug, Clone, Serialize, Deserialize)]
59#[serde(deny_unknown_fields)]
60pub struct InputInfoSources {
61    pub input_device_state: InputState,
62}
63
64#[derive(PartialEq, Default, Debug, Clone, Copy, Serialize, Deserialize)]
65// DO NOT USE - this type is deprecated and will be replaced by
66// the use of InputDevice.
67pub struct Microphone {
68    pub muted: bool,
69}
70
71#[derive(PartialEq, Debug, Default, Clone, Serialize, Deserialize)]
72/// The top-level struct for the input state. It categorizes the input devices
73/// by their device type.
74pub struct InputState {
75    /// The input devices categorized by device type.
76    pub input_categories: HashMap<InputDeviceType, InputCategory>,
77}
78
79impl InputState {
80    pub(crate) fn new() -> Self {
81        Self::default()
82    }
83
84    /// Insert an InputDevice's state into the internal InputState hierarchy, updating the
85    /// state if it already exists or adding the state if it does not.
86    pub(crate) fn insert_device(&mut self, input_device: InputDevice, source: DeviceStateSource) {
87        self.set_source_state(
88            input_device.device_type,
89            input_device.name,
90            source,
91            input_device.state,
92        );
93    }
94
95    /// Set the `state` for a given device and `source`.
96    /// The combination of `device_type` and `device_name`
97    /// uniquely identifies the device.
98    pub(crate) fn set_source_state(
99        &mut self,
100        device_type: InputDeviceType,
101        device_name: String,
102        source: DeviceStateSource,
103        state: DeviceState,
104    ) {
105        // Ensure the category has an entry in the categories map.
106        let category = self.input_categories.entry(device_type).or_default();
107
108        // Ensure the device has an entry in the devices map.
109        let input_device = category
110            .devices
111            .entry(device_name.clone())
112            .or_insert_with(|| InputDevice::new(device_name, device_type));
113
114        // Replace or add the source state in the map. Ignore the old value.
115        let _ = input_device.source_states.insert(source, state);
116        input_device.compute_input_state();
117    }
118
119    /// Retrieve the state of a given device for one of its `source`s.
120    /// The combination of `device_type` and `device_name`
121    /// uniquely identifies the device. Returns None if it fails to find
122    /// the corresponding state for the given arguments.
123    pub(crate) fn get_source_state(
124        &self,
125        device_type: InputDeviceType,
126        device_name: String,
127        source: DeviceStateSource,
128    ) -> Result<DeviceState, Error> {
129        Ok(*self
130            .input_categories
131            .get(&device_type)
132            .ok_or_else(|| {
133                InputError::UnexpectedError("Failed to get input category by input type".into())
134            })?
135            .devices
136            .get(&device_name)
137            .ok_or_else(|| {
138                InputError::UnexpectedError("Failed to get input device by device name".into())
139            })?
140            .source_states
141            .get(&source)
142            .ok_or_else(|| {
143                InputError::UnexpectedError("Failed to get state from source states".into())
144            })?)
145    }
146
147    /// Retrieve the overall state of a given device.
148    /// The combination of `device_type` and `device_name`
149    /// uniquely identifies the device. Returns None if it fails to find
150    /// the corresponding state for the given arguments.
151    #[cfg(test)]
152    pub(crate) fn get_state(
153        &self,
154        device_type: InputDeviceType,
155        device_name: String,
156    ) -> Result<DeviceState, Error> {
157        Ok(self
158            .input_categories
159            .get(&device_type)
160            .ok_or_else(|| {
161                InputError::UnexpectedError("Failed to get input category by input type".into())
162            })?
163            .devices
164            .get(&device_name)
165            .ok_or_else(|| {
166                InputError::UnexpectedError("Failed to get input device by device name".into())
167            })?
168            .state)
169    }
170
171    /// Returns true if the state map is empty.
172    pub(crate) fn is_empty(&self) -> bool {
173        self.input_categories.is_empty()
174    }
175
176    /// Returns a set of the `InputDeviceType`s contained in the
177    /// state map.
178    pub(crate) fn device_types(&self) -> HashSet<InputDeviceType> {
179        self.input_categories.keys().cloned().collect()
180    }
181}
182
183impl From<InputConfiguration> for InputState {
184    fn from(config: InputConfiguration) -> Self {
185        let mut categories = HashMap::<InputDeviceType, InputCategory>::new();
186        let devices = config.devices;
187
188        devices.iter().for_each(|device_config| {
189            // Ensure the category has an entry in the categories map.
190            let input_device_type = device_config.device_type;
191            let category = categories.entry(input_device_type).or_default();
192
193            // Ensure the device has an entry in the devices map.
194            let device_name = device_config.device_name.clone();
195            let device = category
196                .devices
197                .entry(device_name.clone())
198                .or_insert_with(|| InputDevice::new(device_name, input_device_type));
199
200            // Set the entry on the source states map.
201            device_config.source_states.iter().for_each(|source_state| {
202                let value = DeviceState::from_bits(source_state.state).unwrap_or_default();
203                // Ignore the old value.
204                let _ = device.source_states.insert(source_state.source, value);
205            });
206
207            // Recompute the overall state.
208            device.compute_input_state();
209        });
210        InputState { input_categories: categories }
211    }
212}
213
214#[derive(PartialEq, Debug, Default, Clone, Serialize, Deserialize)]
215pub struct InputCategory {
216    // Map of input devices in this category, identified by names.
217    // It is recommended that the name be the lower-case string
218    // representation of the device type if there is only one input
219    // device in this category.
220    pub devices: HashMap<String, InputDevice>,
221}
222
223#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
224pub struct InputDevice {
225    /// The unique name within the device type.
226    pub name: String,
227
228    /// The type of input device.
229    pub device_type: InputDeviceType,
230
231    /// The states for each source.
232    pub source_states: HashMap<DeviceStateSource, DeviceState>,
233
234    /// The overall state of the device considering the `source_state`s.
235    pub state: DeviceState,
236}
237
238impl InputDevice {
239    fn new(name: String, device_type: InputDeviceType) -> Self {
240        Self {
241            name,
242            device_type,
243            source_states: HashMap::<DeviceStateSource, DeviceState>::new(),
244            state: DeviceState::new(),
245        }
246    }
247
248    fn compute_input_state(&mut self) {
249        let mut computed_state = DeviceState::from_bits(0).unwrap();
250
251        for state in self.source_states.values() {
252            if state.has_error() {
253                computed_state |= DeviceState::ERROR;
254            }
255            if state.has_state(DeviceState::DISABLED) {
256                computed_state |= DeviceState::DISABLED | DeviceState::MUTED;
257            }
258            if state.has_state(DeviceState::MUTED) {
259                computed_state |= DeviceState::MUTED;
260            }
261            if state.has_state(DeviceState::ACTIVE) {
262                computed_state |= DeviceState::ACTIVE | DeviceState::AVAILABLE;
263            }
264        }
265
266        // If any source has ERROR, DISABLED, MUTED, or ACTIVE, the overall
267        // state is that state, in order of precedence. Otherwise, the overall state
268        // is AVAILABLE.
269        if computed_state.has_error() {
270            self.state = DeviceState::ERROR;
271        } else if computed_state.has_state(DeviceState::DISABLED) {
272            self.state = DeviceState::DISABLED | DeviceState::MUTED;
273        } else if computed_state.has_state(DeviceState::MUTED) {
274            self.state = DeviceState::MUTED;
275        } else if computed_state.has_state(DeviceState::ACTIVE) {
276            self.state = DeviceState::ACTIVE | DeviceState::AVAILABLE;
277        } else {
278            self.state = DeviceState::AVAILABLE;
279        }
280    }
281}
282
283impl From<InputDevice> for FidlInputDevice {
284    fn from(device: InputDevice) -> Self {
285        let mut result = FidlInputDevice::default();
286
287        // Convert source states.
288        let source_states = Some(
289            device
290                .source_states
291                .keys()
292                .map(|source| FidlSourceState {
293                    source: Some((*source).into()),
294                    state: Some(
295                        (*device.source_states.get(source).expect("Source state map key missing"))
296                            .into(),
297                    ),
298                    ..Default::default()
299                })
300                .collect(),
301        );
302
303        let mutable_toggle_state: FidlDeviceState =
304            DeviceState::default_mutable_toggle_state().into();
305        result.device_name = Some(device.name.clone());
306        result.device_type = Some(device.device_type.into());
307        result.source_states = source_states;
308        result.mutable_toggle_state = mutable_toggle_state.toggle_flags;
309        result.state = Some(device.state.into());
310        result
311    }
312}
313
314#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, Serialize, Deserialize)]
315#[allow(clippy::upper_case_acronyms)]
316pub enum InputDeviceType {
317    CAMERA,
318    MICROPHONE,
319}
320
321/// Instead of defining our own fmt function, an easier way
322/// is to derive the 'Display' trait for enums using `enum-display-derive` crate
323///
324/// <https://docs.rs/enum-display-derive/0.1.0/enum_display_derive/>
325///
326/// Since addition of this in third_party/rust_crates needs OSRB approval, we
327/// define our own function here.
328impl fmt::Display for InputDeviceType {
329    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
330        match self {
331            InputDeviceType::CAMERA => fmt.write_str("camera"),
332            InputDeviceType::MICROPHONE => fmt.write_str("microphone"),
333        }
334    }
335}
336
337impl From<FidlDeviceType> for InputDeviceType {
338    fn from(device_type: FidlDeviceType) -> Self {
339        match device_type {
340            FidlDeviceType::Camera => InputDeviceType::CAMERA,
341            FidlDeviceType::Microphone => InputDeviceType::MICROPHONE,
342        }
343    }
344}
345
346impl From<InputDeviceType> for FidlDeviceType {
347    fn from(device_type: InputDeviceType) -> Self {
348        match device_type {
349            InputDeviceType::CAMERA => FidlDeviceType::Camera,
350            InputDeviceType::MICROPHONE => FidlDeviceType::Microphone,
351        }
352    }
353}
354
355#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, Serialize, Deserialize)]
356#[allow(clippy::upper_case_acronyms)]
357pub enum DeviceStateSource {
358    HARDWARE,
359    SOFTWARE,
360}
361
362impl From<FidlDeviceStateSource> for DeviceStateSource {
363    fn from(device_state_source: FidlDeviceStateSource) -> Self {
364        match device_state_source {
365            FidlDeviceStateSource::Hardware => DeviceStateSource::HARDWARE,
366            FidlDeviceStateSource::Software => DeviceStateSource::SOFTWARE,
367        }
368    }
369}
370
371impl From<DeviceStateSource> for FidlDeviceStateSource {
372    fn from(device_state_source: DeviceStateSource) -> Self {
373        match device_state_source {
374            DeviceStateSource::HARDWARE => FidlDeviceStateSource::Hardware,
375            DeviceStateSource::SOFTWARE => FidlDeviceStateSource::Software,
376        }
377    }
378}
379
380bitflags! {
381    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
382    pub struct DeviceState : u64 {
383        const AVAILABLE = 0b00000001;
384        const ACTIVE = 0b00000010;
385        const MUTED = 0b00000100;
386        const DISABLED = 0b00001000;
387        const ERROR = 0b00010000;
388    }
389}
390
391impl Default for DeviceState {
392    fn default() -> Self {
393        Self::new()
394    }
395}
396
397impl DeviceState {
398    pub(crate) fn new() -> Self {
399        // Represents AVAILABLE as the default.
400        Self::AVAILABLE
401    }
402
403    /// The flags that clients can manipulate by default.
404    fn default_mutable_toggle_state() -> Self {
405        DeviceState::MUTED | DeviceState::DISABLED
406    }
407
408    /// Returns true if the current state contains the given state.
409    /// e.g. All the 1 bits in the given `state` are also 1s in the
410    /// current state.
411    pub(crate) fn has_state(&self, state: DeviceState) -> bool {
412        *self & state == state
413    }
414
415    /// Returns true if the device's state has an error.
416    fn has_error(&self) -> bool {
417        let is_err = *self & DeviceState::ERROR == DeviceState::ERROR;
418        let incompatible_state = self.has_state(DeviceState::ACTIVE | DeviceState::DISABLED)
419            || self.has_state(DeviceState::ACTIVE | DeviceState::MUTED)
420            || self.has_state(DeviceState::AVAILABLE | DeviceState::DISABLED)
421            || self.has_state(DeviceState::AVAILABLE | DeviceState::MUTED);
422        is_err || incompatible_state
423    }
424}
425
426impl From<FidlDeviceState> for DeviceState {
427    fn from(device_state: FidlDeviceState) -> Self {
428        if let Some(toggle_flags) = device_state.toggle_flags {
429            if let Some(res) = Self::from_bits(toggle_flags.bits()) {
430                return res;
431            }
432        }
433        Self::default_mutable_toggle_state()
434    }
435}
436
437impl From<DeviceState> for FidlDeviceState {
438    fn from(device_state: DeviceState) -> Self {
439        FidlDeviceState {
440            toggle_flags: FidlToggleFlags::from_bits(device_state.bits()),
441            ..Default::default()
442        }
443    }
444}
445
446bitflags_serde_legacy::impl_traits!(DeviceState);
447
448#[cfg(test)]
449mod tests {
450    use super::*;
451    use crate::input::input_device_configuration::{InputDeviceConfiguration, SourceState};
452
453    const DEFAULT_MIC_NAME: &str = "microphone";
454    const DEFAULT_CAMERA_NAME: &str = "camera";
455    const AVAILABLE_BITS: u64 = 1;
456    const MUTED_BITS: u64 = 4;
457    const MUTED_DISABLED_BITS: u64 = 12;
458
459    /// Helper to create a `FidlInputDevice`.
460    fn create_fidl_input_device(
461        device_name: &str,
462        device_type: FidlDeviceType,
463        sw_bits: u64,
464        hw_bits: u64,
465        overall_bits: u64,
466    ) -> FidlInputDevice {
467        FidlInputDevice {
468            device_name: Some(device_name.to_string()),
469            device_type: Some(device_type),
470            source_states: Some(vec![
471                FidlSourceState {
472                    source: Some(FidlDeviceStateSource::Hardware),
473                    state: Some(FidlDeviceState {
474                        toggle_flags: FidlToggleFlags::from_bits(hw_bits),
475                        ..Default::default()
476                    }),
477                    ..Default::default()
478                },
479                FidlSourceState {
480                    source: Some(FidlDeviceStateSource::Software),
481                    state: Some(FidlDeviceState {
482                        toggle_flags: FidlToggleFlags::from_bits(sw_bits),
483                        ..Default::default()
484                    }),
485                    ..Default::default()
486                },
487            ]),
488            mutable_toggle_state: FidlToggleFlags::from_bits(MUTED_DISABLED_BITS),
489            state: Some(FidlDeviceState {
490                toggle_flags: FidlToggleFlags::from_bits(overall_bits),
491                ..Default::default()
492            }),
493            ..Default::default()
494        }
495    }
496
497    /// Helper to create an [`InputDevice`].
498    fn create_input_device(
499        device_name: &str,
500        device_type: InputDeviceType,
501        sw_bits: u64,
502        hw_bits: u64,
503        overall_bits: u64,
504    ) -> InputDevice {
505        let mut input_device = InputDevice::new(device_name.to_string(), device_type);
506        let _ = input_device
507            .source_states
508            .insert(DeviceStateSource::SOFTWARE, DeviceState::from_bits(sw_bits).unwrap());
509        let _ = input_device
510            .source_states
511            .insert(DeviceStateSource::HARDWARE, DeviceState::from_bits(hw_bits).unwrap());
512        input_device.state = DeviceState::from_bits(overall_bits).unwrap();
513        input_device
514    }
515
516    /// Helper for creating the config for an `InputDevice`.
517    fn create_device_config(
518        device_name: &str,
519        device_type: InputDeviceType,
520        sw_state: u64,
521        hw_state: u64,
522    ) -> InputDeviceConfiguration {
523        InputDeviceConfiguration {
524            device_name: device_name.to_string(),
525            device_type,
526            source_states: vec![
527                SourceState { source: DeviceStateSource::SOFTWARE, state: sw_state },
528                SourceState { source: DeviceStateSource::HARDWARE, state: hw_state },
529            ],
530            mutable_toggle_state: MUTED_DISABLED_BITS,
531        }
532    }
533
534    /// Helper for verifying the equality of a `FidlInputDevice`. Cannot directly
535    /// compare because the order of the source_states vector may vary.
536    fn verify_fidl_input_device_eq(res: FidlInputDevice, expected: FidlInputDevice) {
537        assert_eq!(res.device_name, expected.device_name);
538        assert_eq!(res.device_type, expected.device_type);
539        assert_eq!(res.mutable_toggle_state, expected.mutable_toggle_state);
540        assert_eq!(res.state, expected.state);
541        let res_source_states = res.source_states.unwrap();
542        for source_state in expected.source_states.unwrap() {
543            assert!(&res_source_states.contains(&source_state));
544        }
545    }
546
547    #[fuchsia::test]
548    fn test_input_state_manipulation() {
549        let mut input_state = InputState::new();
550
551        // Set the source state for each source and device type.
552        input_state.set_source_state(
553            InputDeviceType::MICROPHONE,
554            DEFAULT_MIC_NAME.to_string(),
555            DeviceStateSource::SOFTWARE,
556            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
557        );
558        input_state.set_source_state(
559            InputDeviceType::MICROPHONE,
560            DEFAULT_MIC_NAME.to_string(),
561            DeviceStateSource::HARDWARE,
562            DeviceState::from_bits(MUTED_BITS).unwrap(),
563        );
564        input_state.set_source_state(
565            InputDeviceType::CAMERA,
566            DEFAULT_CAMERA_NAME.to_string(),
567            DeviceStateSource::SOFTWARE,
568            DeviceState::from_bits(MUTED_BITS).unwrap(),
569        );
570        input_state.set_source_state(
571            InputDeviceType::CAMERA,
572            DEFAULT_CAMERA_NAME.to_string(),
573            DeviceStateSource::HARDWARE,
574            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
575        );
576
577        // Get the source state for each source and device type.
578        assert_eq!(
579            input_state
580                .get_source_state(
581                    InputDeviceType::MICROPHONE,
582                    DEFAULT_MIC_NAME.to_string(),
583                    DeviceStateSource::SOFTWARE
584                )
585                .unwrap(),
586            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
587        );
588        assert_eq!(
589            input_state
590                .get_source_state(
591                    InputDeviceType::MICROPHONE,
592                    DEFAULT_MIC_NAME.to_string(),
593                    DeviceStateSource::HARDWARE
594                )
595                .unwrap(),
596            DeviceState::from_bits(MUTED_BITS).unwrap(),
597        );
598        assert_eq!(
599            input_state
600                .get_source_state(
601                    InputDeviceType::CAMERA,
602                    DEFAULT_CAMERA_NAME.to_string(),
603                    DeviceStateSource::SOFTWARE
604                )
605                .unwrap(),
606            DeviceState::from_bits(MUTED_BITS).unwrap(),
607        );
608        assert_eq!(
609            input_state
610                .get_source_state(
611                    InputDeviceType::CAMERA,
612                    DEFAULT_CAMERA_NAME.to_string(),
613                    DeviceStateSource::HARDWARE
614                )
615                .unwrap(),
616            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
617        );
618
619        // Get the overall states for each device.
620        assert_eq!(
621            input_state
622                .get_state(InputDeviceType::MICROPHONE, DEFAULT_MIC_NAME.to_string())
623                .unwrap(),
624            DeviceState::from_bits(MUTED_BITS).unwrap(),
625        );
626        assert_eq!(
627            input_state
628                .get_state(InputDeviceType::CAMERA, DEFAULT_CAMERA_NAME.to_string())
629                .unwrap(),
630            DeviceState::from_bits(MUTED_BITS).unwrap(),
631        );
632
633        // Switch the mic hardware on.
634        input_state.set_source_state(
635            InputDeviceType::MICROPHONE,
636            DEFAULT_MIC_NAME.to_string(),
637            DeviceStateSource::HARDWARE,
638            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
639        );
640        assert_eq!(
641            input_state
642                .get_state(InputDeviceType::MICROPHONE, DEFAULT_MIC_NAME.to_string())
643                .unwrap(),
644            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
645        );
646
647        // Switch the camera software on.
648        input_state.set_source_state(
649            InputDeviceType::CAMERA,
650            DEFAULT_CAMERA_NAME.to_string(),
651            DeviceStateSource::SOFTWARE,
652            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
653        );
654        assert_eq!(
655            input_state
656                .get_state(InputDeviceType::CAMERA, DEFAULT_CAMERA_NAME.to_string())
657                .unwrap(),
658            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
659        );
660    }
661
662    #[fuchsia::test]
663    fn test_input_configuration_to_input_state() {
664        let config = InputConfiguration {
665            devices: vec![
666                create_device_config(
667                    DEFAULT_MIC_NAME,
668                    InputDeviceType::MICROPHONE,
669                    MUTED_BITS,
670                    AVAILABLE_BITS,
671                ),
672                create_device_config(
673                    DEFAULT_CAMERA_NAME,
674                    InputDeviceType::CAMERA,
675                    AVAILABLE_BITS,
676                    AVAILABLE_BITS,
677                ),
678                create_device_config(
679                    "camera2",
680                    InputDeviceType::CAMERA,
681                    AVAILABLE_BITS,
682                    MUTED_DISABLED_BITS,
683                ),
684            ],
685        };
686        let result: InputState = config.into();
687        assert_eq!(
688            result
689                .get_source_state(
690                    InputDeviceType::MICROPHONE,
691                    DEFAULT_MIC_NAME.to_string(),
692                    DeviceStateSource::SOFTWARE,
693                )
694                .unwrap(),
695            DeviceState::from_bits(MUTED_BITS).unwrap(),
696        );
697        assert_eq!(
698            result
699                .get_source_state(
700                    InputDeviceType::MICROPHONE,
701                    DEFAULT_MIC_NAME.to_string(),
702                    DeviceStateSource::HARDWARE,
703                )
704                .unwrap(),
705            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
706        );
707        assert_eq!(
708            result
709                .get_source_state(
710                    InputDeviceType::CAMERA,
711                    DEFAULT_CAMERA_NAME.to_string(),
712                    DeviceStateSource::SOFTWARE,
713                )
714                .unwrap(),
715            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
716        );
717        assert_eq!(
718            result
719                .get_source_state(
720                    InputDeviceType::CAMERA,
721                    DEFAULT_CAMERA_NAME.to_string(),
722                    DeviceStateSource::HARDWARE,
723                )
724                .unwrap(),
725            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
726        );
727        assert_eq!(
728            result
729                .get_source_state(
730                    InputDeviceType::CAMERA,
731                    "camera2".to_string(),
732                    DeviceStateSource::SOFTWARE,
733                )
734                .unwrap(),
735            DeviceState::from_bits(AVAILABLE_BITS).unwrap(),
736        );
737        assert_eq!(
738            result
739                .get_source_state(
740                    InputDeviceType::CAMERA,
741                    "camera2".to_string(),
742                    DeviceStateSource::HARDWARE,
743                )
744                .unwrap(),
745            DeviceState::from_bits(MUTED_DISABLED_BITS).unwrap(),
746        );
747    }
748
749    #[fuchsia::test]
750    /// Test that the combination of the input device's source states results
751    /// in the correct overall device state.
752    fn test_overall_state() {
753        // The last number doesn't matter here, it will be overwritten by the
754        // compute_input_state calls.
755        let mut mic_available = create_input_device(
756            DEFAULT_MIC_NAME,
757            InputDeviceType::MICROPHONE,
758            AVAILABLE_BITS,
759            AVAILABLE_BITS,
760            AVAILABLE_BITS,
761        );
762        let mut mic_disabled = create_input_device(
763            DEFAULT_MIC_NAME,
764            InputDeviceType::MICROPHONE,
765            MUTED_DISABLED_BITS,
766            AVAILABLE_BITS,
767            MUTED_DISABLED_BITS,
768        );
769        let mut mic_muted = create_input_device(
770            DEFAULT_MIC_NAME,
771            InputDeviceType::MICROPHONE,
772            AVAILABLE_BITS,
773            MUTED_BITS,
774            MUTED_BITS,
775        );
776        let mut mic_active = create_input_device(
777            DEFAULT_MIC_NAME,
778            InputDeviceType::MICROPHONE,
779            3,
780            AVAILABLE_BITS,
781            3,
782        );
783        let mut mic_error = create_input_device(
784            DEFAULT_MIC_NAME,
785            InputDeviceType::MICROPHONE,
786            10,
787            AVAILABLE_BITS,
788            16,
789        );
790
791        mic_available.compute_input_state();
792        mic_disabled.compute_input_state();
793        mic_muted.compute_input_state();
794        mic_active.compute_input_state();
795        mic_error.compute_input_state();
796
797        assert_eq!(mic_available.state, DeviceState::AVAILABLE);
798        assert_eq!(mic_disabled.state, DeviceState::DISABLED | DeviceState::MUTED);
799        assert_eq!(mic_muted.state, DeviceState::MUTED);
800        assert_eq!(mic_active.state, DeviceState::ACTIVE | DeviceState::AVAILABLE);
801        assert_eq!(mic_error.state, DeviceState::ERROR);
802    }
803
804    #[fuchsia::test]
805    fn test_input_device_to_fidl_input_device() {
806        let expected_mic: FidlInputDevice = create_fidl_input_device(
807            DEFAULT_MIC_NAME,
808            FidlDeviceType::Microphone,
809            AVAILABLE_BITS,
810            AVAILABLE_BITS,
811            AVAILABLE_BITS,
812        );
813        let expected_cam: FidlInputDevice = create_fidl_input_device(
814            DEFAULT_CAMERA_NAME,
815            FidlDeviceType::Camera,
816            AVAILABLE_BITS,
817            MUTED_BITS,
818            MUTED_BITS,
819        );
820
821        let mut mic = InputDevice::new(DEFAULT_MIC_NAME.to_string(), InputDeviceType::MICROPHONE);
822        let _ = mic
823            .source_states
824            .insert(DeviceStateSource::SOFTWARE, DeviceState::from_bits(AVAILABLE_BITS).unwrap());
825        let _ = mic
826            .source_states
827            .insert(DeviceStateSource::HARDWARE, DeviceState::from_bits(AVAILABLE_BITS).unwrap());
828        mic.state = DeviceState::from_bits(AVAILABLE_BITS).unwrap();
829
830        let mut cam = InputDevice::new(DEFAULT_CAMERA_NAME.to_string(), InputDeviceType::CAMERA);
831        let _ = cam
832            .source_states
833            .insert(DeviceStateSource::SOFTWARE, DeviceState::from_bits(AVAILABLE_BITS).unwrap());
834        let _ = cam
835            .source_states
836            .insert(DeviceStateSource::HARDWARE, DeviceState::from_bits(MUTED_BITS).unwrap());
837        cam.state = DeviceState::from_bits(MUTED_BITS).unwrap();
838
839        let mic_res: FidlInputDevice = mic.into();
840        let cam_res: FidlInputDevice = cam.into();
841
842        verify_fidl_input_device_eq(mic_res, expected_mic);
843        verify_fidl_input_device_eq(cam_res, expected_cam);
844    }
845
846    #[fuchsia::test]
847    fn test_input_device_type_to_string() {
848        assert_eq!(InputDeviceType::CAMERA.to_string(), DEFAULT_CAMERA_NAME);
849        assert_eq!(InputDeviceType::MICROPHONE.to_string(), DEFAULT_MIC_NAME);
850    }
851
852    #[fuchsia::test]
853    fn test_fidl_device_type_to_device_type() {
854        let cam_res: FidlDeviceType = InputDeviceType::CAMERA.into();
855        let mic_res: FidlDeviceType = InputDeviceType::MICROPHONE.into();
856        assert_eq!(cam_res, FidlDeviceType::Camera);
857        assert_eq!(mic_res, FidlDeviceType::Microphone);
858    }
859
860    #[fuchsia::test]
861    fn test_device_type_to_fidl_device_type() {
862        let cam_res: InputDeviceType = FidlDeviceType::Camera.into();
863        let mic_res: InputDeviceType = FidlDeviceType::Microphone.into();
864        assert_eq!(cam_res, InputDeviceType::CAMERA);
865        assert_eq!(mic_res, InputDeviceType::MICROPHONE);
866    }
867
868    #[fuchsia::test]
869    fn test_fidl_device_state_source_to_device_state_source() {
870        let hw_res: FidlDeviceStateSource = DeviceStateSource::HARDWARE.into();
871        let sw_res: FidlDeviceStateSource = DeviceStateSource::SOFTWARE.into();
872        assert_eq!(hw_res, FidlDeviceStateSource::Hardware);
873        assert_eq!(sw_res, FidlDeviceStateSource::Software);
874    }
875
876    #[fuchsia::test]
877    fn test_device_state_source_to_fidl_device_state_source() {
878        let hw_res: DeviceStateSource = FidlDeviceStateSource::Hardware.into();
879        let sw_res: DeviceStateSource = FidlDeviceStateSource::Software.into();
880        assert_eq!(hw_res, DeviceStateSource::HARDWARE);
881        assert_eq!(sw_res, DeviceStateSource::SOFTWARE);
882    }
883
884    #[fuchsia::test]
885    fn test_device_state_errors() {
886        let available_disabled = DeviceState::from_bits(9).unwrap();
887        let available_muted = DeviceState::from_bits(5).unwrap();
888        let active_muted = DeviceState::from_bits(6).unwrap();
889        let active_disabled = DeviceState::from_bits(10).unwrap();
890        assert!(available_disabled.has_error());
891        assert!(available_muted.has_error());
892        assert!(active_muted.has_error());
893        assert!(active_disabled.has_error());
894    }
895
896    #[fuchsia::test]
897    fn test_fidl_device_state_to_device_state() {
898        let device_state: DeviceState = FidlDeviceState {
899            toggle_flags: FidlToggleFlags::from_bits(MUTED_BITS),
900            ..Default::default()
901        }
902        .into();
903        assert_eq!(device_state, DeviceState::from_bits(MUTED_BITS).unwrap(),);
904    }
905
906    #[fuchsia::test]
907    fn test_device_state_to_fidl_device_state() {
908        let fidl_device_state: FidlDeviceState = DeviceState::from_bits(MUTED_BITS).unwrap().into();
909        assert_eq!(
910            fidl_device_state,
911            FidlDeviceState {
912                toggle_flags: FidlToggleFlags::from_bits(MUTED_BITS),
913                ..Default::default()
914            }
915        );
916    }
917}