1 use crate::btif::{BluetoothInterface, RawAddress}; 2 use crate::topstack::get_dispatchers; 3 4 use num_traits::cast::FromPrimitive; 5 use std::sync::{Arc, Mutex}; 6 use topshim_macros::cb_variant; 7 8 #[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] 9 #[repr(u32)] 10 pub enum BtavConnectionState { 11 Disconnected = 0, 12 Connecting, 13 Connected, 14 Disconnecting, 15 } 16 17 impl From<u32> for BtavConnectionState { from(item: u32) -> Self18 fn from(item: u32) -> Self { 19 BtavConnectionState::from_u32(item).unwrap() 20 } 21 } 22 23 #[derive(Debug, FromPrimitive, PartialEq, PartialOrd)] 24 #[repr(u32)] 25 pub enum BtavAudioState { 26 RemoteSuspend = 0, 27 Stopped, 28 Started, 29 } 30 31 impl From<u32> for BtavAudioState { from(item: u32) -> Self32 fn from(item: u32) -> Self { 33 BtavAudioState::from_u32(item).unwrap() 34 } 35 } 36 37 #[derive(Debug, FromPrimitive, PartialEq, PartialOrd)] 38 #[repr(u32)] 39 pub enum A2dpCodecIndex { 40 SrcSbc = 0, 41 SrcAac, 42 SrcAptx, 43 SrcAptxHD, 44 SrcLdac, 45 SinkSbc, 46 SinkAac, 47 SinkLdac, 48 Max, 49 } 50 51 impl A2dpCodecIndex { 52 pub const SRC_MIN: A2dpCodecIndex = A2dpCodecIndex::SrcSbc; 53 pub const SRC_MAX: A2dpCodecIndex = A2dpCodecIndex::SinkSbc; 54 pub const SINK_MIN: A2dpCodecIndex = A2dpCodecIndex::SinkSbc; 55 pub const SINK_MAX: A2dpCodecIndex = A2dpCodecIndex::Max; 56 pub const MAX: A2dpCodecIndex = A2dpCodecIndex::Max; 57 pub const MIN: A2dpCodecIndex = A2dpCodecIndex::SrcSbc; 58 } 59 60 impl From<i32> for A2dpCodecIndex { from(item: i32) -> Self61 fn from(item: i32) -> Self { 62 A2dpCodecIndex::from_i32(item).unwrap_or_else(|| A2dpCodecIndex::MIN) 63 } 64 } 65 66 #[derive(Debug, FromPrimitive, PartialEq, PartialOrd)] 67 #[repr(i32)] 68 pub enum A2dpCodecPriority { 69 Disabled = -1, 70 Default = 0, 71 Highest = 1_000_000, 72 } 73 74 impl From<i32> for A2dpCodecPriority { from(item: i32) -> Self75 fn from(item: i32) -> Self { 76 A2dpCodecPriority::from_i32(item).unwrap_or_else(|| A2dpCodecPriority::Default) 77 } 78 } 79 80 bitflags! { 81 pub struct A2dpCodecSampleRate: i32 { 82 const RATE_NONE = 0x0; 83 const RATE_44100 = 0x01; 84 const RATE_48000 = 0x02; 85 const RATE_88200 = 0x04; 86 const RATE_96000 = 0x08; 87 const RATE_176400 = 0x10; 88 const RATE_192000 = 0x20; 89 const RATE_16000 = 0x40; 90 const RATE_24000 = 0x80; 91 } 92 } 93 94 impl A2dpCodecSampleRate { validate_bits(val: i32) -> bool95 pub fn validate_bits(val: i32) -> bool { 96 val <= A2dpCodecSampleRate::all().bits() 97 } 98 } 99 100 bitflags! { 101 pub struct A2dpCodecBitsPerSample: i32 { 102 const SAMPLE_NONE = 0x0; 103 const SAMPLE_16 = 0x01; 104 const SAMPLE_24 = 0x02; 105 const SAMPLE_32 = 0x04; 106 } 107 } 108 109 impl A2dpCodecBitsPerSample { validate_bits(val: i32) -> bool110 pub fn validate_bits(val: i32) -> bool { 111 val <= A2dpCodecBitsPerSample::all().bits() 112 } 113 } 114 115 bitflags! { 116 pub struct A2dpCodecChannelMode: i32 { 117 const MODE_NONE = 0x0; 118 const MODE_MONO = 0x01; 119 const MODE_STEREO = 0x02; 120 } 121 } 122 123 impl A2dpCodecChannelMode { validate_bits(val: i32) -> bool124 pub fn validate_bits(val: i32) -> bool { 125 val <= A2dpCodecChannelMode::all().bits() 126 } 127 } 128 129 #[cxx::bridge(namespace = bluetooth::topshim::rust)] 130 pub mod ffi { 131 #[derive(Debug, Copy, Clone)] 132 pub struct RustRawAddress { 133 address: [u8; 6], 134 } 135 136 #[derive(Debug, Copy, Clone)] 137 pub struct A2dpCodecConfig { 138 pub codec_type: i32, 139 pub codec_priority: i32, 140 pub sample_rate: i32, 141 pub bits_per_sample: i32, 142 pub channel_mode: i32, 143 pub codec_specific_1: i64, 144 pub codec_specific_2: i64, 145 pub codec_specific_3: i64, 146 pub codec_specific_4: i64, 147 } 148 149 #[derive(Debug, Default)] 150 pub struct RustPresentationPosition { 151 remote_delay_report_ns: u64, 152 total_bytes_read: u64, 153 data_position_sec: i64, 154 data_position_nsec: i32, 155 } 156 157 unsafe extern "C++" { 158 include!("btav/btav_shim.h"); 159 include!("btav_sink/btav_sink_shim.h"); 160 161 type A2dpIntf; 162 type A2dpSinkIntf; 163 GetA2dpProfile(btif: *const u8) -> UniquePtr<A2dpIntf>164 unsafe fn GetA2dpProfile(btif: *const u8) -> UniquePtr<A2dpIntf>; 165 init(self: &A2dpIntf) -> i32166 fn init(self: &A2dpIntf) -> i32; connect(self: &A2dpIntf, bt_addr: RustRawAddress) -> i32167 fn connect(self: &A2dpIntf, bt_addr: RustRawAddress) -> i32; disconnect(self: &A2dpIntf, bt_addr: RustRawAddress) -> i32168 fn disconnect(self: &A2dpIntf, bt_addr: RustRawAddress) -> i32; set_silence_device(self: &A2dpIntf, bt_addr: RustRawAddress, silent: bool) -> i32169 fn set_silence_device(self: &A2dpIntf, bt_addr: RustRawAddress, silent: bool) -> i32; set_active_device(self: &A2dpIntf, bt_addr: RustRawAddress) -> i32170 fn set_active_device(self: &A2dpIntf, bt_addr: RustRawAddress) -> i32; config_codec( self: &A2dpIntf, bt_addr: RustRawAddress, codec_preferences: Vec<A2dpCodecConfig>, ) -> i32171 fn config_codec( 172 self: &A2dpIntf, 173 bt_addr: RustRawAddress, 174 codec_preferences: Vec<A2dpCodecConfig>, 175 ) -> i32; set_audio_config(self: &A2dpIntf, config: A2dpCodecConfig) -> bool176 fn set_audio_config(self: &A2dpIntf, config: A2dpCodecConfig) -> bool; start_audio_request(self: &A2dpIntf) -> bool177 fn start_audio_request(self: &A2dpIntf) -> bool; stop_audio_request(self: &A2dpIntf) -> bool178 fn stop_audio_request(self: &A2dpIntf) -> bool; cleanup(self: &A2dpIntf)179 fn cleanup(self: &A2dpIntf); get_presentation_position(self: &A2dpIntf) -> RustPresentationPosition180 fn get_presentation_position(self: &A2dpIntf) -> RustPresentationPosition; 181 // A2dp sink functions 182 GetA2dpSinkProfile(btif: *const u8) -> UniquePtr<A2dpSinkIntf>183 unsafe fn GetA2dpSinkProfile(btif: *const u8) -> UniquePtr<A2dpSinkIntf>; 184 init(self: &A2dpSinkIntf) -> i32185 fn init(self: &A2dpSinkIntf) -> i32; connect(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32186 fn connect(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32; disconnect(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32187 fn disconnect(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32; set_active_device(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32188 fn set_active_device(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32; cleanup(self: &A2dpSinkIntf)189 fn cleanup(self: &A2dpSinkIntf); 190 } 191 extern "Rust" { connection_state_callback(addr: RustRawAddress, state: u32)192 fn connection_state_callback(addr: RustRawAddress, state: u32); audio_state_callback(addr: RustRawAddress, state: u32)193 fn audio_state_callback(addr: RustRawAddress, state: u32); audio_config_callback( addr: RustRawAddress, codec_config: A2dpCodecConfig, codecs_local_capabilities: Vec<A2dpCodecConfig>, codecs_selectable_capabilities: Vec<A2dpCodecConfig>, )194 fn audio_config_callback( 195 addr: RustRawAddress, 196 codec_config: A2dpCodecConfig, 197 codecs_local_capabilities: Vec<A2dpCodecConfig>, 198 codecs_selectable_capabilities: Vec<A2dpCodecConfig>, 199 ); mandatory_codec_preferred_callback(addr: RustRawAddress)200 fn mandatory_codec_preferred_callback(addr: RustRawAddress); 201 } 202 } 203 204 pub type FfiAddress = ffi::RustRawAddress; 205 pub type A2dpCodecConfig = ffi::A2dpCodecConfig; 206 pub type PresentationPosition = ffi::RustPresentationPosition; 207 208 impl From<RawAddress> for FfiAddress { from(addr: RawAddress) -> Self209 fn from(addr: RawAddress) -> Self { 210 FfiAddress { address: addr.val } 211 } 212 } 213 214 impl Into<RawAddress> for FfiAddress { into(self) -> RawAddress215 fn into(self) -> RawAddress { 216 RawAddress { val: self.address } 217 } 218 } 219 220 impl Default for A2dpCodecConfig { default() -> A2dpCodecConfig221 fn default() -> A2dpCodecConfig { 222 A2dpCodecConfig { 223 codec_type: 0, 224 codec_priority: 0, 225 sample_rate: 0, 226 bits_per_sample: 0, 227 channel_mode: 0, 228 codec_specific_1: 0, 229 codec_specific_2: 0, 230 codec_specific_3: 0, 231 codec_specific_4: 0, 232 } 233 } 234 } 235 236 #[derive(Debug)] 237 pub enum A2dpCallbacks { 238 ConnectionState(RawAddress, BtavConnectionState), 239 AudioState(RawAddress, BtavAudioState), 240 AudioConfig(RawAddress, A2dpCodecConfig, Vec<A2dpCodecConfig>, Vec<A2dpCodecConfig>), 241 MandatoryCodecPreferred(RawAddress), 242 } 243 244 pub struct A2dpCallbacksDispatcher { 245 pub dispatch: Box<dyn Fn(A2dpCallbacks) + Send>, 246 } 247 248 type A2dpCb = Arc<Mutex<A2dpCallbacksDispatcher>>; 249 250 cb_variant!(A2dpCb, connection_state_callback -> A2dpCallbacks::ConnectionState, 251 FfiAddress -> RawAddress, u32 -> BtavConnectionState, { 252 let _0 = _0.into(); 253 }); 254 255 cb_variant!(A2dpCb, audio_state_callback -> A2dpCallbacks::AudioState, 256 FfiAddress -> RawAddress, u32 -> BtavAudioState, { 257 let _0 = _0.into(); 258 }); 259 260 cb_variant!(A2dpCb, mandatory_codec_preferred_callback -> A2dpCallbacks::MandatoryCodecPreferred, 261 FfiAddress -> RawAddress, { 262 let _0 = _0.into(); 263 }); 264 265 cb_variant!(A2dpCb, audio_config_callback -> A2dpCallbacks::AudioConfig, 266 FfiAddress -> RawAddress, A2dpCodecConfig, Vec<A2dpCodecConfig>, Vec<A2dpCodecConfig>, { 267 let _0 = _0.into(); 268 }); 269 270 pub struct A2dp { 271 internal: cxx::UniquePtr<ffi::A2dpIntf>, 272 _is_init: bool, 273 } 274 275 // For *const u8 opaque btif 276 unsafe impl Send for A2dp {} 277 278 impl A2dp { new(intf: &BluetoothInterface) -> A2dp279 pub fn new(intf: &BluetoothInterface) -> A2dp { 280 let a2dpif: cxx::UniquePtr<ffi::A2dpIntf>; 281 unsafe { 282 a2dpif = ffi::GetA2dpProfile(intf.as_raw_ptr()); 283 } 284 285 A2dp { internal: a2dpif, _is_init: false } 286 } 287 initialize(&mut self, callbacks: A2dpCallbacksDispatcher) -> bool288 pub fn initialize(&mut self, callbacks: A2dpCallbacksDispatcher) -> bool { 289 if get_dispatchers().lock().unwrap().set::<A2dpCb>(Arc::new(Mutex::new(callbacks))) { 290 panic!("Tried to set dispatcher for A2dp callbacks while it already exists"); 291 } 292 self.internal.init(); 293 true 294 } 295 connect(&mut self, addr: RawAddress)296 pub fn connect(&mut self, addr: RawAddress) { 297 self.internal.connect(addr.into()); 298 } 299 set_active_device(&mut self, addr: RawAddress)300 pub fn set_active_device(&mut self, addr: RawAddress) { 301 self.internal.set_active_device(addr.into()); 302 } 303 disconnect(&mut self, addr: RawAddress)304 pub fn disconnect(&mut self, addr: RawAddress) { 305 self.internal.disconnect(addr.into()); 306 } 307 set_audio_config(&self, sample_rate: i32, bits_per_sample: i32, channel_mode: i32)308 pub fn set_audio_config(&self, sample_rate: i32, bits_per_sample: i32, channel_mode: i32) { 309 let config = 310 A2dpCodecConfig { sample_rate, bits_per_sample, channel_mode, ..Default::default() }; 311 self.internal.set_audio_config(config); 312 } start_audio_request(&self)313 pub fn start_audio_request(&self) { 314 self.internal.start_audio_request(); 315 } 316 stop_audio_request(&self)317 pub fn stop_audio_request(&self) { 318 self.internal.stop_audio_request(); 319 } 320 get_presentation_position(&self) -> PresentationPosition321 pub fn get_presentation_position(&self) -> PresentationPosition { 322 self.internal.get_presentation_position() 323 } 324 } 325 326 #[derive(Debug)] 327 pub enum A2dpSinkCallbacks { 328 ConnectionState(RawAddress, BtavConnectionState), 329 } 330 331 pub struct A2dpSinkCallbacksDispatcher { 332 pub dispatch: Box<dyn Fn(A2dpSinkCallbacks) + Send>, 333 } 334 335 type A2dpSinkCb = Arc<Mutex<A2dpSinkCallbacksDispatcher>>; 336 337 pub struct A2dpSink { 338 internal: cxx::UniquePtr<ffi::A2dpSinkIntf>, 339 _is_init: bool, 340 } 341 342 // For *const u8 opaque btif 343 unsafe impl Send for A2dpSink {} 344 345 impl A2dpSink { new(intf: &BluetoothInterface) -> A2dpSink346 pub fn new(intf: &BluetoothInterface) -> A2dpSink { 347 let a2dp_sink: cxx::UniquePtr<ffi::A2dpSinkIntf>; 348 unsafe { 349 a2dp_sink = ffi::GetA2dpSinkProfile(intf.as_raw_ptr()); 350 } 351 352 A2dpSink { internal: a2dp_sink, _is_init: false } 353 } 354 initialize(&mut self, callbacks: A2dpSinkCallbacksDispatcher) -> bool355 pub fn initialize(&mut self, callbacks: A2dpSinkCallbacksDispatcher) -> bool { 356 if get_dispatchers().lock().unwrap().set::<A2dpSinkCb>(Arc::new(Mutex::new(callbacks))) { 357 panic!("Tried to set dispatcher for A2dp Sink Callbacks while it already exists"); 358 } 359 self.internal.init(); 360 true 361 } 362 connect(&mut self, bt_addr: RawAddress)363 pub fn connect(&mut self, bt_addr: RawAddress) { 364 self.internal.connect(bt_addr.into()); 365 } 366 disconnect(&mut self, bt_addr: RawAddress)367 pub fn disconnect(&mut self, bt_addr: RawAddress) { 368 self.internal.disconnect(bt_addr.into()); 369 } 370 set_active_device(&mut self, bt_addr: RawAddress)371 pub fn set_active_device(&mut self, bt_addr: RawAddress) { 372 self.internal.set_active_device(bt_addr.into()); 373 } 374 cleanup(&mut self)375 pub fn cleanup(&mut self) {} 376 } 377 378 #[cfg(test)] 379 mod tests { 380 use super::*; 381 382 #[test] validate_sample_rate()383 fn validate_sample_rate() { 384 assert!(!A2dpCodecSampleRate::validate_bits(256)); 385 assert!(A2dpCodecSampleRate::validate_bits(2 + 32 + 128)); 386 } 387 388 #[test] validate_bits_per_sample()389 fn validate_bits_per_sample() { 390 assert!(!A2dpCodecBitsPerSample::validate_bits(8)); 391 assert!(A2dpCodecBitsPerSample::validate_bits(1 + 4)); 392 } 393 394 #[test] validate_channel_mode()395 fn validate_channel_mode() { 396 assert!(!A2dpCodecChannelMode::validate_bits(4)); 397 assert!(A2dpCodecChannelMode::validate_bits(1 + 2)); 398 } 399 } 400