Skip to content

Commit e47f748

Browse files
authored
[Linux] Handle BLE adapter re-appearance due to BlueZ restart (#32847)
* Keep all timer handlers in BLEManagerImpl class * [Linux] Handle BLE adapter re-appearance * Simplify casting for callbacks * Fix adapter added notification * Add TODO for potential issue with LE-only adapters
1 parent 9ff29ed commit e47f748

File tree

4 files changed

+96
-43
lines changed

4 files changed

+96
-43
lines changed

src/platform/Linux/BLEManagerImpl.cpp

+54-26
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,6 @@ const ChipBleUUID ChipUUID_CHIPoBLEChar_RX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0
7777
const ChipBleUUID ChipUUID_CHIPoBLEChar_TX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F,
7878
0x9D, 0x12 } };
7979

80-
void HandleConnectTimeout(chip::System::Layer *, void * apEndpoint)
81-
{
82-
VerifyOrDie(apEndpoint != nullptr);
83-
static_cast<BluezEndpoint *>(apEndpoint)->CancelConnect();
84-
BLEManagerImpl::HandleConnectFailed(CHIP_ERROR_TIMEOUT);
85-
}
86-
8780
} // namespace
8881

8982
BLEManagerImpl BLEManagerImpl::sInstance;
@@ -116,16 +109,17 @@ CHIP_ERROR BLEManagerImpl::_Init()
116109

117110
void BLEManagerImpl::_Shutdown()
118111
{
119-
// Ensure scan resources are cleared (e.g. timeout timers).
112+
// Make sure that timers are stopped before shutting down the BLE layer.
113+
DeviceLayer::SystemLayer().CancelTimer(HandleScanTimer, this);
114+
DeviceLayer::SystemLayer().CancelTimer(HandleAdvertisingTimer, this);
115+
DeviceLayer::SystemLayer().CancelTimer(HandleConnectTimer, this);
116+
120117
mDeviceScanner.Shutdown();
121-
// Stop advertising and free resources.
122118
mBLEAdvertisement.Shutdown();
123-
// Make sure that the endpoint is not used by the timer.
124-
DeviceLayer::SystemLayer().CancelTimer(HandleConnectTimeout, &mEndpoint);
125-
// Release BLE connection resources (unregister from BlueZ).
126119
mEndpoint.Shutdown();
120+
127121
mBluezObjectManager.Shutdown();
128-
mFlags.Clear(Flags::kBluezBLELayerInitialized);
122+
mFlags.Clear(Flags::kBluezManagerInitialized);
129123
}
130124

131125
CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val)
@@ -262,15 +256,30 @@ void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEv
262256
apEvent->Platform.BLEAdapter.mAdapterAddress);
263257
if (apEvent->Platform.BLEAdapter.mAdapterId == mAdapterId)
264258
{
265-
// TODO: Handle adapter added
259+
mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled;
260+
DriveBLEState();
266261
}
267262
break;
268263
case DeviceEventType::kPlatformLinuxBLEAdapterRemoved:
269264
ChipLogDetail(DeviceLayer, "BLE adapter removed: id=%u address=%s", apEvent->Platform.BLEAdapter.mAdapterId,
270265
apEvent->Platform.BLEAdapter.mAdapterAddress);
271266
if (apEvent->Platform.BLEAdapter.mAdapterId == mAdapterId)
272267
{
273-
// TODO: Handle adapter removed
268+
// Shutdown all BLE operations and release resources
269+
mDeviceScanner.Shutdown();
270+
mBLEAdvertisement.Shutdown();
271+
mEndpoint.Shutdown();
272+
// Drop reference to the adapter
273+
mAdapter.reset();
274+
// Clear all flags related to BlueZ BLE operations
275+
mFlags.Clear(Flags::kBluezAdapterAvailable);
276+
mFlags.Clear(Flags::kBluezBLELayerInitialized);
277+
mFlags.Clear(Flags::kAdvertisingConfigured);
278+
mFlags.Clear(Flags::kAppRegistered);
279+
mFlags.Clear(Flags::kAdvertising);
280+
CleanScanConfig();
281+
// Indicate that the adapter is no longer available
282+
err = BLE_ERROR_ADAPTER_UNAVAILABLE;
274283
}
275284
break;
276285
case DeviceEventType::kPlatformLinuxBLECentralConnected:
@@ -344,9 +353,7 @@ void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEv
344353
exit:
345354
if (err != CHIP_NO_ERROR)
346355
{
347-
ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err));
348-
mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled;
349-
DeviceLayer::SystemLayer().CancelTimer(HandleAdvertisingTimer, this);
356+
DisableBLEService(err);
350357
mFlags.Clear(Flags::kControlOpInProgress);
351358
}
352359
}
@@ -680,9 +687,23 @@ void BLEManagerImpl::DriveBLEState()
680687
exit:
681688
if (err != CHIP_NO_ERROR)
682689
{
683-
ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err));
690+
DisableBLEService(err);
691+
}
692+
}
693+
694+
void BLEManagerImpl::DisableBLEService(CHIP_ERROR err)
695+
{
696+
ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %" CHIP_ERROR_FORMAT, err.Format());
697+
mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled;
698+
// Stop all timers if the error is other than BLE adapter unavailable. In case of BLE adapter
699+
// beeing unavailable, we will keep timers running, as the adapter might become available in
700+
// the nearest future (e.g. BlueZ restart due to crash). By doing that we will ensure that BLE
701+
// adapter reappearance will not extend timeouts for the ongoing operations.
702+
if (err != BLE_ERROR_ADAPTER_UNAVAILABLE)
703+
{
704+
DeviceLayer::SystemLayer().CancelTimer(HandleScanTimer, this);
684705
DeviceLayer::SystemLayer().CancelTimer(HandleAdvertisingTimer, this);
685-
mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled;
706+
DeviceLayer::SystemLayer().CancelTimer(HandleConnectTimer, this);
686707
}
687708
}
688709

@@ -768,7 +789,7 @@ void BLEManagerImpl::InitiateScan(BleScanState scanType)
768789
ChipLogError(Ble, "Failed to start BLE scan: %" CHIP_ERROR_FORMAT, err.Format());
769790
});
770791

771-
err = DeviceLayer::SystemLayer().StartTimer(kNewConnectionScanTimeout, HandleScannerTimer, this);
792+
err = DeviceLayer::SystemLayer().StartTimer(kNewConnectionScanTimeout, HandleScanTimer, this);
772793
VerifyOrExit(err == CHIP_NO_ERROR, {
773794
mBLEScanConfig.mBleScanState = BleScanState::kNotScanning;
774795
mDeviceScanner.StopScan();
@@ -782,7 +803,7 @@ void BLEManagerImpl::InitiateScan(BleScanState scanType)
782803
}
783804
}
784805

785-
void BLEManagerImpl::HandleScannerTimer(chip::System::Layer *, void * appState)
806+
void BLEManagerImpl::HandleScanTimer(chip::System::Layer *, void * appState)
786807
{
787808
auto * manager = static_cast<BLEManagerImpl *>(appState);
788809
manager->OnScanError(CHIP_ERROR_TIMEOUT);
@@ -792,7 +813,7 @@ void BLEManagerImpl::HandleScannerTimer(chip::System::Layer *, void * appState)
792813
void BLEManagerImpl::CleanScanConfig()
793814
{
794815
if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting)
795-
DeviceLayer::SystemLayer().CancelTimer(HandleConnectTimeout, &mEndpoint);
816+
DeviceLayer::SystemLayer().CancelTimer(HandleConnectTimer, this);
796817

797818
mBLEScanConfig.mBleScanState = BleScanState::kNotScanning;
798819
}
@@ -813,7 +834,7 @@ CHIP_ERROR BLEManagerImpl::CancelConnection()
813834
// If in discovery mode, stop scan.
814835
else if (mBLEScanConfig.mBleScanState != BleScanState::kNotScanning)
815836
{
816-
DeviceLayer::SystemLayer().CancelTimer(HandleScannerTimer, this);
837+
DeviceLayer::SystemLayer().CancelTimer(HandleScanTimer, this);
817838
mDeviceScanner.StopScan();
818839
}
819840
return CHIP_NO_ERROR;
@@ -904,10 +925,10 @@ void BLEManagerImpl::OnDeviceScanned(BluezDevice1 & device, const chip::Ble::Chi
904925
// We StartScan in the ChipStack thread.
905926
// StopScan should also be performed in the ChipStack thread.
906927
// At the same time, the scan timer also needs to be canceled in the ChipStack thread.
907-
DeviceLayer::SystemLayer().CancelTimer(HandleScannerTimer, this);
928+
DeviceLayer::SystemLayer().CancelTimer(HandleScanTimer, this);
908929
mDeviceScanner.StopScan();
909930
// Stop scanning and then start connecting timer
910-
DeviceLayer::SystemLayer().StartTimer(kConnectTimeout, HandleConnectTimeout, &mEndpoint);
931+
DeviceLayer::SystemLayer().StartTimer(kConnectTimeout, HandleConnectTimer, this);
911932
chip::DeviceLayer::PlatformMgr().UnlockChipStack();
912933

913934
CHIP_ERROR err = mEndpoint.ConnectDevice(device);
@@ -916,6 +937,13 @@ void BLEManagerImpl::OnDeviceScanned(BluezDevice1 & device, const chip::Ble::Chi
916937
ChipLogProgress(Ble, "New device connected: %s", address);
917938
}
918939

940+
void BLEManagerImpl::HandleConnectTimer(chip::System::Layer *, void * appState)
941+
{
942+
auto * manager = static_cast<BLEManagerImpl *>(appState);
943+
manager->mEndpoint.CancelConnect();
944+
BLEManagerImpl::HandleConnectFailed(CHIP_ERROR_TIMEOUT);
945+
}
946+
919947
void BLEManagerImpl::OnScanComplete()
920948
{
921949
switch (mBLEScanConfig.mBleScanState)

src/platform/Linux/BLEManagerImpl.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,15 @@ class BLEManagerImpl final : public BLEManager,
184184
};
185185

186186
void DriveBLEState();
187+
void DisableBLEService(CHIP_ERROR err);
187188
BluezAdvertisement::AdvertisingIntervals GetAdvertisingIntervals() const;
188-
static void HandleAdvertisingTimer(chip::System::Layer *, void * appState);
189189
void InitiateScan(BleScanState scanType);
190-
static void HandleScannerTimer(chip::System::Layer *, void * appState);
191190
void CleanScanConfig();
192191

192+
static void HandleAdvertisingTimer(chip::System::Layer *, void * appState);
193+
static void HandleScanTimer(chip::System::Layer *, void * appState);
194+
static void HandleConnectTimer(chip::System::Layer *, void * appState);
195+
193196
CHIPoBLEServiceMode mServiceMode;
194197
BitFlags<Flags> mFlags;
195198

src/platform/Linux/bluez/BluezObjectManager.cpp

+34-12
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,25 @@ BluezObjectManager::NotificationsDelegates BluezObjectManager::GetDeviceNotifica
188188
return delegates;
189189
}
190190

191-
void BluezObjectManager::OnObjectAdded(GDBusObjectManager * aMgr, GDBusObject * aObj)
191+
void BluezObjectManager::OnObjectAdded(GDBusObjectManager * aMgr, BluezObject * aObj)
192192
{
193-
GAutoPtr<BluezAdapter1> adapter(bluez_object_get_adapter1(reinterpret_cast<BluezObject *>(aObj)));
194-
if (adapter)
193+
GAutoPtr<BluezAdapter1> adapter(bluez_object_get_adapter1(aObj));
194+
// Verify that the adapter is properly initialized - the class property must be set.
195+
// BlueZ can export adapter objects on the bus before it is fully initialized. Such
196+
// adapter objects are not usable and must be ignored.
197+
//
198+
// TODO: Find a better way to determine whether the adapter interface exposed by
199+
// BlueZ D-Bus service is fully functional. The current approach is based on
200+
// the assumption that the class property is non-zero, which is true only
201+
// for BR/EDR + LE adapters. LE-only adapters do not have HCI command to read
202+
// the class property and BlueZ sets it to 0 as a default value.
203+
if (adapter && bluez_adapter1_get_class(adapter.get()) != 0)
195204
{
196205
NotifyAdapterAdded(adapter.get());
197206
return;
198207
}
199208

200-
GAutoPtr<BluezDevice1> device(bluez_object_get_device1(reinterpret_cast<BluezObject *>(aObj)));
209+
GAutoPtr<BluezDevice1> device(bluez_object_get_device1(aObj));
201210
if (device)
202211
{
203212
for (auto delegate : GetDeviceNotificationsDelegates(device.get()))
@@ -207,17 +216,17 @@ void BluezObjectManager::OnObjectAdded(GDBusObjectManager * aMgr, GDBusObject *
207216
}
208217
}
209218

210-
void BluezObjectManager::OnObjectRemoved(GDBusObjectManager * aMgr, GDBusObject * aObj)
219+
void BluezObjectManager::OnObjectRemoved(GDBusObjectManager * aMgr, BluezObject * aObj)
211220
{
212-
GAutoPtr<BluezAdapter1> adapter(bluez_object_get_adapter1(reinterpret_cast<BluezObject *>(aObj)));
221+
GAutoPtr<BluezAdapter1> adapter(bluez_object_get_adapter1(aObj));
213222
if (adapter)
214223
{
215224
RemoveAdapterSubscriptions(adapter.get());
216225
NotifyAdapterRemoved(adapter.get());
217226
return;
218227
}
219228

220-
GAutoPtr<BluezDevice1> device(bluez_object_get_device1(reinterpret_cast<BluezObject *>(aObj)));
229+
GAutoPtr<BluezDevice1> device(bluez_object_get_device1(aObj));
221230
if (device)
222231
{
223232
for (auto delegate : GetDeviceNotificationsDelegates(device.get()))
@@ -227,10 +236,22 @@ void BluezObjectManager::OnObjectRemoved(GDBusObjectManager * aMgr, GDBusObject
227236
}
228237
}
229238

230-
void BluezObjectManager::OnInterfacePropertiesChanged(GDBusObjectManagerClient * aMgr, GDBusObjectProxy * aObj, GDBusProxy * aIface,
239+
void BluezObjectManager::OnInterfacePropertiesChanged(GDBusObjectManagerClient * aMgr, BluezObject * aObj, GDBusProxy * aIface,
231240
GVariant * aChangedProps, const char * const * aInvalidatedProps)
232241
{
233-
GAutoPtr<BluezDevice1> device(bluez_object_get_device1(reinterpret_cast<BluezObject *>(aObj)));
242+
uint32_t classValue = 0;
243+
GAutoPtr<BluezAdapter1> adapter(bluez_object_get_adapter1(aObj));
244+
// When the adapter's readonly class property is set, it means that the adapter has been
245+
// fully initialized and is ready to be used. It's most likely that the adapter has been
246+
// previously ignored in the OnObjectAdded callback, so now we can notify the application
247+
// about the new adapter.
248+
if (adapter && g_variant_lookup(aChangedProps, "Class", "u", &classValue) && classValue != 0)
249+
{
250+
NotifyAdapterAdded(adapter.get());
251+
return;
252+
}
253+
254+
GAutoPtr<BluezDevice1> device(bluez_object_get_device1(aObj));
234255
if (device)
235256
{
236257
for (auto delegate : GetDeviceNotificationsDelegates(device.get()))
@@ -256,18 +277,19 @@ CHIP_ERROR BluezObjectManager::SetupObjectManager()
256277

257278
g_signal_connect(mObjectManager.get(), "object-added",
258279
G_CALLBACK(+[](GDBusObjectManager * mgr, GDBusObject * obj, BluezObjectManager * self) {
259-
return self->OnObjectAdded(mgr, obj);
280+
return self->OnObjectAdded(mgr, reinterpret_cast<BluezObject *>(obj));
260281
}),
261282
this);
262283
g_signal_connect(mObjectManager.get(), "object-removed",
263284
G_CALLBACK(+[](GDBusObjectManager * mgr, GDBusObject * obj, BluezObjectManager * self) {
264-
return self->OnObjectRemoved(mgr, obj);
285+
return self->OnObjectRemoved(mgr, reinterpret_cast<BluezObject *>(obj));
265286
}),
266287
this);
267288
g_signal_connect(mObjectManager.get(), "interface-proxy-properties-changed",
268289
G_CALLBACK(+[](GDBusObjectManagerClient * mgr, GDBusObjectProxy * obj, GDBusProxy * iface, GVariant * changed,
269290
const char * const * invalidated, BluezObjectManager * self) {
270-
return self->OnInterfacePropertiesChanged(mgr, obj, iface, changed, invalidated);
291+
return self->OnInterfacePropertiesChanged(mgr, reinterpret_cast<BluezObject *>(obj), iface, changed,
292+
invalidated);
271293
}),
272294
this);
273295

src/platform/Linux/bluez/BluezObjectManager.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ class BluezObjectManager
8989
using NotificationsDelegates = std::vector<BluezObjectManagerAdapterNotificationsDelegate *>;
9090
NotificationsDelegates GetDeviceNotificationsDelegates(BluezDevice1 * device);
9191

92-
void OnObjectAdded(GDBusObjectManager * aMgr, GDBusObject * aObj);
93-
void OnObjectRemoved(GDBusObjectManager * aMgr, GDBusObject * aObj);
94-
void OnInterfacePropertiesChanged(GDBusObjectManagerClient * aMgr, GDBusObjectProxy * aObj, GDBusProxy * aIface,
92+
void OnObjectAdded(GDBusObjectManager * aMgr, BluezObject * aObj);
93+
void OnObjectRemoved(GDBusObjectManager * aMgr, BluezObject * aObj);
94+
void OnInterfacePropertiesChanged(GDBusObjectManagerClient * aMgr, BluezObject * aObj, GDBusProxy * aIface,
9595
GVariant * aChangedProps, const char * const * aInvalidatedProps);
9696

9797
GAutoPtr<GDBusConnection> mConnection;

0 commit comments

Comments
 (0)