summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2019-04-10 13:02:00 +0200
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2019-04-16 11:23:59 +0000
commit717b95c7863278332ec4eb4dfbbfb6147a5ac9ed (patch)
treedbb61dbe037f946ad9eca9257cd01777c69790a8
parent6aade96108f48d20382950aff5610e0df24e5616 (diff)
CoreBluetooth: add a missing -peripheral:didModifyServices: method
With some peculiar device we suddenly (during the service details discovery) got a crash with CBDescriptor suddenly becoming something else - NSString, NSMutableArray etc. - meaning the object was deleted and its memory re-used. It would appear, CBPeripheral can suddenly change it's services tree and it informs its delegate (aka 'us') about this change using the (previously) missing method. In this method we cannot do much, due to the specificity of our public API that allows concurrent discoveries, it's 'non-monolitic' (in several steps) discoveries etc. etc. So the only thing we can do - stop everything, remove all services, transition to QLowEnergyController::ConnectedState and wait for a user to re-discover services. Fixes: QTBUG-75043 Change-Id: Ie98d90aea112e40b4c6771e3f7315772dfd92b39 Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager.mm24
-rw-r--r--src/bluetooth/osx/osxbtnotifier_p.h1
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm17
-rw-r--r--src/bluetooth/qlowenergycontroller_osx_p.h1
4 files changed, 43 insertions, 0 deletions
diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm
index 41713909..6b742684 100644
--- a/src/bluetooth/osx/osxbtcentralmanager.mm
+++ b/src/bluetooth/osx/osxbtcentralmanager.mm
@@ -1373,6 +1373,30 @@ QT_USE_NAMESPACE
[self discoverIncludedServices];
}
+- (void)peripheral:(CBPeripheral *)aPeripheral
+ didModifyServices:(NSArray<CBService *> *)invalidatedServices
+{
+ Q_UNUSED(aPeripheral)
+ Q_UNUSED(invalidatedServices)
+
+ qCWarning(QT_BT_OSX) << "The peripheral has modified its services.";
+ // "This method is invoked whenever one or more services of a peripheral have changed.
+ // A peripheral’s services have changed if:
+ // * A service is removed from the peripheral’s database
+ // * A new service is added to the peripheral’s database
+ // * A service that was previously removed from the peripheral’s
+ // database is readded to the database at a different location"
+
+ // In case new services were added - we have to discover them.
+ // In case some were removed - we can end up with dangling pointers
+ // (see our 'watchdogs', for example). To handle the situation
+ // we stop all current operations here, report to QLowEnergyController
+ // so that it can trigger re-discovery.
+ [self reset];
+ managerState = OSXBluetooth::CentralManagerIdle;
+ if (notifier)
+ emit notifier->servicesWereModified();
+}
- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverIncludedServicesForService:(CBService *)service
error:(NSError *)error
diff --git a/src/bluetooth/osx/osxbtnotifier_p.h b/src/bluetooth/osx/osxbtnotifier_p.h
index 47ee6ba1..dca6c268 100644
--- a/src/bluetooth/osx/osxbtnotifier_p.h
+++ b/src/bluetooth/osx/osxbtnotifier_p.h
@@ -89,6 +89,7 @@ Q_SIGNALS:
void descriptorRead(QLowEnergyHandle descHandle, const QByteArray &value);
void descriptorWritten(QLowEnergyHandle descHandle, const QByteArray &value);
void notificationEnabled(QLowEnergyHandle charHandle, bool enabled);
+ void servicesWereModified();
void LEnotSupported();
void CBManagerError(QBluetoothDeviceDiscoveryAgent::Error error);
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index 8cef621c..8bcdc22e 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -339,6 +339,21 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedP
qtService->setState(QLowEnergyService::ServiceDiscovered);
}
+void QLowEnergyControllerPrivateOSX::_q_servicesWereModified()
+{
+ if (!(controllerState == QLowEnergyController::DiscoveringState
+ || controllerState == QLowEnergyController::DiscoveredState)) {
+ qCWarning(QT_BT_OSX) << "services were modified while controller is not in Discovered/Discovering state";
+ return;
+ }
+
+ if (controllerState == QLowEnergyController::DiscoveredState)
+ invalidateServices();
+
+ controllerState = QLowEnergyController::ConnectedState;
+ q_ptr->discoverServices();
+}
+
void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle,
const QByteArray &value)
{
@@ -989,6 +1004,8 @@ bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotif
this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished);
ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished,
this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished);
+ ok = ok && connect(notifier, &LECBManagerNotifier::servicesWereModified,
+ this, &QLowEnergyControllerPrivateOSX::_q_servicesWereModified);
ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead,
this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead);
ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten,
diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_osx_p.h
index 24b7c6e9..da959895 100644
--- a/src/bluetooth/qlowenergycontroller_osx_p.h
+++ b/src/bluetooth/qlowenergycontroller_osx_p.h
@@ -98,6 +98,7 @@ private Q_SLOTS:
void _q_serviceDiscoveryFinished();
void _q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service);
+ void _q_servicesWereModified();
void _q_characteristicRead(QLowEnergyHandle charHandle, const QByteArray &value);
void _q_characteristicWritten(QLowEnergyHandle charHandle, const QByteArray &value);