| //! Shim for `bt_interface_t`, providing access to libbluetooth. |
| //! |
| //! This is a shim interface for calling the C++ bluetooth interface via Rust. |
| |
| use crate::bindings::root as bindings; |
| use crate::topstack::get_dispatchers; |
| use crate::utils::{LTCheckedPtr, LTCheckedPtrMut}; |
| use num_derive::{FromPrimitive, ToPrimitive}; |
| use num_traits::cast::{FromPrimitive, ToPrimitive}; |
| use std::cmp; |
| use std::convert::TryFrom; |
| use std::fmt::{Debug, Display, Formatter, Result}; |
| use std::hash::{Hash, Hasher}; |
| use std::mem; |
| use std::os::raw::c_char; |
| use std::sync::{Arc, Mutex}; |
| use std::vec::Vec; |
| use topshim_macros::cb_variant; |
| |
| use cxx::{type_id, ExternType}; |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtState { |
| Off = 0, |
| On, |
| } |
| |
| impl From<bindings::bt_state_t> for BtState { |
| fn from(item: bindings::bt_state_t) -> Self { |
| BtState::from_u32(item).unwrap_or(BtState::Off) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd, Copy)] |
| #[repr(u32)] |
| pub enum BtTransport { |
| Auto = 0, |
| Bredr, |
| Le, |
| } |
| |
| impl From<i32> for BtTransport { |
| fn from(item: i32) -> Self { |
| BtTransport::from_i32(item).unwrap_or(BtTransport::Auto) |
| } |
| } |
| |
| impl From<BtTransport> for i32 { |
| fn from(item: BtTransport) -> Self { |
| item.to_i32().unwrap_or(0) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtSspVariant { |
| PasskeyConfirmation = 0, |
| PasskeyEntry, |
| Consent, |
| PasskeyNotification, |
| } |
| |
| impl From<bindings::bt_ssp_variant_t> for BtSspVariant { |
| fn from(item: bindings::bt_ssp_variant_t) -> Self { |
| BtSspVariant::from_u32(item).unwrap_or(BtSspVariant::PasskeyConfirmation) |
| } |
| } |
| |
| impl From<BtSspVariant> for bindings::bt_ssp_variant_t { |
| fn from(item: BtSspVariant) -> Self { |
| item.to_u32().unwrap_or(0) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtBondState { |
| NotBonded = 0, |
| Bonding, |
| Bonded, |
| } |
| |
| impl From<bindings::bt_bond_state_t> for BtBondState { |
| fn from(item: bindings::bt_bond_state_t) -> Self { |
| BtBondState::from_u32(item).unwrap_or(BtBondState::NotBonded) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtConnectionState { |
| NotConnected = 0, |
| ConnectedOnly = 1, |
| EncryptedBredr = 3, |
| EncryptedLe = 5, |
| EncryptedBoth = 7, |
| } |
| |
| impl From<i32> for BtConnectionState { |
| fn from(item: i32) -> Self { |
| let fallback = if item > 0 { |
| BtConnectionState::ConnectedOnly |
| } else { |
| BtConnectionState::NotConnected |
| }; |
| |
| BtConnectionState::from_i32(item).unwrap_or(fallback) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtAclState { |
| Connected = 0, |
| Disconnected, |
| } |
| |
| impl From<bindings::bt_acl_state_t> for BtAclState { |
| fn from(item: bindings::bt_acl_state_t) -> Self { |
| BtAclState::from_u32(item).unwrap_or(BtAclState::Disconnected) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtDeviceType { |
| Unknown = 0, |
| Bredr, |
| Ble, |
| Dual, |
| } |
| |
| #[derive(Clone, Debug, Eq, Hash, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtPropertyType { |
| BdName = 0x1, |
| BdAddr, |
| Uuids, |
| ClassOfDevice, |
| TypeOfDevice, |
| ServiceRecord, |
| AdapterScanMode, |
| AdapterBondedDevices, |
| AdapterDiscoverableTimeout, |
| RemoteFriendlyName, |
| RemoteRssi, |
| RemoteVersionInfo, |
| LocalLeFeatures, |
| LocalIoCaps, |
| LocalIoCapsBle, |
| DynamicAudioBuffer, |
| RemoteIsCoordinatedSetMember, |
| Appearance, |
| VendorProductInfo, |
| |
| Unknown = 0xFE, |
| RemoteDeviceTimestamp = 0xFF, |
| } |
| |
| impl From<u32> for BtPropertyType { |
| fn from(item: u32) -> Self { |
| BtPropertyType::from_u32(item).unwrap_or(BtPropertyType::Unknown) |
| } |
| } |
| |
| impl From<BtPropertyType> for u32 { |
| fn from(item: BtPropertyType) -> Self { |
| item.to_u32().unwrap_or(0) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtDiscoveryState { |
| Stopped = 0x0, |
| Started, |
| } |
| |
| impl From<u32> for BtDiscoveryState { |
| fn from(item: u32) -> Self { |
| BtDiscoveryState::from_u32(item).unwrap_or(BtDiscoveryState::Stopped) |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtStatus { |
| Success = 0, |
| Fail, |
| NotReady, |
| NoMemory, |
| Busy, |
| Done, |
| Unsupported, |
| InvalidParam, |
| Unhandled, |
| AuthFailure, |
| RemoteDeviceDown, |
| AuthRejected, |
| JniEnvironmentError, |
| JniThreadAttachError, |
| WakeLockError, |
| |
| // Any statuses that couldn't be cleanly converted |
| Unknown = 0xff, |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtConnectionDirection { |
| Unknown = 0, |
| Outgoing, |
| Incoming, |
| } |
| |
| impl From<u32> for BtConnectionDirection { |
| fn from(item: u32) -> Self { |
| BtConnectionDirection::from_u32(item).unwrap_or(BtConnectionDirection::Unknown) |
| } |
| } |
| |
| pub fn ascii_to_string(data: &[u8], length: usize) -> String { |
| // We need to reslice data because from_utf8 tries to interpret the |
| // whole slice and not just what is before the null terminated portion |
| let ascii = data |
| .iter() |
| .enumerate() |
| .take_while(|&(pos, &c)| c != 0 && pos < length) |
| .map(|(_pos, &x)| x.clone()) |
| .collect::<Vec<u8>>(); |
| |
| return String::from_utf8(ascii).unwrap_or_default(); |
| } |
| |
| fn u32_from_bytes(item: &[u8]) -> u32 { |
| let mut u: [u8; 4] = [0; 4]; |
| let len = std::cmp::min(item.len(), 4); |
| u[0..len].copy_from_slice(&item); |
| u32::from_ne_bytes(u) |
| } |
| |
| fn u16_from_bytes(item: &[u8]) -> u16 { |
| let mut u: [u8; 2] = [0; 2]; |
| let len = std::cmp::min(item.len(), 2); |
| u[0..len].copy_from_slice(&item); |
| u16::from_ne_bytes(u) |
| } |
| |
| impl From<bindings::bt_status_t> for BtStatus { |
| fn from(item: bindings::bt_status_t) -> Self { |
| match BtStatus::from_u32(item) { |
| Some(x) => x, |
| _ => BtStatus::Unknown, |
| } |
| } |
| } |
| |
| impl Into<u32> for BtStatus { |
| fn into(self) -> u32 { |
| self.to_u32().unwrap_or_default() |
| } |
| } |
| |
| impl Into<i32> for BtStatus { |
| fn into(self) -> i32 { |
| self.to_i32().unwrap_or_default() |
| } |
| } |
| |
| impl From<bindings::bt_bdname_t> for String { |
| fn from(item: bindings::bt_bdname_t) -> Self { |
| ascii_to_string(&item.name, item.name.len()) |
| } |
| } |
| |
| #[derive(Debug, Clone)] |
| pub struct BtServiceRecord { |
| pub uuid: bindings::bluetooth::Uuid, |
| pub channel: u16, |
| pub name: String, |
| } |
| |
| impl From<bindings::bt_service_record_t> for BtServiceRecord { |
| fn from(item: bindings::bt_service_record_t) -> Self { |
| let name = item.name.iter().map(|&x| x.clone() as u8).collect::<Vec<u8>>(); |
| |
| BtServiceRecord { |
| uuid: item.uuid, |
| channel: item.channel, |
| name: ascii_to_string(name.as_slice(), name.len()), |
| } |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtScanMode { |
| None_, |
| Connectable, |
| ConnectableDiscoverable, |
| ConnectableLimitedDiscoverable, |
| } |
| |
| impl From<bindings::bt_scan_mode_t> for BtScanMode { |
| fn from(item: bindings::bt_scan_mode_t) -> Self { |
| BtScanMode::from_u32(item).unwrap_or(BtScanMode::None_) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtDiscMode { |
| // reference to system/stack/btm/neighbor_inquiry.h |
| NonDiscoverable = 0, |
| LimitedDiscoverable = 1, |
| GeneralDiscoverable = 2, |
| } |
| |
| impl From<u32> for BtDiscMode { |
| fn from(num: u32) -> Self { |
| BtDiscMode::from_u32(num).unwrap_or(BtDiscMode::NonDiscoverable) |
| } |
| } |
| |
| impl Into<u32> for BtDiscMode { |
| fn into(self) -> u32 { |
| self.to_u32().unwrap_or(0) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] |
| #[repr(u32)] |
| pub enum BtIoCap { |
| Out, |
| InOut, |
| In, |
| None_, |
| KbDisp, |
| Max, |
| Unknown = 0xff, |
| } |
| |
| impl From<bindings::bt_io_cap_t> for BtIoCap { |
| fn from(item: bindings::bt_io_cap_t) -> Self { |
| BtIoCap::from_u32(item).unwrap_or(BtIoCap::Unknown) |
| } |
| } |
| |
| pub type BtHciErrorCode = u8; |
| pub type BtLocalLeFeatures = bindings::bt_local_le_features_t; |
| pub type BtPinCode = bindings::bt_pin_code_t; |
| pub type BtRemoteVersion = bindings::bt_remote_version_t; |
| pub type BtVendorProductInfo = bindings::bt_vendor_product_info_t; |
| pub type Uuid = bindings::bluetooth::Uuid; |
| pub type Uuid128Bit = bindings::bluetooth::Uuid_UUID128Bit; |
| |
| impl TryFrom<Uuid> for Vec<u8> { |
| type Error = &'static str; |
| |
| fn try_from(value: Uuid) -> std::result::Result<Self, Self::Error> { |
| Ok((&value.uu).to_vec()) |
| } |
| } |
| |
| impl TryFrom<Vec<u8>> for Uuid { |
| type Error = &'static str; |
| |
| fn try_from(value: Vec<u8>) -> std::result::Result<Self, Self::Error> { |
| // base UUID defined in the Bluetooth specification |
| let mut uu: [u8; 16] = |
| [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34, 0xfb]; |
| match value.len() { |
| 2 => { |
| uu[2..4].copy_from_slice(&value[0..2]); |
| Ok(Uuid::from(uu)) |
| } |
| 4 => { |
| uu[0..4].copy_from_slice(&value[0..4]); |
| Ok(Uuid::from(uu)) |
| } |
| 16 => { |
| uu.copy_from_slice(&value[0..16]); |
| Ok(Uuid::from(uu)) |
| } |
| _ => { |
| Err("Vector size must be exactly 2 (16 bit UUID), 4 (32 bit UUID), or 16 (128 bit UUID).") |
| } |
| } |
| } |
| } |
| |
| impl From<[u8; 16]> for Uuid { |
| fn from(value: [u8; 16]) -> Self { |
| Self { uu: value } |
| } |
| } |
| |
| impl From<Uuid> for [u8; 16] { |
| fn from(uuid: Uuid) -> Self { |
| uuid.uu |
| } |
| } |
| |
| impl Hash for Uuid { |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| self.uu.hash(state); |
| } |
| } |
| |
| impl Uuid { |
| /// Creates a Uuid from little endian slice of bytes |
| pub fn try_from_little_endian(value: &[u8]) -> std::result::Result<Uuid, &'static str> { |
| Uuid::try_from(value.iter().rev().cloned().collect::<Vec<u8>>()) |
| } |
| |
| /// Formats this UUID to a human-readable representation. |
| pub fn format(uuid: &Uuid128Bit, f: &mut Formatter) -> Result { |
| write!(f, "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", |
| uuid[0], uuid[1], uuid[2], uuid[3], |
| uuid[4], uuid[5], |
| uuid[6], uuid[7], |
| uuid[8], uuid[9], |
| uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]) |
| } |
| } |
| |
| impl Display for Uuid { |
| fn fmt(&self, f: &mut Formatter) -> Result { |
| Uuid::format(&self.uu, f) |
| } |
| } |
| |
| /// All supported Bluetooth properties after conversion. |
| #[derive(Debug, Clone)] |
| pub enum BluetoothProperty { |
| BdName(String), |
| BdAddr(RawAddress), |
| Uuids(Vec<Uuid>), |
| ClassOfDevice(u32), |
| TypeOfDevice(BtDeviceType), |
| ServiceRecord(BtServiceRecord), |
| AdapterScanMode(BtScanMode), |
| AdapterBondedDevices(Vec<RawAddress>), |
| AdapterDiscoverableTimeout(u32), |
| RemoteFriendlyName(String), |
| RemoteRssi(i8), |
| RemoteVersionInfo(BtRemoteVersion), |
| LocalLeFeatures(BtLocalLeFeatures), |
| LocalIoCaps(BtIoCap), |
| LocalIoCapsBle(BtIoCap), |
| DynamicAudioBuffer(), |
| RemoteIsCoordinatedSetMember(bool), |
| Appearance(u16), |
| VendorProductInfo(BtVendorProductInfo), |
| RemoteDeviceTimestamp(), |
| |
| Unknown(), |
| } |
| |
| /// Wherever names are sent in bindings::bt_property_t, the size of the character |
| /// arrays are 256. Keep one extra byte for null termination. |
| const PROPERTY_NAME_MAX: usize = 255; |
| |
| impl BluetoothProperty { |
| pub fn get_type(&self) -> BtPropertyType { |
| match &*self { |
| BluetoothProperty::BdName(_) => BtPropertyType::BdName, |
| BluetoothProperty::BdAddr(_) => BtPropertyType::BdAddr, |
| BluetoothProperty::Uuids(_) => BtPropertyType::Uuids, |
| BluetoothProperty::ClassOfDevice(_) => BtPropertyType::ClassOfDevice, |
| BluetoothProperty::TypeOfDevice(_) => BtPropertyType::TypeOfDevice, |
| BluetoothProperty::ServiceRecord(_) => BtPropertyType::ServiceRecord, |
| BluetoothProperty::AdapterScanMode(_) => BtPropertyType::AdapterScanMode, |
| BluetoothProperty::AdapterBondedDevices(_) => BtPropertyType::AdapterBondedDevices, |
| BluetoothProperty::AdapterDiscoverableTimeout(_) => { |
| BtPropertyType::AdapterDiscoverableTimeout |
| } |
| BluetoothProperty::RemoteFriendlyName(_) => BtPropertyType::RemoteFriendlyName, |
| BluetoothProperty::RemoteRssi(_) => BtPropertyType::RemoteRssi, |
| BluetoothProperty::RemoteVersionInfo(_) => BtPropertyType::RemoteVersionInfo, |
| BluetoothProperty::LocalLeFeatures(_) => BtPropertyType::LocalLeFeatures, |
| BluetoothProperty::LocalIoCaps(_) => BtPropertyType::LocalIoCaps, |
| BluetoothProperty::LocalIoCapsBle(_) => BtPropertyType::LocalIoCapsBle, |
| BluetoothProperty::DynamicAudioBuffer() => BtPropertyType::DynamicAudioBuffer, |
| BluetoothProperty::RemoteIsCoordinatedSetMember(_) => { |
| BtPropertyType::RemoteIsCoordinatedSetMember |
| } |
| BluetoothProperty::Appearance(_) => BtPropertyType::Appearance, |
| BluetoothProperty::VendorProductInfo(_) => BtPropertyType::VendorProductInfo, |
| BluetoothProperty::RemoteDeviceTimestamp() => BtPropertyType::RemoteDeviceTimestamp, |
| BluetoothProperty::Unknown() => BtPropertyType::Unknown, |
| } |
| } |
| |
| fn get_len(&self) -> usize { |
| match &*self { |
| BluetoothProperty::BdName(name) => cmp::min(PROPERTY_NAME_MAX, name.len() + 1), |
| BluetoothProperty::BdAddr(addr) => addr.address.len(), |
| BluetoothProperty::Uuids(uulist) => uulist.len() * mem::size_of::<Uuid>(), |
| BluetoothProperty::ClassOfDevice(_) => mem::size_of::<u32>(), |
| BluetoothProperty::TypeOfDevice(_) => mem::size_of::<BtDeviceType>(), |
| BluetoothProperty::ServiceRecord(rec) => { |
| mem::size_of::<BtServiceRecord>() + cmp::min(PROPERTY_NAME_MAX, rec.name.len() + 1) |
| } |
| BluetoothProperty::AdapterScanMode(_) => mem::size_of::<BtScanMode>(), |
| BluetoothProperty::AdapterBondedDevices(devlist) => { |
| devlist.len() * mem::size_of::<RawAddress>() |
| } |
| BluetoothProperty::AdapterDiscoverableTimeout(_) => mem::size_of::<u32>(), |
| BluetoothProperty::RemoteFriendlyName(name) => { |
| cmp::min(PROPERTY_NAME_MAX, name.len() + 1) |
| } |
| BluetoothProperty::RemoteRssi(_) => mem::size_of::<i8>(), |
| BluetoothProperty::RemoteVersionInfo(_) => mem::size_of::<BtRemoteVersion>(), |
| BluetoothProperty::LocalLeFeatures(_) => mem::size_of::<BtLocalLeFeatures>(), |
| BluetoothProperty::LocalIoCaps(_) => mem::size_of::<BtIoCap>(), |
| BluetoothProperty::LocalIoCapsBle(_) => mem::size_of::<BtIoCap>(), |
| BluetoothProperty::RemoteIsCoordinatedSetMember(_) => mem::size_of::<bool>(), |
| BluetoothProperty::Appearance(_) => mem::size_of::<u16>(), |
| BluetoothProperty::VendorProductInfo(_) => mem::size_of::<BtVendorProductInfo>(), |
| |
| // TODO(abps) - Figure out sizes for these |
| BluetoothProperty::DynamicAudioBuffer() => 0, |
| BluetoothProperty::RemoteDeviceTimestamp() => 0, |
| BluetoothProperty::Unknown() => 0, |
| } |
| } |
| |
| /// Given a mutable array, this will copy the data to that array and return a |
| /// LTCheckedPtrMut to it. |
| /// |
| /// The lifetime of the returned pointer is tied to that of the slice given. |
| fn get_data_ptr<'a>(&'a self, data: &'a mut [u8]) -> LTCheckedPtrMut<'a, u8> { |
| let len = self.get_len(); |
| match &*self { |
| BluetoothProperty::BdName(name) => { |
| let copy_len = len - 1; |
| data[0..copy_len].copy_from_slice(&name.as_bytes()[0..copy_len]); |
| data[copy_len] = 0; |
| } |
| BluetoothProperty::BdAddr(addr) => { |
| data.copy_from_slice(&addr.address); |
| } |
| BluetoothProperty::Uuids(uulist) => { |
| for (idx, &uuid) in uulist.iter().enumerate() { |
| let start = idx * mem::size_of::<Uuid>(); |
| let end = start + mem::size_of::<Uuid>(); |
| data[start..end].copy_from_slice(&uuid.uu); |
| } |
| } |
| BluetoothProperty::ClassOfDevice(cod) => { |
| data.copy_from_slice(&cod.to_ne_bytes()); |
| } |
| BluetoothProperty::TypeOfDevice(tod) => { |
| data.copy_from_slice(&BtDeviceType::to_u32(tod).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::ServiceRecord(sr) => { |
| // Do an unsafe cast to binding:: type and assign the values |
| // The underlying memory location is provided by |data| which will |
| // have enough space because it uses get_len() |
| let mut record = |
| unsafe { &mut *(data.as_mut_ptr() as *mut bindings::bt_service_record_t) }; |
| record.uuid = sr.uuid; |
| record.channel = sr.channel; |
| let name_len = len - mem::size_of::<BtServiceRecord>() - 1; |
| record.name[0..name_len].copy_from_slice( |
| &(sr.name.as_bytes().iter().map(|x| *x as c_char).collect::<Vec<c_char>>()) |
| [0..name_len], |
| ); |
| record.name[name_len] = 0; |
| } |
| BluetoothProperty::AdapterScanMode(sm) => { |
| data.copy_from_slice(&BtScanMode::to_u32(sm).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::AdapterBondedDevices(devlist) => { |
| for (idx, &dev) in devlist.iter().enumerate() { |
| let start = idx * mem::size_of::<RawAddress>(); |
| let end = idx + mem::size_of::<RawAddress>(); |
| data[start..end].copy_from_slice(&dev.address); |
| } |
| } |
| BluetoothProperty::AdapterDiscoverableTimeout(timeout) => { |
| data.copy_from_slice(&timeout.to_ne_bytes()); |
| } |
| BluetoothProperty::RemoteFriendlyName(name) => { |
| let copy_len = len - 1; |
| data[0..copy_len].copy_from_slice(&name.as_bytes()[0..copy_len]); |
| data[copy_len] = 0; |
| } |
| BluetoothProperty::RemoteRssi(rssi) => { |
| data[0] = *rssi as u8; |
| } |
| BluetoothProperty::RemoteVersionInfo(rvi) => { |
| let ptr: *const BtRemoteVersion = rvi; |
| let slice = unsafe { |
| std::slice::from_raw_parts(ptr as *mut u8, mem::size_of::<BtRemoteVersion>()) |
| }; |
| data.copy_from_slice(&slice); |
| } |
| BluetoothProperty::LocalLeFeatures(llf) => { |
| let ptr: *const BtLocalLeFeatures = llf; |
| let slice = unsafe { |
| std::slice::from_raw_parts(ptr as *mut u8, mem::size_of::<BtLocalLeFeatures>()) |
| }; |
| data.copy_from_slice(&slice); |
| } |
| BluetoothProperty::LocalIoCaps(iocap) => { |
| data.copy_from_slice(&BtIoCap::to_u32(iocap).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::LocalIoCapsBle(iocap) => { |
| data.copy_from_slice(&BtIoCap::to_u32(iocap).unwrap_or_default().to_ne_bytes()); |
| } |
| BluetoothProperty::RemoteIsCoordinatedSetMember(icsm) => { |
| data[0] = *icsm as u8; |
| } |
| BluetoothProperty::Appearance(appearance) => { |
| data.copy_from_slice(&appearance.to_ne_bytes()); |
| } |
| BluetoothProperty::VendorProductInfo(vpi) => { |
| let ptr: *const BtVendorProductInfo = vpi; |
| let slice = unsafe { |
| std::slice::from_raw_parts( |
| ptr as *mut u8, |
| mem::size_of::<BtVendorProductInfo>(), |
| ) |
| }; |
| data.copy_from_slice(&slice); |
| } |
| |
| BluetoothProperty::DynamicAudioBuffer() => (), |
| BluetoothProperty::RemoteDeviceTimestamp() => (), |
| BluetoothProperty::Unknown() => (), |
| }; |
| |
| data.into() |
| } |
| } |
| |
| // TODO(abps) - Check that sizes are correct when given a BtProperty |
| impl From<bindings::bt_property_t> for BluetoothProperty { |
| fn from(prop: bindings::bt_property_t) -> Self { |
| let slice: &[u8] = |
| unsafe { std::slice::from_raw_parts(prop.val as *mut u8, prop.len as usize) }; |
| let len = prop.len as usize; |
| |
| match BtPropertyType::from(prop.type_) { |
| BtPropertyType::BdName => BluetoothProperty::BdName(ascii_to_string(slice, len)), |
| BtPropertyType::BdAddr => { |
| BluetoothProperty::BdAddr(RawAddress::from_bytes(slice).unwrap_or_default()) |
| } |
| BtPropertyType::Uuids => { |
| let count = len / mem::size_of::<Uuid>(); |
| BluetoothProperty::Uuids(ptr_to_vec(prop.val as *mut Uuid, count)) |
| } |
| BtPropertyType::ClassOfDevice => { |
| BluetoothProperty::ClassOfDevice(u32_from_bytes(slice)) |
| } |
| BtPropertyType::TypeOfDevice => BluetoothProperty::TypeOfDevice( |
| BtDeviceType::from_u32(u32_from_bytes(slice)).unwrap_or(BtDeviceType::Unknown), |
| ), |
| BtPropertyType::ServiceRecord => { |
| let v = unsafe { *(prop.val as *const bindings::bt_service_record_t) }; |
| BluetoothProperty::ServiceRecord(BtServiceRecord::from(v)) |
| } |
| BtPropertyType::AdapterScanMode => BluetoothProperty::AdapterScanMode( |
| BtScanMode::from_u32(u32_from_bytes(slice)).unwrap_or(BtScanMode::None_), |
| ), |
| BtPropertyType::AdapterBondedDevices => { |
| let count = len / mem::size_of::<RawAddress>(); |
| BluetoothProperty::AdapterBondedDevices(ptr_to_vec( |
| prop.val as *mut RawAddress, |
| count, |
| )) |
| } |
| BtPropertyType::AdapterDiscoverableTimeout => { |
| BluetoothProperty::AdapterDiscoverableTimeout(u32_from_bytes(slice)) |
| } |
| BtPropertyType::RemoteFriendlyName => { |
| BluetoothProperty::RemoteFriendlyName(ascii_to_string(slice, len)) |
| } |
| BtPropertyType::RemoteRssi => BluetoothProperty::RemoteRssi(slice[0] as i8), |
| BtPropertyType::RemoteVersionInfo => { |
| let v = unsafe { *(prop.val as *const BtRemoteVersion) }; |
| BluetoothProperty::RemoteVersionInfo(v.clone()) |
| } |
| BtPropertyType::LocalLeFeatures => { |
| let v = unsafe { *(prop.val as *const BtLocalLeFeatures) }; |
| BluetoothProperty::LocalLeFeatures(v.clone()) |
| } |
| BtPropertyType::LocalIoCaps => BluetoothProperty::LocalIoCaps( |
| BtIoCap::from_u32(u32_from_bytes(slice)).unwrap_or(BtIoCap::Unknown), |
| ), |
| BtPropertyType::LocalIoCapsBle => BluetoothProperty::LocalIoCapsBle( |
| BtIoCap::from_u32(u32_from_bytes(slice)).unwrap_or(BtIoCap::Unknown), |
| ), |
| BtPropertyType::RemoteIsCoordinatedSetMember => { |
| BluetoothProperty::RemoteIsCoordinatedSetMember(slice[0] != 0) |
| } |
| BtPropertyType::Appearance => BluetoothProperty::Appearance(u16_from_bytes(slice)), |
| BtPropertyType::VendorProductInfo => { |
| let v = unsafe { *(prop.val as *const BtVendorProductInfo) }; |
| BluetoothProperty::VendorProductInfo(BtVendorProductInfo::from(v)) |
| } |
| |
| // TODO(abps) - Figure out if these values should actually have contents |
| BtPropertyType::DynamicAudioBuffer => BluetoothProperty::DynamicAudioBuffer(), |
| BtPropertyType::RemoteDeviceTimestamp => BluetoothProperty::RemoteDeviceTimestamp(), |
| _ => BluetoothProperty::Unknown(), |
| } |
| } |
| } |
| |
| impl From<BluetoothProperty> for (Box<[u8]>, bindings::bt_property_t) { |
| fn from(prop: BluetoothProperty) -> Self { |
| let dvec: Vec<u8> = vec![0; prop.get_len()]; |
| let mut data: Box<[u8]> = dvec.into_boxed_slice(); |
| let prop = bindings::bt_property_t { |
| type_: prop.get_type().into(), |
| len: prop.get_len() as i32, |
| val: prop.get_data_ptr(&mut data).cast_into::<std::os::raw::c_void>(), |
| }; |
| |
| (data, prop) |
| } |
| } |
| |
| pub enum SupportedProfiles { |
| HidHost, |
| Hfp, |
| A2dp, |
| Gatt, |
| Sdp, |
| Socket, |
| HfClient, |
| AvrcpCtrl, |
| } |
| |
| impl From<SupportedProfiles> for Vec<u8> { |
| fn from(item: SupportedProfiles) -> Self { |
| match item { |
| SupportedProfiles::HidHost => "hidhost", |
| SupportedProfiles::Hfp => "handsfree", |
| SupportedProfiles::A2dp => "a2dp", |
| SupportedProfiles::Gatt => "gatt", |
| SupportedProfiles::Sdp => "sdp", |
| SupportedProfiles::Socket => "socket", |
| SupportedProfiles::HfClient => "handsfree_client", |
| SupportedProfiles::AvrcpCtrl => "avrcp_ctrl", |
| } |
| .bytes() |
| .chain("\0".bytes()) |
| .collect::<Vec<u8>>() |
| } |
| } |
| |
| #[cxx::bridge(namespace = bluetooth::topshim::rust)] |
| mod ffi { |
| unsafe extern "C++" { |
| include!("btif/btif_shim.h"); |
| |
| // For converting init flags from Vec<String> to const char ** |
| type InitFlags; |
| |
| // Convert flgas into an InitFlags object |
| fn ConvertFlags(flags: Vec<String>) -> UniquePtr<InitFlags>; |
| fn GetFlagsPtr(self: &InitFlags) -> *mut *const c_char; |
| } |
| } |
| |
| /// The RawAddress directly exported from the bindings. |
| /// |
| /// To make use of RawAddress in cxx::bridge C++ blocks, |
| /// include the following snippet in the ffi module. |
| /// ```ignore |
| /// #[cxx::bridge(namespace = bluetooth::topshim::rust)] |
| /// mod ffi { |
| /// unsafe extern "C++" { |
| /// include!("gd/rust/topshim/common/type_alias.h"); |
| /// type RawAddress = crate::btif::RawAddress; |
| /// } |
| /// // Place you shared stuff here. |
| /// } |
| /// ``` |
| pub type RawAddress = bindings::RawAddress; |
| pub type OobData = bindings::bt_oob_data_s; |
| |
| unsafe impl ExternType for RawAddress { |
| type Id = type_id!("bluetooth::topshim::rust::RawAddress"); |
| type Kind = cxx::kind::Trivial; |
| } |
| |
| impl Hash for RawAddress { |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| self.address.hash(state); |
| } |
| } |
| |
| // TODO (b/264603574): Handling address hiding in rust logging statements |
| impl ToString for RawAddress { |
| fn to_string(&self) -> String { |
| String::from(format!( |
| "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", |
| self.address[0], |
| self.address[1], |
| self.address[2], |
| self.address[3], |
| self.address[4], |
| self.address[5] |
| )) |
| } |
| } |
| |
| impl RawAddress { |
| /// Constructs a RawAddress from a slice of 6 bytes. |
| pub fn from_bytes(raw_addr: &[u8]) -> Option<RawAddress> { |
| if raw_addr.len() != 6 { |
| return None; |
| } |
| let mut raw: [u8; 6] = [0; 6]; |
| raw.copy_from_slice(raw_addr); |
| return Some(RawAddress { address: raw }); |
| } |
| |
| pub fn from_string<S: Into<String>>(addr: S) -> Option<RawAddress> { |
| let addr: String = addr.into(); |
| let s = addr.split(':').collect::<Vec<&str>>(); |
| |
| if s.len() != 6 { |
| return None; |
| } |
| |
| let mut raw: [u8; 6] = [0; 6]; |
| for i in 0..s.len() { |
| raw[i] = match u8::from_str_radix(s[i], 16) { |
| Ok(res) => res, |
| Err(_) => { |
| return None; |
| } |
| }; |
| } |
| |
| Some(RawAddress { address: raw }) |
| } |
| |
| pub fn to_byte_arr(&self) -> [u8; 6] { |
| self.address.clone() |
| } |
| |
| pub fn empty() -> RawAddress { |
| unsafe { bindings::RawAddress_kEmpty } |
| } |
| } |
| |
| /// Address that is safe to display in logs. |
| pub struct DisplayAddress<'a>(pub &'a RawAddress); |
| impl<'a> Display for DisplayAddress<'a> { |
| fn fmt(&self, f: &mut Formatter) -> Result { |
| write!(f, "xx:xx:xx:xx:{:02X}:{:02X}", &self.0.address[4], &self.0.address[5]) |
| } |
| } |
| |
| /// An enum representing `bt_callbacks_t` from btif. |
| #[derive(Clone, Debug)] |
| pub enum BaseCallbacks { |
| AdapterState(BtState), |
| AdapterProperties(BtStatus, i32, Vec<BluetoothProperty>), |
| RemoteDeviceProperties(BtStatus, RawAddress, i32, Vec<BluetoothProperty>), |
| DeviceFound(i32, Vec<BluetoothProperty>), |
| DiscoveryState(BtDiscoveryState), |
| PinRequest(RawAddress, String, u32, bool), |
| SspRequest(RawAddress, String, u32, BtSspVariant, u32), |
| BondState(BtStatus, RawAddress, BtBondState, i32), |
| AddressConsolidate(RawAddress, RawAddress), |
| LeAddressAssociate(RawAddress, RawAddress), |
| AclState( |
| BtStatus, |
| RawAddress, |
| BtAclState, |
| BtTransport, |
| BtHciErrorCode, |
| BtConnectionDirection, |
| u16, |
| ), |
| // Unimplemented so far: |
| // thread_evt_cb |
| // dut_mode_recv_cb |
| // le_test_mode_cb |
| // energy_info_cb |
| // link_quality_report_cb |
| // switch_buffer_size_cb |
| // switch_codec_cb |
| GenerateLocalOobData(u8, OobData), |
| LeRandCallback(u64), |
| } |
| |
| pub struct BaseCallbacksDispatcher { |
| pub dispatch: Box<dyn Fn(BaseCallbacks) + Send>, |
| } |
| |
| type BaseCb = Arc<Mutex<BaseCallbacksDispatcher>>; |
| |
| cb_variant!(BaseCb, adapter_state_cb -> BaseCallbacks::AdapterState, u32 -> BtState); |
| cb_variant!(BaseCb, adapter_properties_cb -> BaseCallbacks::AdapterProperties, |
| u32 -> BtStatus, i32, *mut bindings::bt_property_t, { |
| let _2 = ptr_to_vec(_2, _1 as usize); |
| }); |
| cb_variant!(BaseCb, remote_device_properties_cb -> BaseCallbacks::RemoteDeviceProperties, |
| u32 -> BtStatus, *mut RawAddress -> RawAddress, i32, *mut bindings::bt_property_t, { |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| let _3 = ptr_to_vec(_3, _2 as usize); |
| }); |
| cb_variant!(BaseCb, device_found_cb -> BaseCallbacks::DeviceFound, |
| i32, *mut bindings::bt_property_t, { |
| let _1 = ptr_to_vec(_1, _0 as usize); |
| }); |
| cb_variant!(BaseCb, discovery_state_cb -> BaseCallbacks::DiscoveryState, |
| bindings::bt_discovery_state_t -> BtDiscoveryState); |
| cb_variant!(BaseCb, pin_request_cb -> BaseCallbacks::PinRequest, |
| *mut RawAddress, *mut bindings::bt_bdname_t, u32, bool, { |
| let _0 = unsafe { *(_0 as *const RawAddress)}; |
| let _1 = String::from(unsafe{*_1}); |
| }); |
| cb_variant!(BaseCb, ssp_request_cb -> BaseCallbacks::SspRequest, |
| *mut RawAddress, *mut bindings::bt_bdname_t, u32, bindings::bt_ssp_variant_t -> BtSspVariant, u32, { |
| let _0 = unsafe { *(_0 as *const RawAddress) }; |
| let _1 = String::from(unsafe{*_1}); |
| }); |
| cb_variant!(BaseCb, bond_state_cb -> BaseCallbacks::BondState, |
| u32 -> BtStatus, *mut RawAddress, bindings::bt_bond_state_t -> BtBondState, i32, { |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| }); |
| |
| cb_variant!(BaseCb, address_consolidate_cb -> BaseCallbacks::AddressConsolidate, |
| *mut RawAddress, *mut RawAddress, { |
| let _0 = unsafe { *(_0 as *const RawAddress) }; |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| }); |
| |
| cb_variant!(BaseCb, le_address_associate_cb -> BaseCallbacks::LeAddressAssociate, |
| *mut RawAddress, *mut RawAddress, { |
| let _0 = unsafe { *(_0 as *const RawAddress) }; |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| }); |
| |
| cb_variant!(BaseCb, acl_state_cb -> BaseCallbacks::AclState, |
| u32 -> BtStatus, *mut RawAddress, bindings::bt_acl_state_t -> BtAclState, i32 -> BtTransport, bindings::bt_hci_error_code_t -> BtHciErrorCode, bindings::bt_conn_direction_t -> BtConnectionDirection, u16 -> u16, { |
| let _1 = unsafe { *(_1 as *const RawAddress) }; |
| }); |
| |
| cb_variant!(BaseCb, generate_local_oob_data_cb -> BaseCallbacks::GenerateLocalOobData, u8, OobData); |
| |
| cb_variant!(BaseCb, le_rand_cb -> BaseCallbacks::LeRandCallback, u64); |
| |
| struct RawInterfaceWrapper { |
| pub raw: *const bindings::bt_interface_t, |
| } |
| |
| unsafe impl Send for RawInterfaceWrapper {} |
| |
| /// Macro to call functions via function pointers. Expects the self object to |
| /// have a raw interface wrapper at `self.internal`. The actual function call is |
| /// marked unsafe since it will need to dereference a C object. This can cause |
| /// segfaults if not validated beforehand. |
| /// |
| /// Example: |
| /// ccall!(self, foobar, arg1, arg2) |
| /// Expands to: unsafe {((*self.internal.raw).foobar.unwrap())(arg1, arg2)} |
| #[macro_export] |
| macro_rules! ccall { |
| ($self:ident,$fn_name:ident) => { |
| unsafe { |
| ((*$self.internal.raw).$fn_name.unwrap())() |
| } |
| }; |
| ($self:ident,$fn_name:ident, $($args:expr),*) => { |
| unsafe { |
| ((*$self.internal.raw).$fn_name.unwrap())($($args),*) |
| } |
| }; |
| } |
| |
| /// Macro to call const functions via cxx. Expects the self object to have the |
| /// cxx object to be called at `self.internal_cxx`. |
| /// |
| /// Example: |
| /// cxxcall!(self, foobar, arg1, arg2) |
| /// Expands to: self.internal_cxx.foobar(arg1, arg2) |
| #[macro_export] |
| macro_rules! cxxcall { |
| ($self:expr,$fn_name:ident) => { |
| $self.internal_cxx.$fn_name() |
| }; |
| ($self:expr,$fn_name:ident, $($args:expr),*) => { |
| $self.internal_cxx.$fn_name($($args),*) |
| }; |
| } |
| |
| /// Macro to call mutable functions via cxx. Mutable functions are always |
| /// required to be defined with `self: Pin<&mut Self>`. The self object must |
| /// have the cxx object at `self.internal_cxx`. |
| /// |
| /// Example: |
| /// mutcxxcall!(self, foobar, arg1, arg2) |
| /// Expands to: self.internal_cxx.pin_mut().foobar(arg1, arg2) |
| #[macro_export] |
| macro_rules! mutcxxcall { |
| ($self:expr,$fn_name:ident) => { |
| $self.internal_cxx.pin_mut().$fn_name() |
| }; |
| ($self:expr,$fn_name:ident, $($args:expr),*) => { |
| $self.internal_cxx.pin_mut().$fn_name($($args),*) |
| }; |
| } |
| |
| #[no_mangle] |
| extern "C" fn wake_lock_noop(_0: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int { |
| // The wakelock mechanism is not available on this platform, |
| // so just returning success to avoid error log. |
| 0 |
| } |
| |
| /// Rust wrapper around `bt_interface_t`. |
| pub struct BluetoothInterface { |
| internal: RawInterfaceWrapper, |
| |
| /// Set to true after `initialize` is called. |
| pub is_init: bool, |
| |
| // Need to take ownership of callbacks so it doesn't get freed after init |
| callbacks: Option<Box<bindings::bt_callbacks_t>>, |
| os_callouts: Option<Box<bindings::bt_os_callouts_t>>, |
| } |
| |
| impl BluetoothInterface { |
| pub fn is_initialized(&self) -> bool { |
| self.is_init |
| } |
| |
| /// Initialize the Bluetooth interface by setting up the underlying interface. |
| /// |
| /// # Arguments |
| /// |
| /// * `callbacks` - Dispatcher struct that accepts [`BaseCallbacks`] |
| /// * `init_flags` - List of flags sent to libbluetooth for init. |
| pub fn initialize( |
| &mut self, |
| callbacks: BaseCallbacksDispatcher, |
| init_flags: Vec<String>, |
| ) -> bool { |
| // Init flags need to be converted from string to null terminated bytes |
| let converted: cxx::UniquePtr<ffi::InitFlags> = ffi::ConvertFlags(init_flags); |
| let flags = (*converted).GetFlagsPtr(); |
| |
| if get_dispatchers().lock().unwrap().set::<BaseCb>(Arc::new(Mutex::new(callbacks))) { |
| panic!("Tried to set dispatcher for BaseCallbacks but it already existed"); |
| } |
| |
| // Fill up callbacks struct to pass to init function (will be copied so |
| // no need to worry about ownership) |
| let mut callbacks = Box::new(bindings::bt_callbacks_t { |
| size: std::mem::size_of::<bindings::bt_callbacks_t>(), |
| adapter_state_changed_cb: Some(adapter_state_cb), |
| adapter_properties_cb: Some(adapter_properties_cb), |
| remote_device_properties_cb: Some(remote_device_properties_cb), |
| device_found_cb: Some(device_found_cb), |
| discovery_state_changed_cb: Some(discovery_state_cb), |
| pin_request_cb: Some(pin_request_cb), |
| ssp_request_cb: Some(ssp_request_cb), |
| bond_state_changed_cb: Some(bond_state_cb), |
| address_consolidate_cb: Some(address_consolidate_cb), |
| le_address_associate_cb: Some(le_address_associate_cb), |
| acl_state_changed_cb: Some(acl_state_cb), |
| thread_evt_cb: None, |
| dut_mode_recv_cb: None, |
| le_test_mode_cb: None, |
| energy_info_cb: None, |
| link_quality_report_cb: None, |
| generate_local_oob_data_cb: Some(generate_local_oob_data_cb), |
| switch_buffer_size_cb: None, |
| switch_codec_cb: None, |
| le_rand_cb: Some(le_rand_cb), |
| }); |
| |
| let cb_ptr = LTCheckedPtrMut::from(&mut callbacks); |
| |
| let (guest_mode, is_common_criteria_mode, config_compare_result, is_atv) = |
| (false, false, 0, false); |
| |
| let init = ccall!( |
| self, |
| init, |
| cb_ptr.into(), |
| guest_mode, |
| is_common_criteria_mode, |
| config_compare_result, |
| flags, |
| is_atv, |
| std::ptr::null() |
| ); |
| |
| self.is_init = init == 0; |
| self.callbacks = Some(callbacks); |
| |
| if self.is_init { |
| // Fill up OSI function table and register it with BTIF. |
| // TODO(b/271931441) - pass a NoOpOsCallouts structure from |
| // gd/rust/linux/stack. |
| let mut callouts = Box::new(bindings::bt_os_callouts_t { |
| size: std::mem::size_of::<bindings::bt_os_callouts_t>(), |
| set_wake_alarm: None, // Not used |
| acquire_wake_lock: Some(wake_lock_noop), |
| release_wake_lock: Some(wake_lock_noop), |
| }); |
| let callouts_ptr = LTCheckedPtrMut::from(&mut callouts); |
| ccall!(self, set_os_callouts, callouts_ptr.into()); |
| self.os_callouts = Some(callouts); |
| } |
| |
| return self.is_init; |
| } |
| |
| pub fn cleanup(&self) { |
| ccall!(self, cleanup) |
| } |
| |
| pub fn enable(&self) -> i32 { |
| ccall!(self, enable) |
| } |
| |
| pub fn disable(&self) -> i32 { |
| ccall!(self, disable) |
| } |
| |
| pub fn get_adapter_properties(&self) -> i32 { |
| ccall!(self, get_adapter_properties) |
| } |
| |
| pub fn get_adapter_property(&self, prop: BtPropertyType) -> i32 { |
| let converted_type = bindings::bt_property_type_t::from(prop); |
| ccall!(self, get_adapter_property, converted_type) |
| } |
| |
| pub fn set_adapter_property(&self, prop: BluetoothProperty) -> i32 { |
| let prop_pair: (Box<[u8]>, bindings::bt_property_t) = prop.into(); |
| let prop_ptr = LTCheckedPtr::from_ref(&prop_pair.1); |
| ccall!(self, set_adapter_property, prop_ptr.into()) |
| } |
| |
| pub fn get_remote_device_properties(&self, addr: &mut RawAddress) -> i32 { |
| let addr_ptr = LTCheckedPtrMut::from_ref(addr); |
| ccall!(self, get_remote_device_properties, addr_ptr.into()) |
| } |
| |
| pub fn get_remote_device_property( |
| &self, |
| addr: &mut RawAddress, |
| prop_type: BtPropertyType, |
| ) -> i32 { |
| let addr_ptr = LTCheckedPtrMut::from_ref(addr); |
| let converted_type = bindings::bt_property_type_t::from(prop_type); |
| ccall!(self, get_remote_device_property, addr_ptr.into(), converted_type) |
| } |
| |
| pub fn set_remote_device_property( |
| &self, |
| addr: &mut RawAddress, |
| prop: BluetoothProperty, |
| ) -> i32 { |
| let prop_pair: (Box<[u8]>, bindings::bt_property_t) = prop.into(); |
| let prop_ptr = LTCheckedPtr::from_ref(&prop_pair.1); |
| let addr_ptr = LTCheckedPtrMut::from_ref(addr); |
| ccall!(self, set_remote_device_property, addr_ptr.into(), prop_ptr.into()) |
| } |
| |
| pub fn get_remote_services(&self, addr: &mut RawAddress, transport: BtTransport) -> i32 { |
| let addr_ptr = LTCheckedPtrMut::from_ref(addr); |
| ccall!(self, get_remote_services, addr_ptr.into(), transport.to_i32().unwrap()) |
| } |
| |
| pub fn start_discovery(&self) -> i32 { |
| ccall!(self, start_discovery) |
| } |
| |
| pub fn cancel_discovery(&self) -> i32 { |
| ccall!(self, cancel_discovery) |
| } |
| |
| pub fn create_bond(&self, addr: &RawAddress, transport: BtTransport) -> i32 { |
| let ctransport: i32 = transport.into(); |
| let addr_ptr = LTCheckedPtr::from_ref(addr); |
| ccall!(self, create_bond, addr_ptr.into(), ctransport) |
| } |
| |
| pub fn remove_bond(&self, addr: &RawAddress) -> i32 { |
| let addr_ptr = LTCheckedPtr::from_ref(addr); |
| ccall!(self, remove_bond, addr_ptr.into()) |
| } |
| |
| pub fn cancel_bond(&self, addr: &RawAddress) -> i32 { |
| let addr_ptr = LTCheckedPtr::from_ref(addr); |
| ccall!(self, cancel_bond, addr_ptr.into()) |
| } |
| |
| pub fn get_connection_state(&self, addr: &RawAddress) -> BtConnectionState { |
| let addr_ptr = LTCheckedPtr::from_ref(addr); |
| ccall!(self, get_connection_state, addr_ptr.into()).into() |
| } |
| |
| pub fn pin_reply( |
| &self, |
| addr: &RawAddress, |
| accept: u8, |
| pin_len: u8, |
| pin_code: &mut BtPinCode, |
| ) -> i32 { |
| let addr_ptr = LTCheckedPtr::from_ref(addr); |
| let pin_code_ptr = LTCheckedPtrMut::from_ref(pin_code); |
| ccall!(self, pin_reply, addr_ptr.into(), accept, pin_len, pin_code_ptr.into()) |
| } |
| |
| pub fn ssp_reply( |
| &self, |
| addr: &RawAddress, |
| variant: BtSspVariant, |
| accept: u8, |
| passkey: u32, |
| ) -> i32 { |
| let addr_ptr = LTCheckedPtr::from_ref(addr); |
| let cvariant = bindings::bt_ssp_variant_t::from(variant); |
| ccall!(self, ssp_reply, addr_ptr.into(), cvariant, accept, passkey) |
| } |
| |
| pub fn clear_event_filter(&self) -> i32 { |
| ccall!(self, clear_event_filter) |
| } |
| |
| pub fn clear_event_mask(&self) -> i32 { |
| ccall!(self, clear_event_mask) |
| } |
| |
| pub fn clear_filter_accept_list(&self) -> i32 { |
| ccall!(self, clear_filter_accept_list) |
| } |
| |
| pub fn disconnect_all_acls(&self) -> i32 { |
| ccall!(self, disconnect_all_acls) |
| } |
| |
| pub fn allow_wake_by_hid(&self) -> i32 { |
| ccall!(self, allow_wake_by_hid) |
| } |
| |
| pub fn get_wbs_supported(&self) -> bool { |
| ccall!(self, get_wbs_supported) |
| } |
| |
| pub fn get_swb_supported(&self) -> bool { |
| ccall!(self, get_swb_supported) |
| } |
| |
| pub fn le_rand(&self) -> i32 { |
| ccall!(self, le_rand) |
| } |
| |
| pub fn generate_local_oob_data(&self, transport: i32) -> i32 { |
| ccall!(self, generate_local_oob_data, transport as u8) |
| } |
| |
| pub fn restore_filter_accept_list(&self) -> i32 { |
| ccall!(self, restore_filter_accept_list) |
| } |
| |
| pub fn set_default_event_mask_except(&self, mask: u64, le_mask: u64) -> i32 { |
| ccall!(self, set_default_event_mask_except, mask, le_mask) |
| } |
| |
| pub fn set_event_filter_inquiry_result_all_devices(&self) -> i32 { |
| ccall!(self, set_event_filter_inquiry_result_all_devices) |
| } |
| |
| pub fn set_event_filter_connection_setup_all_devices(&self) -> i32 { |
| ccall!(self, set_event_filter_connection_setup_all_devices) |
| } |
| |
| pub(crate) fn get_profile_interface( |
| &self, |
| profile: SupportedProfiles, |
| ) -> *const std::os::raw::c_void { |
| let cprofile = Vec::<u8>::from(profile); |
| let cprofile_ptr = LTCheckedPtr::from(&cprofile); |
| ccall!(self, get_profile_interface, cprofile_ptr.cast_into::<std::os::raw::c_char>()) |
| } |
| |
| pub(crate) fn as_raw_ptr(&self) -> *const u8 { |
| self.internal.raw as *const u8 |
| } |
| } |
| |
| pub trait ToggleableProfile { |
| fn is_enabled(&self) -> bool; |
| fn enable(&mut self) -> bool; |
| fn disable(&mut self) -> bool; |
| } |
| |
| pub fn get_btinterface() -> Option<BluetoothInterface> { |
| let mut ret: Option<BluetoothInterface> = None; |
| let mut ifptr: *const bindings::bt_interface_t = std::ptr::null(); |
| |
| unsafe { |
| if bindings::hal_util_load_bt_library(&mut ifptr) == 0 { |
| ret = Some(BluetoothInterface { |
| internal: RawInterfaceWrapper { raw: ifptr }, |
| is_init: false, |
| callbacks: None, |
| os_callouts: None, |
| }); |
| } |
| } |
| |
| ret |
| } |
| |
| // Turns C-array T[] to Vec<U>. |
| pub(crate) fn ptr_to_vec<T: Copy, U: From<T>>(start: *const T, length: usize) -> Vec<U> { |
| unsafe { (0..length).map(|i| U::from(*start.offset(i as isize))).collect::<Vec<U>>() } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| fn make_bdname_from_slice(slice: &[u8]) -> bindings::bt_bdname_t { |
| // Length of slice must be less than bd_name max |
| assert!(slice.len() <= 249); |
| |
| let mut bdname = bindings::bt_bdname_t { name: [128; 249] }; |
| |
| for (i, v) in slice.iter().enumerate() { |
| bdname.name[i] = v.clone(); |
| } |
| |
| bdname |
| } |
| |
| #[test] |
| fn test_bdname_conversions() { |
| let hello_bdname = make_bdname_from_slice(&[72, 69, 76, 76, 79, 0]); |
| assert_eq!("HELLO".to_string(), String::from(hello_bdname)); |
| |
| let empty_bdname = make_bdname_from_slice(&[0]); |
| assert_eq!("".to_string(), String::from(empty_bdname)); |
| |
| let no_nullterm_bdname = make_bdname_from_slice(&[72, 69, 76, 76, 79]); |
| assert_eq!("".to_string(), String::from(no_nullterm_bdname)); |
| |
| let invalid_bdname = make_bdname_from_slice(&[128; 249]); |
| assert_eq!("".to_string(), String::from(invalid_bdname)); |
| } |
| |
| #[test] |
| fn test_ptr_to_vec() { |
| let arr: [i32; 3] = [1, 2, 3]; |
| let vec: Vec<i32> = ptr_to_vec(arr.as_ptr(), arr.len()); |
| let expected: Vec<i32> = vec![1, 2, 3]; |
| assert_eq!(expected, vec); |
| } |
| |
| #[test] |
| fn test_property_with_string_conversions() { |
| { |
| let bdname = BluetoothProperty::BdName("FooBar".into()); |
| let prop_pair: (Box<[u8]>, bindings::bt_property_t) = bdname.into(); |
| let converted: BluetoothProperty = prop_pair.1.into(); |
| assert!(match converted { |
| BluetoothProperty::BdName(name) => "FooBar".to_string() == name, |
| _ => false, |
| }); |
| } |
| |
| { |
| let orig_record = BtServiceRecord { |
| uuid: Uuid::from([0; 16]), |
| channel: 3, |
| name: "FooBar".to_string(), |
| }; |
| let service_record = BluetoothProperty::ServiceRecord(orig_record.clone()); |
| let prop_pair: (Box<[u8]>, bindings::bt_property_t) = service_record.into(); |
| let converted: BluetoothProperty = prop_pair.1.into(); |
| assert!(match converted { |
| BluetoothProperty::ServiceRecord(sr) => { |
| sr.uuid == orig_record.uuid |
| && sr.channel == orig_record.channel |
| && sr.name == orig_record.name |
| } |
| _ => false, |
| }); |
| } |
| |
| { |
| let rfname = BluetoothProperty::RemoteFriendlyName("FooBizz".into()); |
| let prop_pair: (Box<[u8]>, bindings::bt_property_t) = rfname.into(); |
| let converted: BluetoothProperty = prop_pair.1.into(); |
| assert!(match converted { |
| BluetoothProperty::RemoteFriendlyName(name) => "FooBizz".to_string() == name, |
| _ => false, |
| }); |
| } |
| } |
| |
| #[test] |
| fn test_display_address() { |
| assert_eq!( |
| format!("{}", DisplayAddress(&RawAddress::from_string("00:00:00:00:00:00").unwrap())), |
| String::from("xx:xx:xx:xx:00:00") |
| ); |
| assert_eq!( |
| format!("{}", DisplayAddress(&RawAddress::from_string("1a:2b:1a:2b:1a:2b").unwrap())), |
| String::from("xx:xx:xx:xx:1A:2B") |
| ); |
| assert_eq!( |
| format!("{}", DisplayAddress(&RawAddress::from_string("3C:4D:3C:4D:3C:4D").unwrap())), |
| String::from("xx:xx:xx:xx:3C:4D") |
| ); |
| assert_eq!( |
| format!("{}", DisplayAddress(&RawAddress::from_string("11:35:11:35:11:35").unwrap())), |
| String::from("xx:xx:xx:xx:11:35") |
| ); |
| } |
| } |