Skip to content

Commit a0635d7

Browse files
committed
fix(macos): Break the corebluetooth loop when manager turned off
closes #387 In Core Bluetooth when the device is not applicable for background bluetooth manager will create an event for state change. And then change the manager state to power off. Currently, it is not tracked at all which leads to the forever stuck unresolved issues while the connection to peripheral is still held. An additional problem I faced that there is no way to manually kill the event loop of the corebluetooth from outside so the `CoreBluetoothInternal::drop` is never called because it is always living in the stalled thread. In this change, I added an API to access the manager state and exited the event loop when if the manager turned off.
1 parent d59b29b commit a0635d7

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

src/corebluetooth/adapter.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::internal::{run_corebluetooth_thread, CoreBluetoothEvent, CoreBluetoot
22
use super::peripheral::{Peripheral, PeripheralId};
33
use crate::api::{Central, CentralEvent, ScanFilter};
44
use crate::common::adapter_manager::AdapterManager;
5+
use crate::corebluetooth::internal::{CoreBluetoothReply, CoreBluetoothReplyFuture};
56
use crate::{Error, Result};
67
use async_trait::async_trait;
78
use futures::channel::mpsc::{self, Sender};
@@ -29,7 +30,7 @@ impl Adapter {
2930
debug!("Waiting on adapter connect");
3031
if !matches!(
3132
receiver.next().await,
32-
Some(CoreBluetoothEvent::AdapterConnected)
33+
Some(CoreBluetoothEvent::DidUpdateState)
3334
) {
3435
return Err(Error::Other(
3536
"Adapter failed to connect.".to_string().into(),
@@ -39,7 +40,7 @@ impl Adapter {
3940
let manager = Arc::new(AdapterManager::default());
4041

4142
let manager_clone = manager.clone();
42-
let adapter_sender_clone = adapter_sender.clone();
43+
let mut adapter_sender_clone = adapter_sender.clone();
4344
task::spawn(async move {
4445
while let Some(msg) = receiver.next().await {
4546
match msg {
@@ -67,7 +68,18 @@ impl Adapter {
6768
CoreBluetoothEvent::DeviceDisconnected { uuid } => {
6869
manager_clone.emit(CentralEvent::DeviceDisconnected(uuid.into()));
6970
}
70-
_ => {}
71+
CoreBluetoothEvent::DidUpdateState => {
72+
let fut = CoreBluetoothReplyFuture::default();
73+
let _ = adapter_sender_clone
74+
.send(CoreBluetoothMessage::FetchManagerState {
75+
future: fut.get_state_clone(),
76+
})
77+
.await;
78+
79+
if let CoreBluetoothReply::ManagerState(state) = fut.await {
80+
error!("Adapter state changed to {:?}. Aborting manager", state)
81+
}
82+
}
7183
}
7284
}
7385
});

src/corebluetooth/framework.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@ pub mod cb {
8282
}
8383
}
8484

85+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
86+
#[repr(i64)]
87+
#[allow(dead_code)]
88+
pub enum CBManagerState {
89+
Unknown = 0,
90+
Resetting = 1,
91+
Unsupported = 2,
92+
Unauthorized = 3,
93+
PoweredOff = 4,
94+
PoweredOn = 5,
95+
}
96+
97+
unsafe impl Encode for CBManagerState {
98+
const ENCODING: Encoding = i64::ENCODING;
99+
}
100+
101+
pub fn centeralmanger_state(cbcentralmanager: id) -> CBManagerState {
102+
unsafe { msg_send![cbcentralmanager, state] }
103+
}
104+
85105
pub fn centralmanager_stopscan(cbcentralmanager: id) {
86106
unsafe { msg_send![cbcentralmanager, stopScan] }
87107
}
@@ -130,10 +150,11 @@ pub mod cb {
130150
unsafe { msg_send_id![cbperipheral, name] }
131151
}
132152

153+
#[allow(dead_code)]
133154
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
134155
#[repr(i64)]
135156
pub enum CBPeripheralState {
136-
Disonnected = 0,
157+
Disconnected = 0,
137158
Connecting = 1,
138159
Connected = 2,
139160
Disconnecting = 3,

src/corebluetooth/internal.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use super::{
1212
central_delegate::{CentralDelegate, CentralDelegateEvent},
13-
framework::cb::{self, CBManagerAuthorization, CBPeripheralState},
13+
framework::cb::{self, CBManagerAuthorization, CBManagerState, CBPeripheralState},
1414
future::{BtlePlugFuture, BtlePlugFutureStateShared},
1515
utils::{
1616
core_bluetooth::{cbuuid_to_uuid, uuid_to_cbuuid},
@@ -139,6 +139,7 @@ pub enum CoreBluetoothReply {
139139
ReadResult(Vec<u8>),
140140
Connected(BTreeSet<Service>),
141141
State(CBPeripheralState),
142+
ManagerState(CBManagerState),
142143
Ok,
143144
Err(String),
144145
}
@@ -399,11 +400,14 @@ pub enum CoreBluetoothMessage {
399400
data: Vec<u8>,
400401
future: CoreBluetoothReplyStateShared,
401402
},
403+
FetchManagerState {
404+
future: CoreBluetoothReplyStateShared,
405+
},
402406
}
403407

404408
#[derive(Debug)]
405409
pub enum CoreBluetoothEvent {
406-
AdapterConnected,
410+
DidUpdateState,
407411
DeviceDiscovered {
408412
uuid: Uuid,
409413
name: Option<String>,
@@ -789,6 +793,18 @@ impl CoreBluetoothInternal {
789793
}
790794
}
791795

796+
fn get_manager_state(&mut self) -> CBManagerState {
797+
cb::centeralmanger_state(&*self.manager)
798+
}
799+
800+
fn get_manager_state_async(&mut self, fut: CoreBluetoothReplyStateShared) {
801+
let state = cb::centeralmanger_state(&*self.manager);
802+
trace!("Manager state {:?} ", state);
803+
fut.lock()
804+
.unwrap()
805+
.set_reply(CoreBluetoothReply::ManagerState(state));
806+
}
807+
792808
fn write_value(
793809
&mut self,
794810
peripheral_uuid: Uuid,
@@ -831,6 +847,11 @@ impl CoreBluetoothInternal {
831847
characteristic_uuid: Uuid,
832848
fut: CoreBluetoothReplyStateShared,
833849
) {
850+
trace!(
851+
"Manager State {:?}",
852+
cb::centeralmanger_state(&*self.manager)
853+
);
854+
834855
if let Some(peripheral) = self.peripherals.get_mut(&peripheral_uuid) {
835856
if let Some(service) = peripheral.services.get_mut(&service_uuid) {
836857
if let Some(characteristic) = service.characteristics.get_mut(&characteristic_uuid)
@@ -1004,7 +1025,7 @@ impl CoreBluetoothInternal {
10041025
// "ready" variable in our adapter that will cause scans/etc
10051026
// to fail if this hasn't updated.
10061027
CentralDelegateEvent::DidUpdateState => {
1007-
self.dispatch_event(CoreBluetoothEvent::AdapterConnected).await
1028+
self.dispatch_event(CoreBluetoothEvent::DidUpdateState).await
10081029
}
10091030
CentralDelegateEvent::DiscoveredPeripheral{cbperipheral} => {
10101031
self.on_discovered_peripheral(cbperipheral).await
@@ -1103,6 +1124,9 @@ impl CoreBluetoothInternal {
11031124
CoreBluetoothMessage::IsConnected{peripheral_uuid, future} => {
11041125
self.is_connected(peripheral_uuid, future);
11051126
},
1127+
CoreBluetoothMessage::FetchManagerState {future} =>{
1128+
self.get_manager_state_async(future);
1129+
},
11061130
CoreBluetoothMessage::ReadDescriptorValue{peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid, future} => {
11071131
self.read_descriptor_value(peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid, future)
11081132
}
@@ -1184,6 +1208,14 @@ pub fn run_corebluetooth_thread(
11841208
runtime.block_on(async move {
11851209
let mut cbi = CoreBluetoothInternal::new(receiver, event_sender);
11861210
loop {
1211+
// When the IOS or MacOS device if powered off or locked
1212+
// the manager state will suddenly throw DidUpdateState event and turn off.
1213+
// If we are not exiting the main loop here the futures requested after
1214+
// power off will be stuck forever.
1215+
if cbi.get_manager_state() == CBManagerState::PoweredOff {
1216+
trace!("Breaking out of the corebluetooth loop. Manager is off.");
1217+
break;
1218+
}
11871219
cbi.wait_for_message().await;
11881220
}
11891221
})

0 commit comments

Comments
 (0)