Skip to content

Commit ddeba1b

Browse files
kragaclesqdot
authored andcommitted
Split service discovery out from connection in corebluetooth and handle didModifyServices
1 parent 5bbc447 commit ddeba1b

4 files changed

Lines changed: 111 additions & 13 deletions

File tree

src/api/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ pub enum CentralEvent {
323323
DeviceUpdated(PeripheralId),
324324
DeviceConnected(PeripheralId),
325325
DeviceDisconnected(PeripheralId),
326+
/// Only emitted on the corebluetooth subsystem
327+
DeviceServicesModified(PeripheralId),
326328
/// Emitted when a Manufacturer Data advertisement has been received from a device
327329
ManufacturerDataAdvertisement {
328330
id: PeripheralId,

src/corebluetooth/central_delegate.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ pub enum CentralDelegateEvent {
6868
service_uuids: Vec<Uuid>,
6969
rssi: i16,
7070
},
71+
ServicesModified {
72+
peripheral_uuid: Uuid,
73+
},
7174
// DiscoveredIncludedServices(Uuid, HashMap<Uuid, Retained<CBService>>),
7275
DiscoveredCharacteristics {
7376
peripheral_uuid: Uuid,
@@ -265,6 +268,10 @@ impl Debug for CentralDelegateEvent {
265268
.field("service_uuids", service_uuids)
266269
.field("rssi", rssi)
267270
.finish(),
271+
CentralDelegateEvent::ServicesModified { peripheral_uuid } => f
272+
.debug_struct("ServicesModified")
273+
.field("peripheral_uuid", peripheral_uuid)
274+
.finish(),
268275
CentralDelegateEvent::DescriptorNotified {
269276
peripheral_uuid,
270277
service_uuid,
@@ -335,7 +342,6 @@ declare_class!(
335342
peripheral_debug(peripheral)
336343
);
337344
unsafe { peripheral.setDelegate(Some(ProtocolObject::from_ref(self))) };
338-
unsafe { peripheral.discoverServices(None) }
339345
let peripheral_uuid = nsuuid_to_uuid(unsafe { &peripheral.identifier() });
340346
self.send_event(CentralDelegateEvent::ConnectedDevice { peripheral_uuid });
341347
}
@@ -722,6 +728,23 @@ declare_class!(
722728
});
723729
}
724730
}
731+
732+
#[method(peripheral:didModifyServices:)]
733+
fn delegate_peripheral_didmodifyservices(
734+
&self,
735+
peripheral: &CBPeripheral,
736+
_invalidated_services: &NSArray<CBService>,
737+
) {
738+
trace!(
739+
"delegate_peripheral_didmodifyservices {}",
740+
peripheral_debug(peripheral),
741+
);
742+
// NOTE: This is a corebluetooth-only even that makes the peripheral unusable until service discovery has been performed again.
743+
// https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/peripheral(_:didmodifyservices:)?language=objc
744+
self.send_event(CentralDelegateEvent::ServicesModified {
745+
peripheral_uuid: nsuuid_to_uuid(unsafe { &peripheral.identifier() }),
746+
});
747+
}
725748
}
726749
);
727750

src/corebluetooth/internal.rs

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ impl CharacteristicInternal {
148148
pub enum CoreBluetoothReply {
149149
AdapterState(CBManagerState),
150150
ReadResult(Vec<u8>),
151-
Connected(BTreeSet<Service>),
151+
Connected,
152+
ServicesDiscovered(BTreeSet<Service>),
152153
State(CBPeripheralState),
153154
Ok,
154155
Err(String),
@@ -161,6 +162,7 @@ pub enum PeripheralEventInternal {
161162
ManufacturerData(u16, Vec<u8>, i16),
162163
ServiceData(HashMap<Uuid, Vec<u8>>, i16),
163164
Services(Vec<Uuid>, i16),
165+
ServicesModified,
164166
}
165167

166168
pub type CoreBluetoothReplyStateShared = BtlePlugFutureStateShared<CoreBluetoothReply>;
@@ -178,6 +180,7 @@ struct PeripheralInternal {
178180
pub event_sender: Sender<PeripheralEventInternal>,
179181
pub disconnected_future_state: Option<CoreBluetoothReplyStateShared>,
180182
pub connected_future_state: Option<CoreBluetoothReplyStateShared>,
183+
pub services_discovered_future_state: Option<CoreBluetoothReplyStateShared>,
181184
}
182185

183186
impl Debug for PeripheralInternal {
@@ -194,6 +197,10 @@ impl Debug for PeripheralInternal {
194197
)
195198
.field("event_sender", &self.event_sender)
196199
.field("connected_future_state", &self.connected_future_state)
200+
.field(
201+
"services_discovered_future_state",
202+
&self.services_discovered_future_state,
203+
)
197204
.finish()
198205
}
199206
}
@@ -209,6 +216,7 @@ impl PeripheralInternal {
209216
event_sender,
210217
connected_future_state: None,
211218
disconnected_future_state: None,
219+
services_discovered_future_state: None,
212220
}
213221
}
214222

@@ -285,7 +293,7 @@ impl PeripheralInternal {
285293
// back a Connected reply to the waiting future with all of the
286294
// characteristic info in it.
287295
if !self.services.values().any(|service| !service.discovered) {
288-
if self.connected_future_state.is_none() {
296+
if self.services_discovered_future_state.is_none() {
289297
panic!("We should still have a future at this point!");
290298
}
291299
let services = self
@@ -317,12 +325,12 @@ impl PeripheralInternal {
317325
.collect(),
318326
})
319327
.collect();
320-
self.connected_future_state
328+
self.services_discovered_future_state
321329
.take()
322330
.unwrap()
323331
.lock()
324332
.unwrap()
325-
.set_reply(CoreBluetoothReply::Connected(services));
333+
.set_reply(CoreBluetoothReply::ServicesDiscovered(services));
326334
}
327335
}
328336

@@ -452,6 +460,10 @@ pub enum CoreBluetoothMessage {
452460
data: Vec<u8>,
453461
future: CoreBluetoothReplyStateShared,
454462
},
463+
DiscoverServices {
464+
peripheral_uuid: Uuid,
465+
future: CoreBluetoothReplyStateShared,
466+
},
455467
}
456468

457469
#[derive(Debug)]
@@ -568,6 +580,23 @@ impl CoreBluetoothInternal {
568580
}
569581
}
570582

583+
async fn on_services_modified(&mut self, peripheral_uuid: Uuid) {
584+
trace!(
585+
"Peripheral modified services and must be rediscovered! {:?}",
586+
peripheral_uuid
587+
);
588+
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
589+
p.services.clear();
590+
if let Err(e) = p
591+
.event_sender
592+
.send(PeripheralEventInternal::ServicesModified)
593+
.await
594+
{
595+
error!("Error sending notification event: {}", e);
596+
}
597+
}
598+
}
599+
571600
async fn on_discovered_peripheral(
572601
&mut self,
573602
peripheral: Retained<CBPeripheral>,
@@ -670,9 +699,20 @@ impl CoreBluetoothInternal {
670699
}
671700
}
672701

673-
fn on_peripheral_connect(&mut self, _peripheral_uuid: Uuid) {
674-
// Don't actually do anything here. The peripheral will fire the future
675-
// itself when it receives all of its service/characteristic info.
702+
fn on_peripheral_connect(&mut self, peripheral_uuid: Uuid) {
703+
if self.peripherals.contains_key(&peripheral_uuid) {
704+
let peripheral = self
705+
.peripherals
706+
.get_mut(&peripheral_uuid)
707+
.expect("If we're here we should have an ID");
708+
peripheral
709+
.connected_future_state
710+
.take()
711+
.unwrap()
712+
.lock()
713+
.unwrap()
714+
.set_reply(CoreBluetoothReply::Connected);
715+
}
676716
}
677717

678718
fn on_peripheral_connection_failed(
@@ -1083,6 +1123,15 @@ impl CoreBluetoothInternal {
10831123
}
10841124
}
10851125

1126+
fn discover_services(&mut self, peripheral_uuid: Uuid, fut: CoreBluetoothReplyStateShared) {
1127+
if let Some(p) = self.peripherals.get_mut(&peripheral_uuid) {
1128+
trace!("Discovering services!");
1129+
p.services_discovered_future_state = Some(fut);
1130+
// This will trigger the delegate_peripheral_diddiscoverservices in central_delegate.rs
1131+
unsafe { p.peripheral.discoverServices(None) };
1132+
}
1133+
}
1134+
10861135
async fn wait_for_message(&mut self) {
10871136
select! {
10881137
delegate_msg = self.delegate_receiver.select_next_some() => {
@@ -1106,7 +1155,7 @@ impl CoreBluetoothInternal {
11061155
self.on_discovered_characteristic_descriptors(peripheral_uuid, service_uuid, characteristic_uuid, descriptors)
11071156
}
11081157
CentralDelegateEvent::ConnectedDevice{peripheral_uuid} => {
1109-
self.on_peripheral_connect(peripheral_uuid)
1158+
self.on_peripheral_connect(peripheral_uuid)
11101159
},
11111160
CentralDelegateEvent::ConnectionFailed{peripheral_uuid, error_description} => {
11121161
self.on_peripheral_connection_failed(peripheral_uuid, error_description)
@@ -1144,6 +1193,9 @@ impl CoreBluetoothInternal {
11441193
CentralDelegateEvent::Services{peripheral_uuid, service_uuids, rssi} => {
11451194
self.on_services(peripheral_uuid, service_uuids, rssi).await
11461195
},
1196+
CentralDelegateEvent::ServicesModified{peripheral_uuid} => {
1197+
self.on_services_modified(peripheral_uuid).await
1198+
},
11471199
CentralDelegateEvent::DescriptorNotified{
11481200
peripheral_uuid,
11491201
service_uuid,
@@ -1203,6 +1255,9 @@ impl CoreBluetoothInternal {
12031255
data,
12041256
future,
12051257
} => self.write_descriptor_value(peripheral_uuid, service_uuid, characteristic_uuid, descriptor_uuid, data, future),
1258+
CoreBluetoothMessage::DiscoverServices{peripheral_uuid, future} => {
1259+
self.discover_services(peripheral_uuid, future);
1260+
}
12061261
};
12071262
}
12081263
}

src/corebluetooth/peripheral.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ impl Peripheral {
166166
services,
167167
});
168168
}
169+
Some(PeripheralEventInternal::ServicesModified) => {
170+
shared.services.lock().unwrap().clear();
171+
shared.emit_event(CentralEvent::DeviceServicesModified(shared.uuid.into()));
172+
}
169173
Some(PeripheralEventInternal::Disconnected) => (),
170174
None => {
171175
info!("Event receiver died, breaking out of corebluetooth device loop.");
@@ -264,8 +268,7 @@ impl api::Peripheral for Peripheral {
264268
})
265269
.await?;
266270
match fut.await {
267-
CoreBluetoothReply::Connected(services) => {
268-
*(self.shared.services.lock().map_err(Into::<Error>::into)?) = services;
271+
CoreBluetoothReply::Connected => {
269272
self.shared
270273
.emit_event(CentralEvent::DeviceConnected(self.shared.uuid.into()));
271274
}
@@ -298,8 +301,23 @@ impl api::Peripheral for Peripheral {
298301
}
299302

300303
async fn discover_services(&self) -> Result<()> {
301-
// TODO: Actually discover on this, rather than on connection
302-
Ok(())
304+
let fut = CoreBluetoothReplyFuture::default();
305+
self.shared
306+
.message_sender
307+
.to_owned()
308+
.send(CoreBluetoothMessage::DiscoverServices {
309+
peripheral_uuid: self.shared.uuid,
310+
future: fut.get_state_clone(),
311+
})
312+
.await?;
313+
match fut.await {
314+
CoreBluetoothReply::ServicesDiscovered(services) => {
315+
*(self.shared.services.lock().unwrap()) = services.clone();
316+
return Ok(());
317+
}
318+
CoreBluetoothReply::Err(msg) => return Err(Error::RuntimeError(msg)),
319+
_ => panic!("Shouldn't get anything but discovered or err!"),
320+
}
303321
}
304322

305323
async fn write(

0 commit comments

Comments
 (0)