use super::{
central_delegate::{CentralDelegate, CentralDelegateEvent},
framework::cb::{self, CBManagerAuthorization, CBManagerState, CBPeripheralState},
future::{BtlePlugFuture, BtlePlugFutureStateShared},
utils::{
core_bluetooth::{cbuuid_to_uuid, uuid_to_cbuuid},
id, nil, nsuuid_to_uuid, StrongPtr,
},
};
use crate::api::{CharPropFlags, Characteristic, Descriptor, ScanFilter, Service, WriteType};
use crate::Error;
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::rc::Id;
use objc2_foundation::{NSArray, NSData, NSMutableDictionary, NSNumber};
use std::{
collections::{BTreeSet, HashMap, VecDeque},
fmt::{self, Debug, Formatter},
ops::Deref,
thread,
};
use tokio::runtime;
use uuid::Uuid;
struct CBDescriptor {
pub descriptor: StrongPtr,
pub uuid: Uuid,
pub read_future_state: VecDeque<CoreBluetoothReplyStateShared>,
pub write_future_state: VecDeque<CoreBluetoothReplyStateShared>,
}
impl CBDescriptor {
pub fn new(descriptor: StrongPtr) -> Self {
let uuid = cbuuid_to_uuid(cb::attribute_uuid(&*descriptor));
Self {
descriptor,
uuid,
read_future_state: VecDeque::with_capacity(10),
write_future_state: VecDeque::with_capacity(10),
}
}
}
struct CBCharacteristic {
pub characteristic: StrongPtr,
pub uuid: Uuid,
pub properties: CharPropFlags,
pub descriptors: HashMap<Uuid, CBDescriptor>,
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 CBCharacteristic {
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 CBCharacteristic {
pub fn new(characteristic: StrongPtr) -> Self {
let properties = CBCharacteristic::form_flags(&*characteristic);
let uuid = cbuuid_to_uuid(cb::attribute_uuid(&*characteristic));
let descriptors_arr = cb::characteristic_descriptors(&*characteristic);
let mut descriptors = HashMap::new();
if let Some(descriptors_arr) = descriptors_arr {
for d in descriptors_arr {
let descriptor = CBDescriptor::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: id) -> CharPropFlags {
let flags = cb::characteristic_properties(characteristic);
let mut v = CharPropFlags::default();
if (flags & cb::CHARACTERISTICPROPERTY_BROADCAST) != 0 {
v |= CharPropFlags::BROADCAST;
}
if (flags & cb::CHARACTERISTICPROPERTY_READ) != 0 {
v |= CharPropFlags::READ;
}
if (flags & cb::CHARACTERISTICPROPERTY_WRITEWITHOUTRESPONSE) != 0 {
v |= CharPropFlags::WRITE_WITHOUT_RESPONSE;
}
if (flags & cb::CHARACTERISTICPROPERTY_WRITE) != 0 {
v |= CharPropFlags::WRITE;
}
if (flags & cb::CHARACTERISTICPROPERTY_NOTIFY) != 0 {
v |= CharPropFlags::NOTIFY;
}
if (flags & cb::CHARACTERISTICPROPERTY_INDICATE) != 0 {
v |= CharPropFlags::INDICATE;
}
if (flags & cb::CHARACTERISTICPROPERTY_AUTHENTICATEDSIGNEDWRITES) != 0 {
v |= CharPropFlags::AUTHENTICATED_SIGNED_WRITES;
}
trace!("Flags: {:?}", v);
v
}
}
#[derive(Clone, Debug)]
pub enum CoreBluetoothReply {
ReadResult(Vec<u8>),
Connected(BTreeSet<Service>),
State(CBPeripheralState),
ManagerState(CBManagerState),
Ok,
Err(String),
}
#[derive(Debug)]
pub enum CBPeripheralEvent {
Disconnected,
Notification(Uuid, Vec<u8>),
ManufacturerData(u16, Vec<u8>, i16),
ServiceData(HashMap<Uuid, Vec<u8>>, i16),
Services(Vec<Uuid>, i16),
}
pub type CoreBluetoothReplyStateShared = BtlePlugFutureStateShared<CoreBluetoothReply>;
pub type CoreBluetoothReplyFuture = BtlePlugFuture<CoreBluetoothReply>;
struct ServiceInternal {
cbservice: StrongPtr,
characteristics: HashMap<Uuid, CBCharacteristic>,
pub discovered: bool,
}
struct CBPeripheral {
pub peripheral: StrongPtr,
services: HashMap<Uuid, ServiceInternal>,
pub event_sender: Sender<CBPeripheralEvent>,
pub disconnected_future_state: Option<CoreBluetoothReplyStateShared>,
pub connected_future_state: Option<CoreBluetoothReplyStateShared>,
}
impl Debug for CBPeripheral {
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)
.finish()
}
}
impl CBPeripheral {
pub fn new(peripheral: StrongPtr, event_sender: Sender<CBPeripheralEvent>) -> Self {
Self {
peripheral,
services: HashMap::new(),
event_sender,
connected_future_state: None,
disconnected_future_state: None,
}
}
pub fn set_characteristics(
&mut self,
service_uuid: Uuid,
characteristics: HashMap<Uuid, StrongPtr>,
) {
let characteristics = characteristics
.into_iter()
.map(|(characteristic_uuid, characteristic)| {
(characteristic_uuid, CBCharacteristic::new(characteristic))
})
.collect();
let service = self
.services
.get_mut(&service_uuid)
.expect("Got characteristics for a service we don't know about");
service.characteristics = characteristics;
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, StrongPtr>,
) {
let descriptors = descriptors
.into_iter()
.map(|(descriptor_uuid, descriptor)| (descriptor_uuid, CBDescriptor::new(descriptor)))
.collect();
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");
characteristic.descriptors = descriptors;
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.connected_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: cb::service_isprimary(&*service.cbservice),
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.connected_future_state
.take()
.unwrap()
.lock()
.unwrap()
.set_reply(CoreBluetoothReply::Connected(services));
}
}
pub fn confirm_disconnect(&mut self) {
if let Some(future) = self.disconnected_future_state.take() {
future.lock().unwrap().set_reply(CoreBluetoothReply::Ok)
}
}
}
struct CoreBluetoothInternal {
manager: StrongPtr,
delegate: StrongPtr,
peripherals: HashMap<Uuid, CBPeripheral>,
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 {
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,
},
FetchManagerState {
future: CoreBluetoothReplyStateShared,
},
}
#[derive(Debug)]
pub enum CoreBluetoothEvent {
DidUpdateState,
DeviceDiscovered {
uuid: Uuid,
name: Option<String>,
event_receiver: Receiver<CBPeripheralEvent>,
},
DeviceUpdated {
uuid: Uuid,
name: String,
},
DeviceDisconnected {
uuid: Uuid,
},
}
impl CoreBluetoothInternal {
pub fn new(
message_receiver: Receiver<CoreBluetoothMessage>,
event_sender: Sender<CoreBluetoothEvent>,
) -> Self {
unsafe {
let (delegate, delegate_receiver) = CentralDelegate::delegate();
let delegate = StrongPtr::from_raw(delegate as *mut _).unwrap();
Self {
manager: StrongPtr::from_raw(cb::centralmanager(&*delegate) as *mut _).unwrap(),
peripherals: HashMap::new(),
delegate_receiver: delegate_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(CBPeripheralEvent::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(CBPeripheralEvent::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(CBPeripheralEvent::Services(services, rssi))
.await
{
error!("Error sending notification event: {}", e);
}
}
}
async fn on_discovered_peripheral(&mut self, peripheral: StrongPtr) {
let uuid = nsuuid_to_uuid(&cb::peer_identifier(&*peripheral));
let name = cb::peripheral_name(&*peripheral);
if self.peripherals.contains_key(&uuid) {
if let Some(name) = name {
self.dispatch_event(CoreBluetoothEvent::DeviceUpdated {
uuid,
name: name.to_string(),
})
.await;
}
} else {
let (event_sender, event_receiver) = mpsc::channel(256);
self.peripherals
.insert(uuid, CBPeripheral::new(peripheral, event_sender));
self.dispatch_event(CoreBluetoothEvent::DeviceDiscovered {
uuid,
name: name.map(|name| name.to_string()),
event_receiver,
})
.await;
}
}
fn on_discovered_services(
&mut self,
peripheral_uuid: Uuid,
service_map: HashMap<Uuid, StrongPtr>,
) {
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, StrongPtr>,
) {
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, StrongPtr>,
) {
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) {
}
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_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(CBPeripheralEvent::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 CBCharacteristic> {
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 CBDescriptor> {
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!");
let state = characteristic.subscribe_future_state.pop_back().unwrap();
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!");
let state = characteristic.unsubscribe_future_state.pop_back().unwrap();
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(CBPeripheralEvent::Notification(characteristic_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!");
let state = characteristic.write_future_state.pop_back().unwrap();
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);
cb::centralmanager_connectperipheral(&*self.manager, &*p.peripheral);
}
}
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);
cb::centralmanager_cancelperipheralconnection(&*self.manager, &*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);
trace!("Connected state {:?} ", state);
fut.lock()
.unwrap()
.set_reply(CoreBluetoothReply::State(state));
}
}
fn get_manager_state(&mut self) -> CBManagerState {
cb::centeralmanger_state(&*self.manager)
}
fn get_manager_state_async(&mut self, fut: CoreBluetoothReplyStateShared) {
let state = cb::centeralmanger_state(&*self.manager);
trace!("Manager state {:?} ", state);
fut.lock()
.unwrap()
.set_reply(CoreBluetoothReply::ManagerState(state));
}
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);
cb::peripheral_writevalue_forcharacteristic(
&*peripheral.peripheral,
&NSData::from_vec(data),
&*characteristic.characteristic,
match kind {
WriteType::WithResponse => 0,
WriteType::WithoutResponse => 1,
},
);
if kind == WriteType::WithoutResponse {
fut.lock().unwrap().set_reply(CoreBluetoothReply::Ok);
} else {
characteristic.write_future_state.push_front(fut);
}
}
}
}
}
fn read_value(
&mut self,
peripheral_uuid: Uuid,
service_uuid: Uuid,
characteristic_uuid: Uuid,
fut: CoreBluetoothReplyStateShared,
) {
trace!(
"Manager State {:?}",
cb::centeralmanger_state(&*self.manager)
);
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!");
cb::peripheral_readvalue_forcharacteristic(
&*peripheral.peripheral,
&*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!");
cb::peripheral_setnotifyvalue_forcharacteristic(
&*peripheral.peripheral,
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!");
cb::peripheral_setnotifyvalue_forcharacteristic(
&*peripheral.peripheral,
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!");
cb::peripheral_writevalue_fordescriptor(
&*peripheral.peripheral,
&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!");
cb::peripheral_readvalue_fordescriptor(
&*peripheral.peripheral,
&*descriptor.descriptor,
);
descriptor.read_future_state.push_front(fut);
}
}
}
}
}
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);
}
let state = characteristic.read_future_state.pop_back().unwrap();
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!");
let state = descriptor.write_future_state.pop_back().unwrap();
state.lock().unwrap().set_reply(CoreBluetoothReply::Ok);
}
}
async fn wait_for_message(&mut self) {
select! {
delegate_msg = self.delegate_receiver.select_next_some() => {
match delegate_msg {
CentralDelegateEvent::DidUpdateState => {
self.dispatch_event(CoreBluetoothEvent::DidUpdateState).await
}
CentralDelegateEvent::DiscoveredPeripheral{cbperipheral} => {
self.on_discovered_peripheral(cbperipheral).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::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),
};
}
adapter_msg = self.message_receiver.select_next_some() => {
trace!("Adapter message!");
match adapter_msg {
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::FetchManagerState {future} =>{
self.get_manager_state_async(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),
};
}
}
}
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 { cb::CENTRALMANAGERSCANOPTIONALLOWDUPLICATESKEY },
Id::into_super(Id::into_super(Id::into_super(NSNumber::new_bool(true)))),
);
cb::centralmanager_scanforperipheralswithservices_options(
&*self.manager,
service_uuids,
&**options,
);
}
fn stop_discovery(&mut self) {
trace!("BluetoothAdapter::stop_discovery");
cb::centralmanager_stopscan(&*self.manager);
}
}
fn scan_filter_to_service_uuids(filter: ScanFilter) -> id {
if filter.services.is_empty() {
nil
} else {
let service_uuids = filter
.services
.into_iter()
.map(uuid_to_cbuuid)
.collect::<Vec<_>>();
Id::into_raw(NSArray::from_vec(service_uuids)) as id
}
}
impl Drop for CoreBluetoothInternal {
fn drop(&mut self) {
trace!("BluetoothAdapter::drop");
self.stop_discovery();
CentralDelegate::delegate_drop_channel(&*self.delegate);
}
}
pub fn run_corebluetooth_thread(
event_sender: Sender<CoreBluetoothEvent>,
) -> Result<Sender<CoreBluetoothMessage>, Error> {
let authorization = cb::manager_authorization();
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 {
if cbi.get_manager_state() == CBManagerState::PoweredOff {
trace!("Breaking out of the corebluetooth loop. Manager is off.");
break;
}
cbi.wait_for_message().await;
}
})
});
Ok(sender)
}