1use anyhow::{Context, format_err};
6use fuchsia_bluetooth::types::{PeerId, Uuid};
7use futures::stream::BoxStream;
8use std::collections::HashSet;
9use std::str::FromStr;
10use thiserror::Error;
11use {
12 fidl_fuchsia_audio_device as audio_device, fidl_fuchsia_bluetooth_bredr as bredr,
13 fidl_fuchsia_media as media,
14};
15
16use crate::codec_id::CodecId;
17use crate::sco;
18
19#[derive(Error, Debug)]
20pub enum Error {
21 #[error("Parameters aren't supported {:?}", .source)]
22 UnsupportedParameters { source: anyhow::Error },
23 #[error("Audio is already started")]
24 AlreadyStarted,
25 #[error("AudioCore Error: {:?}", .source)]
26 AudioCore { source: anyhow::Error },
27 #[error("FIDL Error: {:?}", .0)]
28 Fidl(#[from] fidl::Error),
29 #[error("Audio is not started")]
30 NotStarted,
31 #[error("Could not find suitable devices")]
32 DiscoveryFailed,
33 #[error("Operation is in progress: {}", .description)]
34 InProgress { description: String },
35}
36
37impl Error {
38 fn audio_core(e: anyhow::Error) -> Self {
39 Self::AudioCore { source: e }
40 }
41}
42
43mod codec;
44pub use codec::CodecControl;
45
46mod dai;
47pub use dai::DaiControl;
48
49mod inband;
50pub use inband::InbandControl;
51
52mod partial_offload;
53pub use partial_offload::PartialOffloadControl;
54
55mod test;
56pub use test::TestControl;
57
58const DEVICE_NAME: &'static str = "Bluetooth HFP";
59
60const HF_INPUT_UUID: Uuid =
62 Uuid::new16(bredr::ServiceClassProfileIdentifier::Handsfree.into_primitive());
63const HF_OUTPUT_UUID: Uuid =
64 Uuid::new16(bredr::ServiceClassProfileIdentifier::HandsfreeAudioGateway.into_primitive());
65
66#[derive(Debug)]
67pub enum ControlEvent {
68 RequestStart { id: PeerId },
72 RequestStop { id: PeerId },
80 Started { id: PeerId },
82 Stopped { id: PeerId, error: Option<Error> },
87}
88
89impl ControlEvent {
90 pub fn id(&self) -> PeerId {
91 match self {
92 ControlEvent::RequestStart { id } => *id,
93 ControlEvent::RequestStop { id } => *id,
94 ControlEvent::Started { id } => *id,
95 ControlEvent::Stopped { id, error: _ } => *id,
96 }
97 }
98}
99
100pub trait Control: Send {
101 fn connect(&mut self, id: PeerId, supported_codecs: &[CodecId]);
106
107 fn disconnect(&mut self, id: PeerId);
111
112 fn start(
116 &mut self,
117 id: PeerId,
118 connection: sco::Connection,
119 codec: CodecId,
120 ) -> Result<(), Error>;
121
122 fn stop(&mut self, id: PeerId) -> Result<(), Error>;
127
128 fn take_events(&self) -> BoxStream<'static, ControlEvent>;
131
132 fn failed_request(&self, request: ControlEvent, error: Error);
136}
137
138#[derive(Debug, Clone, Copy, PartialEq)]
139pub enum OffloadType {
140 Dai,
141 Codec,
142}
143
144impl FromStr for OffloadType {
145 type Err = anyhow::Error;
146
147 fn from_str(s: &str) -> Result<Self, Self::Err> {
148 match s {
149 "dai" => Ok(OffloadType::Dai),
150 "codec" => Ok(OffloadType::Codec),
151 _ => Err(format_err!("Invalid offload type: {}", s)),
152 }
153 }
154}
155
156pub async fn setup_audio(
157 offload_type: OffloadType,
158 controller_codecs: HashSet<CodecId>,
159) -> Result<Box<dyn Control>, anyhow::Error> {
160 match offload_type {
161 OffloadType::Dai => {
162 let audio_proxy = fuchsia_component::client::connect_to_protocol::<
163 media::AudioDeviceEnumeratorMarker,
164 >()
165 .with_context(|| format!("Error connecting to audio_core"))?;
166 Ok(Box::new(
167 PartialOffloadControl::setup_audio_core(audio_proxy, controller_codecs).await?,
168 ))
169 }
170 OffloadType::Codec => {
171 let provider =
172 fuchsia_component::client::connect_to_protocol::<audio_device::ProviderMarker>()
173 .with_context(|| format!("Error connecting to audio_device_registry"))?;
174 let codec = CodecControl::new(provider);
175 Ok(Box::new(codec))
176 }
177 }
178}