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