settings/audio/
audio_fidl_handler.rs

1// Copyright 2019 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 super::audio_controller::{AudioController, Request};
6use super::types::{AudioError, AudioInfo};
7use crate::audio::types::{AudioSettingSource, AudioStream, AudioStreamType, SetAudioStream};
8use crate::{trace, trace_guard};
9use async_utils::hanging_get::server;
10use fidl_fuchsia_media::{AudioRenderUsage, AudioRenderUsage2};
11use fidl_fuchsia_settings::{
12    AudioRequest, AudioRequestStream, AudioSettings, AudioSettings2, AudioStreamSettingSource,
13    AudioStreamSettings, AudioStreamSettings2, AudioWatch2Responder, AudioWatchResponder,
14    Error as SettingsError, Volume,
15};
16use futures::StreamExt;
17use futures::channel::mpsc::UnboundedSender;
18use futures::channel::oneshot;
19use settings_common::inspect::event::{
20    RequestType, ResponseType, UsagePublisher, UsageResponsePublisher,
21};
22use {fuchsia_async as fasync, fuchsia_trace as ftrace};
23
24impl From<&AudioInfo> for AudioSettings {
25    fn from(info: &AudioInfo) -> Self {
26        let mut streams = Vec::new();
27        for stream in &info.streams {
28            let stream_settings = AudioStreamSettings::try_from(*stream);
29            if stream_settings.is_ok() {
30                streams.push(stream_settings.unwrap());
31            }
32        }
33
34        AudioSettings { streams: Some(streams), ..Default::default() }
35    }
36}
37
38impl From<&AudioInfo> for AudioSettings2 {
39    fn from(info: &AudioInfo) -> Self {
40        let mut streams = Vec::new();
41        for stream in &info.streams {
42            streams.push(AudioStreamSettings2::from(*stream));
43        }
44
45        AudioSettings2 { streams: Some(streams), ..Default::default() }
46    }
47}
48
49impl TryFrom<AudioStream> for AudioStreamSettings {
50    type Error = ();
51
52    fn try_from(stream: AudioStream) -> Result<Self, ()> {
53        match AudioRenderUsage::try_from(stream.stream_type) {
54            Err(_) => Err(()),
55            Ok(stream_type) => Ok(AudioStreamSettings {
56                stream: Some(stream_type),
57                source: Some(AudioStreamSettingSource::from(stream.source)),
58                user_volume: Some(Volume {
59                    level: Some(stream.user_volume_level),
60                    muted: Some(stream.user_volume_muted),
61                    ..Default::default()
62                }),
63                ..Default::default()
64            }),
65        }
66    }
67}
68
69impl From<AudioStream> for AudioStreamSettings2 {
70    fn from(stream: AudioStream) -> Self {
71        AudioStreamSettings2 {
72            stream: Some(AudioRenderUsage2::from(stream.stream_type)),
73            source: Some(AudioStreamSettingSource::from(stream.source)),
74            user_volume: Some(Volume {
75                level: Some(stream.user_volume_level),
76                muted: Some(stream.user_volume_muted),
77                ..Default::default()
78            }),
79            ..Default::default()
80        }
81    }
82}
83
84impl From<AudioRenderUsage> for AudioStreamType {
85    fn from(usage: AudioRenderUsage) -> Self {
86        match usage {
87            AudioRenderUsage::Background => AudioStreamType::Background,
88            AudioRenderUsage::Communication => AudioStreamType::Communication,
89            AudioRenderUsage::Interruption => AudioStreamType::Interruption,
90            AudioRenderUsage::Media => AudioStreamType::Media,
91            AudioRenderUsage::SystemAgent => AudioStreamType::SystemAgent,
92        }
93    }
94}
95
96impl TryFrom<AudioStreamType> for AudioRenderUsage {
97    type Error = ();
98    fn try_from(usage: AudioStreamType) -> Result<Self, Self::Error> {
99        match usage {
100            AudioStreamType::Accessibility => Err(()),
101            AudioStreamType::Background => Ok(AudioRenderUsage::Background),
102            AudioStreamType::Communication => Ok(AudioRenderUsage::Communication),
103            AudioStreamType::Interruption => Ok(AudioRenderUsage::Interruption),
104            AudioStreamType::Media => Ok(AudioRenderUsage::Media),
105            AudioStreamType::SystemAgent => Ok(AudioRenderUsage::SystemAgent),
106        }
107    }
108}
109
110impl From<AudioStreamType> for AudioRenderUsage2 {
111    fn from(usage: AudioStreamType) -> Self {
112        match usage {
113            AudioStreamType::Accessibility => AudioRenderUsage2::Accessibility,
114            AudioStreamType::Background => AudioRenderUsage2::Background,
115            AudioStreamType::Communication => AudioRenderUsage2::Communication,
116            AudioStreamType::Interruption => AudioRenderUsage2::Interruption,
117            AudioStreamType::Media => AudioRenderUsage2::Media,
118            AudioStreamType::SystemAgent => AudioRenderUsage2::SystemAgent,
119        }
120    }
121}
122
123impl TryFrom<AudioRenderUsage2> for AudioStreamType {
124    type Error = ();
125    fn try_from(usage: AudioRenderUsage2) -> Result<Self, Self::Error> {
126        match usage {
127            AudioRenderUsage2::Accessibility => Ok(AudioStreamType::Accessibility),
128            AudioRenderUsage2::Background => Ok(AudioStreamType::Background),
129            AudioRenderUsage2::Communication => Ok(AudioStreamType::Communication),
130            AudioRenderUsage2::Interruption => Ok(AudioStreamType::Interruption),
131            AudioRenderUsage2::Media => Ok(AudioStreamType::Media),
132            AudioRenderUsage2::SystemAgent => Ok(AudioStreamType::SystemAgent),
133            _ => Err(()),
134        }
135    }
136}
137
138impl From<AudioStreamSettingSource> for AudioSettingSource {
139    fn from(source: AudioStreamSettingSource) -> Self {
140        match source {
141            AudioStreamSettingSource::User => AudioSettingSource::User,
142            AudioStreamSettingSource::System => AudioSettingSource::System,
143            AudioStreamSettingSource::SystemWithFeedback => AudioSettingSource::SystemWithFeedback,
144        }
145    }
146}
147
148impl From<AudioSettingSource> for AudioStreamSettingSource {
149    fn from(source: AudioSettingSource) -> Self {
150        match source {
151            AudioSettingSource::User => AudioStreamSettingSource::User,
152            AudioSettingSource::System => AudioStreamSettingSource::System,
153            AudioSettingSource::SystemWithFeedback => AudioStreamSettingSource::SystemWithFeedback,
154        }
155    }
156}
157
158// Clippy warns about all variants starting with the same prefix `No`.
159#[allow(clippy::enum_variant_names)]
160#[derive(thiserror::Error, Debug, PartialEq)]
161enum Error {
162    #[error("request has no streams")]
163    NoStreams,
164    #[error("missing user_volume at stream {0}")]
165    NoUserVolume(usize),
166    #[error("missing user_volume.level and user_volume.muted at stream {0}")]
167    MissingVolumeAndMuted(usize),
168    #[error("missing stream at stream {0}")]
169    NoStreamType(usize),
170    #[error("missing source at stream {0}")]
171    NoSource(usize),
172    #[error("request has an unknown stream type")]
173    UnrecognizedStreamType,
174}
175
176fn to_request(settings: AudioSettings, id: ftrace::Id) -> Result<Vec<SetAudioStream>, Error> {
177    trace!(id, c"to_request");
178    settings
179        .streams
180        .map(|streams| {
181            streams
182                .into_iter()
183                .enumerate()
184                .map(|(i, stream)| {
185                    let user_volume = stream.user_volume.ok_or(Error::NoUserVolume(i))?;
186                    let user_volume_level = user_volume.level;
187                    let user_volume_muted = user_volume.muted;
188                    let stream_type = stream.stream.ok_or(Error::NoStreamType(i))?.into();
189                    let source = stream.source.ok_or(Error::NoSource(i))?.into();
190                    let request = SetAudioStream {
191                        stream_type,
192                        source,
193                        user_volume_level,
194                        user_volume_muted,
195                    };
196                    if request.is_valid_payload() {
197                        Ok(request)
198                    } else {
199                        Err(Error::MissingVolumeAndMuted(i))
200                    }
201                })
202                .collect::<Result<Vec<_>, _>>()
203        })
204        .unwrap_or(Err(Error::NoStreams))
205}
206
207fn to_request2(settings: AudioSettings2, id: ftrace::Id) -> Result<Vec<SetAudioStream>, Error> {
208    trace!(id, c"to_request2");
209    settings
210        .streams
211        .map(|streams| {
212            streams
213                .into_iter()
214                .enumerate()
215                .map(|(i, stream)| {
216                    let user_volume = stream.user_volume.ok_or(Error::NoUserVolume(i))?;
217                    let user_volume_level = user_volume.level;
218                    let user_volume_muted = user_volume.muted;
219                    let stream_type = match stream.stream.ok_or(Error::NoStreamType(i))?.try_into()
220                    {
221                        Ok(stream_type) => Ok(stream_type),
222                        Err(_) => Err(Error::UnrecognizedStreamType),
223                    }?;
224                    let source = stream.source.ok_or(Error::NoSource(i))?.into();
225                    let request = SetAudioStream {
226                        stream_type,
227                        source,
228                        user_volume_level,
229                        user_volume_muted,
230                    };
231                    if request.is_valid_payload() {
232                        Ok(request)
233                    } else {
234                        Err(Error::MissingVolumeAndMuted(i))
235                    }
236                })
237                .collect::<Result<Vec<_>, _>>()
238        })
239        .unwrap_or(Err(Error::NoStreams))
240}
241
242pub(crate) type SubscriberObject = (UsageResponsePublisher<AudioInfo>, AudioWatchResponder);
243type HangingGetFn = fn(&AudioInfo, SubscriberObject) -> bool;
244pub(crate) type HangingGet = server::HangingGet<AudioInfo, SubscriberObject, HangingGetFn>;
245pub(crate) type Publisher = server::Publisher<AudioInfo, SubscriberObject, HangingGetFn>;
246pub(crate) type Subscriber = server::Subscriber<AudioInfo, SubscriberObject, HangingGetFn>;
247
248pub(crate) type SubscriberObject2 = (UsageResponsePublisher<AudioInfo>, AudioWatch2Responder);
249type HangingGetFn2 = fn(&AudioInfo, SubscriberObject2) -> bool;
250pub(crate) type HangingGet2 = server::HangingGet<AudioInfo, SubscriberObject2, HangingGetFn2>;
251pub(crate) type Publisher2 = server::Publisher<AudioInfo, SubscriberObject2, HangingGetFn2>;
252pub(crate) type Subscriber2 = server::Subscriber<AudioInfo, SubscriberObject2, HangingGetFn2>;
253
254pub struct AudioFidlHandler {
255    hanging_get: HangingGet,
256    hanging_get2: HangingGet2,
257    controller_tx: UnboundedSender<Request>,
258    usage_publisher: UsagePublisher<AudioInfo>,
259}
260
261impl AudioFidlHandler {
262    pub(crate) fn new(
263        audio_controller: &mut AudioController,
264        usage_publisher: UsagePublisher<AudioInfo>,
265        controller_tx: UnboundedSender<Request>,
266        initial_value: AudioInfo,
267    ) -> Self {
268        let hanging_get = HangingGet::new(initial_value.clone(), Self::hanging_get);
269        let hanging_get2 = HangingGet2::new(initial_value, Self::hanging_get2);
270        audio_controller
271            .register_publishers(hanging_get.new_publisher(), hanging_get2.new_publisher());
272        Self { hanging_get, hanging_get2, controller_tx, usage_publisher }
273    }
274
275    fn hanging_get(info: &AudioInfo, (usage_responder, responder): SubscriberObject) -> bool {
276        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
277        if let Err(e) = responder.send(&AudioSettings::from(info)) {
278            log::warn!("Failed to respond to watch request: {e:?}");
279            return false;
280        }
281        true
282    }
283
284    fn hanging_get2(info: &AudioInfo, (usage_responder, responder): SubscriberObject2) -> bool {
285        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
286        if let Err(e) = responder.send(&AudioSettings2::from(info)) {
287            log::warn!("Failed to respond to watch request: {e:?}");
288            return false;
289        }
290        true
291    }
292
293    pub fn handle_stream(&mut self, mut stream: AudioRequestStream) {
294        let request_handler = RequestHandler {
295            subscriber: self.hanging_get.new_subscriber(),
296            subscriber2: self.hanging_get2.new_subscriber(),
297            controller_tx: self.controller_tx.clone(),
298            usage_publisher: self.usage_publisher.clone(),
299        };
300        fasync::Task::local(async move {
301            while let Some(Ok(request)) = stream.next().await {
302                request_handler.handle_request(request).await;
303            }
304        })
305        .detach();
306    }
307}
308
309#[derive(Debug)]
310enum HandlerError {
311    AlreadySubscribed,
312    InvalidArgument(
313        // Error used by Debug impl for inspect logs.
314        #[allow(dead_code)] Error,
315    ),
316    ControllerStopped,
317    Controller(AudioError),
318}
319
320impl From<&HandlerError> for ResponseType {
321    fn from(error: &HandlerError) -> Self {
322        match error {
323            HandlerError::AlreadySubscribed => ResponseType::AlreadySubscribed,
324            HandlerError::InvalidArgument(_) => ResponseType::InvalidArgument,
325            HandlerError::ControllerStopped => ResponseType::UnexpectedError,
326            HandlerError::Controller(e) => ResponseType::from(e),
327        }
328    }
329}
330
331struct RequestHandler {
332    subscriber: Subscriber,
333    subscriber2: Subscriber2,
334    controller_tx: UnboundedSender<Request>,
335    usage_publisher: UsagePublisher<AudioInfo>,
336}
337
338impl RequestHandler {
339    async fn handle_request(&self, request: AudioRequest) {
340        match request {
341            AudioRequest::Watch { responder } => {
342                let usage_res = self.usage_publisher.request("Watch".to_string(), RequestType::Get);
343                if let Err((usage_res, responder)) =
344                    self.subscriber.register2((usage_res, responder))
345                {
346                    let e = HandlerError::AlreadySubscribed;
347                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
348                    drop(responder);
349                }
350            }
351            AudioRequest::Watch2 { responder } => {
352                let usage_res =
353                    self.usage_publisher.request("Watch2".to_string(), RequestType::Get);
354                if let Err((usage_res, responder)) =
355                    self.subscriber2.register2((usage_res, responder))
356                {
357                    let e = HandlerError::AlreadySubscribed;
358                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
359                    drop(responder);
360                }
361            }
362            AudioRequest::Set { settings, responder } => {
363                let trace_id = ftrace::Id::new();
364                let _guard = trace_guard!(trace_id, c"audio fidl handler set");
365                let usage_res = self
366                    .usage_publisher
367                    .request(format!("Set{{settings:{settings:?}}}"), RequestType::Set);
368                if let Err(e) = self.set(settings, trace_id).await {
369                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
370                    let _ = responder.send(Err(SettingsError::Failed));
371                } else {
372                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
373                    let _ = responder.send(Ok(()));
374                }
375            }
376            AudioRequest::Set2 { settings, responder } => {
377                let trace_id = ftrace::Id::new();
378                let _guard = trace_guard!(trace_id, c"audio fidl handler set2");
379                let usage_res = self
380                    .usage_publisher
381                    .request(format!("Set{{settings:{settings:?}}}"), RequestType::Set);
382                if let Err(e) = self.set2(settings, trace_id).await {
383                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
384                    let _ = responder.send(Err(SettingsError::Failed));
385                } else {
386                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
387                    let _ = responder.send(Ok(()));
388                }
389            }
390            _ => {
391                log::error!("Unknown audio request");
392            }
393        }
394    }
395
396    async fn set(&self, settings: AudioSettings, trace_id: ftrace::Id) -> Result<(), HandlerError> {
397        let (set_tx, set_rx) = oneshot::channel();
398        let input_devices =
399            to_request(settings, trace_id).map_err(HandlerError::InvalidArgument)?;
400        self.controller_tx
401            .unbounded_send(Request::Set(input_devices, trace_id, set_tx))
402            .map_err(|_| HandlerError::ControllerStopped)?;
403        set_rx
404            .await
405            .map_err(|_| HandlerError::ControllerStopped)
406            .and_then(|res| res.map_err(HandlerError::Controller))
407    }
408
409    async fn set2(
410        &self,
411        settings: AudioSettings2,
412        trace_id: ftrace::Id,
413    ) -> Result<(), HandlerError> {
414        let (set_tx, set_rx) = oneshot::channel();
415        let input_devices =
416            to_request2(settings, trace_id).map_err(HandlerError::InvalidArgument)?;
417        self.controller_tx
418            .unbounded_send(Request::Set(input_devices, trace_id, set_tx))
419            .map_err(|_| HandlerError::ControllerStopped)?;
420        set_rx
421            .await
422            .map_err(|_| HandlerError::ControllerStopped)
423            .and_then(|res| res.map_err(HandlerError::Controller))
424    }
425}
426
427#[cfg(test)]
428mod tests {
429    use super::*;
430
431    fn test_stream() -> AudioStreamSettings {
432        AudioStreamSettings {
433            stream: Some(fidl_fuchsia_media::AudioRenderUsage::Media),
434            source: Some(AudioStreamSettingSource::User),
435            user_volume: Some(Volume {
436                level: Some(0.6),
437                muted: Some(false),
438                ..Default::default()
439            }),
440            ..Default::default()
441        }
442    }
443
444    fn test_stream2() -> AudioStreamSettings2 {
445        AudioStreamSettings2 {
446            stream: Some(fidl_fuchsia_media::AudioRenderUsage2::Media),
447            source: Some(AudioStreamSettingSource::User),
448            user_volume: Some(Volume {
449                level: Some(0.6),
450                muted: Some(false),
451                ..Default::default()
452            }),
453            ..Default::default()
454        }
455    }
456
457    // Verifies that an entirely empty settings request results in an appropriate error.
458    #[fuchsia::test]
459    fn test_request_from_settings_empty() {
460        let id = ftrace::Id::new();
461        let request = to_request(AudioSettings::default(), id);
462
463        assert_eq!(request, Err(Error::NoStreams));
464    }
465
466    // Verifies that an entirely empty settings request2 results in an appropriate error.
467    #[fuchsia::test]
468    fn test_request2_from_settings_empty() {
469        let id = ftrace::Id::new();
470        let request = to_request2(AudioSettings2::default(), id);
471
472        assert_eq!(request, Err(Error::NoStreams));
473    }
474
475    // Verifies that a settings request missing user volume info results in an appropriate error.
476    #[fuchsia::test]
477    fn test_request_missing_user_volume() {
478        let mut stream = test_stream();
479        stream.user_volume = None;
480
481        let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
482
483        let id = ftrace::Id::new();
484        let request = to_request(audio_settings, id);
485
486        assert_eq!(request, Err(Error::NoUserVolume(0)));
487    }
488
489    // Verifies that a settings request2 missing user volume info results in an appropriate error.
490    #[fuchsia::test]
491    fn test_request2_missing_user_volume() {
492        let mut stream = test_stream2();
493        stream.user_volume = None;
494
495        let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
496
497        let id = ftrace::Id::new();
498        let request = to_request2(audio_settings, id);
499
500        assert_eq!(request, Err(Error::NoUserVolume(0)));
501    }
502
503    // Verifies that a settings request missing the stream type results in an appropriate error.
504    #[fuchsia::test]
505    fn test_request_missing_stream_type() {
506        let mut stream = test_stream();
507        stream.stream = None;
508
509        let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
510
511        let id = ftrace::Id::new();
512        let request = to_request(audio_settings, id);
513
514        assert_eq!(request, Err(Error::NoStreamType(0)));
515    }
516
517    // Verifies that a settings request2 missing the stream type results in an appropriate error.
518    #[fuchsia::test]
519    fn test_request2_missing_stream_type() {
520        let mut stream = test_stream2();
521        stream.stream = None;
522
523        let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
524
525        let id = ftrace::Id::new();
526        let request = to_request2(audio_settings, id);
527
528        assert_eq!(request, Err(Error::NoStreamType(0)));
529    }
530
531    // Verifies that a settings request missing the source results in an appropriate error.
532    #[fuchsia::test]
533    fn test_request_missing_source() {
534        let mut stream = test_stream();
535        stream.source = None;
536
537        let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
538
539        let id = ftrace::Id::new();
540        let request = to_request(audio_settings, id);
541
542        assert_eq!(request, Err(Error::NoSource(0)));
543    }
544
545    // Verifies that a settings request2 missing the source results in an appropriate error.
546    #[fuchsia::test]
547    fn test_request2_missing_source() {
548        let mut stream = test_stream2();
549        stream.source = None;
550
551        let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
552
553        let id = ftrace::Id::new();
554        let request = to_request2(audio_settings, id);
555
556        assert_eq!(request, Err(Error::NoSource(0)));
557    }
558
559    // Verifies that a settings request missing both the user volume level and mute state results in
560    // an appropriate error.
561    #[fuchsia::test]
562    fn test_request_missing_user_volume_level_and_muted() {
563        let mut stream = test_stream();
564        stream.user_volume = Some(Volume { level: None, muted: None, ..Default::default() });
565
566        let audio_settings = AudioSettings { streams: Some(vec![stream]), ..Default::default() };
567
568        let id = ftrace::Id::new();
569        let request = to_request(audio_settings, id);
570
571        assert_eq!(request, Err(Error::MissingVolumeAndMuted(0)));
572    }
573
574    // Verifies that a settings request2 missing both the user volume level and mute state results in
575    // an appropriate error.
576    #[fuchsia::test]
577    fn test_request2_missing_user_volume_level_and_muted() {
578        let mut stream = test_stream2();
579        stream.user_volume = Some(Volume { level: None, muted: None, ..Default::default() });
580
581        let audio_settings = AudioSettings2 { streams: Some(vec![stream]), ..Default::default() };
582
583        let id = ftrace::Id::new();
584        let request = to_request2(audio_settings, id);
585
586        assert_eq!(request, Err(Error::MissingVolumeAndMuted(0)));
587    }
588}