Skip to main content

bt_a2dp/
stream.rs

1// Copyright 2020 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 anyhow::Error;
6use bt_avdtp::{
7    self as avdtp, ErrorCode, ServiceCapability, ServiceCategory, StreamEndpoint, StreamEndpointId,
8};
9use fidl_fuchsia_bluetooth_bredr::AudioOffloadExtProxy;
10use fuchsia_bluetooth::types::PeerId;
11use fuchsia_inspect::{self as inspect, Property};
12use fuchsia_inspect_derive::{AttachError, Inspect};
13use futures::future::BoxFuture;
14use futures::{FutureExt, TryFutureExt};
15use log::{info, warn};
16use std::collections::HashMap;
17use std::fmt;
18use std::sync::Arc;
19use std::time::Duration;
20
21use crate::codec::{CodecNegotiation, MediaCodecConfig};
22use crate::media_task::{
23    MediaTask, MediaTaskBuilder, MediaTaskError, MediaTaskRunner, MediaTaskStatus,
24};
25
26/// Manages a local StreamEndpoint and its associated media task, starting and stopping the
27/// related media task in sync with the endpoint's configured or streaming state.
28/// Note that this does not coordinate state with peer, which is done by bt_a2dp::Peer.
29pub struct Stream {
30    endpoint: StreamEndpoint,
31    /// The builder for media tasks associated with this endpoint.
32    media_task_builder: Arc<Box<dyn MediaTaskBuilder>>,
33    /// The MediaTaskRunner for this endpoint, if it is configured.
34    media_task_runner: Option<Box<dyn MediaTaskRunner>>,
35    /// The MediaTask, if it is running.
36    media_task: Option<Box<dyn MediaTask>>,
37    /// The peer associated with this endpoint, if it is configured.
38    /// Used during reconfiguration for MediaTask recreation.
39    peer_id: Option<PeerId>,
40    /// Inspect Node for this stream
41    inspect: fuchsia_inspect::Node,
42}
43
44impl fmt::Debug for Stream {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        f.debug_struct("Stream")
47            .field("endpoint", &self.endpoint)
48            .field("peer_id", &self.peer_id)
49            .field("has media_task", &self.media_task.is_some())
50            .finish()
51    }
52}
53
54impl Inspect for &mut Stream {
55    // Set up the StreamEndpoint to update the state
56    // The MediaTask node will be created when the media task is started.
57    fn iattach(self, parent: &inspect::Node, name: impl AsRef<str>) -> Result<(), AttachError> {
58        self.inspect = parent.create_child(name.as_ref());
59
60        let endpoint_state_prop = self.inspect.create_string("endpoint_state", "");
61        let callback =
62            move |stream: &StreamEndpoint| endpoint_state_prop.set(&format!("{:?}", stream));
63        callback(self.endpoint());
64        self.endpoint_mut().set_update_callback(Some(Box::new(callback)));
65        Ok(())
66    }
67}
68
69impl Stream {
70    pub fn build(endpoint: StreamEndpoint, media_task_builder: Box<dyn MediaTaskBuilder>) -> Self {
71        Self {
72            endpoint,
73            media_task_builder: Arc::new(media_task_builder),
74            media_task_runner: None,
75            media_task: None,
76            peer_id: None,
77            inspect: Default::default(),
78        }
79    }
80
81    fn as_new(&self) -> Self {
82        Self {
83            endpoint: self.endpoint.as_new(),
84            media_task_builder: self.media_task_builder.clone(),
85            media_task_runner: None,
86            media_task: None,
87            peer_id: None,
88            inspect: Default::default(),
89        }
90    }
91
92    pub fn endpoint(&self) -> &StreamEndpoint {
93        &self.endpoint
94    }
95
96    pub fn endpoint_mut(&mut self) -> &mut StreamEndpoint {
97        &mut self.endpoint
98    }
99
100    fn media_codec_config(&self) -> Option<MediaCodecConfig> {
101        find_codec_capability(self.endpoint.capabilities())
102            .and_then(|x| MediaCodecConfig::try_from(x).ok())
103    }
104
105    /// Returns true if the config given is a supported configuration of this stream
106    /// Used when the stream is being configured to a specific configuration
107    fn config_supported(&self, config: &MediaCodecConfig) -> bool {
108        let Some(supported) = self.media_codec_config() else {
109            return false;
110        };
111        supported.supports(&config)
112    }
113
114    /// Returns true if this stream and the given config are compatible - a valid configuration
115    /// of this stream can be found within the capabilities of the given config.
116    fn config_compatible(&self, config: &MediaCodecConfig) -> bool {
117        let Some(supported) = self.media_codec_config() else {
118            return false;
119        };
120        MediaCodecConfig::negotiate(&supported, config).is_some()
121    }
122
123    fn build_media_task_runner(
124        &self,
125        peer_id: &PeerId,
126        config: &MediaCodecConfig,
127    ) -> Option<Box<dyn MediaTaskRunner>> {
128        match self.media_task_builder.configure(peer_id, &config) {
129            Err(e) => {
130                warn!("Failed to build media task: {e:?}");
131                None
132            }
133            Ok(mut media_task_runner) => {
134                if let Err(e) = media_task_runner.iattach(&self.inspect, "media_task") {
135                    info!("Media Task inspect: {e}");
136                }
137                Some(media_task_runner)
138            }
139        }
140    }
141
142    fn supported_config_from_capability(
143        &self,
144        requested_cap: &ServiceCapability,
145    ) -> Option<MediaCodecConfig> {
146        MediaCodecConfig::try_from(requested_cap).ok().filter(|c| self.config_supported(c))
147    }
148
149    pub fn configure(
150        &mut self,
151        peer_id: &PeerId,
152        remote_id: &StreamEndpointId,
153        capabilities: Vec<ServiceCapability>,
154    ) -> Result<(), (ServiceCategory, ErrorCode)> {
155        if self.media_task_runner.is_some() {
156            return Err((ServiceCategory::None, ErrorCode::SepInUse));
157        }
158        let unsupported = ErrorCode::UnsupportedConfiguration;
159        let codec_cap =
160            find_codec_capability(&capabilities).ok_or((ServiceCategory::None, unsupported))?;
161        let media_unsupported = (ServiceCategory::MediaCodec, unsupported);
162        let config = self.supported_config_from_capability(codec_cap).ok_or(media_unsupported)?;
163        self.media_task_runner =
164            Some(self.build_media_task_runner(peer_id, &config).ok_or(media_unsupported)?);
165        self.peer_id = Some(peer_id.clone());
166        self.endpoint.configure(remote_id, capabilities).or_else(|e| {
167            self.media_task_runner = None;
168            Err(e)
169        })
170    }
171
172    pub fn set_delay(&mut self, delay: Duration) -> Result<(), ErrorCode> {
173        let Some(runner) = self.media_task_runner.as_mut() else {
174            return Err(ErrorCode::BadState);
175        };
176        match runner.set_delay(delay) {
177            Err(MediaTaskError::NotSupported) => Err(ErrorCode::NotSupportedCommand),
178            Err(_) => Err(ErrorCode::BadState),
179            Ok(()) => Ok(()),
180        }
181    }
182
183    pub fn reconfigure(
184        &mut self,
185        capabilities: Vec<ServiceCapability>,
186    ) -> Result<(), (ServiceCategory, ErrorCode)> {
187        let bad_state = (ServiceCategory::None, ErrorCode::BadState);
188        let _peer_id = self.peer_id.as_ref().ok_or(bad_state)?;
189        if let Some(requested_codec_cap) = find_codec_capability(&capabilities) {
190            let unsupported = (ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration);
191            let requested =
192                self.supported_config_from_capability(requested_codec_cap).ok_or(unsupported)?;
193            self.media_task_runner
194                .as_mut()
195                .ok_or(bad_state)?
196                .reconfigure(&requested)
197                .or(Err(unsupported))?;
198        }
199        self.endpoint.reconfigure(capabilities)
200    }
201
202    fn media_runner_ref(&mut self) -> Result<&mut Box<dyn MediaTaskRunner>, ErrorCode> {
203        self.media_task_runner.as_mut().ok_or(ErrorCode::BadState)
204    }
205
206    /// Attempt to start the endpoint.
207    /// If the endpoint is successfully started, the media task is started and a future that
208    /// will finish when the media task finishes is returned.
209    pub fn start(
210        &mut self,
211    ) -> Result<BoxFuture<'static, Result<MediaTaskStatus, Error>>, ErrorCode> {
212        let peer_id = self.peer_id.ok_or(ErrorCode::BadState)?;
213        if self.media_task_runner.is_none() {
214            return Err(ErrorCode::BadState);
215        }
216        let _ = self.endpoint.start()?;
217        let transport = self.endpoint.take_transport().ok_or(ErrorCode::BadState)?;
218        let offload = self.endpoint.audio_offload();
219        let mut task = match self.media_runner_ref()?.start(transport, offload) {
220            Ok(media_task) => media_task,
221            Err(e) => {
222                warn!("Failed to start media task: {e:?} {peer_id}");
223                let _ = self.endpoint.suspend()?;
224                return Err(ErrorCode::BadState);
225            }
226        };
227        let finished = task.finished();
228        self.media_task = Some(task);
229        Ok(finished.map_err(Into::into).boxed())
230    }
231
232    /// Suspends the media processor and endpoint.
233    pub fn suspend(&mut self) -> Result<(), ErrorCode> {
234        self.endpoint.suspend()?;
235        let _ = self.media_task.take().ok_or(ErrorCode::BadState)?.stop();
236        Ok(())
237    }
238
239    /// Watch for active channel state changes on the media task runner.
240    /// Resolves to true when active, false when inactive.
241    pub fn watch_active(&mut self) -> BoxFuture<'static, bool> {
242        let Some(runner) = self.media_task_runner.as_mut() else {
243            return futures::future::ready(true).boxed();
244        };
245        runner.watch_active()
246    }
247
248    fn stop_media_task(&mut self) {
249        if let Some(mut task) = self.media_task.take() {
250            // Ignoring stop errors, best effort.
251            let _ = task.stop();
252        }
253        self.media_task_runner = None;
254        self.peer_id = None;
255    }
256
257    /// Releases the endpoint and stops the processing of audio.
258    pub fn release(
259        &mut self,
260        responder: avdtp::SimpleResponder,
261        peer: &avdtp::Peer,
262    ) -> avdtp::Result<()> {
263        self.stop_media_task();
264        self.endpoint.release(responder, peer)
265    }
266
267    pub fn abort(&mut self) {
268        self.stop_media_task();
269        self.endpoint.abort()
270    }
271
272    pub async fn initiate_abort(&mut self, peer: &avdtp::Peer) {
273        self.stop_media_task();
274        self.endpoint.initiate_abort(peer).await
275    }
276}
277
278fn find_codec_capability(capabilities: &[ServiceCapability]) -> Option<&ServiceCapability> {
279    capabilities.iter().find(|cap| cap.category() == ServiceCategory::MediaCodec)
280}
281
282/// Iterator which generates SEIDs.  Used by StreamsBuilder to get valid SEIDs.
283#[derive(Clone, Debug)]
284struct SeidRangeFrom {
285    from: u8,
286}
287
288impl Default for SeidRangeFrom {
289    fn default() -> Self {
290        Self { from: 1 }
291    }
292}
293
294impl Iterator for SeidRangeFrom {
295    type Item = u8;
296
297    fn next(&mut self) -> Option<Self::Item> {
298        let res = self.from;
299        if self.from == 0x3E {
300            self.from = 0x01;
301        } else {
302            self.from += 1;
303        }
304        Some(res)
305    }
306}
307
308/// Builds a set of streams, based on the capabilities of a set of MediaTaskBuilders that are
309/// supported and configured by the system.
310pub struct StreamsBuilder {
311    builders: Vec<Box<dyn MediaTaskBuilder>>,
312    seid_range: SeidRangeFrom,
313    node: inspect::Node,
314}
315
316impl Default for StreamsBuilder {
317    fn default() -> Self {
318        Self {
319            builders: Default::default(),
320            seid_range: SeidRangeFrom { from: Self::START_SEID },
321            node: Default::default(),
322        }
323    }
324}
325
326impl Clone for StreamsBuilder {
327    fn clone(&self) -> Self {
328        Self {
329            builders: self.builders.clone(),
330            node: Default::default(),
331            seid_range: self.seid_range.clone(),
332        }
333    }
334}
335
336impl StreamsBuilder {
337    // Randomly chosen by fair dice roll
338    // TODO(https://fxbug.dev/337321738): Do better for randomizing this maybe
339    const START_SEID: u8 = 8;
340
341    /// Add a builder to the set of builders used to generate streams.
342    pub fn add_builder(&mut self, builder: impl MediaTaskBuilder + 'static) {
343        self.builders.push(Box::new(builder));
344        self.node.record_uint("builders", self.builders.len() as u64);
345    }
346
347    pub async fn peer_streams(
348        &self,
349        peer_id: &PeerId,
350        offload: Option<AudioOffloadExtProxy>,
351    ) -> Result<Streams, MediaTaskError> {
352        let mut streams = Streams::default();
353        let mut seid_range = self.seid_range.clone();
354        for builder in &self.builders {
355            let endpoint_type = builder.direction();
356            let supported_res = builder.supported_configs(peer_id, offload.clone()).await;
357            let Ok(supported) = supported_res else {
358                info!(e:? = supported_res.err().unwrap(); "Failed to get supported configs from builder, skipping");
359                continue;
360            };
361            let codec_caps = supported.iter().map(ServiceCapability::from);
362            for codec_cap in codec_caps {
363                let capabilities = match endpoint_type {
364                    avdtp::EndpointType::Source => vec![
365                        ServiceCapability::MediaTransport,
366                        ServiceCapability::DelayReporting,
367                        codec_cap,
368                    ],
369                    avdtp::EndpointType::Sink => {
370                        vec![ServiceCapability::MediaTransport, codec_cap]
371                    }
372                };
373                let endpoint = avdtp::StreamEndpoint::new(
374                    seid_range.next().unwrap(),
375                    avdtp::MediaType::Audio,
376                    endpoint_type,
377                    capabilities,
378                )?;
379                streams.insert(Stream::build(endpoint, builder.clone()));
380            }
381        }
382        Ok(streams)
383    }
384
385    pub async fn negotiation(
386        &self,
387        peer_id: &PeerId,
388        offload: Option<AudioOffloadExtProxy>,
389        preferred_direction: avdtp::EndpointType,
390    ) -> Result<CodecNegotiation, Error> {
391        let mut caps_available = Vec::new();
392        for builder in &self.builders {
393            caps_available.extend(
394                builder
395                    .supported_configs(peer_id, offload.clone())
396                    .await?
397                    .iter()
398                    .map(ServiceCapability::from),
399            );
400        }
401        Ok(CodecNegotiation::build(caps_available, preferred_direction)?)
402    }
403}
404
405impl Inspect for &mut StreamsBuilder {
406    fn iattach(self, parent: &inspect::Node, name: impl AsRef<str>) -> Result<(), AttachError> {
407        self.node = parent.create_child(name.as_ref());
408        self.node.record_uint("builders", self.builders.len() as u64);
409        Ok(())
410    }
411}
412
413/// A set of streams, indexed by their local endpoint ID.
414#[derive(Default)]
415pub struct Streams {
416    streams: HashMap<StreamEndpointId, Stream>,
417    inspect_node: fuchsia_inspect::Node,
418}
419
420impl fmt::Debug for Streams {
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        f.debug_struct("Streams").field("streams", &self.streams).finish()
423    }
424}
425
426impl Streams {
427    /// Makes a copy of this set of streams, but with all streams copied with their states set to
428    /// idle.
429    pub fn as_new(&self) -> Self {
430        let streams =
431            self.streams.iter().map(|(id, stream)| (id.clone(), stream.as_new())).collect();
432        Self { streams, ..Default::default() }
433    }
434
435    /// Returns true if there are no streams in the set.
436    pub fn is_empty(&self) -> bool {
437        self.streams.is_empty()
438    }
439
440    /// Inserts a stream, indexing it by the local endpoint id.
441    /// It replaces any other stream with the same endpoint id.
442    pub fn insert(&mut self, stream: Stream) {
443        let local_id = stream.endpoint().local_id().clone();
444        if self.streams.insert(local_id.clone(), stream).is_some() {
445            warn!("Replacing stream with local id {local_id}");
446        }
447    }
448
449    /// Retrieves a reference to the Stream referenced by `id`, if the stream exists,
450    pub fn get(&self, id: &StreamEndpointId) -> Option<&Stream> {
451        self.streams.get(id)
452    }
453
454    /// Retrieves a mutable reference to the Stream referenced by `id`, if the stream exists,
455    pub fn get_mut(&mut self, id: &StreamEndpointId) -> Option<&mut Stream> {
456        self.streams.get_mut(id)
457    }
458
459    /// Returns a vector of information on all the contained streams.
460    pub fn information(&self) -> Vec<avdtp::StreamInformation> {
461        self.streams.values().map(|x| x.endpoint().information()).collect()
462    }
463
464    /// Returns streams that are in the open (established but not streaming) state
465    pub fn open(&self) -> impl Iterator<Item = &Stream> {
466        self.streams.values().filter(|s| s.endpoint().state() == avdtp::StreamState::Open)
467    }
468
469    /// Returns streams that are streaming.
470    pub fn streaming(&self) -> impl Iterator<Item = &Stream> {
471        self.streams.values().filter(|s| s.endpoint().state() == avdtp::StreamState::Streaming)
472    }
473
474    /// Finds streams in the set which are compatible with `codec_config`.
475    pub fn compatible(&self, codec_config: MediaCodecConfig) -> impl Iterator<Item = &Stream> {
476        self.streams.values().filter(move |s| s.config_compatible(&codec_config))
477    }
478}
479
480impl Inspect for &mut Streams {
481    // Attach self to `parent`
482    fn iattach(self, parent: &inspect::Node, name: impl AsRef<str>) -> Result<(), AttachError> {
483        self.inspect_node = parent.create_child(name.as_ref());
484        for stream in self.streams.values_mut() {
485            stream.iattach(&self.inspect_node, inspect::unique_name("stream_"))?;
486        }
487        Ok(())
488    }
489}
490
491#[cfg(test)]
492pub(crate) mod tests {
493    use super::*;
494    use bt_channel_test_support::{Transport, create_test_channels};
495    use fuchsia_async as fasync;
496    use std::pin::pin;
497    use std::task::Poll;
498    use test_case::test_case;
499
500    use crate::media_task::tests::TestMediaTaskBuilder;
501    use crate::media_types::*;
502
503    pub(crate) fn sbc_mediacodec_capability() -> avdtp::ServiceCapability {
504        let sbc_codec_info = SbcCodecInfo::new(
505            SbcSamplingFrequency::FREQ48000HZ,
506            SbcChannelMode::MONO | SbcChannelMode::JOINT_STEREO,
507            SbcBlockCount::MANDATORY_SRC,
508            SbcSubBands::MANDATORY_SRC,
509            SbcAllocation::MANDATORY_SRC,
510            SbcCodecInfo::BITPOOL_MIN,
511            SbcCodecInfo::BITPOOL_MAX,
512        )
513        .expect("SBC codec info");
514
515        ServiceCapability::MediaCodec {
516            media_type: avdtp::MediaType::Audio,
517            codec_type: avdtp::MediaCodecType::AUDIO_SBC,
518            codec_extra: sbc_codec_info.to_bytes().to_vec(),
519        }
520    }
521
522    pub(crate) fn aac_mediacodec_capability(bitrate: u32) -> avdtp::ServiceCapability {
523        let codec_info = AacCodecInfo::new(
524            AacObjectType::MANDATORY_SRC,
525            AacSamplingFrequency::FREQ48000HZ,
526            AacChannels::TWO,
527            true,
528            bitrate,
529        )
530        .expect("should work");
531        ServiceCapability::MediaCodec {
532            media_type: avdtp::MediaType::Audio,
533            codec_type: avdtp::MediaCodecType::AUDIO_AAC,
534            codec_extra: codec_info.to_bytes().to_vec(),
535        }
536    }
537
538    pub(crate) fn make_sbc_endpoint(seid: u8, direction: avdtp::EndpointType) -> StreamEndpoint {
539        StreamEndpoint::new(
540            seid,
541            avdtp::MediaType::Audio,
542            direction,
543            vec![avdtp::ServiceCapability::MediaTransport, sbc_mediacodec_capability()],
544        )
545        .expect("endpoint creation should succeed")
546    }
547
548    const LOW_BITRATE: u32 = 320_000;
549    const HIGH_BITRATE: u32 = 393_216;
550
551    pub(crate) fn make_aac_endpoint(seid: u8, direction: avdtp::EndpointType) -> StreamEndpoint {
552        StreamEndpoint::new(
553            seid,
554            avdtp::MediaType::Audio,
555            direction,
556            vec![avdtp::ServiceCapability::MediaTransport, aac_mediacodec_capability(LOW_BITRATE)],
557        )
558        .expect("endpoint creation should succeed")
559    }
560
561    fn make_stream(seid: u8, codec_type: avdtp::MediaCodecType) -> Stream {
562        let endpoint = match codec_type {
563            avdtp::MediaCodecType::AUDIO_SBC => {
564                make_sbc_endpoint(seid, avdtp::EndpointType::Source)
565            }
566            avdtp::MediaCodecType::AUDIO_AAC => {
567                make_aac_endpoint(seid, avdtp::EndpointType::Source)
568            }
569            _ => panic!("Unsupported codec_type"),
570        };
571        Stream::build(endpoint, TestMediaTaskBuilder::new().builder())
572    }
573
574    #[fuchsia::test]
575    fn streams_basic_functionality() {
576        let mut streams = Streams::default();
577
578        streams.insert(make_stream(1, avdtp::MediaCodecType::AUDIO_SBC));
579        streams.insert(make_stream(6, avdtp::MediaCodecType::AUDIO_AAC));
580
581        let first_id = 1_u8.try_into().expect("good id");
582        let missing_id = 5_u8.try_into().expect("good id");
583
584        assert!(streams.get(&first_id).is_some());
585        assert!(streams.get(&missing_id).is_none());
586
587        assert!(streams.get_mut(&first_id).is_some());
588        assert!(streams.get_mut(&missing_id).is_none());
589
590        let expected_info = vec![
591            make_sbc_endpoint(1, avdtp::EndpointType::Source).information(),
592            make_aac_endpoint(6, avdtp::EndpointType::Source).information(),
593        ];
594
595        let infos = streams.information();
596
597        assert_eq!(expected_info.len(), infos.len());
598
599        if infos[0].id() == &first_id {
600            assert_eq!(expected_info[0], infos[0]);
601            assert_eq!(expected_info[1], infos[1]);
602        } else {
603            assert_eq!(expected_info[0], infos[1]);
604            assert_eq!(expected_info[1], infos[0]);
605        }
606    }
607
608    #[fuchsia::test]
609    fn streams_filters_compatible_codecs() {
610        let mut streams = Streams::default();
611        streams.insert(make_stream(1, avdtp::MediaCodecType::AUDIO_SBC));
612        streams.insert(make_stream(6, avdtp::MediaCodecType::AUDIO_AAC));
613
614        // Even if the other bitrate is higher, we can negotiate to the lower bitrate.
615        let config_high_bitrate_aac =
616            MediaCodecConfig::try_from(&aac_mediacodec_capability(HIGH_BITRATE)).unwrap();
617
618        let compatible: Vec<_> = streams.compatible(config_high_bitrate_aac).collect();
619        assert_eq!(compatible.len(), 1);
620        let codec_capability = compatible[0]
621            .endpoint()
622            .capabilities()
623            .iter()
624            .find(|x| x.category() == avdtp::ServiceCategory::MediaCodec)
625            .expect("should have a codec");
626        assert_eq!(
627            MediaCodecConfig::try_from(codec_capability).unwrap().codec_type(),
628            &avdtp::MediaCodecType::AUDIO_AAC
629        );
630    }
631
632    #[test_case(Transport::Socket ; "socket")]
633    #[test_case(Transport::Fidl ; "fidl")]
634    #[fuchsia::test]
635    fn rejects_unsupported_configurations(transport: Transport) {
636        // Needed to make fasync::Tasks.
637        let _exec = fasync::TestExecutor::new();
638        let mut builder = TestMediaTaskBuilder::new_reconfigurable();
639        let mut stream =
640            Stream::build(make_sbc_endpoint(1, avdtp::EndpointType::Source), builder.builder());
641
642        // the default test stream only supports 48000hz
643        let unsupported_sbc_codec_info = SbcCodecInfo::new(
644            SbcSamplingFrequency::FREQ44100HZ,
645            SbcChannelMode::JOINT_STEREO,
646            SbcBlockCount::SIXTEEN,
647            SbcSubBands::EIGHT,
648            SbcAllocation::LOUDNESS,
649            53,
650            53,
651        )
652        .expect("SBC codec info");
653
654        let unsupported_caps = vec![ServiceCapability::MediaCodec {
655            media_type: avdtp::MediaType::Audio,
656            codec_type: avdtp::MediaCodecType::AUDIO_SBC,
657            codec_extra: unsupported_sbc_codec_info.to_bytes().to_vec(),
658        }];
659
660        let peer_id = PeerId(1);
661        let stream_id = 1.try_into().expect("StreamEndpointId");
662        let res = stream.configure(&peer_id, &stream_id, unsupported_caps.clone());
663        assert!(res.is_err());
664        assert_eq!(
665            res.err(),
666            Some((ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration))
667        );
668
669        assert_eq!(
670            stream.reconfigure(unsupported_caps.clone()),
671            Err((ServiceCategory::None, ErrorCode::BadState))
672        );
673
674        let supported_sbc_codec_info = SbcCodecInfo::new(
675            SbcSamplingFrequency::FREQ48000HZ,
676            SbcChannelMode::JOINT_STEREO,
677            SbcBlockCount::SIXTEEN,
678            SbcSubBands::EIGHT,
679            SbcAllocation::LOUDNESS,
680            53,
681            53,
682        )
683        .expect("SBC codec info");
684
685        let sbc_codec_cap = ServiceCapability::MediaCodec {
686            media_type: avdtp::MediaType::Audio,
687            codec_type: avdtp::MediaCodecType::AUDIO_SBC,
688            codec_extra: supported_sbc_codec_info.to_bytes().to_vec(),
689        };
690
691        let supported_caps = vec![ServiceCapability::MediaTransport, sbc_codec_cap.clone()];
692
693        let res = stream.configure(&peer_id, &stream_id, supported_caps.clone());
694        assert!(res.is_ok());
695
696        // need to be in the open state for reconfigure
697        assert!(stream.endpoint_mut().establish().is_ok());
698        let (transport, _remote) = create_test_channels(transport);
699        match stream.endpoint_mut().receive_channel(transport) {
700            Ok(false) => {}
701            Ok(true) => panic!("Only should be expecting one channel"),
702            Err(e) => panic!("Expected channel to be accepted, got {:?}", e),
703        };
704
705        assert_eq!(
706            stream.reconfigure(unsupported_caps.clone()),
707            Err((ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration))
708        );
709
710        let new_codec_caps = vec![ServiceCapability::MediaCodec {
711            media_type: avdtp::MediaType::Audio,
712            codec_type: avdtp::MediaCodecType::AUDIO_SBC,
713            codec_extra: supported_sbc_codec_info.to_bytes().to_vec(),
714        }];
715
716        assert!(stream.reconfigure(new_codec_caps.clone()).is_ok());
717
718        // Should be able to start after reconfigure, and we used the right configuration.
719        let _ = stream.start().expect("stream should start ok");
720        let task = builder.expect_task();
721        assert_eq!(task.codec_config, MediaCodecConfig::try_from(&new_codec_caps[0]).unwrap());
722    }
723
724    #[test_case(Transport::Socket ; "socket")]
725    #[test_case(Transport::Fidl ; "fidl")]
726    #[fuchsia::test]
727    fn reconfigure_runner_fails(transport: Transport) {
728        // Needed to make fasync::Tasks.
729        let _exec = fasync::TestExecutor::new();
730        let mut builder = TestMediaTaskBuilder::new();
731        let mut stream =
732            Stream::build(make_sbc_endpoint(1, avdtp::EndpointType::Source), builder.builder());
733
734        let supported_sbc_codec_info = SbcCodecInfo::new(
735            SbcSamplingFrequency::FREQ48000HZ,
736            SbcChannelMode::JOINT_STEREO,
737            SbcBlockCount::SIXTEEN,
738            SbcSubBands::EIGHT,
739            SbcAllocation::LOUDNESS,
740            53,
741            53,
742        )
743        .expect("SBC codec info");
744
745        let orig_codec_cap = ServiceCapability::MediaCodec {
746            media_type: avdtp::MediaType::Audio,
747            codec_type: avdtp::MediaCodecType::AUDIO_SBC,
748            codec_extra: supported_sbc_codec_info.to_bytes().to_vec(),
749        };
750
751        let supported_caps = vec![ServiceCapability::MediaTransport, orig_codec_cap.clone()];
752
753        let res = stream.configure(&PeerId(1), &(1.try_into().unwrap()), supported_caps.clone());
754        assert!(res.is_ok());
755
756        // need to be in the open state for reconfigure
757        assert!(stream.endpoint_mut().establish().is_ok());
758        let (transport, _remote) = create_test_channels(transport);
759        match stream.endpoint_mut().receive_channel(transport) {
760            Ok(false) => {}
761            Ok(true) => panic!("Only should be expecting one channel"),
762            Err(e) => panic!("Expected channel to be accepted, got {:?}", e),
763        };
764
765        // Should be able to start after configure, and we used the right configuration.
766        let _ = stream.start().expect("stream should start ok");
767        let task = builder.expect_task();
768        assert_eq!(task.codec_config, MediaCodecConfig::try_from(&orig_codec_cap).unwrap());
769        stream.suspend().expect("stream should suspend ok");
770
771        // Try to reconfigure with a supported configuration, but the builder doesn't reconfigure.
772        let mono_sbc_codec_info = SbcCodecInfo::new(
773            SbcSamplingFrequency::FREQ48000HZ,
774            SbcChannelMode::MONO,
775            SbcBlockCount::SIXTEEN,
776            SbcSubBands::EIGHT,
777            SbcAllocation::LOUDNESS,
778            53,
779            53,
780        )
781        .expect("SBC codec info");
782
783        let new_codec_caps = vec![ServiceCapability::MediaCodec {
784            media_type: avdtp::MediaType::Audio,
785            codec_type: avdtp::MediaCodecType::AUDIO_SBC,
786            codec_extra: mono_sbc_codec_info.to_bytes().to_vec(),
787        }];
788
789        // Media Builder fails to reconfigure (as it's failing all reconfigures)
790        assert_eq!(
791            stream.reconfigure(new_codec_caps.clone()),
792            Err((ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration))
793        );
794
795        // Should be able to start after reconfigure, but it will use the old configuration.
796        let _ = stream.start().expect("stream should start ok");
797        let task = builder.expect_task();
798        assert_eq!(task.codec_config, MediaCodecConfig::try_from(&orig_codec_cap).unwrap());
799        stream.suspend().expect("stream should suspend ok")
800    }
801
802    #[test_case(Transport::Socket ; "socket")]
803    #[test_case(Transport::Fidl ; "fidl")]
804    #[fuchsia::test]
805    fn suspend_stops_media_task(transport: Transport) {
806        let mut exec = fasync::TestExecutor::new();
807
808        let mut task_builder = TestMediaTaskBuilder::new();
809        let mut stream = Stream::build(
810            make_sbc_endpoint(1, avdtp::EndpointType::Source),
811            task_builder.builder(),
812        );
813        let next_task_fut = task_builder.next_task();
814        let remote_id = 1_u8.try_into().expect("good id");
815
816        let sbc_codec_cap = sbc_mediacodec_capability();
817        let expected_codec_config =
818            MediaCodecConfig::try_from(&sbc_codec_cap).expect("codec config");
819
820        assert!(stream.configure(&PeerId(1), &remote_id, vec![]).is_err());
821        assert!(stream.configure(&PeerId(1), &remote_id, vec![sbc_codec_cap]).is_ok());
822
823        stream.endpoint_mut().establish().expect("establishment should start okay");
824        let (transport, _remote) = create_test_channels(transport);
825        let _ = stream.endpoint_mut().receive_channel(transport).expect("ready for a channel");
826
827        assert!(stream.start().is_ok());
828
829        // Task should be created here.
830        let task = {
831            let mut next_task_fut = pin!(next_task_fut);
832            match exec.run_until_stalled(&mut next_task_fut) {
833                Poll::Ready(Some(task)) => task,
834                x => panic!("Expected next task to be sent after start, got {:?}", x),
835            }
836        };
837
838        assert_eq!(task.peer_id, PeerId(1));
839        assert_eq!(task.codec_config, expected_codec_config);
840
841        assert!(task.is_started());
842        assert!(stream.suspend().is_ok());
843        assert!(!task.is_started());
844        assert!(stream.start().is_ok());
845
846        let next_task_fut = task_builder.next_task();
847        // Task should be created here.
848        let task = {
849            let mut next_task_fut = pin!(next_task_fut);
850            match exec.run_until_stalled(&mut next_task_fut) {
851                Poll::Ready(Some(task)) => task,
852                x => panic!("Expected next task to be sent after start, got {:?}", x),
853            }
854        };
855
856        assert!(task.is_started());
857    }
858
859    #[test_case(Transport::Socket ; "socket")]
860    #[test_case(Transport::Fidl ; "fidl")]
861    #[fuchsia::test]
862    fn media_task_ending_ends_future(transport: Transport) {
863        let mut exec = fasync::TestExecutor::new();
864
865        let mut task_builder = TestMediaTaskBuilder::new();
866        let mut stream = Stream::build(
867            make_sbc_endpoint(1, avdtp::EndpointType::Source),
868            task_builder.builder(),
869        );
870        let next_task_fut = task_builder.next_task();
871        let peer_id = PeerId(1);
872        let remote_id = 1_u8.try_into().expect("good id");
873
874        let sbc_codec_cap = sbc_mediacodec_capability();
875        let expected_codec_config =
876            MediaCodecConfig::try_from(&sbc_codec_cap).expect("codec config");
877
878        assert!(stream.configure(&peer_id, &remote_id, vec![]).is_err());
879        assert!(stream.configure(&peer_id, &remote_id, vec![sbc_codec_cap]).is_ok());
880
881        stream.endpoint_mut().establish().expect("establishment should start okay");
882        let (transport, _remote) = create_test_channels(transport);
883        let _ = stream.endpoint_mut().receive_channel(transport).expect("ready for a channel");
884
885        let stream_finish_fut = stream.start().expect("start to succeed with a future");
886        let mut stream_finish_fut = pin!(stream_finish_fut);
887
888        let task = {
889            let mut next_task_fut = pin!(next_task_fut);
890            match exec.run_until_stalled(&mut next_task_fut) {
891                Poll::Ready(Some(task)) => task,
892                x => panic!("Expected next task to be sent after start, got {:?}", x),
893            }
894        };
895
896        assert_eq!(task.peer_id, PeerId(1));
897        assert_eq!(task.codec_config, expected_codec_config);
898
899        // Does not need to be polled to be started.
900        assert!(task.is_started());
901
902        assert!(exec.run_until_stalled(&mut stream_finish_fut).is_pending());
903
904        task.end_prematurely(Some(Ok(MediaTaskStatus::Stopped)));
905        assert!(!task.is_started());
906
907        // The future should be finished, since the task ended.
908        match exec.run_until_stalled(&mut stream_finish_fut) {
909            Poll::Ready(Ok(MediaTaskStatus::Stopped)) => {}
910            x => panic!("Expected to get ready Ok from finish future, but got {x:?}"),
911        };
912
913        // Should still be able to suspend the stream.
914        assert!(stream.suspend().is_ok());
915
916        // And be able to restart it.
917        let result_fut = stream.start().expect("start to succeed with a future");
918
919        let next_task_fut = task_builder.next_task();
920        let mut next_task_fut = pin!(next_task_fut);
921        let task = match exec.run_until_stalled(&mut next_task_fut) {
922            Poll::Ready(Some(task)) => task,
923            x => panic!("Expected next task to be sent after restart, got {x:?}"),
924        };
925
926        assert!(task.is_started());
927
928        // Dropping the result future shouldn't stop the media task.
929        drop(result_fut);
930
931        assert!(task.is_started());
932    }
933
934    #[test_case(Transport::Socket ; "socket")]
935    #[test_case(Transport::Fidl ; "fidl")]
936    #[fuchsia::test]
937    fn set_delay_correct_results_transmits_to_task(transport: Transport) {
938        let mut _exec = fasync::TestExecutor::new();
939
940        let mut task_builder = TestMediaTaskBuilder::new_delayable();
941        let mut stream = Stream::build(
942            make_sbc_endpoint(1, avdtp::EndpointType::Source),
943            task_builder.builder(),
944        );
945        let peer_id = PeerId(1);
946        let remote_id = 1_u8.try_into().expect("good id");
947
948        let sbc_codec_cap = sbc_mediacodec_capability();
949
950        let code = stream
951            .set_delay(std::time::Duration::ZERO)
952            .expect_err("before configure, can't set a delay");
953        assert_eq!(ErrorCode::BadState, code);
954
955        assert!(stream.configure(&peer_id, &remote_id, vec![]).is_err());
956        assert!(stream.configure(&peer_id, &remote_id, vec![sbc_codec_cap]).is_ok());
957
958        let delay_set = std::time::Duration::from_nanos(0xfeed);
959
960        stream.set_delay(delay_set.clone()).expect("after configure, delay is fine");
961
962        stream.endpoint_mut().establish().expect("establishment should start okay");
963        let (transport, _remote) = create_test_channels(transport);
964        let _ = stream.endpoint_mut().receive_channel(transport).expect("ready for a channel");
965        let _stream_finish_fut = stream.start().expect("start to succeed with a future");
966
967        let media_task = task_builder.expect_task();
968        assert_eq!(delay_set, media_task.delay);
969    }
970}