settings_audio/
types.rs

1// Copyright 2021 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::audio_default_settings::{AudioInfoLoader, create_default_audio_stream};
6use crate::{ModifiedCounters, create_default_modified_counters};
7use anyhow::{Error, anyhow};
8use serde::{Deserialize, Serialize};
9use settings_common::inspect::event::{Nameable, ResponseType};
10use settings_storage::device_storage::DeviceStorageCompatible;
11use std::borrow::Cow;
12use std::collections::HashMap;
13use std::ops::RangeInclusive;
14
15const RANGE: RangeInclusive<f32> = 0.0..=1.0;
16
17#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
18pub enum AudioSettingSource {
19    User,
20    System,
21    SystemWithFeedback,
22}
23
24#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq)]
25pub enum AudioStreamType {
26    Background,
27    Media,
28    Interruption,
29    SystemAgent,
30    Communication,
31    Accessibility,
32}
33
34pub const AUDIO_STREAM_TYPE_COUNT: usize = 6;
35pub(crate) const LEGACY_AUDIO_STREAM_TYPE_COUNT: usize = 5;
36
37impl AudioStreamType {
38    /// Legacy stream types are the subset of AudioStreamType values which correspond to values in
39    /// |fuchsia.media.AudioRenderUsage|. FIDL tables |AudioSettings| and |AudioStreamSettings|,
40    /// and FIDL methods |Set| and |Watch|, are limited to these stream types.
41    ///
42    /// |fuchsia.media.AudioRenderUsage2| contains all the |AudioRenderUsage| values, and more.
43    /// |AudioStreamType| is based on |AudioRenderUsage2|, and |AudioRenderUsage2| is used with
44    /// tables |AudioSettings2| and |AudioStreamSettings2|, and with methods |Set2| and |Watch2|.
45    pub fn is_legacy(&self) -> bool {
46        matches!(
47            self,
48            AudioStreamType::Background
49                | AudioStreamType::Communication
50                | AudioStreamType::Interruption
51                | AudioStreamType::Media
52                | AudioStreamType::SystemAgent
53        )
54    }
55}
56
57#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
58pub struct AudioStream {
59    pub stream_type: AudioStreamType,
60    pub source: AudioSettingSource,
61    pub user_volume_level: f32,
62    pub user_volume_muted: bool,
63}
64
65impl AudioStream {
66    pub(crate) fn has_valid_volume_level(&self) -> bool {
67        RANGE.contains(&self.user_volume_level)
68    }
69}
70
71#[derive(PartialEq, Debug, Clone, Copy)]
72pub struct SetAudioStream {
73    pub stream_type: AudioStreamType,
74    pub source: AudioSettingSource,
75    pub user_volume_level: Option<f32>,
76    pub user_volume_muted: Option<bool>,
77}
78
79impl SetAudioStream {
80    pub(crate) fn has_valid_volume_level(&self) -> bool {
81        self.user_volume_level.map(|v| RANGE.contains(&v)).unwrap_or(true)
82    }
83
84    pub(crate) fn is_valid_payload(&self) -> bool {
85        self.user_volume_level.is_some() || self.user_volume_muted.is_some()
86    }
87}
88
89impl From<AudioStream> for SetAudioStream {
90    fn from(stream: AudioStream) -> Self {
91        Self {
92            stream_type: stream.stream_type,
93            source: stream.source,
94            user_volume_level: Some(stream.user_volume_level),
95            user_volume_muted: Some(stream.user_volume_muted),
96        }
97    }
98}
99
100#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
101pub struct AudioInfo {
102    pub streams: [AudioStream; AUDIO_STREAM_TYPE_COUNT],
103    pub modified_counters: Option<ModifiedCounters>,
104}
105
106impl Nameable for AudioInfo {
107    const NAME: &str = "Audio";
108}
109
110impl DeviceStorageCompatible for AudioInfo {
111    type Loader = AudioInfoLoader;
112    const KEY: &'static str = "audio_info";
113
114    fn try_deserialize_from(value: &str) -> Result<Self, Error> {
115        Self::extract(value).or_else(|_| AudioInfoV3::try_deserialize_from(value).map(Self::from))
116    }
117}
118
119// /////////////////////////////////////////////////////////////
120//  Past versions of AudioInfo.
121// /////////////////////////////////////////////////////////////
122
123/// The following struct should never be modified. It represents an old
124/// version of the audio settings.
125#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
126pub(crate) struct AudioInfoV3 {
127    pub streams: [AudioStream; LEGACY_AUDIO_STREAM_TYPE_COUNT],
128    pub modified_counters: Option<ModifiedCounters>,
129}
130
131impl AudioInfoV3 {
132    pub(super) fn try_deserialize_from(value: &str) -> Result<Self, Error> {
133        serde_json::from_str(value)
134            .map_err(|e| anyhow!("could not deserialize: {e:?}"))
135            .or_else(|_| AudioInfoV2::try_deserialize_from(value).map(Self::from))
136    }
137
138    #[cfg(test)]
139    pub(super) fn default_value(default_setting: AudioInfo) -> Self {
140        AudioInfoV3 {
141            streams: default_setting.streams[0..LEGACY_AUDIO_STREAM_TYPE_COUNT].try_into().unwrap(),
142            modified_counters: None,
143        }
144    }
145}
146
147impl From<AudioInfoV3> for AudioInfo {
148    fn from(v3: AudioInfoV3) -> AudioInfo {
149        let mut stream_vec = v3.streams.to_vec();
150        stream_vec.push(create_default_audio_stream(AudioStreamType::Accessibility));
151
152        AudioInfo {
153            streams: stream_vec.try_into().unwrap(),
154            modified_counters: v3.modified_counters,
155        }
156    }
157}
158
159#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
160pub struct AudioInputInfo {
161    pub mic_mute: bool,
162}
163
164/// The following struct should never be modified. It represents an old
165/// version of the audio settings.
166#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
167pub(crate) struct AudioInfoV2 {
168    pub streams: [AudioStream; LEGACY_AUDIO_STREAM_TYPE_COUNT],
169    pub input: AudioInputInfo,
170    pub modified_counters: Option<ModifiedCounters>,
171}
172
173impl AudioInfoV2 {
174    pub(super) fn try_deserialize_from(value: &str) -> Result<Self, Error> {
175        serde_json::from_str(value)
176            .map_err(|e| anyhow!("could not deserialize: {e:?}"))
177            .or_else(|_| AudioInfoV1::try_deserialize_from(value).map(Self::from))
178    }
179
180    #[cfg(test)]
181    pub(super) fn default_value(default_setting: AudioInfo) -> Self {
182        AudioInfoV2 {
183            streams: default_setting.streams[0..LEGACY_AUDIO_STREAM_TYPE_COUNT].try_into().unwrap(),
184            input: AudioInputInfo { mic_mute: false },
185            modified_counters: None,
186        }
187    }
188}
189
190impl From<AudioInfoV2> for AudioInfoV3 {
191    fn from(v2: AudioInfoV2) -> AudioInfoV3 {
192        AudioInfoV3 { streams: v2.streams, modified_counters: v2.modified_counters }
193    }
194}
195
196/// The following struct should never be modified. It represents an old
197/// version of the audio settings.
198#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
199pub(crate) struct AudioInfoV1 {
200    pub streams: [AudioStream; LEGACY_AUDIO_STREAM_TYPE_COUNT],
201    pub input: AudioInputInfo,
202    pub modified_timestamps: Option<HashMap<AudioStreamType, String>>,
203}
204
205impl AudioInfoV1 {
206    pub(super) fn try_deserialize_from(value: &str) -> Result<Self, Error> {
207        serde_json::from_str(value)
208            .map_err(|e| anyhow!("could not deserialize: {e:?}"))
209            .or_else(|_| AudioInfoV1::try_deserialize_from(value))
210    }
211
212    #[cfg(test)]
213    pub(super) fn default_value(default_setting: AudioInfo) -> Self {
214        AudioInfoV1 {
215            streams: default_setting.streams[0..LEGACY_AUDIO_STREAM_TYPE_COUNT].try_into().unwrap(),
216            input: AudioInputInfo { mic_mute: false },
217            modified_timestamps: None,
218        }
219    }
220}
221
222impl From<AudioInfoV1> for AudioInfoV2 {
223    fn from(v1: AudioInfoV1) -> Self {
224        AudioInfoV2 {
225            streams: v1.streams,
226            input: v1.input,
227            modified_counters: Some(create_default_modified_counters()),
228        }
229    }
230}
231
232#[derive(thiserror::Error, Debug)]
233pub enum AudioError {
234    #[error("Invalid argument for audio: argument:{0:?} value:{1:?}")]
235    InvalidArgument(&'static str, String),
236    #[error("Write failed for audio: {0:?}")]
237    WriteFailure(Error),
238    #[error("External failure for audio: dependency: {0:?} request:{1:?} error:{2}")]
239    ExternalFailure(&'static str, Cow<'static, str>, String),
240}
241
242impl From<&AudioError> for ResponseType {
243    fn from(error: &AudioError) -> Self {
244        match error {
245            AudioError::InvalidArgument(..) => ResponseType::InvalidArgument,
246            AudioError::WriteFailure(..) => ResponseType::StorageFailure,
247            AudioError::ExternalFailure(..) => ResponseType::ExternalFailure,
248        }
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    const VALID_AUDIO_STREAM: AudioStream = AudioStream {
257        stream_type: AudioStreamType::Background,
258        source: AudioSettingSource::User,
259        user_volume_level: 0.5,
260        user_volume_muted: false,
261    };
262    const INVALID_NEGATIVE_AUDIO_STREAM: AudioStream =
263        AudioStream { user_volume_level: -0.1, ..VALID_AUDIO_STREAM };
264    const INVALID_GREATER_THAN_ONE_AUDIO_STREAM: AudioStream =
265        AudioStream { user_volume_level: 1.1, ..VALID_AUDIO_STREAM };
266
267    #[fuchsia::test]
268    fn test_volume_level_validation() {
269        assert!(VALID_AUDIO_STREAM.has_valid_volume_level());
270        assert!(!INVALID_NEGATIVE_AUDIO_STREAM.has_valid_volume_level());
271        assert!(!INVALID_GREATER_THAN_ONE_AUDIO_STREAM.has_valid_volume_level());
272    }
273
274    #[fuchsia::test]
275    fn test_set_audio_stream_validation() {
276        let valid_set_audio_stream = SetAudioStream::from(VALID_AUDIO_STREAM);
277        assert!(valid_set_audio_stream.has_valid_volume_level());
278        let invalid_set_audio_stream = SetAudioStream::from(INVALID_NEGATIVE_AUDIO_STREAM);
279        assert!(!invalid_set_audio_stream.has_valid_volume_level());
280        let invalid_set_audio_stream = SetAudioStream::from(INVALID_GREATER_THAN_ONE_AUDIO_STREAM);
281        assert!(!invalid_set_audio_stream.has_valid_volume_level());
282    }
283}