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