1use std::collections::hash_map::DefaultHasher;
6use std::hash::{Hash, Hasher};
7
8use fidl::prelude::*;
9use fidl_fuchsia_media::{AudioRenderUsage, AudioRenderUsage2};
10use fidl_fuchsia_settings::{
11 AudioMarker, AudioRequest, AudioSet2Responder, AudioSet2Result, AudioSetResponder,
12 AudioSetResult, AudioSettings, AudioSettings2, AudioStreamSettingSource, AudioStreamSettings,
13 AudioStreamSettings2, AudioWatch2Responder, AudioWatchResponder, Volume,
14};
15use fuchsia_trace as ftrace;
16
17use crate::audio::types::{AudioSettingSource, AudioStream, AudioStreamType, SetAudioStream};
18use crate::base::{SettingInfo, SettingType};
19use crate::handler::base::Request;
20use crate::ingress::{request, watch, Scoped};
21use crate::job::source::{Error as JobError, ErrorResponder};
22use crate::job::Job;
23use crate::{trace, trace_guard};
24
25struct AudioSetTraceResponder {
28 responder: AudioSetResponder,
29 _guard: Option<ftrace::AsyncScope>,
30}
31impl request::Responder<Scoped<AudioSetResult>> for AudioSetTraceResponder {
32 fn respond(self, Scoped(response): Scoped<AudioSetResult>) {
33 let _ = self.responder.send(response);
34 }
35}
36impl ErrorResponder for AudioSetTraceResponder {
37 fn id(&self) -> &'static str {
38 "Audio_Set"
39 }
40
41 fn respond(self: Box<Self>, error: fidl_fuchsia_settings::Error) -> Result<(), fidl::Error> {
42 self.responder.send(Err(error))
43 }
44}
45impl request::Responder<Scoped<AudioSetResult>> for AudioSetResponder {
46 fn respond(self, Scoped(response): Scoped<AudioSetResult>) {
47 let _ = self.send(response);
48 }
49}
50
51impl watch::Responder<AudioSettings, zx::Status> for AudioWatchResponder {
52 fn respond(self, response: Result<AudioSettings, zx::Status>) {
53 match response {
54 Ok(settings) => {
55 let _ = self.send(&settings);
56 }
57 Err(error) => {
58 self.control_handle().shutdown_with_epitaph(error);
59 }
60 }
61 }
62}
63
64struct AudioSet2TraceResponder {
67 responder: AudioSet2Responder,
68 _guard: Option<ftrace::AsyncScope>,
69}
70impl request::Responder<Scoped<AudioSet2Result>> for AudioSet2TraceResponder {
71 fn respond(self, Scoped(response): Scoped<AudioSet2Result>) {
72 let _ = self.responder.send(response);
73 }
74}
75impl ErrorResponder for AudioSet2TraceResponder {
76 fn id(&self) -> &'static str {
77 "Audio_Set2"
78 }
79
80 fn respond(self: Box<Self>, error: fidl_fuchsia_settings::Error) -> Result<(), fidl::Error> {
81 self.responder.send(Err(error))
82 }
83}
84impl request::Responder<Scoped<AudioSet2Result>> for AudioSet2Responder {
85 fn respond(self, Scoped(response): Scoped<AudioSet2Result>) {
86 let _ = self.send(response);
87 }
88}
89
90impl watch::Responder<AudioSettings2, zx::Status> for AudioWatch2Responder {
91 fn respond(self, response: Result<AudioSettings2, zx::Status>) {
92 match response {
93 Ok(settings) => {
94 let _ = self.send(&settings);
95 }
96 Err(error) => {
97 self.control_handle().shutdown_with_epitaph(error);
98 }
99 }
100 }
101}
102
103impl TryFrom<AudioRequest> for Job {
104 type Error = JobError;
105
106 fn try_from(item: AudioRequest) -> Result<Self, Self::Error> {
107 #[allow(unreachable_patterns)]
108 match item {
109 AudioRequest::Set { settings, responder } => {
110 let id = ftrace::Id::new();
111 let guard = trace_guard!(id, c"audio fidl handler set");
112 let responder = AudioSetTraceResponder { responder, _guard: guard };
113 match to_request(settings, id) {
114 Ok(request) => {
115 Ok(request::Work::new(SettingType::Audio, request, responder).into())
116 }
117 Err(err) => {
118 log::error!(
119 "{}: Failed to process 'set' request: {:?}",
120 AudioMarker::DEBUG_NAME,
121 err
122 );
123 Err(JobError::InvalidInput(Box::new(responder)))
124 }
125 }
126 }
127 AudioRequest::Set2 { settings, responder } => {
128 let id = ftrace::Id::new();
129 let guard = trace_guard!(id, c"audio fidl handler set2");
130 let responder = AudioSet2TraceResponder { responder, _guard: guard };
131 match to_request2(settings, id) {
132 Ok(request) => {
133 Ok(request::Work::new(SettingType::Audio, request, responder).into())
134 }
135 Err(err) => {
136 log::error!(
137 "{}: Failed2 to process 'set2' request: {:?}",
138 AudioMarker::DEBUG_NAME,
139 err
140 );
141 Err(JobError::InvalidInput(Box::new(responder)))
142 }
143 }
144 }
145 AudioRequest::Watch { responder } => {
146 let mut hasher = DefaultHasher::new();
147 "audio_watch".hash(&mut hasher);
148 Ok(watch::Work::new_job_with_change_function(
153 SettingType::Audio,
154 responder,
155 watch::ChangeFunction::new(
156 hasher.finish(),
157 Box::new(move |old: &SettingInfo, new: &SettingInfo| match (old, new) {
158 (SettingInfo::Audio(old_info), SettingInfo::Audio(new_info)) => {
159 let mut old_streams = old_info.streams.iter();
160 let new_streams = new_info.streams.iter();
161 for new_stream in new_streams {
162 let old_stream = old_streams
163 .find(|stream| stream.stream_type == new_stream.stream_type)
164 .expect("stream type should be found in existing streams");
165 if (old_stream != new_stream)
167 && new_stream.stream_type.is_legacy()
168 {
169 return true;
170 }
171 }
172 false
173 }
174 _ => false,
175 }),
176 ),
177 ))
178 }
179 AudioRequest::Watch2 { responder } => {
180 let mut hasher = DefaultHasher::new();
181 "audio_watch2".hash(&mut hasher);
182 Ok(watch::Work::new_job_with_change_function(
187 SettingType::Audio,
188 responder,
189 watch::ChangeFunction::new(
190 hasher.finish(),
191 Box::new(move |old: &SettingInfo, new: &SettingInfo| match (old, new) {
192 (SettingInfo::Audio(old_info), SettingInfo::Audio(new_info)) => {
193 let mut old_streams = old_info.streams.iter();
194 let new_streams = new_info.streams.iter();
195 for new_stream in new_streams {
196 let old_stream = old_streams
197 .find(|stream| stream.stream_type == new_stream.stream_type)
198 .expect("stream type should be found in existing streams");
199 if old_stream != new_stream {
201 return true;
202 }
203 }
204 false
205 }
206 _ => false,
207 }),
208 ),
209 ))
210 }
211 _ => {
212 log::warn!("Received a call to an unsupported API: {:?}", item);
213 Err(JobError::Unsupported)
214 }
215 }
216 }
217}
218
219impl From<SettingInfo> for AudioSettings {
220 fn from(response: SettingInfo) -> Self {
221 if let SettingInfo::Audio(info) = response {
222 let mut streams = Vec::new();
223 for stream in &info.streams {
224 let stream_settings = AudioStreamSettings::try_from(*stream);
225 if stream_settings.is_ok() {
226 streams.push(stream_settings.unwrap());
227 }
228 }
229
230 AudioSettings { streams: Some(streams), ..Default::default() }
231 } else {
232 panic!("incorrect value sent to audio");
233 }
234 }
235}
236
237impl From<SettingInfo> for AudioSettings2 {
238 fn from(response: SettingInfo) -> Self {
239 if let SettingInfo::Audio(info) = response {
240 let mut streams = Vec::new();
241 for stream in &info.streams {
242 streams.push(AudioStreamSettings2::from(*stream));
243 }
244
245 AudioSettings2 { streams: Some(streams), ..Default::default() }
246 } else {
247 panic!("incorrect value sent to audio");
248 }
249 }
250}
251
252impl TryFrom<AudioStream> for AudioStreamSettings {
253 type Error = ();
254
255 fn try_from(stream: AudioStream) -> Result<Self, ()> {
256 match AudioRenderUsage::try_from(stream.stream_type) {
257 Err(_) => Err(()),
258 Ok(stream_type) => Ok(AudioStreamSettings {
259 stream: Some(stream_type),
260 source: Some(AudioStreamSettingSource::from(stream.source)),
261 user_volume: Some(Volume {
262 level: Some(stream.user_volume_level),
263 muted: Some(stream.user_volume_muted),
264 ..Default::default()
265 }),
266 ..Default::default()
267 }),
268 }
269 }
270}
271
272impl From<AudioStream> for AudioStreamSettings2 {
273 fn from(stream: AudioStream) -> Self {
274 AudioStreamSettings2 {
275 stream: Some(AudioRenderUsage2::from(stream.stream_type)),
276 source: Some(AudioStreamSettingSource::from(stream.source)),
277 user_volume: Some(Volume {
278 level: Some(stream.user_volume_level),
279 muted: Some(stream.user_volume_muted),
280 ..Default::default()
281 }),
282 ..Default::default()
283 }
284 }
285}
286
287impl From<AudioRenderUsage> for AudioStreamType {
288 fn from(usage: AudioRenderUsage) -> Self {
289 match usage {
290 AudioRenderUsage::Background => AudioStreamType::Background,
291 AudioRenderUsage::Communication => AudioStreamType::Communication,
292 AudioRenderUsage::Interruption => AudioStreamType::Interruption,
293 AudioRenderUsage::Media => AudioStreamType::Media,
294 AudioRenderUsage::SystemAgent => AudioStreamType::SystemAgent,
295 }
296 }
297}
298
299impl TryFrom<AudioStreamType> for AudioRenderUsage {
300 type Error = ();
301 fn try_from(usage: AudioStreamType) -> Result<Self, Self::Error> {
302 match usage {
303 AudioStreamType::Accessibility => Err(()),
304 AudioStreamType::Background => Ok(AudioRenderUsage::Background),
305 AudioStreamType::Communication => Ok(AudioRenderUsage::Communication),
306 AudioStreamType::Interruption => Ok(AudioRenderUsage::Interruption),
307 AudioStreamType::Media => Ok(AudioRenderUsage::Media),
308 AudioStreamType::SystemAgent => Ok(AudioRenderUsage::SystemAgent),
309 }
310 }
311}
312
313impl From<AudioStreamType> for AudioRenderUsage2 {
314 fn from(usage: AudioStreamType) -> Self {
315 match usage {
316 AudioStreamType::Accessibility => AudioRenderUsage2::Accessibility,
317 AudioStreamType::Background => AudioRenderUsage2::Background,
318 AudioStreamType::Communication => AudioRenderUsage2::Communication,
319 AudioStreamType::Interruption => AudioRenderUsage2::Interruption,
320 AudioStreamType::Media => AudioRenderUsage2::Media,
321 AudioStreamType::SystemAgent => AudioRenderUsage2::SystemAgent,
322 }
323 }
324}
325
326impl TryFrom<AudioRenderUsage2> for AudioStreamType {
327 type Error = ();
328 fn try_from(usage: AudioRenderUsage2) -> Result<Self, Self::Error> {
329 match usage {
330 AudioRenderUsage2::Accessibility => Ok(AudioStreamType::Accessibility),
331 AudioRenderUsage2::Background => Ok(AudioStreamType::Background),
332 AudioRenderUsage2::Communication => Ok(AudioStreamType::Communication),
333 AudioRenderUsage2::Interruption => Ok(AudioStreamType::Interruption),
334 AudioRenderUsage2::Media => Ok(AudioStreamType::Media),
335 AudioRenderUsage2::SystemAgent => Ok(AudioStreamType::SystemAgent),
336 _ => Err(()),
337 }
338 }
339}
340
341impl From<AudioStreamSettingSource> for AudioSettingSource {
342 fn from(source: AudioStreamSettingSource) -> Self {
343 match source {
344 AudioStreamSettingSource::User => AudioSettingSource::User,
345 AudioStreamSettingSource::System => AudioSettingSource::System,
346 AudioStreamSettingSource::SystemWithFeedback => AudioSettingSource::SystemWithFeedback,
347 }
348 }
349}
350
351impl From<AudioSettingSource> for AudioStreamSettingSource {
352 fn from(source: AudioSettingSource) -> Self {
353 match source {
354 AudioSettingSource::User => AudioStreamSettingSource::User,
355 AudioSettingSource::System => AudioStreamSettingSource::System,
356 AudioSettingSource::SystemWithFeedback => AudioStreamSettingSource::SystemWithFeedback,
357 }
358 }
359}
360
361#[allow(clippy::enum_variant_names)]
363#[derive(thiserror::Error, Debug, PartialEq)]
364enum Error {
365 #[error("request has no streams")]
366 NoStreams,
367 #[error("missing user_volume at stream {0}")]
368 NoUserVolume(usize),
369 #[error("missing user_volume.level and user_volume.muted at stream {0}")]
370 MissingVolumeAndMuted(usize),
371 #[error("missing stream at stream {0}")]
372 NoStreamType(usize),
373 #[error("missing source at stream {0}")]
374 NoSource(usize),
375 #[error("request has an unknown stream type")]
376 UnrecognizedStreamType,
377}
378
379fn to_request(settings: AudioSettings, id: ftrace::Id) -> Result<Request, Error> {
380 trace!(id, c"to_request");
381 settings
382 .streams
383 .map(|streams| {
384 streams
385 .into_iter()
386 .enumerate()
387 .map(|(i, stream)| {
388 let user_volume = stream.user_volume.ok_or(Error::NoUserVolume(i))?;
389 let user_volume_level = user_volume.level;
390 let user_volume_muted = user_volume.muted;
391 let stream_type = stream.stream.ok_or(Error::NoStreamType(i))?.into();
392 let source = stream.source.ok_or(Error::NoSource(i))?.into();
393 let request = SetAudioStream {
394 stream_type,
395 source,
396 user_volume_level,
397 user_volume_muted,
398 };
399 if request.is_valid_payload() {
400 Ok(request)
401 } else {
402 Err(Error::MissingVolumeAndMuted(i))
403 }
404 })
405 .collect::<Result<Vec<_>, _>>()
406 .map(|streams| Request::SetVolume(streams, id))
407 })
408 .unwrap_or(Err(Error::NoStreams))
409}
410
411fn to_request2(settings: AudioSettings2, id: ftrace::Id) -> Result<Request, Error> {
412 trace!(id, c"to_request2");
413 settings
414 .streams
415 .map(|streams| {
416 streams
417 .into_iter()
418 .enumerate()
419 .map(|(i, stream)| {
420 let user_volume = stream.user_volume.ok_or(Error::NoUserVolume(i))?;
421 let user_volume_level = user_volume.level;
422 let user_volume_muted = user_volume.muted;
423 let stream_type = match stream.stream.ok_or(Error::NoStreamType(i))?.try_into()
424 {
425 Ok(stream_type) => Ok(stream_type),
426 Err(_) => Err(Error::UnrecognizedStreamType),
427 }?;
428 let source = stream.source.ok_or(Error::NoSource(i))?.into();
429 let request = SetAudioStream {
430 stream_type,
431 source,
432 user_volume_level,
433 user_volume_muted,
434 };
435 if request.is_valid_payload() {
436 Ok(request)
437 } else {
438 Err(Error::MissingVolumeAndMuted(i))
439 }
440 })
441 .collect::<Result<Vec<_>, _>>()
442 .map(|streams| Request::SetVolume(streams, id))
443 })
444 .unwrap_or(Err(Error::NoStreams))
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450
451 fn test_stream() -> AudioStreamSettings {
452 AudioStreamSettings {
453 stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media),
454 source: Some(AudioStreamSettingSource::User),
455 user_volume: Some(Volume {
456 level: Some(0.6),
457 muted: Some(false),
458 ..Default::default()
459 }),
460 ..Default::default()
461 }
462 }
463
464 fn test_stream2() -> AudioStreamSettings2 {
465 AudioStreamSettings2 {
466 stream: Some(fidl_fuchsia_media::AudioRenderUsage2::Media),
467 source: Some(AudioStreamSettingSource::User),
468 user_volume: Some(Volume {
469 level: Some(0.6),
470 muted: Some(false),
471 ..Default::default()
472 }),
473 ..Default::default()
474 }
475 }
476
477 #[fuchsia::test]
479 fn test_request_from_settings_empty() {
480 let id = ftrace::Id::new();
481 let request = to_request(AudioSettings::default(), id);
482
483 assert_eq!(request, Err(Error::NoStreams));
484 }
485
486 #[fuchsia::test]
488 fn test_request2_from_settings_empty() {
489 let id = ftrace::Id::new();
490 let request = to_request2(AudioSettings2::default(), id);
491
492 assert_eq!(request, Err(Error::NoStreams));
493 }
494
495 #[fuchsia::test]
497 fn test_request_missing_user_volume() {
498 let mut stream = test_stream();
499 stream.user_volume = None;
500
501 let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
502
503 let id = ftrace::Id::new();
504 let request = to_request(audio_settings, id);
505
506 assert_eq!(request, Err(Error::NoUserVolume(0)));
507 }
508
509 #[fuchsia::test]
511 fn test_request2_missing_user_volume() {
512 let mut stream = test_stream2();
513 stream.user_volume = None;
514
515 let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
516
517 let id = ftrace::Id::new();
518 let request = to_request2(audio_settings, id);
519
520 assert_eq!(request, Err(Error::NoUserVolume(0)));
521 }
522
523 #[fuchsia::test]
525 fn test_request_missing_stream_type() {
526 let mut stream = test_stream();
527 stream.stream = None;
528
529 let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
530
531 let id = ftrace::Id::new();
532 let request = to_request(audio_settings, id);
533
534 assert_eq!(request, Err(Error::NoStreamType(0)));
535 }
536
537 #[fuchsia::test]
539 fn test_request2_missing_stream_type() {
540 let mut stream = test_stream2();
541 stream.stream = None;
542
543 let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
544
545 let id = ftrace::Id::new();
546 let request = to_request2(audio_settings, id);
547
548 assert_eq!(request, Err(Error::NoStreamType(0)));
549 }
550
551 #[fuchsia::test]
553 fn test_request_missing_source() {
554 let mut stream = test_stream();
555 stream.source = None;
556
557 let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
558
559 let id = ftrace::Id::new();
560 let request = to_request(audio_settings, id);
561
562 assert_eq!(request, Err(Error::NoSource(0)));
563 }
564
565 #[fuchsia::test]
567 fn test_request2_missing_source() {
568 let mut stream = test_stream2();
569 stream.source = None;
570
571 let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
572
573 let id = ftrace::Id::new();
574 let request = to_request2(audio_settings, id);
575
576 assert_eq!(request, Err(Error::NoSource(0)));
577 }
578
579 #[fuchsia::test]
582 fn test_request_missing_user_volume_level_and_muted() {
583 let mut stream = test_stream();
584 stream.user_volume = Some(Volume { level: None, muted: None, ..Default::default() });
585
586 let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
587
588 let id = ftrace::Id::new();
589 let request = to_request(audio_settings, id);
590
591 assert_eq!(request, Err(Error::MissingVolumeAndMuted(0)));
592 }
593
594 #[fuchsia::test]
597 fn test_request2_missing_user_volume_level_and_muted() {
598 let mut stream = test_stream2();
599 stream.user_volume = Some(Volume { level: None, muted: None, ..Default::default() });
600
601 let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
602
603 let id = ftrace::Id::new();
604 let request = to_request2(audio_settings, id);
605
606 assert_eq!(request, Err(Error::MissingVolumeAndMuted(0)));
607 }
608}