1use 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 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#[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#[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#[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}