1 //! Anything related to audio and media API.
2
3 use bt_topshim::btif::{BluetoothInterface, RawAddress};
4 use bt_topshim::profiles::a2dp::{
5 A2dp, A2dpCallbacks, A2dpCallbacksDispatcher, A2dpCodecBitsPerSample, A2dpCodecChannelMode,
6 A2dpCodecConfig, A2dpCodecSampleRate, BtavConnectionState, PresentationPosition,
7 };
8 use bt_topshim::profiles::avrcp::{Avrcp, AvrcpCallbacks, AvrcpCallbacksDispatcher};
9 use bt_topshim::profiles::hfp::{
10 BthfAudioState, BthfConnectionState, Hfp, HfpCallbacks, HfpCallbacksDispatcher,
11 HfpCodecCapability,
12 };
13
14 use bt_topshim::topstack;
15
16 use log::{info, warn};
17 use num_traits::cast::ToPrimitive;
18 use std::collections::HashMap;
19 use std::convert::TryFrom;
20 use std::sync::Arc;
21 use std::sync::Mutex;
22
23 use tokio::sync::mpsc::Sender;
24 use tokio::task::JoinHandle;
25 use tokio::time::{sleep, Duration};
26
27 use crate::bluetooth::{Bluetooth, BluetoothDevice, IBluetooth};
28 use crate::Message;
29
30 const DEFAULT_PROFILE_DISCOVERY_TIMEOUT_SEC: u64 = 5;
31
32 pub trait IBluetoothMedia {
33 ///
register_callback(&mut self, callback: Box<dyn IBluetoothMediaCallback + Send>) -> bool34 fn register_callback(&mut self, callback: Box<dyn IBluetoothMediaCallback + Send>) -> bool;
35
36 /// initializes media (both A2dp and AVRCP) stack
initialize(&mut self) -> bool37 fn initialize(&mut self) -> bool;
38
39 /// clean up media stack
cleanup(&mut self) -> bool40 fn cleanup(&mut self) -> bool;
41
connect(&mut self, device: String)42 fn connect(&mut self, device: String);
set_active_device(&mut self, device: String)43 fn set_active_device(&mut self, device: String);
disconnect(&mut self, device: String)44 fn disconnect(&mut self, device: String);
set_audio_config( &mut self, sample_rate: i32, bits_per_sample: i32, channel_mode: i32, ) -> bool45 fn set_audio_config(
46 &mut self,
47 sample_rate: i32,
48 bits_per_sample: i32,
49 channel_mode: i32,
50 ) -> bool;
set_volume(&mut self, volume: i32)51 fn set_volume(&mut self, volume: i32);
start_audio_request(&mut self)52 fn start_audio_request(&mut self);
stop_audio_request(&mut self)53 fn stop_audio_request(&mut self);
get_presentation_position(&mut self) -> PresentationPosition54 fn get_presentation_position(&mut self) -> PresentationPosition;
55
start_sco_call(&mut self, device: String)56 fn start_sco_call(&mut self, device: String);
stop_sco_call(&mut self, device: String)57 fn stop_sco_call(&mut self, device: String);
58 }
59
60 pub trait IBluetoothMediaCallback {
61 /// Triggered when a Bluetooth audio device is ready to be used. This should
62 /// only be triggered once for a device and send an event to clients. If the
63 /// device supports both HFP and A2DP, both should be ready when this is
64 /// triggered.
on_bluetooth_audio_device_added(&self, device: BluetoothAudioDevice)65 fn on_bluetooth_audio_device_added(&self, device: BluetoothAudioDevice);
66
67 ///
on_bluetooth_audio_device_removed(&self, addr: String)68 fn on_bluetooth_audio_device_removed(&self, addr: String);
69
70 ///
on_absolute_volume_supported_changed(&self, supported: bool)71 fn on_absolute_volume_supported_changed(&self, supported: bool);
72
73 ///
on_absolute_volume_changed(&self, volume: i32)74 fn on_absolute_volume_changed(&self, volume: i32);
75 }
76
77 /// Serializable device used in.
78 #[derive(Debug, Default, Clone)]
79 pub struct BluetoothAudioDevice {
80 pub address: String,
81 pub name: String,
82 pub a2dp_caps: Vec<A2dpCodecConfig>,
83 pub hfp_cap: HfpCodecCapability,
84 pub absolute_volume: bool,
85 }
86
87 impl BluetoothAudioDevice {
new( address: String, name: String, a2dp_caps: Vec<A2dpCodecConfig>, hfp_cap: HfpCodecCapability, absolute_volume: bool, ) -> BluetoothAudioDevice88 pub(crate) fn new(
89 address: String,
90 name: String,
91 a2dp_caps: Vec<A2dpCodecConfig>,
92 hfp_cap: HfpCodecCapability,
93 absolute_volume: bool,
94 ) -> BluetoothAudioDevice {
95 BluetoothAudioDevice { address, name, a2dp_caps, hfp_cap, absolute_volume }
96 }
97 }
98 /// Actions that `BluetoothMedia` can take on behalf of the stack.
99 pub enum MediaActions {
100 Connect(String),
101 Disconnect(String),
102 }
103
104 pub struct BluetoothMedia {
105 intf: Arc<Mutex<BluetoothInterface>>,
106 initialized: bool,
107 callbacks: Arc<Mutex<Vec<(u32, Box<dyn IBluetoothMediaCallback + Send>)>>>,
108 callback_last_id: u32,
109 tx: Sender<Message>,
110 adapter: Option<Arc<Mutex<Box<Bluetooth>>>>,
111 a2dp: Option<A2dp>,
112 avrcp: Option<Avrcp>,
113 a2dp_states: HashMap<RawAddress, BtavConnectionState>,
114 hfp: Option<Hfp>,
115 hfp_states: HashMap<RawAddress, BthfConnectionState>,
116 selectable_caps: HashMap<RawAddress, Vec<A2dpCodecConfig>>,
117 hfp_caps: HashMap<RawAddress, HfpCodecCapability>,
118 device_added_tasks: Arc<Mutex<HashMap<RawAddress, Option<JoinHandle<()>>>>>,
119 absolute_volume: bool,
120 }
121
122 impl BluetoothMedia {
new(tx: Sender<Message>, intf: Arc<Mutex<BluetoothInterface>>) -> BluetoothMedia123 pub fn new(tx: Sender<Message>, intf: Arc<Mutex<BluetoothInterface>>) -> BluetoothMedia {
124 BluetoothMedia {
125 intf,
126 initialized: false,
127 callbacks: Arc::new(Mutex::new(vec![])),
128 callback_last_id: 0,
129 tx,
130 adapter: None,
131 a2dp: None,
132 avrcp: None,
133 a2dp_states: HashMap::new(),
134 hfp: None,
135 hfp_states: HashMap::new(),
136 selectable_caps: HashMap::new(),
137 hfp_caps: HashMap::new(),
138 device_added_tasks: Arc::new(Mutex::new(HashMap::new())),
139 absolute_volume: false,
140 }
141 }
142
set_adapter(&mut self, adapter: Arc<Mutex<Box<Bluetooth>>>)143 pub fn set_adapter(&mut self, adapter: Arc<Mutex<Box<Bluetooth>>>) {
144 self.adapter = Some(adapter);
145 }
146
dispatch_a2dp_callbacks(&mut self, cb: A2dpCallbacks)147 pub fn dispatch_a2dp_callbacks(&mut self, cb: A2dpCallbacks) {
148 match cb {
149 A2dpCallbacks::ConnectionState(addr, state) => {
150 if !self.a2dp_states.get(&addr).is_none()
151 && state == *self.a2dp_states.get(&addr).unwrap()
152 {
153 return;
154 }
155 match state {
156 BtavConnectionState::Connected => {
157 info!("[{}]: a2dp connected.", addr.to_string());
158 self.notify_media_capability_added(addr);
159 self.a2dp_states.insert(addr, state);
160 }
161 BtavConnectionState::Disconnected => match self.a2dp_states.remove(&addr) {
162 Some(_) => self.notify_media_capability_removed(addr),
163 None => {
164 warn!("[{}]: Unknown address a2dp disconnected.", addr.to_string());
165 }
166 },
167 _ => {
168 self.a2dp_states.insert(addr, state);
169 }
170 }
171 }
172 A2dpCallbacks::AudioState(_addr, _state) => {}
173 A2dpCallbacks::AudioConfig(addr, _config, _local_caps, selectable_caps) => {
174 self.selectable_caps.insert(addr, selectable_caps);
175 }
176 A2dpCallbacks::MandatoryCodecPreferred(_addr) => {}
177 }
178 }
179
dispatch_avrcp_callbacks(&mut self, cb: AvrcpCallbacks)180 pub fn dispatch_avrcp_callbacks(&mut self, cb: AvrcpCallbacks) {
181 match cb {
182 AvrcpCallbacks::AvrcpAbsoluteVolumeEnabled(supported) => {
183 self.absolute_volume = supported;
184 self.for_all_callbacks(|callback| {
185 callback.on_absolute_volume_supported_changed(supported);
186 });
187 }
188 AvrcpCallbacks::AvrcpAbsoluteVolumeUpdate(volume) => {
189 self.for_all_callbacks(|callback| {
190 callback.on_absolute_volume_changed(i32::from(volume));
191 });
192 }
193 }
194 }
195
dispatch_media_actions(&mut self, action: MediaActions)196 pub fn dispatch_media_actions(&mut self, action: MediaActions) {
197 match action {
198 MediaActions::Connect(address) => self.connect(address),
199 MediaActions::Disconnect(address) => self.disconnect(address),
200 }
201 }
202
dispatch_hfp_callbacks(&mut self, cb: HfpCallbacks)203 pub fn dispatch_hfp_callbacks(&mut self, cb: HfpCallbacks) {
204 match cb {
205 HfpCallbacks::ConnectionState(state, addr) => {
206 if !self.hfp_states.get(&addr).is_none()
207 && state == *self.hfp_states.get(&addr).unwrap()
208 {
209 return;
210 }
211 match state {
212 BthfConnectionState::Connected => {
213 info!("[{}]: hfp connected.", addr.to_string());
214 }
215 BthfConnectionState::SlcConnected => {
216 info!("[{}]: hfp slc connected.", addr.to_string());
217 // TODO(b/214148074): Support WBS
218 self.hfp_caps.insert(addr, HfpCodecCapability::CVSD);
219 self.notify_media_capability_added(addr);
220 }
221 BthfConnectionState::Disconnected => {
222 info!("[{}]: hfp disconnected.", addr.to_string());
223 match self.hfp_states.remove(&addr) {
224 Some(_) => self.notify_media_capability_removed(addr),
225 None => {
226 warn!("[{}] Unknown address hfp disconnected.", addr.to_string())
227 }
228 }
229 return;
230 }
231 BthfConnectionState::Connecting => {
232 info!("[{}]: hfp connecting.", addr.to_string());
233 }
234 BthfConnectionState::Disconnecting => {
235 info!("[{}]: hfp disconnecting.", addr.to_string());
236 }
237 }
238
239 self.hfp_states.insert(addr, state);
240 }
241 HfpCallbacks::AudioState(state, addr) => {
242 if self.hfp_states.get(&addr).is_none()
243 || BthfConnectionState::SlcConnected != *self.hfp_states.get(&addr).unwrap()
244 {
245 warn!("[{}]: Unknown address hfp or slc not ready", addr.to_string());
246 return;
247 }
248 match state {
249 BthfAudioState::Connected => {
250 info!("[{}]: hfp audio connected.", addr.to_string());
251 }
252 BthfAudioState::Disconnected => {
253 info!("[{}]: hfp audio disconnected.", addr.to_string());
254 }
255 BthfAudioState::Connecting => {
256 info!("[{}]: hfp audio connecting.", addr.to_string());
257 }
258 BthfAudioState::Disconnecting => {
259 info!("[{}]: hfp audio disconnecting.", addr.to_string());
260 }
261 }
262 }
263 }
264 }
265
notify_media_capability_added(&self, addr: RawAddress)266 fn notify_media_capability_added(&self, addr: RawAddress) {
267 // Return true if the device added message is sent by the call.
268 fn dedup_added_cb(
269 device_added_tasks: Arc<Mutex<HashMap<RawAddress, Option<JoinHandle<()>>>>>,
270 addr: RawAddress,
271 callbacks: Arc<Mutex<Vec<(u32, Box<dyn IBluetoothMediaCallback + Send>)>>>,
272 device: BluetoothAudioDevice,
273 is_delayed: bool,
274 ) -> bool {
275 // Closure used to lock and trigger the device added callbacks.
276 let trigger_device_added = || {
277 for callback in &*callbacks.lock().unwrap() {
278 callback.1.on_bluetooth_audio_device_added(device.clone());
279 }
280 };
281 let mut guard = device_added_tasks.lock().unwrap();
282 let task = guard.insert(addr, None);
283 match task {
284 // None handler means the device has just been added
285 Some(handler) if handler.is_none() => {
286 warn!("[{}]: A device with the same address has been added.", addr.to_string());
287 false
288 }
289 // Not None handler means there is a pending task.
290 Some(handler) => {
291 trigger_device_added();
292
293 // Abort the delayed callback if the caller is not delayed.
294 // Otherwise, it is the delayed callback task itself.
295 // The abort call can be out of the critical section as we
296 // have updated the device_added_tasks and send the message.
297 drop(guard);
298 if !is_delayed {
299 handler.unwrap().abort();
300 }
301 true
302 }
303 // The delayed callback task has been removed and couldn't be found.
304 None if is_delayed => false,
305 // No delayed callback and the device hasn't been added.
306 None => {
307 trigger_device_added();
308 true
309 }
310 }
311 }
312
313 let cur_a2dp_caps = self.selectable_caps.get(&addr);
314 let cur_hfp_cap = self.hfp_caps.get(&addr);
315 let name = self.adapter_get_remote_name(addr);
316 let absolute_volume = self.absolute_volume;
317 match (cur_a2dp_caps, cur_hfp_cap) {
318 (None, None) => warn!(
319 "[{}]: Try to add a device without a2dp and hfp capability.",
320 addr.to_string()
321 ),
322 (Some(caps), Some(hfp_cap)) => {
323 dedup_added_cb(
324 self.device_added_tasks.clone(),
325 addr,
326 self.callbacks.clone(),
327 BluetoothAudioDevice::new(
328 addr.to_string(),
329 name.clone(),
330 caps.to_vec(),
331 *hfp_cap,
332 absolute_volume,
333 ),
334 false,
335 );
336 }
337 (_, _) => {
338 let mut guard = self.device_added_tasks.lock().unwrap();
339 if guard.get(&addr).is_none() {
340 let callbacks = self.callbacks.clone();
341 let device_added_tasks = self.device_added_tasks.clone();
342 let device = BluetoothAudioDevice::new(
343 addr.to_string(),
344 name.clone(),
345 cur_a2dp_caps.unwrap_or(&Vec::new()).to_vec(),
346 *cur_hfp_cap.unwrap_or(&HfpCodecCapability::UNSUPPORTED),
347 absolute_volume,
348 );
349 let task = topstack::get_runtime().spawn(async move {
350 sleep(Duration::from_secs(DEFAULT_PROFILE_DISCOVERY_TIMEOUT_SEC)).await;
351 if dedup_added_cb(device_added_tasks, addr, callbacks, device, true) {
352 warn!(
353 "[{}]: Add a device with only hfp or a2dp capability after timeout.",
354 addr.to_string()
355 );
356 }
357 });
358 guard.insert(addr, Some(task));
359 }
360 }
361 }
362 }
363
notify_media_capability_removed(&self, addr: RawAddress)364 fn notify_media_capability_removed(&self, addr: RawAddress) {
365 if let Some(task) = self.device_added_tasks.lock().unwrap().remove(&addr) {
366 match task {
367 // Abort what is pending
368 Some(handler) => handler.abort(),
369 // This addr has been added so tell audio server to remove it
370 None => self.for_all_callbacks(|callback| {
371 callback.on_bluetooth_audio_device_removed(addr.to_string());
372 }),
373 }
374 } else {
375 warn!("[{}]: Device hasn't been added yet.", addr.to_string());
376 }
377 }
378
for_all_callbacks<F: Fn(&Box<dyn IBluetoothMediaCallback + Send>)>(&self, f: F)379 fn for_all_callbacks<F: Fn(&Box<dyn IBluetoothMediaCallback + Send>)>(&self, f: F) {
380 for callback in &*self.callbacks.lock().unwrap() {
381 f(&callback.1);
382 }
383 }
384
adapter_get_remote_name(&self, addr: RawAddress) -> String385 fn adapter_get_remote_name(&self, addr: RawAddress) -> String {
386 let device = BluetoothDevice::new(
387 addr.to_string(),
388 // get_remote_name needs a BluetoothDevice just for its address, the
389 // name field is unused so construct one with a fake name.
390 "Classic Device".to_string(),
391 );
392 if let Some(adapter) = &self.adapter {
393 match adapter.lock().unwrap().get_remote_name(device).as_str() {
394 "" => addr.to_string(),
395 name => name.into(),
396 }
397 } else {
398 addr.to_string()
399 }
400 }
401
get_hfp_connection_state(&self) -> u32402 pub fn get_hfp_connection_state(&self) -> u32 {
403 for state in self.hfp_states.values() {
404 return BthfConnectionState::to_u32(state).unwrap_or(0);
405 }
406 0
407 }
408
get_a2dp_connection_state(&self) -> u32409 pub fn get_a2dp_connection_state(&self) -> u32 {
410 for state in self.a2dp_states.values() {
411 return BtavConnectionState::to_u32(state).unwrap_or(0);
412 }
413 0
414 }
415 }
416
get_a2dp_dispatcher(tx: Sender<Message>) -> A2dpCallbacksDispatcher417 fn get_a2dp_dispatcher(tx: Sender<Message>) -> A2dpCallbacksDispatcher {
418 A2dpCallbacksDispatcher {
419 dispatch: Box::new(move |cb| {
420 let txl = tx.clone();
421 topstack::get_runtime().spawn(async move {
422 let _ = txl.send(Message::A2dp(cb)).await;
423 });
424 }),
425 }
426 }
427
get_avrcp_dispatcher(tx: Sender<Message>) -> AvrcpCallbacksDispatcher428 fn get_avrcp_dispatcher(tx: Sender<Message>) -> AvrcpCallbacksDispatcher {
429 AvrcpCallbacksDispatcher {
430 dispatch: Box::new(move |cb| {
431 let txl = tx.clone();
432 topstack::get_runtime().spawn(async move {
433 let _ = txl.send(Message::Avrcp(cb)).await;
434 });
435 }),
436 }
437 }
438
get_hfp_dispatcher(tx: Sender<Message>) -> HfpCallbacksDispatcher439 fn get_hfp_dispatcher(tx: Sender<Message>) -> HfpCallbacksDispatcher {
440 HfpCallbacksDispatcher {
441 dispatch: Box::new(move |cb| {
442 let txl = tx.clone();
443 topstack::get_runtime().spawn(async move {
444 let _ = txl.send(Message::Hfp(cb)).await;
445 });
446 }),
447 }
448 }
449
450 impl IBluetoothMedia for BluetoothMedia {
register_callback(&mut self, callback: Box<dyn IBluetoothMediaCallback + Send>) -> bool451 fn register_callback(&mut self, callback: Box<dyn IBluetoothMediaCallback + Send>) -> bool {
452 self.callback_last_id += 1;
453 self.callbacks.lock().unwrap().push((self.callback_last_id, callback));
454 true
455 }
456
initialize(&mut self) -> bool457 fn initialize(&mut self) -> bool {
458 if self.initialized {
459 return false;
460 }
461 self.initialized = true;
462
463 // TEST A2dp
464 let a2dp_dispatcher = get_a2dp_dispatcher(self.tx.clone());
465 self.a2dp = Some(A2dp::new(&self.intf.lock().unwrap()));
466 self.a2dp.as_mut().unwrap().initialize(a2dp_dispatcher);
467
468 // AVRCP
469 let avrcp_dispatcher = get_avrcp_dispatcher(self.tx.clone());
470 self.avrcp = Some(Avrcp::new(&self.intf.lock().unwrap()));
471 self.avrcp.as_mut().unwrap().initialize(avrcp_dispatcher);
472
473 // HFP
474 let hfp_dispatcher = get_hfp_dispatcher(self.tx.clone());
475 self.hfp = Some(Hfp::new(&self.intf.lock().unwrap()));
476 self.hfp.as_mut().unwrap().initialize(hfp_dispatcher);
477
478 true
479 }
480
connect(&mut self, device: String)481 fn connect(&mut self, device: String) {
482 if let Some(addr) = RawAddress::from_string(device.clone()) {
483 self.a2dp.as_mut().unwrap().connect(addr);
484 self.hfp.as_mut().unwrap().connect(addr);
485 } else {
486 warn!("Invalid device string {}", device);
487 }
488 }
489
cleanup(&mut self) -> bool490 fn cleanup(&mut self) -> bool {
491 true
492 }
493
set_active_device(&mut self, device: String)494 fn set_active_device(&mut self, device: String) {
495 if let Some(addr) = RawAddress::from_string(device.clone()) {
496 self.a2dp.as_mut().unwrap().set_active_device(addr);
497 } else {
498 warn!("Invalid device string {}", device);
499 }
500 }
501
disconnect(&mut self, device: String)502 fn disconnect(&mut self, device: String) {
503 if let Some(addr) = RawAddress::from_string(device.clone()) {
504 self.a2dp.as_mut().unwrap().disconnect(addr);
505 self.hfp.as_mut().unwrap().disconnect(addr);
506 } else {
507 warn!("Invalid device string {}", device);
508 }
509 }
510
set_audio_config( &mut self, sample_rate: i32, bits_per_sample: i32, channel_mode: i32, ) -> bool511 fn set_audio_config(
512 &mut self,
513 sample_rate: i32,
514 bits_per_sample: i32,
515 channel_mode: i32,
516 ) -> bool {
517 if !A2dpCodecSampleRate::validate_bits(sample_rate)
518 || !A2dpCodecBitsPerSample::validate_bits(bits_per_sample)
519 || !A2dpCodecChannelMode::validate_bits(channel_mode)
520 {
521 return false;
522 }
523 self.a2dp.as_mut().unwrap().set_audio_config(sample_rate, bits_per_sample, channel_mode);
524 true
525 }
526
set_volume(&mut self, volume: i32)527 fn set_volume(&mut self, volume: i32) {
528 match i8::try_from(volume) {
529 Ok(val) => self.avrcp.as_mut().unwrap().set_volume(val),
530 _ => (),
531 };
532 }
533
start_audio_request(&mut self)534 fn start_audio_request(&mut self) {
535 self.a2dp.as_mut().unwrap().start_audio_request();
536 }
537
stop_audio_request(&mut self)538 fn stop_audio_request(&mut self) {
539 self.a2dp.as_mut().unwrap().stop_audio_request();
540 }
541
start_sco_call(&mut self, device: String)542 fn start_sco_call(&mut self, device: String) {
543 if let Some(addr) = RawAddress::from_string(device.clone()) {
544 info!("Start sco call for {}", device);
545 match self.hfp.as_mut().unwrap().connect_audio(addr) {
546 0 => {
547 info!("SCO connect_audio status success.");
548 }
549 x => {
550 warn!("SCO connect_audio status failed: {}", x);
551 }
552 };
553 } else {
554 warn!("Can't start sco call with: {}", device);
555 }
556 }
557
stop_sco_call(&mut self, device: String)558 fn stop_sco_call(&mut self, device: String) {
559 if let Some(addr) = RawAddress::from_string(device.clone()) {
560 info!("Stop sco call for {}", device);
561 self.hfp.as_mut().unwrap().disconnect_audio(addr);
562 } else {
563 warn!("Can't stop sco call with: {}", device);
564 }
565 }
566
get_presentation_position(&mut self) -> PresentationPosition567 fn get_presentation_position(&mut self) -> PresentationPosition {
568 let position = self.a2dp.as_mut().unwrap().get_presentation_position();
569 PresentationPosition {
570 remote_delay_report_ns: position.remote_delay_report_ns,
571 total_bytes_read: position.total_bytes_read,
572 data_position_sec: position.data_position_sec,
573 data_position_nsec: position.data_position_nsec,
574 }
575 }
576 }
577