diff --git a/Cargo.toml b/Cargo.toml index 6b25d88b..721f258b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,8 +60,17 @@ objc2-foundation = { version = "0.2.2", features = [ "NSValue", ] } objc2-core-bluetooth = { version = "0.2.2", features = [ - "CBPeripheral", + "CBAdvertisementData", + "CBAttribute", "CBCentralManager", + "CBCentralManagerConstants", + "CBCharacteristic", + "CBDescriptor", + "CBManager", + "CBPeer", + "CBPeripheral", + "CBService", + "CBUUID", ] } [target.'cfg(target_os = "windows")'.dependencies] diff --git a/src/corebluetooth/central_delegate.rs b/src/corebluetooth/central_delegate.rs index daca55f0..96581a44 100644 --- a/src/corebluetooth/central_delegate.rs +++ b/src/corebluetooth/central_delegate.rs @@ -16,21 +16,17 @@ // This file may not be copied, modified, or distributed except // according to those terms. -use super::{ - framework::cb, - utils::{ - core_bluetooth::{ - cbuuid_to_uuid, characteristic_debug, descriptor_debug, peripheral_debug, service_debug, - }, - id, nsuuid_to_uuid, StrongPtr, - }, -}; +use super::utils::{core_bluetooth::cbuuid_to_uuid, nsuuid_to_uuid}; use futures::channel::mpsc::Sender; use futures::sink::SinkExt; use log::{error, trace}; -use objc2::{declare_class, msg_send_id, rc::Retained, ClassType, DeclaredClass}; -use objc2::{mutability, runtime::AnyObject}; -use objc2_core_bluetooth::{CBCentralManagerDelegate, CBPeripheralDelegate}; +use objc2::runtime::{AnyObject, ProtocolObject}; +use objc2::{declare_class, msg_send_id, mutability, rc::Retained, ClassType, DeclaredClass}; +use objc2_core_bluetooth::{ + CBAdvertisementDataManufacturerDataKey, CBAdvertisementDataServiceDataKey, + CBAdvertisementDataServiceUUIDsKey, CBCentralManager, CBCentralManagerDelegate, + CBCharacteristic, CBDescriptor, CBPeripheral, CBPeripheralDelegate, CBService, CBUUID, +}; use objc2_foundation::{ NSArray, NSData, NSDictionary, NSError, NSNumber, NSObject, NSObjectProtocol, NSString, }; @@ -45,12 +41,11 @@ use uuid::Uuid; pub enum CentralDelegateEvent { DidUpdateState, DiscoveredPeripheral { - cbperipheral: StrongPtr, + cbperipheral: Retained, }, DiscoveredServices { peripheral_uuid: Uuid, - /// Service UUID to CBService - services: HashMap, + services: HashMap>, }, ManufacturerData { peripheral_uuid: Uuid, @@ -68,18 +63,18 @@ pub enum CentralDelegateEvent { service_uuids: Vec, rssi: i16, }, - // DiscoveredIncludedServices(Uuid, HashMap), + // DiscoveredIncludedServices(Uuid, HashMap>), DiscoveredCharacteristics { peripheral_uuid: Uuid, service_uuid: Uuid, /// Characteristic UUID to CBCharacteristic - characteristics: HashMap, + characteristics: HashMap>, }, DiscoveredCharacteristicDescriptors { peripheral_uuid: Uuid, service_uuid: Uuid, characteristic_uuid: Uuid, - descriptors: HashMap, + descriptors: HashMap>, }, ConnectedDevice { peripheral_uuid: Uuid, @@ -306,77 +301,68 @@ declare_class!( unsafe impl CBCentralManagerDelegate for CentralDelegate { #[method(centralManagerDidUpdateState:)] - fn delegate_centralmanagerdidupdatestate( - &self, - _central: id, - ) { + fn delegate_centralmanagerdidupdatestate(&self, _central: &CBCentralManager) { trace!("delegate_centralmanagerdidupdatestate"); self.send_event(CentralDelegateEvent::DidUpdateState); } // #[method(centralManager:willRestoreState:)] - // fn delegate_centralmanager_willrestorestate(&self, _central: id, _dict: id) { + // fn delegate_centralmanager_willrestorestate(&self, _central: &CBCentralManager, _dict: &NSDictionary) { // trace!("delegate_centralmanager_willrestorestate"); // } #[method(centralManager:didConnectPeripheral:)] fn delegate_centralmanager_didconnectperipheral( &self, - _central: id, - peripheral: id, + _central: &CBCentralManager, + peripheral: &CBPeripheral, ) { trace!( "delegate_centralmanager_didconnectperipheral {}", peripheral_debug(peripheral) ); - cb::peripheral_setdelegate(peripheral, self); - cb::peripheral_discoverservices(peripheral); - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); - self.send_event( - CentralDelegateEvent::ConnectedDevice { peripheral_uuid }, - ); + unsafe { peripheral.setDelegate(Some(ProtocolObject::from_ref(self))) }; + unsafe { peripheral.discoverServices(None) } + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + self.send_event(CentralDelegateEvent::ConnectedDevice { peripheral_uuid }); } #[method(centralManager:didDisconnectPeripheral:error:)] fn delegate_centralmanager_diddisconnectperipheral_error( &self, - _central: id, - peripheral: id, - _error: id, + _central: &CBCentralManager, + peripheral: &CBPeripheral, + _error: Option<&NSError>, ) { trace!( "delegate_centralmanager_diddisconnectperipheral_error {}", peripheral_debug(peripheral) ); - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); - self.send_event( - CentralDelegateEvent::DisconnectedDevice { peripheral_uuid }, - ); + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + self.send_event(CentralDelegateEvent::DisconnectedDevice { peripheral_uuid }); } #[method(centralManager:didFailToConnectPeripheral:error:)] fn delegate_centralmanager_didfailtoconnectperipheral_error( &self, - _central: id, - peripheral: id, + _central: &CBCentralManager, + peripheral: &CBPeripheral, error: Option<&NSError>, ) { trace!("delegate_centralmanager_didfailtoconnectperipheral_error"); - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); let error_description = error.map(|error| error.localizedDescription().to_string()); - self.send_event( - CentralDelegateEvent::ConnectionFailed { - peripheral_uuid, - error_description, - }, - ); + self.send_event(CentralDelegateEvent::ConnectionFailed { + peripheral_uuid, + error_description, + }); } #[method(centralManager:didDiscoverPeripheral:advertisementData:RSSI:)] fn delegate_centralmanager_diddiscoverperipheral_advertisementdata_rssi( &self, - _central: id, - peripheral: id, + _central: &CBCentralManager, + peripheral: &CBPeripheral, adv_data: &NSDictionary, rssi: &NSNumber, ) { @@ -385,19 +371,15 @@ declare_class!( peripheral_debug(peripheral) ); - let held_peripheral = unsafe { StrongPtr::retain(peripheral as *mut _).unwrap() }; - self.send_event( - CentralDelegateEvent::DiscoveredPeripheral { - cbperipheral: held_peripheral, - }, - ); + self.send_event(CentralDelegateEvent::DiscoveredPeripheral { + cbperipheral: peripheral.retain(), + }); let rssi_value = rssi.as_i16(); - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); - let manufacturer_data = - adv_data.get(unsafe { cb::ADVERTISEMENT_DATA_MANUFACTURER_DATA_KEY }); + let manufacturer_data = adv_data.get(unsafe { CBAdvertisementDataManufacturerDataKey }); if let Some(manufacturer_data) = manufacturer_data { // SAFETY: manufacturer_data is `NSData` let manufacturer_data: *const AnyObject = manufacturer_data; @@ -408,60 +390,52 @@ declare_class!( let (manufacturer_id, manufacturer_data) = manufacturer_data.bytes().split_at(2); - self.send_event( - CentralDelegateEvent::ManufacturerData { - peripheral_uuid, - manufacturer_id: u16::from_le_bytes( - manufacturer_id.try_into().unwrap(), - ), - data: Vec::from(manufacturer_data), - rssi: rssi_value, - }, - ); + self.send_event(CentralDelegateEvent::ManufacturerData { + peripheral_uuid, + manufacturer_id: u16::from_le_bytes(manufacturer_id.try_into().unwrap()), + data: Vec::from(manufacturer_data), + rssi: rssi_value, + }); } } - let service_data = adv_data.get(unsafe { cb::ADVERTISEMENT_DATA_SERVICE_DATA_KEY }); + let service_data = adv_data.get(unsafe { CBAdvertisementDataServiceDataKey }); if let Some(service_data) = service_data { // SAFETY: service_data is `NSDictionary` let service_data: *const AnyObject = service_data; - let service_data: *const NSDictionary = service_data.cast(); + let service_data: *const NSDictionary = service_data.cast(); let service_data = unsafe { &*service_data }; let mut result = HashMap::new(); for uuid in service_data.keys() { let data = &service_data[uuid]; - result.insert(cbuuid_to_uuid(&**uuid), data.bytes().to_vec()); + result.insert(cbuuid_to_uuid(uuid), data.bytes().to_vec()); } - self.send_event( - CentralDelegateEvent::ServiceData { - peripheral_uuid, - service_data: result, - rssi: rssi_value, - }, - ); + self.send_event(CentralDelegateEvent::ServiceData { + peripheral_uuid, + service_data: result, + rssi: rssi_value, + }); } - let services = adv_data.get(unsafe { cb::ADVERTISEMENT_DATA_SERVICE_UUIDS_KEY }); + let services = adv_data.get(unsafe { CBAdvertisementDataServiceUUIDsKey }); if let Some(services) = services { // SAFETY: services is `NSArray` let services: *const AnyObject = services; - let services: *const NSArray = services.cast(); + let services: *const NSArray = services.cast(); let services = unsafe { &*services }; let mut service_uuids = Vec::new(); for uuid in services { - service_uuids.push(cbuuid_to_uuid(&**uuid)); + service_uuids.push(cbuuid_to_uuid(uuid)); } - self.send_event( - CentralDelegateEvent::Services { - peripheral_uuid, - service_uuids, - rssi: rssi_value, - }, - ); + self.send_event(CentralDelegateEvent::Services { + peripheral_uuid, + service_uuids, + rssi: rssi_value, + }); } } } @@ -470,7 +444,7 @@ declare_class!( #[method(peripheral:didDiscoverServices:)] fn delegate_peripheral_diddiscoverservices( &self, - peripheral: id, + peripheral: &CBPeripheral, error: Option<&NSError>, ) { trace!( @@ -479,32 +453,32 @@ declare_class!( localized_description(error) ); if error.is_none() { - let services = cb::peripheral_services(peripheral).unwrap_or_default(); + let services = unsafe { peripheral.services() }.unwrap_or_default(); let mut service_map = HashMap::new(); for s in services { // go ahead and ask for characteristics and other services - cb::peripheral_discovercharacteristicsforservice(peripheral, &s); - cb::peripheral_discoverincludedservicesforservice(peripheral, &s); + unsafe { + peripheral.discoverCharacteristics_forService(None, &s); + peripheral.discoverIncludedServices_forService(None, &s); + } // Create the map entry we'll need to export. - let uuid = cbuuid_to_uuid(cb::attribute_uuid(&*s)); + let uuid = cbuuid_to_uuid(unsafe { &s.UUID() }); service_map.insert(uuid, s); } - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); - self.send_event( - CentralDelegateEvent::DiscoveredServices { - peripheral_uuid, - services: service_map, - }, - ); + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + self.send_event(CentralDelegateEvent::DiscoveredServices { + peripheral_uuid, + services: service_map, + }); } } #[method(peripheral:didDiscoverIncludedServicesForService:error:)] fn delegate_peripheral_diddiscoverincludedservicesforservice_error( &self, - peripheral: id, - service: id, + peripheral: &CBPeripheral, + service: &CBService, error: Option<&NSError>, ) { trace!( @@ -514,9 +488,9 @@ declare_class!( localized_description(error) ); if error.is_none() { - let includes = cb::service_includedservices(service).unwrap_or_default(); + let includes = unsafe { service.includedServices() }.unwrap_or_default(); for s in includes { - cb::peripheral_discovercharacteristicsforservice(peripheral, &s); + unsafe { peripheral.discoverCharacteristics_forService(None, &s) }; } } } @@ -524,8 +498,8 @@ declare_class!( #[method(peripheral:didDiscoverCharacteristicsForService:error:)] fn delegate_peripheral_diddiscovercharacteristicsforservice_error( &self, - peripheral: id, - service: id, + peripheral: &CBPeripheral, + service: &CBService, error: Option<&NSError>, ) { trace!( @@ -536,30 +510,28 @@ declare_class!( ); if error.is_none() { let mut characteristics = HashMap::new(); - let chars = cb::service_characteristics(service).unwrap_or_default(); + let chars = unsafe { service.characteristics() }.unwrap_or_default(); for c in chars { - cb::peripheral_discoverdescriptorsforcharacteristic(peripheral, &c); + unsafe { peripheral.discoverDescriptorsForCharacteristic(&c) }; // Create the map entry we'll need to export. - let uuid = cbuuid_to_uuid(cb::attribute_uuid(&*c)); + let uuid = cbuuid_to_uuid(unsafe { &c.UUID() }); characteristics.insert(uuid, c); } - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); - let service_uuid = cbuuid_to_uuid(cb::attribute_uuid(service)); - self.send_event( - CentralDelegateEvent::DiscoveredCharacteristics { - peripheral_uuid, - service_uuid, - characteristics, - }, - ); + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + let service_uuid = cbuuid_to_uuid(unsafe { &service.UUID() }); + self.send_event(CentralDelegateEvent::DiscoveredCharacteristics { + peripheral_uuid, + service_uuid, + characteristics, + }); } } #[method(peripheral:didDiscoverDescriptorsForCharacteristic:error:)] fn delegate_peripheral_diddiscoverdescriptorsforcharacteristic_error( &self, - peripheral: id, - characteristic: id, + peripheral: &CBPeripheral, + characteristic: &CBCharacteristic, error: Option<&NSError>, ) { trace!( @@ -570,32 +542,30 @@ declare_class!( ); if error.is_none() { let mut descriptors = HashMap::new(); - let descs = cb::characteristic_descriptors(characteristic).unwrap_or_default(); + let descs = unsafe { characteristic.descriptors() }.unwrap_or_default(); for d in descs { // Create the map entry we'll need to export. - let uuid = cbuuid_to_uuid(cb::attribute_uuid(&*d)); + let uuid = cbuuid_to_uuid(unsafe { &d.UUID() }); descriptors.insert(uuid, d); } - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); - let service = cb::characteristic_service(characteristic); - let service_uuid = cbuuid_to_uuid(cb::attribute_uuid(service)); - let characteristic_uuid = cbuuid_to_uuid(cb::attribute_uuid(characteristic)); - self.send_event( - CentralDelegateEvent::DiscoveredCharacteristicDescriptors { - peripheral_uuid, - service_uuid, - characteristic_uuid, - descriptors, - }, - ); + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + let service = unsafe { characteristic.service() }.unwrap(); + let service_uuid = cbuuid_to_uuid(unsafe { &service.UUID() }); + let characteristic_uuid = cbuuid_to_uuid(unsafe { &characteristic.UUID() }); + self.send_event(CentralDelegateEvent::DiscoveredCharacteristicDescriptors { + peripheral_uuid, + service_uuid, + characteristic_uuid, + descriptors, + }); } } #[method(peripheral:didUpdateValueForCharacteristic:error:)] fn delegate_peripheral_didupdatevalueforcharacteristic_error( &self, - peripheral: id, - characteristic: id, + peripheral: &CBPeripheral, + characteristic: &CBCharacteristic, error: Option<&NSError>, ) { trace!( @@ -605,15 +575,13 @@ declare_class!( localized_description(error) ); if error.is_none() { - let service = cb::characteristic_service(characteristic); - self.send_event( - CentralDelegateEvent::CharacteristicNotified { - peripheral_uuid: nsuuid_to_uuid(&cb::peer_identifier(peripheral)), - service_uuid: cbuuid_to_uuid(cb::attribute_uuid(service)), - characteristic_uuid: cbuuid_to_uuid(cb::attribute_uuid(characteristic)), - data: get_characteristic_value(characteristic), - }, - ); + let service = unsafe { characteristic.service() }.unwrap(); + self.send_event(CentralDelegateEvent::CharacteristicNotified { + peripheral_uuid: nsuuid_to_uuid(unsafe { &peripheral.identifier() }), + service_uuid: cbuuid_to_uuid(unsafe { &service.UUID() }), + characteristic_uuid: cbuuid_to_uuid(unsafe { &characteristic.UUID() }), + data: get_characteristic_value(characteristic), + }); // Notify BluetoothGATTCharacteristic::read_value that read was successful. } } @@ -621,8 +589,8 @@ declare_class!( #[method(peripheral:didUpdateNotificationStateForCharacteristic:error:)] fn delegate_peripheral_didwritevalueforcharacteristic_error( &self, - peripheral: id, - characteristic: id, + peripheral: &CBPeripheral, + characteristic: &CBCharacteristic, error: Option<&NSError>, ) { trace!( @@ -632,54 +600,48 @@ declare_class!( localized_description(error) ); if error.is_none() { - let service = cb::characteristic_service(characteristic); - self.send_event( - CentralDelegateEvent::CharacteristicWritten { - peripheral_uuid: nsuuid_to_uuid(&cb::peer_identifier(peripheral)), - service_uuid: cbuuid_to_uuid(cb::attribute_uuid(service)), - characteristic_uuid: cbuuid_to_uuid(cb::attribute_uuid(characteristic)), - }, - ); + let service = unsafe { characteristic.service() }.unwrap(); + self.send_event(CentralDelegateEvent::CharacteristicWritten { + peripheral_uuid: nsuuid_to_uuid(unsafe { &peripheral.identifier() }), + service_uuid: cbuuid_to_uuid(unsafe { &service.UUID() }), + characteristic_uuid: cbuuid_to_uuid(unsafe { &characteristic.UUID() }), + }); } } #[method(peripheral:didWriteValueForCharacteristic:error:)] fn delegate_peripheral_didupdatenotificationstateforcharacteristic_error( &self, - peripheral: id, - characteristic: id, + peripheral: &CBPeripheral, + characteristic: &CBCharacteristic, _error: Option<&NSError>, ) { trace!("delegate_peripheral_didupdatenotificationstateforcharacteristic_error"); // TODO check for error here - let peripheral_uuid = nsuuid_to_uuid(&cb::peer_identifier(peripheral)); - let service = cb::characteristic_service(characteristic); - let service_uuid = cbuuid_to_uuid(cb::attribute_uuid(service)); - let characteristic_uuid = cbuuid_to_uuid(cb::attribute_uuid(characteristic)); - if cb::characteristic_isnotifying(characteristic) { - self.send_event( - CentralDelegateEvent::CharacteristicSubscribed { - peripheral_uuid, - service_uuid, - characteristic_uuid, - }, - ); + let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + let service = unsafe { characteristic.service() }.unwrap(); + let service_uuid = cbuuid_to_uuid(unsafe { &service.UUID() }); + let characteristic_uuid = cbuuid_to_uuid(unsafe { &characteristic.UUID() }); + if unsafe { characteristic.isNotifying() } { + self.send_event(CentralDelegateEvent::CharacteristicSubscribed { + peripheral_uuid, + service_uuid, + characteristic_uuid, + }); } else { - self.send_event( - CentralDelegateEvent::CharacteristicUnsubscribed { - peripheral_uuid, - service_uuid, - characteristic_uuid, - }, - ); + self.send_event(CentralDelegateEvent::CharacteristicUnsubscribed { + peripheral_uuid, + service_uuid, + characteristic_uuid, + }); } } #[method(peripheral:didReadRSSI:error:)] fn delegate_peripheral_didreadrssi_error( &self, - peripheral: id, - _rssi: id, + peripheral: &CBPeripheral, + _rssi: &NSNumber, error: Option<&NSError>, ) { trace!( @@ -692,8 +654,8 @@ declare_class!( #[method(peripheral:didUpdateValueForDescriptor:error:)] fn delegate_peripheral_didupdatevaluefordescriptor_error( &self, - peripheral: id, - descriptor: id, + peripheral: &CBPeripheral, + descriptor: &CBDescriptor, error: Option<&NSError>, ) { trace!( @@ -703,17 +665,15 @@ declare_class!( localized_description(error) ); if error.is_none() { - let characteristic = cb::descriptor_characteristic(descriptor); - let service = cb::characteristic_service(characteristic); - self.send_event( - CentralDelegateEvent::DescriptorNotified { - peripheral_uuid: nsuuid_to_uuid(&cb::peer_identifier(peripheral)), - service_uuid: cbuuid_to_uuid(cb::attribute_uuid(service)), - characteristic_uuid: cbuuid_to_uuid(cb::attribute_uuid(characteristic)), - descriptor_uuid: cbuuid_to_uuid(cb::attribute_uuid(descriptor)), - data: get_characteristic_value(characteristic), - }, - ); + let characteristic = unsafe { descriptor.characteristic() }.unwrap(); + let service = unsafe { characteristic.service() }.unwrap(); + self.send_event(CentralDelegateEvent::DescriptorNotified { + peripheral_uuid: nsuuid_to_uuid(unsafe { &peripheral.identifier() }), + service_uuid: cbuuid_to_uuid(unsafe { &service.UUID() }), + characteristic_uuid: cbuuid_to_uuid(unsafe { &characteristic.UUID() }), + descriptor_uuid: cbuuid_to_uuid(unsafe { &descriptor.UUID() }), + data: get_characteristic_value(&characteristic), + }); // Notify BluetoothGATTCharacteristic::read_value that read was successful. } } @@ -721,8 +681,8 @@ declare_class!( #[method(peripheral:didWriteValueForDescriptor:error:)] fn delegate_peripheral_didwritevaluefordescriptor_error( &self, - peripheral: id, - descriptor: id, + peripheral: &CBPeripheral, + descriptor: &CBDescriptor, error: Option<&NSError>, ) { trace!( @@ -732,16 +692,14 @@ declare_class!( localized_description(error) ); if error.is_none() { - let characteristic = cb::descriptor_characteristic(descriptor); - let service = cb::characteristic_service(characteristic); - self.send_event( - CentralDelegateEvent::DescriptorWritten { - peripheral_uuid: nsuuid_to_uuid(&cb::peer_identifier(peripheral)), - service_uuid: cbuuid_to_uuid(cb::attribute_uuid(service)), - characteristic_uuid: cbuuid_to_uuid(cb::attribute_uuid(characteristic)), - descriptor_uuid: cbuuid_to_uuid(cb::attribute_uuid(descriptor)), - }, - ); + let characteristic = unsafe { descriptor.characteristic() }.unwrap(); + let service = unsafe { characteristic.service() }.unwrap(); + self.send_event(CentralDelegateEvent::DescriptorWritten { + peripheral_uuid: nsuuid_to_uuid(unsafe { &peripheral.identifier() }), + service_uuid: cbuuid_to_uuid(unsafe { &service.UUID() }), + characteristic_uuid: cbuuid_to_uuid(unsafe { &characteristic.UUID() }), + descriptor_uuid: cbuuid_to_uuid(unsafe { &descriptor.UUID() }), + }); } } } @@ -771,10 +729,33 @@ fn localized_description(error: Option<&NSError>) -> String { } } -fn get_characteristic_value(characteristic: id) -> Vec { +fn get_characteristic_value(characteristic: &CBCharacteristic) -> Vec { trace!("Getting data!"); - let value = cb::characteristic_value(characteristic); - let v = value.map(|value| value.bytes().into()); + let v = unsafe { characteristic.value() }.map(|value| value.bytes().into()); trace!("BluetoothGATTCharacteristic::get_value -> {:?}", v); v.unwrap_or_default() } + +fn peripheral_debug(peripheral: &CBPeripheral) -> String { + let uuid = unsafe { peripheral.identifier() }.UUIDString(); + if let Some(name) = unsafe { peripheral.name() } { + format!("CBPeripheral({}, {})", name, uuid) + } else { + format!("CBPeripheral({})", uuid) + } +} + +fn service_debug(service: &CBService) -> String { + let uuid = unsafe { service.UUID().UUIDString() }; + format!("CBService({})", uuid) +} + +fn characteristic_debug(characteristic: &CBCharacteristic) -> String { + let uuid = unsafe { characteristic.UUID().UUIDString() }; + format!("CBCharacteristic({})", uuid) +} + +fn descriptor_debug(descriptor: &CBDescriptor) -> String { + let uuid = unsafe { descriptor.UUID().UUIDString() }; + format!("CBDescriptor({})", uuid) +} diff --git a/src/corebluetooth/ffi.rs b/src/corebluetooth/ffi.rs new file mode 100644 index 00000000..70483a06 --- /dev/null +++ b/src/corebluetooth/ffi.rs @@ -0,0 +1,19 @@ +#![allow(non_camel_case_types)] +use std::os::raw::{c_char, c_void}; + +pub type dispatch_object_s = c_void; +pub type dispatch_queue_t = *mut dispatch_object_s; +pub type dispatch_queue_attr_t = *const dispatch_object_s; + +pub const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t = 0 as dispatch_queue_attr_t; + +extern "C" { + pub fn dispatch_queue_create( + label: *const c_char, + attr: dispatch_queue_attr_t, + ) -> dispatch_queue_t; +} + +// TODO: Do we need to link to AppKit here? +#[cfg_attr(target_os = "macos", link(name = "AppKit", kind = "framework"))] +extern "C" {} diff --git a/src/corebluetooth/framework.rs b/src/corebluetooth/framework.rs deleted file mode 100644 index 1c5dfe39..00000000 --- a/src/corebluetooth/framework.rs +++ /dev/null @@ -1,318 +0,0 @@ -// btleplug Source Code File -// -// Copyright 2020 Nonpolynomial Labs LLC. All rights reserved. -// -// Licensed under the BSD 3-Clause license. See LICENSE file in the project root -// for full license information. -// -// Some portions of this file are taken and/or modified from blurmac -// (https://github.com/servo/devices), using a BSD 3-Clause license under the -// following copyright: -// -// Copyright (c) 2017 Akos Kiss. -// -// Licensed under the BSD 3-Clause License -// . -// This file may not be copied, modified, or distributed except -// according to those terms. - -use super::utils::{id, nil}; -use objc2::encode::{Encode, Encoding}; -use objc2::rc::Id; -use objc2::runtime::AnyObject; -use objc2::{class, msg_send, msg_send_id}; -use objc2_foundation::{NSArray, NSData, NSDictionary, NSString, NSUInteger, NSUUID}; -use std::ffi::CString; -use std::os::raw::c_char; - -pub mod cb { - use super::*; - - #[allow(non_camel_case_types)] - pub enum dispatch_object_s {} - #[allow(non_camel_case_types)] - pub type dispatch_queue_t = *mut dispatch_object_s; - #[allow(non_camel_case_types)] - pub type dispatch_queue_attr_t = *const dispatch_object_s; - pub const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t = 0 as dispatch_queue_attr_t; - - #[cfg_attr(target_os = "macos", link(name = "AppKit", kind = "framework"))] - #[link(name = "CoreBluetooth", kind = "framework")] - extern "C" { - pub fn dispatch_queue_create( - label: *const c_char, - attr: dispatch_queue_attr_t, - ) -> dispatch_queue_t; - } - - mod link { - use super::*; - - #[link(name = "CoreBluetooth", kind = "framework")] - extern "C" { - pub static CBAdvertisementDataManufacturerDataKey: &'static NSString; - pub static CBAdvertisementDataServiceDataKey: &'static NSString; - pub static CBAdvertisementDataServiceUUIDsKey: &'static NSString; - - pub static CBCentralManagerScanOptionAllowDuplicatesKey: &'static NSString; - } - } - - // CBCentralManager - - pub fn centralmanager(delegate: &AnyObject /*CBCentralManagerDelegate* */) -> id /*CBCentralManager* */ - { - let label = CString::new("CBqueue").unwrap(); - unsafe { - let cbcentralmanager: id = msg_send![class!(CBCentralManager), alloc]; - let queue = dispatch_queue_create(label.as_ptr(), DISPATCH_QUEUE_SERIAL); - let queue: id = queue.cast(); - - msg_send![cbcentralmanager, initWithDelegate:delegate queue:queue] - } - } - - pub fn centralmanager_scanforperipheralswithservices_options( - cbcentralmanager: id, - service_uuids: id, /* NSArray */ - options: &NSDictionary, - ) { - unsafe { - msg_send![cbcentralmanager, scanForPeripheralsWithServices:service_uuids options:options] - } - } - - pub fn centralmanager_stopscan(cbcentralmanager: id) { - unsafe { msg_send![cbcentralmanager, stopScan] } - } - - pub fn centralmanager_connectperipheral( - cbcentralmanager: id, - peripheral: id, /* CBPeripheral* */ - ) { - unsafe { msg_send![cbcentralmanager, connectPeripheral:peripheral options:nil] } - } - - pub fn centralmanager_cancelperipheralconnection( - cbcentralmanager: id, - peripheral: id, /* CBPeripheral* */ - ) { - unsafe { msg_send![cbcentralmanager, cancelPeripheralConnection: peripheral] } - } - - // CBManager - pub fn manager_authorization() -> CBManagerAuthorization { - unsafe { msg_send![class!(CBManager), authorization] } - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - #[repr(i64)] - pub enum CBManagerAuthorization { - NotDetermined = 0, - Restricted = 1, - Denied = 2, - AllowedAlways = 3, - } - - unsafe impl Encode for CBManagerAuthorization { - const ENCODING: Encoding = i64::ENCODING; - } - - // CBPeer - - pub fn peer_identifier(cbpeer: id) -> Id { - unsafe { msg_send_id![cbpeer, identifier] } - } - - // CBPeripheral : CBPeer - - pub fn peripheral_name(cbperipheral: id) -> Option> { - unsafe { msg_send_id![cbperipheral, name] } - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - #[repr(i64)] - pub enum CBPeripheralState { - Disonnected = 0, - Connecting = 1, - Connected = 2, - Disconnecting = 3, - } - - unsafe impl Encode for CBPeripheralState { - const ENCODING: Encoding = i64::ENCODING; - } - - pub fn peripheral_state(cbperipheral: id) -> CBPeripheralState { - unsafe { msg_send![cbperipheral, state] } - } - - pub fn peripheral_setdelegate( - cbperipheral: id, - delegate: &AnyObject, /* CBPeripheralDelegate* */ - ) { - unsafe { msg_send![cbperipheral, setDelegate: delegate] } - } - - pub fn peripheral_discoverservices(cbperipheral: id) { - unsafe { msg_send![cbperipheral, discoverServices: nil] } - } - - pub fn peripheral_discoverincludedservicesforservice( - cbperipheral: id, - service: &AnyObject, /* CBService* */ - ) { - unsafe { msg_send![cbperipheral, discoverIncludedServices:nil forService:service] } - } - - pub fn peripheral_services(cbperipheral: id) -> Option>> /* NSArray* */ - { - unsafe { msg_send_id![cbperipheral, services] } - } - - pub fn peripheral_discovercharacteristicsforservice( - cbperipheral: id, - service: &AnyObject, /* CBService* */ - ) { - unsafe { msg_send![cbperipheral, discoverCharacteristics:nil forService:service] } - } - - pub fn peripheral_readvalue_forcharacteristic( - cbperipheral: id, - characteristic: id, /* CBCharacteristic* */ - ) { - unsafe { msg_send![cbperipheral, readValueForCharacteristic: characteristic] } - } - - pub fn peripheral_writevalue_forcharacteristic( - cbperipheral: id, - value: &NSData, - characteristic: id, /* CBCharacteristic* */ - write_type: usize, - ) { - unsafe { - msg_send![cbperipheral, writeValue:value forCharacteristic:characteristic type:write_type] - // CBCharacteristicWriteWithResponse from CBPeripheral.h - } - } - - pub fn peripheral_setnotifyvalue_forcharacteristic( - cbperipheral: id, - value: bool, - characteristic: id, /* CBCharacteristic* */ - ) { - unsafe { msg_send![cbperipheral, setNotifyValue:value forCharacteristic:characteristic] } - } - - pub fn peripheral_discoverdescriptorsforcharacteristic( - cbperipheral: id, - characteristic: &AnyObject, /* CBCharacteristic* */ - ) { - unsafe { - msg_send![ - cbperipheral, - discoverDescriptorsForCharacteristic: characteristic - ] - } - } - - pub fn peripheral_readvalue_fordescriptor( - cbperipheral: id, - descriptor: id, /* CBDescriptor * */ - ) { - unsafe { msg_send![cbperipheral, readValueForDescriptor: descriptor] } - } - - pub fn peripheral_writevalue_fordescriptor( - cbperipheral: id, - value: &NSData, - descriptor: id, /* CBCharacteristic* */ - ) { - unsafe { msg_send![cbperipheral, writeValue:value forDescriptor:descriptor] } - } - - // CBPeripheralState = NSInteger from CBPeripheral.h - - pub const PERIPHERALSTATE_CONNECTED: isize = 2; // CBPeripheralStateConnected - - // CBAttribute - - pub fn attribute_uuid(cbattribute: id) -> id /* CBUUID* */ { - unsafe { msg_send![cbattribute, UUID] } - } - - // CBService : CBAttribute - - pub fn service_isprimary(cbservice: id) -> bool { - unsafe { msg_send![cbservice, isPrimary] } - } - - pub fn service_includedservices(cbservice: id) -> Option>> /* NSArray* */ - { - unsafe { msg_send_id![cbservice, includedServices] } - } - - pub fn service_characteristics(cbservice: id) -> Option>> /* NSArray* */ - { - unsafe { msg_send_id![cbservice, characteristics] } - } - - // CBCharacteristic : CBAttribute - - pub fn characteristic_isnotifying(cbcharacteristic: id) -> bool { - unsafe { msg_send![cbcharacteristic, isNotifying] } - } - - pub fn characteristic_value(cbcharacteristic: id) -> Option> { - unsafe { msg_send_id![cbcharacteristic, value] } - } - - pub fn characteristic_properties(cbcharacteristic: id) -> NSUInteger { - unsafe { msg_send![cbcharacteristic, properties] } - } - - pub fn characteristic_service(cbcharacteristic: id) -> id /* CBService* */ { - unsafe { msg_send![cbcharacteristic, service] } - } - - pub fn characteristic_descriptors(cbcharacteristic: id) -> Option>> /* NSArray* */ - { - unsafe { msg_send_id![cbcharacteristic, descriptors] } - } - - // CBDescriptor : CBAttribute - - pub fn descriptor_characteristic(cbdescriptor: id) -> id /* CBCharacteristic* */ { - unsafe { msg_send![cbdescriptor, characteristic] } - } - - // CBCharacteristicProperties = NSUInteger from CBCharacteristic.h - - pub const CHARACTERISTICPROPERTY_BROADCAST: usize = 0x01; // CBCharacteristicPropertyBroadcast - pub const CHARACTERISTICPROPERTY_READ: usize = 0x02; // CBCharacteristicPropertyRead - pub const CHARACTERISTICPROPERTY_WRITEWITHOUTRESPONSE: usize = 0x04; // CBCharacteristicPropertyWriteWithoutResponse - pub const CHARACTERISTICPROPERTY_WRITE: usize = 0x08; // CBCharacteristicPropertyWrite - pub const CHARACTERISTICPROPERTY_NOTIFY: usize = 0x10; // CBCharacteristicPropertyNotify - pub const CHARACTERISTICPROPERTY_INDICATE: usize = 0x20; // CBCharacteristicPropertyIndicate - pub const CHARACTERISTICPROPERTY_AUTHENTICATEDSIGNEDWRITES: usize = 0x40; // CBCharacteristicPropertyAuthenticatedSignedWrites - - // CBUUID - - pub fn uuid_uuidstring(cbuuid: id) -> Id { - unsafe { msg_send_id![cbuuid, UUIDString] } - } - - pub fn uuid_uuidwithstring(s: &NSString) -> Id /* CBUUID */ { - unsafe { msg_send_id![class!(CBUUID), UUIDWithString: s] } - } - - // CBCentralManagerScanOption...Key - - pub use self::link::CBCentralManagerScanOptionAllowDuplicatesKey as CENTRALMANAGERSCANOPTIONALLOWDUPLICATESKEY; - - // CBAdvertisementData...Key - - pub use self::link::CBAdvertisementDataManufacturerDataKey as ADVERTISEMENT_DATA_MANUFACTURER_DATA_KEY; - pub use self::link::CBAdvertisementDataServiceDataKey as ADVERTISEMENT_DATA_SERVICE_DATA_KEY; - pub use self::link::CBAdvertisementDataServiceUUIDsKey as ADVERTISEMENT_DATA_SERVICE_UUIDS_KEY; -} diff --git a/src/corebluetooth/internal.rs b/src/corebluetooth/internal.rs index 1e826b55..c1efa091 100644 --- a/src/corebluetooth/internal.rs +++ b/src/corebluetooth/internal.rs @@ -10,11 +10,11 @@ use super::{ central_delegate::{CentralDelegate, CentralDelegateEvent}, - framework::cb::{self, CBManagerAuthorization, CBPeripheralState}, + ffi, future::{BtlePlugFuture, BtlePlugFutureStateShared}, utils::{ core_bluetooth::{cbuuid_to_uuid, uuid_to_cbuuid}, - id, nil, nsuuid_to_uuid, StrongPtr, + nsuuid_to_uuid, }, }; use crate::api::{CharPropFlags, Characteristic, Descriptor, ScanFilter, Service, WriteType}; @@ -24,10 +24,17 @@ use futures::select; use futures::sink::SinkExt; use futures::stream::{Fuse, StreamExt}; use log::{error, trace, warn}; -use objc2::rc::{Id, Retained}; +use objc2::{msg_send_id, ClassType}; +use objc2::{rc::Retained, runtime::AnyObject}; +use objc2_core_bluetooth::{ + CBCentralManager, CBCentralManagerScanOptionAllowDuplicatesKey, CBCharacteristic, + CBCharacteristicProperties, CBCharacteristicWriteType, CBDescriptor, CBManager, + CBManagerAuthorization, CBPeripheral, CBPeripheralState, CBService, CBUUID, +}; use objc2_foundation::{NSArray, NSData, NSMutableDictionary, NSNumber}; use std::{ collections::{BTreeSet, HashMap, VecDeque}, + ffi::CString, fmt::{self, Debug, Formatter}, ops::Deref, thread, @@ -36,15 +43,15 @@ use tokio::runtime; use uuid::Uuid; struct DescriptorInternal { - pub descriptor: StrongPtr, + pub descriptor: Retained, pub uuid: Uuid, pub read_future_state: VecDeque, pub write_future_state: VecDeque, } impl DescriptorInternal { - pub fn new(descriptor: StrongPtr) -> Self { - let uuid = cbuuid_to_uuid(cb::attribute_uuid(&*descriptor)); + pub fn new(descriptor: Retained) -> Self { + let uuid = cbuuid_to_uuid(unsafe { &descriptor.UUID() }); Self { descriptor, uuid, @@ -55,7 +62,7 @@ impl DescriptorInternal { } struct CharacteristicInternal { - pub characteristic: StrongPtr, + pub characteristic: Retained, pub uuid: Uuid, pub properties: CharPropFlags, pub descriptors: HashMap, @@ -81,10 +88,10 @@ impl Debug for CharacteristicInternal { } impl CharacteristicInternal { - pub fn new(characteristic: StrongPtr) -> Self { + pub fn new(characteristic: Retained) -> Self { let properties = CharacteristicInternal::form_flags(&*characteristic); - let uuid = cbuuid_to_uuid(cb::attribute_uuid(&*characteristic)); - let descriptors_arr = cb::characteristic_descriptors(&*characteristic); + let uuid = cbuuid_to_uuid(unsafe { &characteristic.UUID() }); + let descriptors_arr = unsafe { characteristic.descriptors() }; let mut descriptors = HashMap::new(); if let Some(descriptors_arr) = descriptors_arr { for d in descriptors_arr { @@ -105,28 +112,31 @@ impl CharacteristicInternal { } } - fn form_flags(characteristic: id) -> CharPropFlags { - let flags = cb::characteristic_properties(characteristic); + fn form_flags(characteristic: &CBCharacteristic) -> CharPropFlags { + let flags = unsafe { characteristic.properties() }; let mut v = CharPropFlags::default(); - if (flags & cb::CHARACTERISTICPROPERTY_BROADCAST) != 0 { + if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyBroadcast) { v |= CharPropFlags::BROADCAST; } - if (flags & cb::CHARACTERISTICPROPERTY_READ) != 0 { + if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyRead) { v |= CharPropFlags::READ; } - if (flags & cb::CHARACTERISTICPROPERTY_WRITEWITHOUTRESPONSE) != 0 { + if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyWriteWithoutResponse) + { v |= CharPropFlags::WRITE_WITHOUT_RESPONSE; } - if (flags & cb::CHARACTERISTICPROPERTY_WRITE) != 0 { + if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyWrite) { v |= CharPropFlags::WRITE; } - if (flags & cb::CHARACTERISTICPROPERTY_NOTIFY) != 0 { + if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyNotify) { v |= CharPropFlags::NOTIFY; } - if (flags & cb::CHARACTERISTICPROPERTY_INDICATE) != 0 { + if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyIndicate) { v |= CharPropFlags::INDICATE; } - if (flags & cb::CHARACTERISTICPROPERTY_AUTHENTICATEDSIGNEDWRITES) != 0 { + if flags + .contains(CBCharacteristicProperties::CBCharacteristicPropertyAuthenticatedSignedWrites) + { v |= CharPropFlags::AUTHENTICATED_SIGNED_WRITES; } trace!("Flags: {:?}", v); @@ -156,13 +166,13 @@ pub type CoreBluetoothReplyStateShared = BtlePlugFutureStateShared; struct ServiceInternal { - cbservice: StrongPtr, + cbservice: Retained, characteristics: HashMap, pub discovered: bool, } struct PeripheralInternal { - pub peripheral: StrongPtr, + pub peripheral: Retained, services: HashMap, pub event_sender: Sender, pub disconnected_future_state: Option, @@ -188,7 +198,10 @@ impl Debug for PeripheralInternal { } impl PeripheralInternal { - pub fn new(peripheral: StrongPtr, event_sender: Sender) -> Self { + pub fn new( + peripheral: Retained, + event_sender: Sender, + ) -> Self { Self { peripheral, services: HashMap::new(), @@ -201,7 +214,7 @@ impl PeripheralInternal { pub fn set_characteristics( &mut self, service_uuid: Uuid, - characteristics: HashMap, + characteristics: HashMap>, ) { let characteristics = characteristics .into_iter() @@ -227,7 +240,7 @@ impl PeripheralInternal { &mut self, service_uuid: Uuid, characteristic_uuid: Uuid, - descriptors: HashMap, + descriptors: HashMap>, ) { let descriptors = descriptors .into_iter() @@ -274,7 +287,7 @@ impl PeripheralInternal { .iter() .map(|(&service_uuid, service)| Service { uuid: service_uuid, - primary: cb::service_isprimary(&*service.cbservice), + primary: unsafe { service.cbservice.isPrimary() }, characteristics: service .characteristics .iter() @@ -321,7 +334,7 @@ impl PeripheralInternal { // ass mut *Object values, keep them in a single struct, in a single thread, and // call it good. Right? struct CoreBluetoothInternal { - manager: StrongPtr, + manager: Retained, delegate: Retained, // Map of identifiers to object pointers peripherals: HashMap, @@ -429,17 +442,25 @@ impl CoreBluetoothInternal { event_sender: Sender, ) -> Self { // Pretty sure these come preallocated? - unsafe { - let (sender, receiver) = mpsc::channel::(256); - let delegate = CentralDelegate::new(sender); - Self { - manager: StrongPtr::from_raw(cb::centralmanager(&delegate) as *mut _).unwrap(), - peripherals: HashMap::new(), - delegate_receiver: receiver.fuse(), - event_sender, - message_receiver: message_receiver.fuse(), - delegate, - } + let (sender, receiver) = mpsc::channel::(256); + let delegate = CentralDelegate::new(sender); + + let label = CString::new("CBqueue").unwrap(); + let queue = + unsafe { ffi::dispatch_queue_create(label.as_ptr(), ffi::DISPATCH_QUEUE_SERIAL) }; + let queue: *mut AnyObject = queue.cast(); + + let manager = unsafe { + msg_send_id![CBCentralManager::alloc(), initWithDelegate: &*delegate, queue: queue] + }; + + Self { + manager, + peripherals: HashMap::new(), + delegate_receiver: receiver.fuse(), + event_sender, + message_receiver: message_receiver.fuse(), + delegate, } } @@ -508,9 +529,9 @@ impl CoreBluetoothInternal { } } - async fn on_discovered_peripheral(&mut self, peripheral: StrongPtr) { - let uuid = nsuuid_to_uuid(&cb::peer_identifier(&*peripheral)); - let name = cb::peripheral_name(&*peripheral); + async fn on_discovered_peripheral(&mut self, peripheral: Retained) { + let uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() }); + let name = unsafe { peripheral.name() }; if self.peripherals.contains_key(&uuid) { if let Some(name) = name { self.dispatch_event(CoreBluetoothEvent::DeviceUpdated { @@ -536,7 +557,7 @@ impl CoreBluetoothInternal { fn on_discovered_services( &mut self, peripheral_uuid: Uuid, - service_map: HashMap, + service_map: HashMap>, ) { trace!("Found services!"); for id in service_map.keys() { @@ -564,7 +585,7 @@ impl CoreBluetoothInternal { &mut self, peripheral_uuid: Uuid, service_uuid: Uuid, - characteristics: HashMap, + characteristics: HashMap>, ) { trace!( "Found characteristics for peripheral {} service {}:", @@ -584,7 +605,7 @@ impl CoreBluetoothInternal { peripheral_uuid: Uuid, service_uuid: Uuid, characteristic_uuid: Uuid, - descriptors: HashMap, + descriptors: HashMap>, ) { trace!( "Found descriptors for peripheral {} service {} characteristic {}:", @@ -774,7 +795,7 @@ impl CoreBluetoothInternal { if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) { trace!("Connecting peripheral!"); p.connected_future_state = Some(fut); - cb::centralmanager_connectperipheral(&*self.manager, &*p.peripheral); + unsafe { self.manager.connectPeripheral_options(&p.peripheral, None) }; } } @@ -783,13 +804,13 @@ impl CoreBluetoothInternal { if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) { trace!("Disconnecting peripheral!"); p.disconnected_future_state = Some(fut); - cb::centralmanager_cancelperipheralconnection(&*self.manager, &*p.peripheral); + unsafe { self.manager.cancelPeripheralConnection(&p.peripheral) }; } } fn is_connected(&mut self, peripheral_uuid: Uuid, fut: CoreBluetoothReplyStateShared) { if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) { - let state = cb::peripheral_state(&*p.peripheral); + let state = unsafe { p.peripheral.state() }; trace!("Connected state {:?} ", state); fut.lock() .unwrap() @@ -811,15 +832,20 @@ impl CoreBluetoothInternal { if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid) { trace!("Writing value! With kind {:?}", kind); - cb::peripheral_writevalue_forcharacteristic( - &*peripheral.peripheral, - &NSData::from_vec(data), - &*characteristic.characteristic, - match kind { - WriteType::WithResponse => 0, - WriteType::WithoutResponse => 1, - }, - ); + unsafe { + peripheral.peripheral.writeValue_forCharacteristic_type( + &NSData::from_vec(data), + &characteristic.characteristic, + match kind { + WriteType::WithResponse => { + CBCharacteristicWriteType::CBCharacteristicWriteWithResponse + } + WriteType::WithoutResponse => { + CBCharacteristicWriteType::CBCharacteristicWriteWithoutResponse + } + }, + ); + } // WriteWithoutResponse does not call the corebluetooth // callback, it just always succeeds silently. if kind == WriteType::WithoutResponse { @@ -844,10 +870,11 @@ impl CoreBluetoothInternal { if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid) { trace!("Reading value!"); - cb::peripheral_readvalue_forcharacteristic( - &*peripheral.peripheral, - &*characteristic.characteristic, - ); + unsafe { + peripheral + .peripheral + .readValueForCharacteristic(&characteristic.characteristic); + } characteristic.read_future_state.push_front(fut); } } @@ -866,11 +893,11 @@ impl CoreBluetoothInternal { if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid) { trace!("Setting subscribe!"); - cb::peripheral_setnotifyvalue_forcharacteristic( - &*peripheral.peripheral, - true, - &*characteristic.characteristic, - ); + unsafe { + peripheral + .peripheral + .setNotifyValue_forCharacteristic(true, &characteristic.characteristic); + } characteristic.subscribe_future_state.push_front(fut); } } @@ -889,11 +916,12 @@ impl CoreBluetoothInternal { if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid) { trace!("Setting subscribe!"); - cb::peripheral_setnotifyvalue_forcharacteristic( - &*peripheral.peripheral, - false, - &*characteristic.characteristic, - ); + unsafe { + peripheral.peripheral.setNotifyValue_forCharacteristic( + false, + &characteristic.characteristic, + ); + } characteristic.unsubscribe_future_state.push_front(fut); } } @@ -915,11 +943,12 @@ impl CoreBluetoothInternal { { if let Some(descriptor) = characteristic.descriptors.get_mut(&descriptor_uuid) { trace!("Writing descriptor value!"); - cb::peripheral_writevalue_fordescriptor( - &*peripheral.peripheral, - &NSData::from_vec(data), - &*descriptor.descriptor, - ); + unsafe { + peripheral.peripheral.writeValue_forDescriptor( + &NSData::from_vec(data), + &descriptor.descriptor, + ); + } descriptor.write_future_state.push_front(fut); } } @@ -941,10 +970,11 @@ impl CoreBluetoothInternal { { if let Some(descriptor) = characteristic.descriptors.get_mut(&descriptor_uuid) { trace!("Reading descriptor value!"); - cb::peripheral_readvalue_fordescriptor( - &*peripheral.peripheral, - &*descriptor.descriptor, - ); + unsafe { + peripheral + .peripheral + .readValueForDescriptor(&descriptor.descriptor); + } descriptor.read_future_state.push_front(fut); } } @@ -964,7 +994,8 @@ impl CoreBluetoothInternal { if let Some(service) = peripheral.services.get_mut(&service_uuid) { if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid) { - if let Some(descriptor) = characteristic.descriptors.get_mut(&descriptor_uuid) { + if let Some(_descriptor) = characteristic.descriptors.get_mut(&descriptor_uuid) + { trace!("Got read event!"); let mut data_clone = Vec::new(); @@ -1133,34 +1164,35 @@ impl CoreBluetoothInternal { // NOTE: If duplicates are not allowed then a peripheral will not show // up again once connected and then disconnected. options.insert_id( - unsafe { cb::CENTRALMANAGERSCANOPTIONALLOWDUPLICATESKEY }, - Id::into_super(Id::into_super(Id::into_super(NSNumber::new_bool(true)))), - ); - cb::centralmanager_scanforperipheralswithservices_options( - &*self.manager, - service_uuids, - &**options, + unsafe { CBCentralManagerScanOptionAllowDuplicatesKey }, + Retained::into_super(Retained::into_super(Retained::into_super( + NSNumber::new_bool(true), + ))), ); + unsafe { + self.manager + .scanForPeripheralsWithServices_options(service_uuids.as_deref(), Some(&options)) + }; } fn stop_discovery(&mut self) { trace!("BluetoothAdapter::stop_discovery"); - cb::centralmanager_stopscan(&*self.manager); + unsafe { self.manager.stopScan() }; } } /// Convert a `ScanFilter` to the appropriate `NSArray *` to use for discovery. If the /// filter has an empty list of services then this will return `nil`, to discover all devices. -fn scan_filter_to_service_uuids(filter: ScanFilter) -> id { +fn scan_filter_to_service_uuids(filter: ScanFilter) -> Option>> { if filter.services.is_empty() { - nil + None } else { let service_uuids = filter .services .into_iter() .map(uuid_to_cbuuid) .collect::>(); - Id::into_raw(NSArray::from_vec(service_uuids)) as id + Some(NSArray::from_vec(service_uuids)) } } @@ -1175,7 +1207,7 @@ impl Drop for CoreBluetoothInternal { pub fn run_corebluetooth_thread( event_sender: Sender, ) -> Result, Error> { - let authorization = cb::manager_authorization(); + let authorization = unsafe { CBManager::authorization_class() }; if authorization != CBManagerAuthorization::AllowedAlways && authorization != CBManagerAuthorization::NotDetermined { diff --git a/src/corebluetooth/mod.rs b/src/corebluetooth/mod.rs index 54cc18e6..004e39bb 100644 --- a/src/corebluetooth/mod.rs +++ b/src/corebluetooth/mod.rs @@ -7,7 +7,7 @@ pub mod adapter; mod central_delegate; -mod framework; +mod ffi; mod future; mod internal; pub mod manager; diff --git a/src/corebluetooth/peripheral.rs b/src/corebluetooth/peripheral.rs index eae79172..a57c1730 100644 --- a/src/corebluetooth/peripheral.rs +++ b/src/corebluetooth/peripheral.rs @@ -5,11 +5,8 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use super::{ - framework::cb::CBPeripheralState, - internal::{ - CoreBluetoothMessage, CoreBluetoothReply, CoreBluetoothReplyFuture, PeripheralEventInternal, - }, +use super::internal::{ + CoreBluetoothMessage, CoreBluetoothReply, CoreBluetoothReplyFuture, PeripheralEventInternal, }; use crate::{ api::{ @@ -24,6 +21,7 @@ use futures::channel::mpsc::{Receiver, SendError, Sender}; use futures::sink::SinkExt; use futures::stream::{Stream, StreamExt}; use log::*; +use objc2_core_bluetooth::CBPeripheralState; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "serde")] diff --git a/src/corebluetooth/utils/core_bluetooth.rs b/src/corebluetooth/utils/core_bluetooth.rs index 9166bff7..1b37296b 100644 --- a/src/corebluetooth/utils/core_bluetooth.rs +++ b/src/corebluetooth/utils/core_bluetooth.rs @@ -16,20 +16,17 @@ // This file may not be copied, modified, or distributed except // according to those terms. -use objc2::rc::Id; -use objc2::runtime::AnyObject; +use objc2::rc::Retained; +use objc2_core_bluetooth::CBUUID; use objc2_foundation::NSString; use uuid::Uuid; -use super::super::framework::cb; -use super::{id, nil}; - /// Convert a CBUUID object to the standard Uuid type. -pub fn cbuuid_to_uuid(cbuuid: id) -> Uuid { +pub fn cbuuid_to_uuid(cbuuid: &CBUUID) -> Uuid { // NOTE: CoreBluetooth tends to return uppercase UUID strings, and only 4 // character long if the UUID is short (16 bits). It can also return 8 // character strings if the rest of the UUID matches the generic UUID. - let uuid = cb::uuid_uuidstring(cbuuid).to_string(); + let uuid = unsafe { cbuuid.UUIDString() }.to_string(); let long = if uuid.len() == 4 { format!("0000{}-0000-1000-8000-00805f9b34fb", uuid) } else if uuid.len() == 8 { @@ -42,59 +39,21 @@ pub fn cbuuid_to_uuid(cbuuid: id) -> Uuid { } /// Convert a `Uuid` to a `CBUUID`. -pub fn uuid_to_cbuuid(uuid: Uuid) -> Id { - cb::uuid_uuidwithstring(&NSString::from_str(&uuid.to_string())) -} - -pub fn peripheral_debug(peripheral: id) -> String { - if peripheral == nil { - return String::from("nil"); - } - let uuid = cb::peer_identifier(peripheral).UUIDString(); - if let Some(name) = cb::peripheral_name(peripheral) { - format!("CBPeripheral({}, {})", name, uuid) - } else { - format!("CBPeripheral({})", uuid) - } -} - -pub fn service_debug(service: id) -> String { - if service == nil { - return String::from("nil"); - } - let uuid = cb::uuid_uuidstring(cb::attribute_uuid(service)); - format!("CBService({})", uuid) -} - -pub fn characteristic_debug(characteristic: id) -> String { - if characteristic == nil { - return String::from("nil"); - } - let uuid = cb::uuid_uuidstring(cb::attribute_uuid(characteristic)); - format!("CBCharacteristic({})", uuid) -} - -pub fn descriptor_debug(descriptor: id) -> String { - if descriptor == nil { - return String::from("nil"); - } - let uuid = cb::uuid_uuidstring(cb::attribute_uuid(descriptor)); - format!("CBDescriptor({})", uuid) +pub fn uuid_to_cbuuid(uuid: Uuid) -> Retained { + unsafe { CBUUID::UUIDWithString(&NSString::from_str(&uuid.to_string())) } } #[cfg(test)] mod tests { use objc2_foundation::ns_string; - use super::super::super::framework::cb::uuid_uuidwithstring; - use super::*; #[test] fn parse_uuid_short() { let uuid_string = "1234"; let uuid_nsstring = NSString::from_str(uuid_string); - let cbuuid = uuid_uuidwithstring(&uuid_nsstring); + let cbuuid = unsafe { CBUUID::UUIDWithString(&uuid_nsstring) }; let uuid = cbuuid_to_uuid(&*cbuuid); assert_eq!( uuid, @@ -105,7 +64,7 @@ mod tests { #[test] fn parse_uuid_long() { let uuid_nsstring = ns_string!("12345678-0000-1111-2222-333344445555"); - let cbuuid = uuid_uuidwithstring(uuid_nsstring); + let cbuuid = unsafe { CBUUID::UUIDWithString(uuid_nsstring) }; let uuid = cbuuid_to_uuid(&*cbuuid); assert_eq!( uuid, diff --git a/src/corebluetooth/utils/mod.rs b/src/corebluetooth/utils/mod.rs index ac2440d2..e4046d91 100644 --- a/src/corebluetooth/utils/mod.rs +++ b/src/corebluetooth/utils/mod.rs @@ -16,8 +16,6 @@ // This file may not be copied, modified, or distributed except // according to those terms. -use objc2::rc::Id; -use objc2::runtime::AnyObject; use objc2_foundation::NSUUID; use uuid::Uuid; @@ -26,9 +24,3 @@ pub mod core_bluetooth; pub fn nsuuid_to_uuid(uuid: &NSUUID) -> Uuid { uuid.UUIDString().to_string().parse().unwrap() } - -#[allow(non_camel_case_types)] -pub type id = *const objc2::runtime::AnyObject; -#[allow(non_upper_case_globals)] -pub const nil: id = std::ptr::null(); -pub type StrongPtr = Id;