Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(macos): Break the corebluetooth loop when manager turned off #388

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/corebluetooth/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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(),
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
}
}
}
});
Expand Down
23 changes: 22 additions & 1 deletion src/corebluetooth/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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] }
}
Expand Down Expand Up @@ -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,
Expand Down
38 changes: 35 additions & 3 deletions src/corebluetooth/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -139,6 +139,7 @@ pub enum CoreBluetoothReply {
ReadResult(Vec<u8>),
Connected(BTreeSet<Service>),
State(CBPeripheralState),
ManagerState(CBManagerState),
Ok,
Err(String),
}
Expand Down Expand Up @@ -399,11 +400,14 @@ pub enum CoreBluetoothMessage {
data: Vec<u8>,
future: CoreBluetoothReplyStateShared,
},
FetchManagerState {
future: CoreBluetoothReplyStateShared,
},
}

#[derive(Debug)]
pub enum CoreBluetoothEvent {
AdapterConnected,
DidUpdateState,
DeviceDiscovered {
uuid: Uuid,
name: Option<String>,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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;
}
})
Expand Down
Loading