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