Skip to content

Commit

Permalink
CoreBluetooth: Handle orphaned objects
Browse files Browse the repository at this point in the history
When a device has disconnected and then reconnected, existing references to bluetooth objects may become orphaned and accessing their parent object will return `nil`.
  • Loading branch information
alexmoon committed Oct 4, 2024
1 parent 70be759 commit 054dfe8
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 26 deletions.
58 changes: 44 additions & 14 deletions src/corebluetooth/characteristic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,13 @@ impl CharacteristicImpl {

/// Read the value of this characteristic from the device
pub async fn read(&self) -> Result<Vec<u8>> {
let service = self.inner.service();
let peripheral = service.peripheral();
let service = self
.inner
.service()
.ok_or(Error::new(ErrorKind::NotFound, None, "service not found"))?;
let peripheral = service
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;
let mut receiver = self.delegate.sender().new_receiver();

if peripheral.state() != CBPeripheralState::CONNECTED {
Expand Down Expand Up @@ -92,8 +97,13 @@ impl CharacteristicImpl {
/// Write the value of this descriptor on the device to `value` and request the device return a response indicating
/// a successful write.
pub async fn write(&self, value: &[u8]) -> Result<()> {
let service = self.inner.service();
let peripheral = service.peripheral();
let service = self
.inner
.service()
.ok_or(Error::new(ErrorKind::NotFound, None, "service not found"))?;
let peripheral = service
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;
let mut receiver = self.delegate.sender().new_receiver();

if peripheral.state() != CBPeripheralState::CONNECTED {
Expand Down Expand Up @@ -126,8 +136,13 @@ impl CharacteristicImpl {

/// Write the value of this descriptor on the device to `value` without requesting a response.
pub async fn write_without_response(&self, value: &[u8]) -> Result<()> {
let service = self.inner.service();
let peripheral = service.peripheral();
let service = self
.inner
.service()
.ok_or(Error::new(ErrorKind::NotFound, None, "service not found"))?;
let peripheral = service
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;
let mut receiver = self.delegate.sender().new_receiver();

if peripheral.state() != CBPeripheralState::CONNECTED {
Expand Down Expand Up @@ -156,7 +171,11 @@ impl CharacteristicImpl {

/// Get the maximum amount of data that can be written in a single packet for this characteristic.
pub fn max_write_len(&self) -> Result<usize> {
let peripheral = self.inner.service().peripheral();
let peripheral = self.inner.service().and_then(|x| x.peripheral()).ok_or(Error::new(
ErrorKind::NotFound,
None,
"peripheral not found",
))?;
Ok(peripheral.maximum_write_value_length_for_type(CBCharacteristicWriteType::WithoutResponse))
}

Expand All @@ -174,12 +193,17 @@ impl CharacteristicImpl {
return Err(Error::new(
ErrorKind::NotSupported,
None,
"characteristic does not support indications or notifications".to_string(),
"characteristic does not support indications or notifications",
));
};

let service = self.inner.service();
let peripheral = service.peripheral();
let service = self
.inner
.service()
.ok_or(Error::new(ErrorKind::NotFound, None, "service not found"))?;
let peripheral = service
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;
let mut receiver = self.delegate.sender().new_receiver();

if peripheral.state() != CBPeripheralState::CONNECTED {
Expand All @@ -188,8 +212,9 @@ impl CharacteristicImpl {

peripheral.set_notify(&self.inner, true);
let guard = defer(move || {
let peripheral = self.inner.service().peripheral();
peripheral.set_notify(&self.inner, false);
if let Some(peripheral) = self.inner.service().and_then(|x| x.peripheral()) {
peripheral.set_notify(&self.inner, false);
}
});

loop {
Expand Down Expand Up @@ -254,8 +279,13 @@ impl CharacteristicImpl {

/// Discover the descriptors associated with this characteristic.
pub async fn discover_descriptors(&self) -> Result<Vec<Descriptor>> {
let service = self.inner.service();
let peripheral = service.peripheral();
let service = self
.inner
.service()
.ok_or(Error::new(ErrorKind::NotFound, None, "service not found"))?;
let peripheral = service
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;
let mut receiver = self.delegate.sender().new_receiver();

if peripheral.state() != CBPeripheralState::CONNECTED {
Expand Down
20 changes: 16 additions & 4 deletions src/corebluetooth/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,14 @@ impl DescriptorImpl {

/// Read the value of this descriptor from the device
pub async fn read(&self) -> Result<Vec<u8>> {
let service = self.inner.characteristic().service();
let peripheral = service.peripheral();
let service = self.inner.characteristic().and_then(|x| x.service()).ok_or(Error::new(
ErrorKind::NotFound,
None,
"service not found",
))?;
let peripheral = service
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;
let mut receiver = self.delegate.sender().new_receiver();

if peripheral.state() != CBPeripheralState::CONNECTED {
Expand Down Expand Up @@ -108,8 +114,14 @@ impl DescriptorImpl {

/// Write the value of this descriptor on the device to `value`
pub async fn write(&self, value: &[u8]) -> Result<()> {
let service = self.inner.characteristic().service();
let peripheral = service.peripheral();
let service = self.inner.characteristic().and_then(|x| x.service()).ok_or(Error::new(
ErrorKind::NotFound,
None,
"service not found",
))?;
let peripheral = service
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;
let mut receiver = self.delegate.sender().new_receiver();

if peripheral.state() != CBPeripheralState::CONNECTED {
Expand Down
10 changes: 8 additions & 2 deletions src/corebluetooth/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ impl ServiceImpl {
}

async fn discover_characteristics_inner(&self, uuids: Option<&NSArray<CBUUID>>) -> Result<Vec<Characteristic>> {
let peripheral = self.inner.peripheral();
let peripheral =
self.inner
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;

if peripheral.state() != CBPeripheralState::CONNECTED {
return Err(ErrorKind::NotConnected.into());
Expand Down Expand Up @@ -123,7 +126,10 @@ impl ServiceImpl {
}

async fn discover_included_services_inner(&self, uuids: Option<&NSArray<CBUUID>>) -> Result<Vec<Service>> {
let peripheral = self.inner.peripheral();
let peripheral =
self.inner
.peripheral()
.ok_or(Error::new(ErrorKind::NotFound, None, "peripheral not found"))?;

if peripheral.state() != CBPeripheralState::CONNECTED {
return Err(ErrorKind::NotConnected.into());
Expand Down
12 changes: 6 additions & 6 deletions src/corebluetooth/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,8 @@ impl CBService {
autoreleasepool(move || unsafe { ShareId::from_ptr(msg_send![self, UUID]) })
}

pub fn peripheral(&self) -> ShareId<CBPeripheral> {
autoreleasepool(move || unsafe { ShareId::from_ptr(msg_send![self, peripheral]) })
pub fn peripheral(&self) -> Option<ShareId<CBPeripheral>> {
autoreleasepool(move || unsafe { option_from_ptr(msg_send![self, peripheral]) })
}

pub fn is_primary(&self) -> bool {
Expand All @@ -522,8 +522,8 @@ impl CBCharacteristic {
autoreleasepool(move || unsafe { ShareId::from_ptr(msg_send![self, UUID]) })
}

pub fn service(&self) -> ShareId<CBService> {
autoreleasepool(move || unsafe { ShareId::from_ptr(msg_send![self, service]) })
pub fn service(&self) -> Option<ShareId<CBService>> {
autoreleasepool(move || unsafe { option_from_ptr(msg_send![self, service]) })
}

pub fn value(&self) -> Option<ShareId<NSData>> {
Expand Down Expand Up @@ -554,8 +554,8 @@ impl CBDescriptor {
autoreleasepool(move || unsafe { ShareId::from_ptr(msg_send![self, UUID]) })
}

pub fn characteristic(&self) -> ShareId<CBCharacteristic> {
autoreleasepool(move || unsafe { ShareId::from_ptr(msg_send![self, characteristic]) })
pub fn characteristic(&self) -> Option<ShareId<CBCharacteristic>> {
autoreleasepool(move || unsafe { option_from_ptr(msg_send![self, characteristic]) })
}

pub fn value(&self) -> Option<ShareId<NSObject>> {
Expand Down

0 comments on commit 054dfe8

Please sign in to comment.