diff --git a/src/corebluetooth/adapter.rs b/src/corebluetooth/adapter.rs index c139c43d..fb3dc251 100644 --- a/src/corebluetooth/adapter.rs +++ b/src/corebluetooth/adapter.rs @@ -2,6 +2,7 @@ use super::internal::{run_corebluetooth_thread, CoreBluetoothEvent, CoreBluetoot use super::peripheral::{Peripheral, PeripheralId}; use crate::api::{Central, CentralEvent, ScanFilter}; use crate::common::adapter_manager::AdapterManager; +use crate::corebluetooth::internal::{CoreBluetoothReply, CoreBluetoothReplyFuture}; use crate::{Error, Result}; use async_trait::async_trait; use futures::channel::mpsc::{self, Sender}; @@ -29,7 +30,7 @@ impl Adapter { debug!("Waiting on adapter connect"); if !matches!( receiver.next().await, - Some(CoreBluetoothEvent::AdapterConnected) + Some(CoreBluetoothEvent::DidUpdateState) ) { return Err(Error::Other( "Adapter failed to connect.".to_string().into(), @@ -39,7 +40,7 @@ impl Adapter { let manager = Arc::new(AdapterManager::default()); let manager_clone = manager.clone(); - let adapter_sender_clone = adapter_sender.clone(); + let mut adapter_sender_clone = adapter_sender.clone(); task::spawn(async move { while let Some(msg) = receiver.next().await { match msg { @@ -67,7 +68,18 @@ impl Adapter { CoreBluetoothEvent::DeviceDisconnected { uuid } => { manager_clone.emit(CentralEvent::DeviceDisconnected(uuid.into())); } - _ => {} + CoreBluetoothEvent::DidUpdateState => { + let fut = CoreBluetoothReplyFuture::default(); + let _ = adapter_sender_clone + .send(CoreBluetoothMessage::FetchManagerState { + future: fut.get_state_clone(), + }) + .await; + + if let CoreBluetoothReply::ManagerState(state) = fut.await { + error!("Adapter state changed to {:?}. Aborting manager", state) + } + } } } }); diff --git a/src/corebluetooth/framework.rs b/src/corebluetooth/framework.rs index a9f40c4b..a6c61f20 100644 --- a/src/corebluetooth/framework.rs +++ b/src/corebluetooth/framework.rs @@ -82,6 +82,26 @@ pub mod cb { } } + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + #[repr(i64)] + #[allow(dead_code)] + pub enum CBManagerState { + Unknown = 0, + Resetting = 1, + Unsupported = 2, + Unauthorized = 3, + PoweredOff = 4, + PoweredOn = 5, + } + + unsafe impl Encode for CBManagerState { + const ENCODING: Encoding = i64::ENCODING; + } + + pub fn centeralmanger_state(cbcentralmanager: id) -> CBManagerState { + unsafe { msg_send![cbcentralmanager, state] } + } + pub fn centralmanager_stopscan(cbcentralmanager: id) { unsafe { msg_send![cbcentralmanager, stopScan] } } @@ -130,10 +150,11 @@ pub mod cb { unsafe { msg_send_id![cbperipheral, name] } } + #[allow(dead_code)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(i64)] pub enum CBPeripheralState { - Disonnected = 0, + Disconnected = 0, Connecting = 1, Connected = 2, Disconnecting = 3, diff --git a/src/corebluetooth/internal.rs b/src/corebluetooth/internal.rs index 9ce8ffe7..2a1d109a 100644 --- a/src/corebluetooth/internal.rs +++ b/src/corebluetooth/internal.rs @@ -10,7 +10,7 @@ use super::{ central_delegate::{CentralDelegate, CentralDelegateEvent}, - framework::cb::{self, CBManagerAuthorization, CBPeripheralState}, + framework::cb::{self, CBManagerAuthorization, CBManagerState, CBPeripheralState}, future::{BtlePlugFuture, BtlePlugFutureStateShared}, utils::{ core_bluetooth::{cbuuid_to_uuid, uuid_to_cbuuid}, @@ -139,6 +139,7 @@ pub enum CoreBluetoothReply { ReadResult(Vec), Connected(BTreeSet), State(CBPeripheralState), + ManagerState(CBManagerState), Ok, Err(String), } @@ -399,11 +400,14 @@ pub enum CoreBluetoothMessage { data: Vec, future: CoreBluetoothReplyStateShared, }, + FetchManagerState { + future: CoreBluetoothReplyStateShared, + }, } #[derive(Debug)] pub enum CoreBluetoothEvent { - AdapterConnected, + DidUpdateState, DeviceDiscovered { uuid: Uuid, name: Option, @@ -789,6 +793,18 @@ impl CoreBluetoothInternal { } } + 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, @@ -831,6 +847,11 @@ impl CoreBluetoothInternal { 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) @@ -1004,7 +1025,7 @@ impl CoreBluetoothInternal { // "ready" variable in our adapter that will cause scans/etc // to fail if this hasn't updated. CentralDelegateEvent::DidUpdateState => { - self.dispatch_event(CoreBluetoothEvent::AdapterConnected).await + self.dispatch_event(CoreBluetoothEvent::DidUpdateState).await } CentralDelegateEvent::DiscoveredPeripheral{cbperipheral} => { self.on_discovered_peripheral(cbperipheral).await @@ -1103,6 +1124,9 @@ impl CoreBluetoothInternal { 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) } @@ -1184,6 +1208,14 @@ pub fn run_corebluetooth_thread( runtime.block_on(async move { let mut cbi = CoreBluetoothInternal::new(receiver, event_sender); loop { + // When the IOS or MacOS device if powered off or locked + // the manager state will suddenly throw DidUpdateState event and turn off. + // If we are not exiting the main loop here the futures requested after + // power off will be stuck forever. + if cbi.get_manager_state() == CBManagerState::PoweredOff { + trace!("Breaking out of the corebluetooth loop. Manager is off."); + break; + } cbi.wait_for_message().await; } })