use super::{
central_delegate::{CentralDelegate, CentralDelegateEvent},
ffi,
future::{BtlePlugFuture, BtlePlugFutureStateShared},
utils::{
core_bluetooth::{cbuuid_to_uuid, uuid_to_cbuuid},
nsuuid_to_uuid,
},
};
use crate::Error;
use crate::api::{CharPropFlags, Characteristic, Descriptor, ScanFilter, Service, WriteType};
use futures::channel::mpsc::{self, Receiver, Sender};
use futures::select;
use futures::sink::SinkExt;
use futures::stream::{Fuse, StreamExt};
use log::{error, trace, warn};
use objc2::{ClassType, msg_send_id};
use objc2::{rc::Retained, runtime::AnyObject};
use objc2_core_bluetooth::{
CBCentralManager, CBCentralManagerScanOptionAllowDuplicatesKey, CBCharacteristic,
CBCharacteristicProperties, CBCharacteristicWriteType, CBDescriptor, CBManager,
CBManagerAuthorization, CBManagerState, 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,
};
use tokio::runtime;
use uuid::Uuid;
struct DescriptorInternal {
pub descriptor: Retained<CBDescriptor>,
pub uuid: Uuid,
pub read_future_state: VecDeque<CoreBluetoothReplyStateShared>,
pub write_future_state: VecDeque<CoreBluetoothReplyStateShared>,
}
impl DescriptorInternal {
pub fn new(descriptor: Retained<CBDescriptor>) -> Self {
let raw_uuid = unsafe { descriptor.UUID() };
let uuid = cbuuid_to_uuid(&raw_uuid);
Self {
descriptor,
uuid,
read_future_state: VecDeque::with_capacity(10),
write_future_state: VecDeque::with_capacity(10),
}
}
}
struct CharacteristicInternal {
pub characteristic: Retained<CBCharacteristic>,
pub uuid: Uuid,
pub properties: CharPropFlags,
pub descriptors: HashMap<Uuid, DescriptorInternal>,
pub read_future_state: VecDeque<CoreBluetoothReplyStateShared>,
pub write_future_state: VecDeque<CoreBluetoothReplyStateShared>,
pub subscribe_future_state: VecDeque<CoreBluetoothReplyStateShared>,
pub unsubscribe_future_state: VecDeque<CoreBluetoothReplyStateShared>,
pub discovered: bool,
}
impl Debug for CharacteristicInternal {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("CBCharacteristic")
.field("characteristic", self.characteristic.deref())
.field("uuid", &self.uuid)
.field("properties", &self.properties)
.field("read_future_state", &self.read_future_state)
.field("write_future_state", &self.write_future_state)
.field("subscribe_future_state", &self.subscribe_future_state)
.field("unsubscribe_future_state", &self.unsubscribe_future_state)
.finish()
}
}
impl CharacteristicInternal {
pub fn new(characteristic: Retained<CBCharacteristic>) -> Self {
let properties = CharacteristicInternal::form_flags(&*characteristic);
let raw_uuid = unsafe { characteristic.UUID() };
let uuid = cbuuid_to_uuid(&raw_uuid);
let descriptors_arr = unsafe { characteristic.descriptors() };
let mut descriptors = HashMap::new();
if let Some(descriptors_arr) = descriptors_arr {
for d in descriptors_arr {
let descriptor = DescriptorInternal::new(d);
descriptors.insert(descriptor.uuid, descriptor);
}
}
Self {
characteristic,
uuid,
properties,
descriptors,
read_future_state: VecDeque::with_capacity(10),
write_future_state: VecDeque::with_capacity(10),
subscribe_future_state: VecDeque::with_capacity(10),
unsubscribe_future_state: VecDeque::with_capacity(10),
discovered: false,
}
}
fn form_flags(characteristic: &CBCharacteristic) -> CharPropFlags {
let flags = unsafe { characteristic.properties() };
let mut v = CharPropFlags::default();
if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyBroadcast) {
v |= CharPropFlags::BROADCAST;
}
if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyRead) {
v |= CharPropFlags::READ;
}
if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyWriteWithoutResponse)
{
v |= CharPropFlags::WRITE_WITHOUT_RESPONSE;
}
if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyWrite) {
v |= CharPropFlags::WRITE;
}
if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyNotify) {
v |= CharPropFlags::NOTIFY;
}
if flags.contains(CBCharacteristicProperties::CBCharacteristicPropertyIndicate) {
v |= CharPropFlags::INDICATE;
}
if flags
.contains(CBCharacteristicProperties::CBCharacteristicPropertyAuthenticatedSignedWrites)
{
v |= CharPropFlags::AUTHENTICATED_SIGNED_WRITES;
}
trace!("Flags: {:?}", v);
v
}
}
struct PendingWriteWithoutResponse {
service_uuid: Uuid,
characteristic_uuid: Uuid,
data: Vec<u8>,
fut: CoreBluetoothReplyStateShared,
}
#[derive(Clone, Debug)]
pub enum CoreBluetoothReply {
AdapterState(CBManagerState),
ReadResult(Vec<u8>),
ReadRssi(i16),
Connected,
ServicesDiscovered(BTreeSet<Service>),
State(CBPeripheralState),
Ok,
Err(String),
}
#[derive(Debug)]
pub enum PeripheralEventInternal {
Disconnected,
Notification(Uuid, Uuid, Vec<u8>),
ManufacturerData(u16, Vec<u8>, i16),
ServiceData(HashMap<Uuid, Vec<u8>>, i16),
Services(Vec<Uuid>, i16),
ServicesModified,
TxPowerLevel(i16),
RssiRead(i16),
}
pub type CoreBluetoothReplyStateShared = BtlePlugFutureStateShared<CoreBluetoothReply>;
pub type CoreBluetoothReplyFuture = BtlePlugFuture<CoreBluetoothReply>;
struct ServiceInternal {
cbservice: Retained<CBService>,
characteristics: HashMap<Uuid, CharacteristicInternal>,
pub discovered: bool,
}
struct PeripheralInternal {
pub peripheral: Retained<CBPeripheral>,
services: HashMap<Uuid, ServiceInternal>,
pub event_sender: Sender<PeripheralEventInternal>,
pub disconnected_future_state: Option<CoreBluetoothReplyStateShared>,
pub connected_future_state: Option<CoreBluetoothReplyStateShared>,
pub services_discovered_future_state: Option<CoreBluetoothReplyStateShared>,
pub read_rssi_future_state: VecDeque<CoreBluetoothReplyStateShared>,
pub write_without_response_queue: VecDeque<PendingWriteWithoutResponse>,
}
impl Debug for PeripheralInternal {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("CBPeripheral")
.field("peripheral", self.peripheral.deref())
.field(
"services",
&self
.services
.iter()
.map(|(service_uuid, service)| (service_uuid, service.characteristics.len()))
.collect::<HashMap<_, _>>(),
)
.field("event_sender", &self.event_sender)
.field("connected_future_state", &self.connected_future_state)
.field(
"services_discovered_future_state",
&self.services_discovered_future_state,
)
.finish()
}
}
impl PeripheralInternal {
pub fn new(
peripheral: Retained<CBPeripheral>,
event_sender: Sender<PeripheralEventInternal>,
) -> Self {
Self {
peripheral,
services: HashMap::new(),
event_sender,
connected_future_state: None,
disconnected_future_state: None,
services_discovered_future_state: None,
read_rssi_future_state: VecDeque::with_capacity(4),
write_without_response_queue: VecDeque::new(),
}
}
pub fn set_characteristics(
&mut self,
service_uuid: Uuid,
characteristics: HashMap<Uuid, Retained<CBCharacteristic>>,
) {
let service = self
.services
.get_mut(&service_uuid)
.expect("Got characteristics for a service we don't know about");
for (characteristic_uuid, cb_characteristic) in characteristics {
if let Some(existing) = service.characteristics.get_mut(&characteristic_uuid) {
existing.properties = CharacteristicInternal::form_flags(&*cb_characteristic);
existing.characteristic = cb_characteristic;
} else {
service.characteristics.insert(
characteristic_uuid,
CharacteristicInternal::new(cb_characteristic),
);
}
}
if service.characteristics.is_empty() {
service.discovered = true;
self.check_discovered();
}
}
pub fn set_characteristic_descriptors(
&mut self,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptors: HashMap<Uuid, Retained<CBDescriptor>>,
) {
let service = self
.services
.get_mut(&service_uuid)
.expect("Got descriptors for a service we don't know about");
let characteristic = service
.characteristics
.get_mut(&characteristic_uuid)
.expect("Got descriptors for a characteristic we don't know about");
for (descriptor_uuid, cb_descriptor) in descriptors {
if let Some(existing) = characteristic.descriptors.get_mut(&descriptor_uuid) {
existing.descriptor = cb_descriptor;
} else {
characteristic
.descriptors
.insert(descriptor_uuid, DescriptorInternal::new(cb_descriptor));
}
}
characteristic.discovered = true;
if !service
.characteristics
.values()
.any(|characteristic| !characteristic.discovered)
{
service.discovered = true;
self.check_discovered()
}
}
fn check_discovered(&mut self) {
if !self.services.values().any(|service| !service.discovered) {
if self.services_discovered_future_state.is_none() {
panic!("We should still have a future at this point!");
}
let services = self
.services
.iter()
.map(|(&service_uuid, service)| Service {
uuid: service_uuid,
primary: unsafe { service.cbservice.isPrimary() },
characteristics: service
.characteristics
.iter()
.map(|(&characteristic_uuid, characteristic)| {
let descriptors = characteristic
.descriptors
.iter()
.map(|(&descriptor_uuid, _)| Descriptor {
uuid: descriptor_uuid,
service_uuid,
characteristic_uuid,
})
.collect();
Characteristic {
uuid: characteristic_uuid,
service_uuid,
descriptors,
properties: characteristic.properties,
}
})
.collect(),
})
.collect();
self.services_discovered_future_state
.take()
.unwrap()
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::ServicesDiscovered(services));
}
}
pub fn confirm_disconnect(&mut self) {
if let Some(future) = self.disconnected_future_state.take() {
future.lock().unwrap().set_reply(CoreBluetoothReply::Ok)
}
let error = CoreBluetoothReply::Err(String::from("Device disconnected"));
for state in self.read_rssi_future_state.drain(..) {
state.lock().unwrap().set_reply(error.clone());
}
for pending in self.write_without_response_queue.drain(..) {
pending.fut.lock().unwrap().set_reply(error.clone());
}
self.services.iter().for_each(|(_, service)| {
service
.characteristics
.iter()
.for_each(|(_, characteristic)| {
let CharacteristicInternal {
read_future_state,
write_future_state,
subscribe_future_state,
unsubscribe_future_state,
..
} = characteristic;
let futures = read_future_state
.into_iter()
.chain(write_future_state.into_iter())
.chain(subscribe_future_state.into_iter())
.chain(unsubscribe_future_state.into_iter());
for state in futures {
state.lock().unwrap().set_reply(error.clone());
}
});
});
}
}
struct CoreBluetoothInternal {
manager: Retained<CBCentralManager>,
delegate: Retained<CentralDelegate>,
peripherals: HashMap<Uuid, PeripheralInternal>,
delegate_receiver: Fuse<Receiver<CentralDelegateEvent>>,
event_sender: Sender<CoreBluetoothEvent>,
message_receiver: Fuse<Receiver<CoreBluetoothMessage>>,
}
impl Debug for CoreBluetoothInternal {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("CoreBluetoothInternal")
.field("manager", self.manager.deref())
.field("delegate", self.delegate.deref())
.field("peripherals", &self.peripherals)
.field("delegate_receiver", &self.delegate_receiver)
.field("event_sender", &self.event_sender)
.field("message_receiver", &self.message_receiver)
.finish()
}
}
#[derive(Debug)]
pub enum CoreBluetoothMessage {
GetAdapterState {
future: CoreBluetoothReplyStateShared,
},
StartScanning {
filter: ScanFilter,
},
StopScanning,
ConnectDevice {
peripheral_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
DisconnectDevice {
peripheral_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
ReadValue {
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
WriteValue {
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
data: Vec<u8>,
write_type: WriteType,
future: CoreBluetoothReplyStateShared,
},
Subscribe {
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
Unsubscribe {
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
IsConnected {
peripheral_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
ReadDescriptorValue {
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptor_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
WriteDescriptorValue {
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptor_uuid: Uuid,
data: Vec<u8>,
future: CoreBluetoothReplyStateShared,
},
DiscoverServices {
peripheral_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
ReadRssi {
peripheral_uuid: Uuid,
future: CoreBluetoothReplyStateShared,
},
}
#[derive(Debug)]
pub enum CoreBluetoothEvent {
DidUpdateState {
state: CBManagerState,
},
DeviceDiscovered {
uuid: Uuid,
local_name: Option<String>,
advertisement_name: Option<String>,
event_receiver: Receiver<PeripheralEventInternal>,
},
DeviceUpdated {
uuid: Uuid,
local_name: Option<String>,
advertisement_name: Option<String>,
},
DeviceDisconnected {
uuid: Uuid,
},
}
impl CoreBluetoothInternal {
pub fn new(
message_receiver: Receiver<CoreBluetoothMessage>,
event_sender: Sender<CoreBluetoothEvent>,
) -> Self {
let (sender, receiver) = mpsc::channel::<CentralDelegateEvent>(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,
}
}
async fn dispatch_event(&self, event: CoreBluetoothEvent) {
let mut s = self.event_sender.clone();
if let Err(e) = s.send(event).await {
error!("Error dispatching event: {:?}", e);
}
}
async fn on_manufacturer_data(
&mut self,
peripheral_uuid: Uuid,
manufacturer_id: u16,
manufacturer_data: Vec<u8>,
rssi: i16,
) {
trace!(
"Got manufacturer data advertisement! {}: {:?}",
manufacturer_id, manufacturer_data
);
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
if let Err(e) = p
.event_sender
.send(PeripheralEventInternal::ManufacturerData(
manufacturer_id,
manufacturer_data,
rssi,
))
.await
{
error!("Error sending notification event: {}", e);
}
}
}
async fn on_service_data(
&mut self,
peripheral_uuid: Uuid,
service_data: HashMap<Uuid, Vec<u8>>,
rssi: i16,
) {
trace!("Got service data advertisement! {:?}", service_data);
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
if let Err(e) = p
.event_sender
.send(PeripheralEventInternal::ServiceData(service_data, rssi))
.await
{
error!("Error sending notification event: {}", e);
}
}
}
async fn on_services(&mut self, peripheral_uuid: Uuid, services: Vec<Uuid>, rssi: i16) {
trace!("Got service advertisement! {:?}", services);
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
if let Err(e) = p
.event_sender
.send(PeripheralEventInternal::Services(services, rssi))
.await
{
error!("Error sending notification event: {}", e);
}
}
}
async fn on_services_modified(&mut self, peripheral_uuid: Uuid) {
trace!(
"Peripheral modified services and must be rediscovered! {:?}",
peripheral_uuid
);
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
p.services.clear();
if let Err(e) = p
.event_sender
.send(PeripheralEventInternal::ServicesModified)
.await
{
error!("Error sending notification event: {}", e);
}
}
}
async fn on_discovered_peripheral(
&mut self,
peripheral: Retained<CBPeripheral>,
advertisement_name: Option<String>,
) {
let id = unsafe { peripheral.identifier() };
let uuid = nsuuid_to_uuid(&id);
let peripheral_name = unsafe { peripheral.name() };
let local_name = peripheral_name
.map(|n| n.to_string())
.or(advertisement_name.clone());
if self.peripherals.contains_key(&uuid) {
if local_name.is_some() || advertisement_name.is_some() {
self.dispatch_event(CoreBluetoothEvent::DeviceUpdated {
uuid,
local_name,
advertisement_name,
})
.await;
}
} else {
let (event_sender, event_receiver) = mpsc::channel(256);
self.peripherals
.insert(uuid, PeripheralInternal::new(peripheral, event_sender));
self.dispatch_event(CoreBluetoothEvent::DeviceDiscovered {
uuid,
local_name,
advertisement_name,
event_receiver,
})
.await;
}
}
fn on_discovered_services(
&mut self,
peripheral_uuid: Uuid,
service_map: HashMap<Uuid, Retained<CBService>>,
) {
trace!("Found services!");
for id in service_map.keys() {
trace!("{}", id);
}
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
let services = service_map
.into_iter()
.map(|(service_uuid, cbservice)| {
(
service_uuid,
ServiceInternal {
cbservice,
characteristics: HashMap::new(),
discovered: false,
},
)
})
.collect();
p.services = services;
}
}
fn on_discovered_characteristics(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristics: HashMap<Uuid, Retained<CBCharacteristic>>,
) {
trace!(
"Found characteristics for peripheral {} service {}:",
peripheral_uuid, service_uuid
);
for id in characteristics.keys() {
trace!("{}", id);
}
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
p.set_characteristics(service_uuid, characteristics);
}
}
fn on_discovered_characteristic_descriptors(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptors: HashMap<Uuid, Retained<CBDescriptor>>,
) {
trace!(
"Found descriptors for peripheral {} service {} characteristic {}:",
peripheral_uuid, service_uuid, characteristic_uuid,
);
for id in descriptors.keys() {
trace!("{}", id);
}
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
p.set_characteristic_descriptors(service_uuid, characteristic_uuid, descriptors);
}
}
fn on_peripheral_connect(&mut self, peripheral_uuid: Uuid) {
if self.peripherals.contains_key(&peripheral_uuid) {
let peripheral = self
.peripherals
.get_mut(&peripheral_uuid)
.expect("If we're here we should have an ID");
peripheral
.connected_future_state
.take()
.unwrap()
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::Connected);
}
}
fn on_peripheral_connection_failed(
&mut self,
peripheral_uuid: Uuid,
error_description: Option<String>,
) {
trace!("Got connection fail event!");
let error = error_description.unwrap_or(String::from("Connection failed"));
if self.peripherals.contains_key(&peripheral_uuid) {
let peripheral = self
.peripherals
.get_mut(&peripheral_uuid)
.expect("If we're here we should have an ID");
peripheral
.connected_future_state
.take()
.unwrap()
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::Err(error));
}
}
async fn on_adapter_powered_off(&mut self) {
warn!("Adapter powered off, canceling all pending operations");
let peripheral_uuids: Vec<Uuid> = self.peripherals.keys().cloned().collect();
for uuid in peripheral_uuids {
if let Err(e) = self
.peripherals
.get_mut(&uuid)
.unwrap()
.event_sender
.send(PeripheralEventInternal::Disconnected)
.await
{
error!("Error sending disconnect event for {}: {}", uuid, e);
}
self.peripherals
.get_mut(&uuid)
.unwrap()
.confirm_disconnect();
self.dispatch_event(CoreBluetoothEvent::DeviceDisconnected { uuid })
.await;
}
self.peripherals.clear();
}
async fn on_peripheral_disconnect(&mut self, peripheral_uuid: Uuid) {
trace!("Got disconnect event!");
if self.peripherals.contains_key(&peripheral_uuid) {
if let Err(e) = self
.peripherals
.get_mut(&peripheral_uuid)
.expect("If we're here we should have an ID")
.event_sender
.send(PeripheralEventInternal::Disconnected)
.await
{
error!("Error sending notification event: {}", e);
}
self.peripherals
.get_mut(&peripheral_uuid)
.expect("If we're here we should have an ID")
.confirm_disconnect();
self.peripherals.remove(&peripheral_uuid);
self.dispatch_event(CoreBluetoothEvent::DeviceDisconnected {
uuid: peripheral_uuid,
})
.await;
}
}
fn get_characteristic(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
) -> Option<&mut CharacteristicInternal> {
self.peripherals
.get_mut(&peripheral_uuid)?
.services
.get_mut(&service_uuid)?
.characteristics
.get_mut(&characteristic_uuid)
}
fn get_descriptor(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptor_uuid: Uuid,
) -> Option<&mut DescriptorInternal> {
self.get_characteristic(peripheral_uuid, service_uuid, characteristic_uuid)?
.descriptors
.get_mut(&descriptor_uuid)
}
fn on_characteristic_subscribed(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
) {
if let Some(characteristic) =
self.get_characteristic(peripheral_uuid, service_uuid, characteristic_uuid)
{
trace!("Got subscribed event!");
if let Some(state) = characteristic.subscribe_future_state.pop_back() {
state.lock().unwrap().set_reply(CoreBluetoothReply::Ok);
}
}
}
fn on_characteristic_unsubscribed(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
) {
if let Some(characteristic) =
self.get_characteristic(peripheral_uuid, service_uuid, characteristic_uuid)
{
trace!("Got unsubscribed event!");
if let Some(state) = characteristic.unsubscribe_future_state.pop_back() {
state.lock().unwrap().set_reply(CoreBluetoothReply::Ok);
}
}
}
async fn on_characteristic_read(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
data: Vec<u8>,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
if let Some(service) = peripheral.services.get_mut(&service_uuid) {
if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid)
{
trace!("Got read event!");
let mut data_clone = Vec::new();
for byte in data.iter() {
data_clone.push(*byte);
}
if !characteristic.read_future_state.is_empty() {
let state = characteristic.read_future_state.pop_back().unwrap();
state
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::ReadResult(data_clone));
} else if let Err(e) = peripheral
.event_sender
.send(PeripheralEventInternal::Notification(
characteristic_uuid,
service_uuid,
data,
))
.await
{
error!("Error sending notification event: {}", e);
}
}
}
}
}
fn on_characteristic_written(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
) {
if let Some(characteristic) =
self.get_characteristic(peripheral_uuid, service_uuid, characteristic_uuid)
{
trace!("Got written event!");
if let Some(state) = characteristic.write_future_state.pop_back() {
state.lock().unwrap().set_reply(CoreBluetoothReply::Ok);
}
}
}
fn connect_peripheral(&mut self, peripheral_uuid: Uuid, fut: CoreBluetoothReplyStateShared) {
trace!("Trying to connect peripheral!");
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
trace!("Connecting peripheral!");
p.connected_future_state = Some(fut);
unsafe { self.manager.connectPeripheral_options(&p.peripheral, None) };
}
}
fn disconnect_peripheral(&mut self, peripheral_uuid: Uuid, fut: CoreBluetoothReplyStateShared) {
trace!("Trying to disconnect peripheral!");
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
trace!("Disconnecting peripheral!");
p.disconnected_future_state = Some(fut);
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 = unsafe { p.peripheral.state() };
trace!("Connected state {:?} ", state);
fut.lock()
.unwrap()
.set_reply(CoreBluetoothReply::State(state));
} else {
fut.lock()
.unwrap()
.set_reply(CoreBluetoothReply::State(CBPeripheralState::Disconnected));
}
}
fn write_value(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
data: Vec<u8>,
kind: WriteType,
fut: CoreBluetoothReplyStateShared,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
if let Some(service) = peripheral.services.get_mut(&service_uuid) {
if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid)
{
trace!("Writing value! With kind {:?}", kind);
match kind {
WriteType::WithoutResponse => {
if unsafe { peripheral.peripheral.canSendWriteWithoutResponse() } {
unsafe {
peripheral.peripheral.writeValue_forCharacteristic_type(
&NSData::from_vec(data),
&characteristic.characteristic,
CBCharacteristicWriteType::CBCharacteristicWriteWithoutResponse,
);
}
fut.lock().unwrap().set_reply(CoreBluetoothReply::Ok);
} else {
trace!("Queueing write-without-response (peripheral not ready)");
peripheral.write_without_response_queue.push_back(
PendingWriteWithoutResponse {
service_uuid,
characteristic_uuid,
data,
fut,
},
);
}
}
WriteType::WithResponse => {
unsafe {
peripheral.peripheral.writeValue_forCharacteristic_type(
&NSData::from_vec(data),
&characteristic.characteristic,
CBCharacteristicWriteType::CBCharacteristicWriteWithResponse,
);
}
characteristic.write_future_state.push_front(fut);
}
}
}
}
}
}
fn drain_write_without_response_queue(&mut self, peripheral_uuid: Uuid) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
while let Some(pending) = peripheral.write_without_response_queue.pop_front() {
if !unsafe { peripheral.peripheral.canSendWriteWithoutResponse() } {
peripheral.write_without_response_queue.push_front(pending);
break;
}
if let Some(service) = peripheral.services.get(&pending.service_uuid) {
if let Some(characteristic) =
service.characteristics.get(&pending.characteristic_uuid)
{
unsafe {
peripheral.peripheral.writeValue_forCharacteristic_type(
&NSData::from_vec(pending.data),
&characteristic.characteristic,
CBCharacteristicWriteType::CBCharacteristicWriteWithoutResponse,
);
}
pending
.fut
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::Ok);
} else {
pending
.fut
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::Err(
"Characteristic no longer available".into(),
));
}
} else {
pending
.fut
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::Err(
"Service no longer available".into(),
));
}
}
}
}
fn read_value(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
fut: CoreBluetoothReplyStateShared,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
if let Some(service) = peripheral.services.get_mut(&service_uuid) {
if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid)
{
trace!("Reading value!");
unsafe {
peripheral
.peripheral
.readValueForCharacteristic(&characteristic.characteristic);
}
characteristic.read_future_state.push_front(fut);
}
}
}
}
fn subscribe(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
fut: CoreBluetoothReplyStateShared,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
if let Some(service) = peripheral.services.get_mut(&service_uuid) {
if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid)
{
trace!("Setting subscribe!");
unsafe {
peripheral
.peripheral
.setNotifyValue_forCharacteristic(true, &characteristic.characteristic);
}
characteristic.subscribe_future_state.push_front(fut);
}
}
}
}
fn unsubscribe(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
fut: CoreBluetoothReplyStateShared,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
if let Some(service) = peripheral.services.get_mut(&service_uuid) {
if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid)
{
trace!("Setting subscribe!");
unsafe {
peripheral.peripheral.setNotifyValue_forCharacteristic(
false,
&characteristic.characteristic,
);
}
characteristic.unsubscribe_future_state.push_front(fut);
}
}
}
}
fn write_descriptor_value(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptor_uuid: Uuid,
data: Vec<u8>,
fut: CoreBluetoothReplyStateShared,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
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) {
trace!("Writing descriptor value!");
unsafe {
peripheral.peripheral.writeValue_forDescriptor(
&NSData::from_vec(data),
&descriptor.descriptor,
);
}
descriptor.write_future_state.push_front(fut);
}
}
}
}
}
fn read_descriptor_value(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptor_uuid: Uuid,
fut: CoreBluetoothReplyStateShared,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
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) {
trace!("Reading descriptor value!");
unsafe {
peripheral
.peripheral
.readValueForDescriptor(&descriptor.descriptor);
}
descriptor.read_future_state.push_front(fut);
}
}
}
}
}
fn read_rssi(&mut self, peripheral_uuid: Uuid, fut: CoreBluetoothReplyStateShared) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
trace!("Reading RSSI!");
unsafe {
peripheral.peripheral.readRSSI();
}
peripheral.read_rssi_future_state.push_front(fut);
}
}
async fn on_read_rssi(&mut self, peripheral_uuid: Uuid, rssi: i16) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
trace!("Got RSSI read event: {}", rssi);
if let Some(state) = peripheral.read_rssi_future_state.pop_back() {
state
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::ReadRssi(rssi));
}
if let Err(e) = peripheral
.event_sender
.send(PeripheralEventInternal::RssiRead(rssi))
.await
{
error!("Error sending RSSI event: {}", e);
}
}
}
async fn on_tx_power_level(&mut self, peripheral_uuid: Uuid, tx_power_level: i16) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
if let Err(e) = peripheral
.event_sender
.send(PeripheralEventInternal::TxPowerLevel(tx_power_level))
.await
{
error!("Error sending tx_power_level event: {}", e);
}
}
}
async fn on_descriptor_read(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptor_uuid: Uuid,
data: Vec<u8>,
) {
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
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) {
trace!("Got read event!");
let mut data_clone = Vec::new();
for byte in data.iter() {
data_clone.push(*byte);
}
if let Some(state) = descriptor.read_future_state.pop_back() {
state
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::ReadResult(data_clone));
}
}
}
}
}
}
fn on_descriptor_written(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
descriptor_uuid: Uuid,
) {
if let Some(descriptor) = self.get_descriptor(
peripheral_uuid,
service_uuid,
characteristic_uuid,
descriptor_uuid,
) {
trace!("Got written event!");
if let Some(state) = descriptor.write_future_state.pop_back() {
state.lock().unwrap().set_reply(CoreBluetoothReply::Ok);
}
}
}
fn discover_services(&mut self, peripheral_uuid: Uuid, fut: CoreBluetoothReplyStateShared) {
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
trace!("Discovering services!");
p.services_discovered_future_state = Some(fut);
unsafe { p.peripheral.discoverServices(None) };
}
}
async fn wait_for_message(&mut self) {
select! {
delegate_msg = self.delegate_receiver.select_next_some() => {
match delegate_msg {
CentralDelegateEvent::DidUpdateState{state} => {
if state == CBManagerState::PoweredOff {
self.on_adapter_powered_off().await;
}
self.dispatch_event(CoreBluetoothEvent::DidUpdateState{state}).await
}
CentralDelegateEvent::DiscoveredPeripheral{cbperipheral, advertisement_name} => {
self.on_discovered_peripheral(cbperipheral, advertisement_name).await
}
CentralDelegateEvent::DiscoveredServices{peripheral_uuid, services} => {
self.on_discovered_services(peripheral_uuid, services)
}
CentralDelegateEvent::DiscoveredCharacteristics{peripheral_uuid, service_uuid, characteristics} => {
self.on_discovered_characteristics(peripheral_uuid, service_uuid, characteristics)
}
CentralDelegateEvent::DiscoveredCharacteristicDescriptors{peripheral_uuid, service_uuid, characteristic_uuid, descriptors} => {
self.on_discovered_characteristic_descriptors(peripheral_uuid, service_uuid, characteristic_uuid, descriptors)
}
CentralDelegateEvent::ConnectedDevice{peripheral_uuid} => {
self.on_peripheral_connect(peripheral_uuid)
},
CentralDelegateEvent::ConnectionFailed{peripheral_uuid, error_description} => {
self.on_peripheral_connection_failed(peripheral_uuid, error_description)
},
CentralDelegateEvent::DisconnectedDevice{peripheral_uuid} => {
self.on_peripheral_disconnect(peripheral_uuid).await
}
CentralDelegateEvent::CharacteristicSubscribed{
peripheral_uuid,
service_uuid,
characteristic_uuid,
} => self.on_characteristic_subscribed(peripheral_uuid, service_uuid, characteristic_uuid),
CentralDelegateEvent::CharacteristicUnsubscribed{
peripheral_uuid,
service_uuid,
characteristic_uuid,
} => self.on_characteristic_unsubscribed(peripheral_uuid, service_uuid,characteristic_uuid),
CentralDelegateEvent::CharacteristicNotified{
peripheral_uuid,
service_uuid,
characteristic_uuid,
data,
} => self.on_characteristic_read(peripheral_uuid, service_uuid,characteristic_uuid, data).await,
CentralDelegateEvent::CharacteristicWritten{
peripheral_uuid,
service_uuid,
characteristic_uuid,
} => self.on_characteristic_written(peripheral_uuid, service_uuid, characteristic_uuid),
CentralDelegateEvent::ManufacturerData{peripheral_uuid, manufacturer_id, data, rssi} => {
self.on_manufacturer_data(peripheral_uuid, manufacturer_id, data, rssi).await
},
CentralDelegateEvent::ServiceData{peripheral_uuid, service_data, rssi} => {
self.on_service_data(peripheral_uuid, service_data, rssi).await
},
CentralDelegateEvent::Services{peripheral_uuid, service_uuids, rssi} => {
self.on_services(peripheral_uuid, service_uuids, rssi).await
},
CentralDelegateEvent::ServicesModified{peripheral_uuid} => {
self.on_services_modified(peripheral_uuid).await
},
CentralDelegateEvent::DescriptorNotified{
peripheral_uuid,
service_uuid,
characteristic_uuid,
descriptor_uuid,
data,
} => self.on_descriptor_read(peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid, data).await,
CentralDelegateEvent::DescriptorWritten{
peripheral_uuid,
service_uuid,
characteristic_uuid,
descriptor_uuid,
} => self.on_descriptor_written(peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid),
CentralDelegateEvent::TxPowerLevel{peripheral_uuid, tx_power_level} => {
self.on_tx_power_level(peripheral_uuid, tx_power_level).await
},
CentralDelegateEvent::DidReadRssi{peripheral_uuid, rssi} => {
self.on_read_rssi(peripheral_uuid, rssi).await
},
CentralDelegateEvent::ReadyToSendWriteWithoutResponse{peripheral_uuid} => {
self.drain_write_without_response_queue(peripheral_uuid)
},
};
}
adapter_msg = self.message_receiver.select_next_some() => {
trace!("Adapter message!");
match adapter_msg {
CoreBluetoothMessage::GetAdapterState { future } => {
self.get_adapter_state(future);
},
CoreBluetoothMessage::StartScanning{filter} => self.start_discovery(filter),
CoreBluetoothMessage::StopScanning => self.stop_discovery(),
CoreBluetoothMessage::ConnectDevice{peripheral_uuid, future} => {
trace!("got connectdevice msg!");
self.connect_peripheral(peripheral_uuid, future);
}
CoreBluetoothMessage::DisconnectDevice{peripheral_uuid, future} => {
self.disconnect_peripheral(peripheral_uuid, future);
}
CoreBluetoothMessage::ReadValue{peripheral_uuid, service_uuid,characteristic_uuid, future} => {
self.read_value(peripheral_uuid, service_uuid,characteristic_uuid, future)
}
CoreBluetoothMessage::WriteValue{
peripheral_uuid,service_uuid,
characteristic_uuid,
data,
write_type,
future,
} => self.write_value(peripheral_uuid, service_uuid,characteristic_uuid, data, write_type, future),
CoreBluetoothMessage::Subscribe{peripheral_uuid, service_uuid,characteristic_uuid, future} => {
self.subscribe(peripheral_uuid, service_uuid,characteristic_uuid, future)
}
CoreBluetoothMessage::Unsubscribe{peripheral_uuid, service_uuid,characteristic_uuid, future} => {
self.unsubscribe(peripheral_uuid, service_uuid,characteristic_uuid, future)
}
CoreBluetoothMessage::IsConnected{peripheral_uuid, future} => {
self.is_connected(peripheral_uuid, future);
},
CoreBluetoothMessage::ReadDescriptorValue{peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid, future} => {
self.read_descriptor_value(peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid, future)
}
CoreBluetoothMessage::WriteDescriptorValue{
peripheral_uuid,service_uuid,
characteristic_uuid,
descriptor_uuid,
data,
future,
} => self.write_descriptor_value(peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid, data, future),
CoreBluetoothMessage::DiscoverServices{peripheral_uuid, future} => {
self.discover_services(peripheral_uuid, future);
}
CoreBluetoothMessage::ReadRssi{peripheral_uuid, future} => {
self.read_rssi(peripheral_uuid, future)
}
};
}
}
}
fn get_adapter_state(&mut self, fut: CoreBluetoothReplyStateShared) {
let state = unsafe { self.manager.state() };
fut.lock()
.unwrap()
.set_reply(CoreBluetoothReply::AdapterState(state))
}
fn start_discovery(&mut self, filter: ScanFilter) {
trace!("BluetoothAdapter::start_discovery");
let service_uuids = scan_filter_to_service_uuids(filter);
let mut options = NSMutableDictionary::new();
options.insert_id(
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");
unsafe { self.manager.stopScan() };
}
}
fn scan_filter_to_service_uuids(filter: ScanFilter) -> Option<Retained<NSArray<CBUUID>>> {
if filter.services.is_empty() {
None
} else {
let service_uuids = filter
.services
.into_iter()
.map(uuid_to_cbuuid)
.collect::<Vec<_>>();
Some(NSArray::from_vec(service_uuids))
}
}
impl Drop for CoreBluetoothInternal {
fn drop(&mut self) {
trace!("BluetoothAdapter::drop");
self.stop_discovery();
}
}
pub fn run_corebluetooth_thread(
event_sender: Sender<CoreBluetoothEvent>,
) -> Result<Sender<CoreBluetoothMessage>, Error> {
let authorization = unsafe { CBManager::authorization_class() };
if authorization != CBManagerAuthorization::AllowedAlways
&& authorization != CBManagerAuthorization::NotDetermined
{
warn!("Authorization status {:?}", authorization);
return Err(Error::PermissionDenied);
} else {
trace!("Authorization status {:?}", authorization);
}
let (sender, receiver) = mpsc::channel::<CoreBluetoothMessage>(256);
thread::spawn(move || {
let runtime = runtime::Builder::new_current_thread().build().unwrap();
runtime.block_on(async move {
let mut cbi = CoreBluetoothInternal::new(receiver, event_sender);
loop {
cbi.wait_for_message().await;
}
})
});
Ok(sender)
}