Skip to main content

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