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 Windows crash on start or stop scan #143

Merged
merged 4 commits into from
Feb 25, 2025
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 0.17.0
* Fix Windows crash when calling pair APIs with an unknown deviceId
* Fix Windows crash when calling startScan again after some time
* Fix Windows warning `Unable to establish connection on channel` when hot restart

## 0.16.0
Expand Down
78 changes: 59 additions & 19 deletions windows/src/universal_ble_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,20 +130,29 @@ namespace universal_ble

std::optional<FlutterError> UniversalBlePlugin::StartScan(const UniversalScanFilter *filter)
{
if (bluetoothRadio && bluetoothRadio.State() == RadioState::On)

if (!bluetoothRadio || bluetoothRadio.State() != RadioState::On)
{
return FlutterError("Bluetooth is not available");
}

try
{
setupDeviceWatcher();
scanResults.clear();
DeviceWatcherStatus status = deviceWatcher.Status();
if (status != DeviceWatcherStatus::Started)
DeviceWatcherStatus deviceWatcherStatus = deviceWatcher.Status();
// std::cout << "DeviceWatcherState: " << DeviceWatcherStatusToString(deviceWatcherStatus) << std::endl;
// DeviceWatcher can only start if its in Created, Stopped, or Aborted state
if (deviceWatcherStatus == DeviceWatcherStatus::Created || deviceWatcherStatus == DeviceWatcherStatus::Stopped || deviceWatcherStatus == DeviceWatcherStatus::Aborted)
{
deviceWatcher.Start();
}
else
else if (deviceWatcherStatus == DeviceWatcherStatus::Stopping)
{
return FlutterError("Already scanning");
return FlutterError("StoppingScan in progress");
}

// Setup LeWatcher and apply filters
if (!bluetoothLEWatcher)
{
bluetoothLEWatcher = BluetoothLEAdvertisementWatcher();
Expand Down Expand Up @@ -176,29 +185,41 @@ namespace universal_ble
bluetoothLEWatcherReceivedToken = bluetoothLEWatcher.Received({this, &UniversalBlePlugin::BluetoothLEWatcher_Received});
}
bluetoothLEWatcher.Start();

return std::nullopt;
}
else
catch (...)
{
return FlutterError("Bluetooth is not available");
std::cout << "Unknown error StartScan" << std::endl;
return FlutterError("Unknown error");
}
};

std::optional<FlutterError> UniversalBlePlugin::StopScan()
{

if (bluetoothRadio && bluetoothRadio.State() == RadioState::On)
{
if (bluetoothLEWatcher)
try
{
bluetoothLEWatcher.Stop();
bluetoothLEWatcher.Received(bluetoothLEWatcherReceivedToken);
if (bluetoothLEWatcher)
{
bluetoothLEWatcher.Received(bluetoothLEWatcherReceivedToken);
bluetoothLEWatcher.Stop();
}
bluetoothLEWatcher = nullptr;
disposeDeviceWatcher();
scanResults.clear();
return std::nullopt;
}
catch (const winrt::hresult_error &err)
{
int errorCode = err.code();
std::cout << "StopScanLog: " << winrt::to_string(err.message()) << " ErrorCode: " << std::to_string(errorCode) << std::endl;
return FlutterError(std::to_string(errorCode), winrt::to_string(err.message()));
}
catch (...)
{
return FlutterError("Failed to Stop");
}
bluetoothLEWatcher = nullptr;
disposeDeviceWatcher();
scanResults.clear();
return std::nullopt;
}
else
{
Expand Down Expand Up @@ -751,19 +772,38 @@ namespace universal_ble
deviceWatcherDevices.erase(deviceId);
// On Device Removed
});

deviceWatcherEnumerationCompletedToken = deviceWatcher.EnumerationCompleted([this](DeviceWatcher sender, IInspectable args)
{
std::cout << "DeviceWatcherEvent: EnumerationCompleted" << std::endl;
disposeDeviceWatcher();
// EnumerationCompleted
});

deviceWatcherStoppedToken = deviceWatcher.Stopped([this](DeviceWatcher sender, IInspectable args)
{
// std::cout << "DeviceWatcherEvent: Stopped" << std::endl;
// disposeDeviceWatcher();
// DeviceWatcher Stopped
});
}

void UniversalBlePlugin::disposeDeviceWatcher()
{
if (deviceWatcher != nullptr)
{
if (deviceWatcher.Status() == DeviceWatcherStatus::Started)
deviceWatcher.Stop();
deviceWatcher.Added(deviceWatcherAddedToken);
deviceWatcher.Updated(deviceWatcherUpdatedToken);
deviceWatcher.Removed(deviceWatcherRemovedToken);
deviceWatcher.EnumerationCompleted(deviceWatcherEnumerationCompletedToken);
deviceWatcher.Stopped(deviceWatcherStoppedToken);
auto status = deviceWatcher.Status();
// std::cout << "DisposingDeviceWatcher, CurrentState: " << DeviceWatcherStatusToString(status) << std::endl;
if (status == DeviceWatcherStatus::Started)
{
deviceWatcher.Stop();
}
deviceWatcher = nullptr;
// Dispose tokens
deviceWatcherDevices.clear();
}
}
Expand Down
22 changes: 22 additions & 0 deletions windows/src/universal_ble_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ namespace universal_ble
winrt::event_token deviceWatcherAddedToken;
winrt::event_token deviceWatcherUpdatedToken;
winrt::event_token deviceWatcherRemovedToken;
winrt::event_token deviceWatcherEnumerationCompletedToken;
winrt::event_token deviceWatcherStoppedToken;

winrt::fire_and_forget InitializeAsync();
void Radio_StateChanged(Radio sender, IInspectable args);
Expand Down Expand Up @@ -176,6 +178,26 @@ namespace universal_ble
void GetSystemDevices(
const flutter::EncodableList &with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);

std::string DeviceWatcherStatusToString(DeviceWatcherStatus result)
{
switch (result)
{
case DeviceWatcherStatus::Created:
return "Created";
case DeviceWatcherStatus::Aborted:
return "Aborted";
case DeviceWatcherStatus::EnumerationCompleted:
return "EnumerationCompleted";
case DeviceWatcherStatus::Started:
return "Started";
case DeviceWatcherStatus::Stopped:
return "Stopped";
case DeviceWatcherStatus::Stopping:
return "Stopping";
}
return "";
}
};

} // namespace universal_ble
Expand Down