1use fuchsia_bluetooth::types::{PeerId, Uuid};
6use fuchsia_sync::Mutex;
7use futures::stream::BoxStream;
8use futures::StreamExt;
9use std::collections::{HashMap, HashSet};
10use std::sync::Arc;
11use thiserror::Error;
12use {fidl_fuchsia_bluetooth_bredr as bredr, fidl_fuchsia_media as media};
13
14use crate::codec_id::CodecId;
15use crate::sco;
16
17#[derive(Error, Debug)]
18pub enum Error {
19 #[error("Parameters aren't supported {:?}", .source)]
20 UnsupportedParameters { source: anyhow::Error },
21 #[error("Audio is already started")]
22 AlreadyStarted,
23 #[error("AudioCore Error: {:?}", .source)]
24 AudioCore { source: anyhow::Error },
25 #[error("FIDL Error: {:?}", .0)]
26 Fidl(#[from] fidl::Error),
27 #[error("Audio is not started")]
28 NotStarted,
29 #[error("Could not find suitable devices")]
30 DiscoveryFailed,
31 #[error("Operation is in progress: {}", .description)]
32 InProgress { description: String },
33}
34
35impl Error {
36 fn audio_core(e: anyhow::Error) -> Self {
37 Self::AudioCore { source: e }
38 }
39}
40
41mod dai;
42use dai::DaiControl;
43
44mod inband;
45use inband::InbandControl;
46
47mod codec;
48pub use codec::CodecControl;
49
50const DEVICE_NAME: &'static str = "Bluetooth HFP";
51
52const HF_INPUT_UUID: Uuid =
54 Uuid::new16(bredr::ServiceClassProfileIdentifier::Handsfree.into_primitive());
55const HF_OUTPUT_UUID: Uuid =
56 Uuid::new16(bredr::ServiceClassProfileIdentifier::HandsfreeAudioGateway.into_primitive());
57
58#[derive(Debug)]
59pub enum ControlEvent {
60 RequestStart { id: PeerId },
64 RequestStop { id: PeerId },
72 Started { id: PeerId },
74 Stopped { id: PeerId, error: Option<Error> },
79}
80
81impl ControlEvent {
82 pub fn id(&self) -> PeerId {
83 match self {
84 ControlEvent::RequestStart { id } => *id,
85 ControlEvent::RequestStop { id } => *id,
86 ControlEvent::Started { id } => *id,
87 ControlEvent::Stopped { id, error: _ } => *id,
88 }
89 }
90}
91
92pub trait Control: Send {
93 fn connect(&mut self, id: PeerId, supported_codecs: &[CodecId]);
98
99 fn disconnect(&mut self, id: PeerId);
103
104 fn start(
108 &mut self,
109 id: PeerId,
110 connection: sco::Connection,
111 codec: CodecId,
112 ) -> Result<(), Error>;
113
114 fn stop(&mut self, id: PeerId) -> Result<(), Error>;
119
120 fn take_events(&self) -> BoxStream<'static, ControlEvent>;
123
124 fn failed_request(&self, request: ControlEvent, error: Error);
128}
129
130pub struct PartialOffloadControl {
134 offload_codecids: HashSet<CodecId>,
135 offload: Box<dyn Control>,
137 inband: InbandControl,
139 started: HashMap<PeerId, bool>,
141}
142
143impl PartialOffloadControl {
144 pub async fn setup_audio_core(
145 audio_proxy: media::AudioDeviceEnumeratorProxy,
146 offload_supported: HashSet<CodecId>,
147 ) -> Result<Self, Error> {
148 let dai = DaiControl::discover(audio_proxy.clone()).await?;
149 let inband = InbandControl::create(audio_proxy)?;
150 Ok(Self {
151 offload_codecids: offload_supported,
152 offload: Box::new(dai),
153 inband,
154 started: Default::default(),
155 })
156 }
157}
158
159impl Control for PartialOffloadControl {
160 fn start(
161 &mut self,
162 id: PeerId,
163 connection: sco::Connection,
164 codec: CodecId,
165 ) -> Result<(), Error> {
166 if self.started.contains_key(&id) {
167 return Err(Error::AlreadyStarted);
168 }
169 let result = if self.offload_codecids.contains(&codec) {
170 self.offload.start(id, connection, codec)
171 } else {
172 self.inband.start(id, connection, codec)
173 };
174 if result.is_ok() {
175 let _ = self.started.insert(id, self.offload_codecids.contains(&codec));
176 }
177 result
178 }
179
180 fn stop(&mut self, id: PeerId) -> Result<(), Error> {
181 let stop_result = match self.started.get(&id) {
182 None => return Err(Error::NotStarted),
183 Some(true) => self.offload.stop(id),
184 Some(false) => self.inband.stop(id),
185 };
186 if stop_result.is_ok() {
187 let _ = self.started.remove(&id);
188 }
189 stop_result
190 }
191
192 fn connect(&mut self, id: PeerId, supported_codecs: &[CodecId]) {
193 self.inband.connect(id, supported_codecs);
196 if supported_codecs.iter().any(|i| self.offload_codecids.contains(i)) {
197 self.offload.connect(id, supported_codecs);
198 }
199 }
200
201 fn disconnect(&mut self, id: PeerId) {
202 self.inband.disconnect(id);
203 self.offload.disconnect(id);
204 }
205
206 fn take_events(&self) -> BoxStream<'static, ControlEvent> {
207 let inband_events = self.inband.take_events();
208 let controller_events = self.offload.take_events();
209 futures::stream::select_all([inband_events, controller_events]).boxed()
210 }
211
212 fn failed_request(&self, request: ControlEvent, error: Error) {
213 self.offload.failed_request(request, error);
216 }
217}
218
219struct TestControlInner {
220 started: HashSet<PeerId>,
221 connected: HashMap<PeerId, HashSet<CodecId>>,
222 connections: HashMap<PeerId, sco::Connection>,
223 event_sender: futures::channel::mpsc::Sender<ControlEvent>,
224}
225
226#[derive(Clone)]
227pub struct TestControl {
228 inner: Arc<Mutex<TestControlInner>>,
229}
230
231impl TestControl {
232 pub fn unexpected_stop(&self, id: PeerId, error: Error) {
233 let mut lock = self.inner.lock();
234 let _ = lock.started.remove(&id);
235 let _ = lock.connections.remove(&id);
236 let _ = lock.event_sender.try_send(ControlEvent::Stopped { id, error: Some(error) });
237 }
238
239 pub fn is_started(&self, id: PeerId) -> bool {
240 let lock = self.inner.lock();
241 lock.started.contains(&id) && lock.connections.contains_key(&id)
242 }
243
244 pub fn is_connected(&self, id: PeerId) -> bool {
245 let lock = self.inner.lock();
246 lock.connected.contains_key(&id)
247 }
248}
249
250impl Default for TestControl {
251 fn default() -> Self {
252 let (event_sender, _) = futures::channel::mpsc::channel(0);
254 Self {
255 inner: Arc::new(Mutex::new(TestControlInner {
256 started: Default::default(),
257 connected: Default::default(),
258 connections: Default::default(),
259 event_sender,
260 })),
261 }
262 }
263}
264
265impl Control for TestControl {
266 fn start(
267 &mut self,
268 id: PeerId,
269 connection: sco::Connection,
270 _codec: CodecId,
271 ) -> Result<(), Error> {
272 let mut lock = self.inner.lock();
273 if !lock.started.insert(id) {
274 return Err(Error::AlreadyStarted);
275 }
276 let _ = lock.connections.insert(id, connection);
277 let _ = lock.event_sender.try_send(ControlEvent::Started { id });
278 Ok(())
279 }
280
281 fn stop(&mut self, id: PeerId) -> Result<(), Error> {
282 let mut lock = self.inner.lock();
283 if !lock.started.remove(&id) {
284 return Err(Error::NotStarted);
285 }
286 let _ = lock.connections.remove(&id);
287 let _ = lock.event_sender.try_send(ControlEvent::Stopped { id, error: None });
288 Ok(())
289 }
290
291 fn connect(&mut self, id: PeerId, supported_codecs: &[CodecId]) {
292 let mut lock = self.inner.lock();
293 let _ = lock.connected.insert(id, supported_codecs.iter().cloned().collect());
294 }
295
296 fn disconnect(&mut self, id: PeerId) {
297 let _ = self.stop(id);
298 let mut lock = self.inner.lock();
299 let _ = lock.connected.remove(&id);
300 }
301
302 fn take_events(&self) -> BoxStream<'static, ControlEvent> {
303 let mut lock = self.inner.lock();
304 let (sender, receiver) = futures::channel::mpsc::channel(1);
306 lock.event_sender = sender;
307 receiver.boxed()
308 }
309
310 fn failed_request(&self, _request: ControlEvent, _error: Error) {
311 }
313}