Fix Windows crash by handling WinRT exceptions in BLE callback paths (#217)
* Fix Windows crash by handling WinRT exceptions in BLE callback paths This patch hardens the Windows plugin against unhandled native exceptions that could terminate the Flutter process during reconnect/disconnect flows. - add shared `log_and_swallow` helpers for consistent native exception logging - wrap `ConnectAsync` in defensive `try/catch` blocks, including per-service characteristic discovery - guard `BluetoothLeDeviceConnectionStatusChanged` to prevent callback exceptions from escaping - harden `CleanConnection` and `DisposeServices` when unregistering handlers/subscriptions - add explicit error handling around CCCD writes in `SetNotifiableAsync` - wrap `GattCharacteristicValueChanged` notification callback path in `try/catch` - add `ResetState()` helper scaffold for full native BLE state cleanup on Windows * Added missing log to an empty catch statement * fix(windows): report native callback failures as disconnect events - emit `OnConnectionChanged(..., false, error)` on WinRT exceptions - clean connection state before reporting disconnect * Update windows/src/universal_ble_plugin.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update windows/src/universal_ble_plugin.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update windows/src/helper/utils.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update windows/src/helper/utils.h --------- Co-authored-by: Navideck Labs <130186950+navidecklabs@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "winrt/base.h"
|
||||
#include "universal_ble_base.h"
|
||||
#include "../generated/universal_ble.g.h"
|
||||
#include "universal_ble_logger.h"
|
||||
|
||||
constexpr uint32_t TEN_SECONDS_IN_MSECS = 10000;
|
||||
|
||||
@@ -64,6 +65,14 @@ namespace universal_ble
|
||||
return create_flutter_error(UniversalBleErrorCode::kUnknownError, message);
|
||||
}
|
||||
|
||||
inline void log_and_swallow(const char* where, const std::exception& ex) {
|
||||
UniversalBleLogger::LogError(std::string(where) + ": " + ex.what());
|
||||
}
|
||||
|
||||
inline void log_and_swallow_unknown(const char* where) {
|
||||
UniversalBleLogger::LogError(std::string(where) + ": unknown native exception");
|
||||
}
|
||||
|
||||
/// To call async functions synchronously
|
||||
template <typename AsyncT>
|
||||
static auto async_get(AsyncT const &async)
|
||||
|
||||
@@ -1083,103 +1083,164 @@ void UniversalBlePlugin::RadioStateChanged(const Radio &sender,
|
||||
});
|
||||
}
|
||||
|
||||
void UniversalBlePlugin::NotifyConnectionChanged(
|
||||
const uint64_t bluetooth_address, const bool connected,
|
||||
std::optional<std::string> error) {
|
||||
ui_thread_handler_.Post(
|
||||
[bluetooth_address, connected, error = std::move(error)] {
|
||||
const std::string *error_ptr =
|
||||
error.has_value() ? &error.value() : nullptr;
|
||||
callback_channel->OnConnectionChanged(
|
||||
mac_address_to_str(bluetooth_address), connected, error_ptr,
|
||||
SuccessCallback, ErrorCallback);
|
||||
});
|
||||
}
|
||||
|
||||
void UniversalBlePlugin::NotifyConnectionException(
|
||||
const uint64_t bluetooth_address, const std::string &error_message) {
|
||||
UniversalBleLogger::LogError(error_message);
|
||||
if (bluetooth_address != 0) {
|
||||
CleanConnection(bluetooth_address);
|
||||
NotifyConnectionChanged(bluetooth_address, false, error_message);
|
||||
}
|
||||
}
|
||||
|
||||
fire_and_forget UniversalBlePlugin::ConnectAsync(uint64_t bluetooth_address) {
|
||||
BluetoothLEDevice device =
|
||||
co_await BluetoothLEDevice::FromBluetoothAddressAsync(bluetooth_address);
|
||||
if (!device) {
|
||||
UniversalBleLogger::LogError(
|
||||
"ConnectionLog: ConnectionFailed: Failed to get device");
|
||||
ui_thread_handler_.Post([bluetooth_address] {
|
||||
callback_channel->OnConnectionChanged(
|
||||
mac_address_to_str(bluetooth_address), false,
|
||||
new std::string("Failed to get device"), SuccessCallback,
|
||||
ErrorCallback);
|
||||
});
|
||||
|
||||
co_return;
|
||||
}
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Device found");
|
||||
auto services_result =
|
||||
co_await device.GetGattServicesAsync((BluetoothCacheMode::Uncached));
|
||||
auto services_result_error =
|
||||
gatt_communication_status_to_error(services_result.Status());
|
||||
if (services_result_error.has_value()) {
|
||||
UniversalBleLogger::LogError("ConnectionFailed: Failed to get services: " +
|
||||
services_result_error.value());
|
||||
ui_thread_handler_.Post([bluetooth_address, services_result_error] {
|
||||
callback_channel->OnConnectionChanged(
|
||||
mac_address_to_str(bluetooth_address), false,
|
||||
&services_result_error.value(), SuccessCallback, ErrorCallback);
|
||||
});
|
||||
co_return;
|
||||
}
|
||||
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Services discovered");
|
||||
std::unordered_map<std::string, GattServiceObject> gatt_map;
|
||||
auto gatt_services = services_result.Services();
|
||||
for (GattDeviceService &&service : gatt_services) {
|
||||
GattServiceObject gatt_service;
|
||||
gatt_service.obj = service;
|
||||
std::string service_uuid = guid_to_uuid(service.Uuid());
|
||||
auto characteristics_result =
|
||||
co_await service.GetCharacteristicsAsync(BluetoothCacheMode::Uncached);
|
||||
auto characteristics_result_error =
|
||||
gatt_communication_status_to_error(characteristics_result.Status());
|
||||
|
||||
if (characteristics_result_error.has_value()) {
|
||||
try {
|
||||
BluetoothLEDevice device =
|
||||
co_await BluetoothLEDevice::FromBluetoothAddressAsync(bluetooth_address);
|
||||
if (!device) {
|
||||
UniversalBleLogger::LogError(
|
||||
"Failed to get characteristics for service: " + service_uuid +
|
||||
", With Status: " + characteristics_result_error.value());
|
||||
continue;
|
||||
// PostConnectionUpdate(bluetoothAddress, ConnectionState::disconnected);
|
||||
// co_return;
|
||||
"ConnectionLog: ConnectionFailed: Failed to get device");
|
||||
NotifyConnectionChanged(bluetooth_address, false,
|
||||
std::string("Failed to get device"));
|
||||
co_return;
|
||||
}
|
||||
auto gatt_characteristics = characteristics_result.Characteristics();
|
||||
for (GattCharacteristic &&characteristic : gatt_characteristics) {
|
||||
GattCharacteristicObject gatt_characteristic;
|
||||
gatt_characteristic.obj = characteristic;
|
||||
gatt_characteristic.subscription_token = std::nullopt;
|
||||
std::string characteristic_uuid = guid_to_uuid(characteristic.Uuid());
|
||||
gatt_service.characteristics.insert_or_assign(
|
||||
characteristic_uuid, std::move(gatt_characteristic));
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Device found");
|
||||
auto services_result =
|
||||
co_await device.GetGattServicesAsync((BluetoothCacheMode::Uncached));
|
||||
auto services_result_error =
|
||||
gatt_communication_status_to_error(services_result.Status());
|
||||
if (services_result_error.has_value()) {
|
||||
UniversalBleLogger::LogError("ConnectionFailed: Failed to get services: " +
|
||||
services_result_error.value());
|
||||
NotifyConnectionChanged(bluetooth_address, false,
|
||||
services_result_error.value());
|
||||
co_return;
|
||||
}
|
||||
gatt_map.insert_or_assign(service_uuid, std::move(gatt_service));
|
||||
}
|
||||
|
||||
event_token connection_status_changed_token = device.ConnectionStatusChanged(
|
||||
{this, &UniversalBlePlugin::BluetoothLeDeviceConnectionStatusChanged});
|
||||
auto device_agent = std::make_unique<BluetoothDeviceAgent>(
|
||||
device, connection_status_changed_token, gatt_map);
|
||||
auto pair = std::make_pair(bluetooth_address, std::move(device_agent));
|
||||
connected_devices_.insert(std::move(pair));
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Connected");
|
||||
ui_thread_handler_.Post([bluetooth_address] {
|
||||
callback_channel->OnConnectionChanged(mac_address_to_str(bluetooth_address),
|
||||
true, nullptr, SuccessCallback,
|
||||
ErrorCallback);
|
||||
});
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Services discovered");
|
||||
std::unordered_map<std::string, GattServiceObject> gatt_map;
|
||||
auto gatt_services = services_result.Services();
|
||||
for (GattDeviceService &&service : gatt_services) {
|
||||
try {
|
||||
GattServiceObject gatt_service;
|
||||
gatt_service.obj = service;
|
||||
std::string service_uuid = guid_to_uuid(service.Uuid());
|
||||
auto characteristics_result = co_await service.GetCharacteristicsAsync(
|
||||
BluetoothCacheMode::Uncached);
|
||||
auto characteristics_result_error =
|
||||
gatt_communication_status_to_error(characteristics_result.Status());
|
||||
|
||||
if (characteristics_result_error.has_value()) {
|
||||
UniversalBleLogger::LogError(
|
||||
"Failed to get characteristics for service: " + service_uuid +
|
||||
", With Status: " + characteristics_result_error.value());
|
||||
continue;
|
||||
}
|
||||
auto gatt_characteristics = characteristics_result.Characteristics();
|
||||
for (GattCharacteristic &&characteristic : gatt_characteristics) {
|
||||
GattCharacteristicObject gatt_characteristic;
|
||||
gatt_characteristic.obj = characteristic;
|
||||
gatt_characteristic.subscription_token = std::nullopt;
|
||||
std::string characteristic_uuid = guid_to_uuid(characteristic.Uuid());
|
||||
gatt_service.characteristics.insert_or_assign(
|
||||
characteristic_uuid, std::move(gatt_characteristic));
|
||||
}
|
||||
gatt_map.insert_or_assign(service_uuid, std::move(gatt_service));
|
||||
} catch (const hresult_error &err) {
|
||||
UniversalBleLogger::LogError(
|
||||
"ConnectAsync service loop hresult_error hr=" +
|
||||
std::to_string(err.code()) + " msg=" + to_string(err.message()));
|
||||
} catch (const std::exception &ex) {
|
||||
UniversalBleLogger::LogError(
|
||||
std::string("ConnectAsync service loop exception: ") + ex.what());
|
||||
} catch (...) {
|
||||
UniversalBleLogger::LogError("ConnectAsync service loop unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
event_token connection_status_changed_token =
|
||||
device.ConnectionStatusChanged(
|
||||
{this, &UniversalBlePlugin::BluetoothLeDeviceConnectionStatusChanged});
|
||||
auto device_agent = std::make_unique<BluetoothDeviceAgent>(
|
||||
device, connection_status_changed_token, gatt_map);
|
||||
auto pair = std::make_pair(bluetooth_address, std::move(device_agent));
|
||||
connected_devices_.insert(std::move(pair));
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Connected");
|
||||
NotifyConnectionChanged(bluetooth_address, true, std::nullopt);
|
||||
} catch (const hresult_error &err) {
|
||||
NotifyConnectionException(
|
||||
bluetooth_address,
|
||||
"ConnectAsync hresult_error hr=" + std::to_string(err.code()) +
|
||||
" msg=" + to_string(err.message()));
|
||||
} catch (const std::exception &ex) {
|
||||
NotifyConnectionException(
|
||||
bluetooth_address,
|
||||
std::string("ConnectAsync std::exception: ") + ex.what());
|
||||
} catch (...) {
|
||||
NotifyConnectionException(bluetooth_address,
|
||||
"ConnectAsync unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
void UniversalBlePlugin::BluetoothLeDeviceConnectionStatusChanged(
|
||||
const BluetoothLEDevice &sender, const IInspectable &) {
|
||||
if (sender.ConnectionStatus() == BluetoothConnectionStatus::Disconnected) {
|
||||
CleanConnection(sender.BluetoothAddress());
|
||||
auto bluetooth_address = sender.BluetoothAddress();
|
||||
ui_thread_handler_.Post([bluetooth_address] {
|
||||
callback_channel->OnConnectionChanged(
|
||||
mac_address_to_str(bluetooth_address), false, nullptr,
|
||||
SuccessCallback, ErrorCallback);
|
||||
});
|
||||
uint64_t bluetooth_address = 0;
|
||||
try {
|
||||
bluetooth_address = sender.BluetoothAddress();
|
||||
if (sender.ConnectionStatus() == BluetoothConnectionStatus::Disconnected) {
|
||||
CleanConnection(bluetooth_address);
|
||||
NotifyConnectionChanged(bluetooth_address, false, std::nullopt);
|
||||
}
|
||||
} catch (const hresult_error &err) {
|
||||
NotifyConnectionException(
|
||||
bluetooth_address,
|
||||
"ConnectionStatusChanged hresult_error hr=" +
|
||||
std::to_string(err.code()) + " msg=" + to_string(err.message()));
|
||||
} catch (const std::exception &ex) {
|
||||
NotifyConnectionException(
|
||||
bluetooth_address,
|
||||
std::string("ConnectionStatusChanged std::exception: ") + ex.what());
|
||||
} catch (...) {
|
||||
NotifyConnectionException(bluetooth_address,
|
||||
"ConnectionStatusChanged unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
void UniversalBlePlugin::CleanConnection(const uint64_t bluetooth_address) {
|
||||
const auto node = connected_devices_.extract(bluetooth_address);
|
||||
if (!node.empty()) {
|
||||
const auto device_agent = std::move(node.mapped());
|
||||
device_agent->device.ConnectionStatusChanged(
|
||||
device_agent->connection_status_changed_token);
|
||||
DisposeServices(device_agent);
|
||||
try {
|
||||
const auto node = connected_devices_.extract(bluetooth_address);
|
||||
if (!node.empty()) {
|
||||
const auto device_agent = std::move(node.mapped());
|
||||
try {
|
||||
device_agent->device.ConnectionStatusChanged(
|
||||
device_agent->connection_status_changed_token);
|
||||
} catch (const hresult_error &err) {
|
||||
UniversalBleLogger::LogError("CleanConnection hresult_error: " +
|
||||
to_string(err.message()));
|
||||
} catch (...) {
|
||||
UniversalBleLogger::LogError("CleanConnection: failed to remove connection status handler");
|
||||
}
|
||||
DisposeServices(device_agent);
|
||||
}
|
||||
} catch (const hresult_error &err) {
|
||||
UniversalBleLogger::LogError("CleanConnection outer hresult_error: " +
|
||||
to_string(err.message()));
|
||||
} catch (const std::exception &ex) {
|
||||
log_and_swallow("CleanConnection std::exception", ex);
|
||||
} catch (...) {
|
||||
log_and_swallow_unknown("CleanConnection");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1188,8 +1249,17 @@ void UniversalBlePlugin::DisposeServices(
|
||||
for (auto &[service_id, service] : device_agent->gatt_map) {
|
||||
for (auto &[char_id, characteristic] : service.characteristics) {
|
||||
if (characteristic.subscription_token.has_value()) {
|
||||
characteristic.obj.ValueChanged(
|
||||
characteristic.subscription_token.value());
|
||||
try {
|
||||
characteristic.obj.ValueChanged(
|
||||
characteristic.subscription_token.value());
|
||||
} catch (const hresult_error &err) {
|
||||
UniversalBleLogger::LogError("DisposeServices hresult_error unsub " +
|
||||
to_string(err.message()));
|
||||
} catch (const std::exception &ex) {
|
||||
log_and_swallow("DisposeServices unsub std::exception", ex);
|
||||
} catch (...) {
|
||||
log_and_swallow_unknown("DisposeServices unsub");
|
||||
}
|
||||
characteristic.subscription_token = std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -1197,6 +1267,55 @@ void UniversalBlePlugin::DisposeServices(
|
||||
device_agent->gatt_map.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief In some cases, it helps to reset the whole Bluetooth state to get
|
||||
* rid of any dangling connections, before scanning or connecting.
|
||||
*/
|
||||
void UniversalBlePlugin::ResetState() {
|
||||
try {
|
||||
// Stop and detach advertisement watcher
|
||||
if (bluetooth_le_watcher_ != nullptr) {
|
||||
try {
|
||||
bluetooth_le_watcher_.Stop();
|
||||
} catch (...) {
|
||||
UniversalBleLogger::LogWarning("ResetState: failed to stop LE watcher");
|
||||
}
|
||||
try {
|
||||
bluetooth_le_watcher_.Received(bluetooth_le_watcher_received_token_);
|
||||
} catch (...) {
|
||||
log_and_swallow_unknown("ResetState: failed to unregister LE watcher received handler");
|
||||
}
|
||||
bluetooth_le_watcher_ = nullptr;
|
||||
}
|
||||
|
||||
// Dispose device watcher and caches
|
||||
DisposeDeviceWatcher();
|
||||
scan_results_.clear();
|
||||
device_watcher_devices_.clear();
|
||||
device_watcher_id_to_mac_.clear();
|
||||
|
||||
// Close all connected devices and clear map
|
||||
std::vector<uint64_t> addrs;
|
||||
addrs.reserve(connected_devices_.size());
|
||||
for (const auto &p : connected_devices_) {
|
||||
addrs.push_back(p.first);
|
||||
}
|
||||
for (const auto addr : addrs) {
|
||||
CleanConnection(addr);
|
||||
}
|
||||
connected_devices_.clear();
|
||||
|
||||
UniversalBleLogger::LogInfo("ResetState: completed clean slate");
|
||||
} catch (const hresult_error &err) {
|
||||
UniversalBleLogger::LogError("ResetState hresult_error: " +
|
||||
to_string(err.message()));
|
||||
} catch (const std::exception &ex) {
|
||||
log_and_swallow("ResetState std::exception", ex);
|
||||
} catch (...) {
|
||||
log_and_swallow_unknown("ResetState");
|
||||
}
|
||||
}
|
||||
|
||||
fire_and_forget UniversalBlePlugin::GetSystemDevicesAsync(
|
||||
std::vector<std::string> with_services,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) {
|
||||
@@ -1385,10 +1504,23 @@ fire_and_forget UniversalBlePlugin::SetNotifiableAsync(
|
||||
const auto uuid = to_uuidstr(gatt_characteristic.Uuid());
|
||||
|
||||
// Write to the descriptor.
|
||||
const auto status =
|
||||
co_await gatt_characteristic
|
||||
.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
descriptor_value);
|
||||
GattCommunicationStatus status{};
|
||||
try {
|
||||
status =
|
||||
co_await gatt_characteristic
|
||||
.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
descriptor_value);
|
||||
} catch (const hresult_error &err) {
|
||||
UniversalBleLogger::LogError(
|
||||
"SET_NOTIFY exception hr=" + std::to_string(err.code()) +
|
||||
" msg=" + to_string(err.message()) + " device=" + device_id +
|
||||
" service=" + service + " char=" + characteristic);
|
||||
result(create_flutter_error(
|
||||
UniversalBleErrorCode::kFailed,
|
||||
"SetNotifiable exception: " + to_string(err.message()),
|
||||
"hr=" + std::to_string(err.code())));
|
||||
co_return;
|
||||
}
|
||||
if (status != GattCommunicationStatus::Success) {
|
||||
UniversalBleLogger::LogError("SET_NOTIFY_FAILED <- " + device_id + " " +
|
||||
service + " " + characteristic + " status=" +
|
||||
@@ -1425,6 +1557,14 @@ fire_and_forget UniversalBlePlugin::SetNotifiableAsync(
|
||||
result(std::nullopt);
|
||||
} catch (const FlutterError &err) {
|
||||
result(err);
|
||||
} catch (const hresult_error &err) {
|
||||
UniversalBleLogger::LogError(
|
||||
"SetNotifiableLog hresult_error: hr=" + std::to_string(err.code()) +
|
||||
" msg=" + to_string(err.message()) + " device=" + device_id +
|
||||
" service=" + service + " char=" + characteristic);
|
||||
result(create_flutter_error(UniversalBleErrorCode::kFailed,
|
||||
to_string(err.message()),
|
||||
std::to_string(err.code())));
|
||||
} catch (...) {
|
||||
UniversalBleLogger::LogError("SetNotifiableLog: Unknown error");
|
||||
result(create_flutter_unknown_error());
|
||||
@@ -1433,22 +1573,36 @@ fire_and_forget UniversalBlePlugin::SetNotifiableAsync(
|
||||
|
||||
void UniversalBlePlugin::GattCharacteristicValueChanged(
|
||||
const GattCharacteristic &sender, const GattValueChangedEventArgs &args) {
|
||||
auto uuid = to_uuidstr(sender.Uuid());
|
||||
auto bytes = to_bytevc(args.CharacteristicValue());
|
||||
auto device_id =
|
||||
mac_address_to_str(sender.Service().Device().BluetoothAddress());
|
||||
uint64_t bluetooth_address = 0;
|
||||
try {
|
||||
bluetooth_address = sender.Service().Device().BluetoothAddress();
|
||||
const auto uuid = to_uuidstr(sender.Uuid());
|
||||
const auto bytes = to_bytevc(args.CharacteristicValue());
|
||||
const auto device_id = mac_address_to_str(bluetooth_address);
|
||||
|
||||
UniversalBleLogger::LogVerboseWithTimestamp(
|
||||
"NOTIFY <- " + device_id + " " + uuid +
|
||||
" len=" + std::to_string(bytes.size()));
|
||||
UniversalBleLogger::LogVerboseWithTimestamp(
|
||||
"NOTIFY <- " + device_id + " " + uuid +
|
||||
" len=" + std::to_string(bytes.size()));
|
||||
|
||||
auto timestamp = GetCurrentTimestampMillis();
|
||||
ui_thread_handler_.Post([sender, bytes, timestamp] {
|
||||
auto uuid = to_uuidstr(sender.Uuid());
|
||||
callback_channel->OnValueChanged(
|
||||
mac_address_to_str(sender.Service().Device().BluetoothAddress()), uuid,
|
||||
bytes, ×tamp, SuccessCallback, ErrorCallback);
|
||||
});
|
||||
const auto timestamp = GetCurrentTimestampMillis();
|
||||
ui_thread_handler_.Post([device_id, uuid, bytes, timestamp] {
|
||||
callback_channel->OnValueChanged(
|
||||
device_id, uuid, bytes, ×tamp, SuccessCallback, ErrorCallback);
|
||||
});
|
||||
} catch (const hresult_error &err) {
|
||||
NotifyConnectionException(
|
||||
bluetooth_address,
|
||||
"GattCharacteristicValueChanged hresult_error hr=" +
|
||||
std::to_string(err.code()) + " msg=" + to_string(err.message()));
|
||||
} catch (const std::exception &ex) {
|
||||
NotifyConnectionException(
|
||||
bluetooth_address,
|
||||
std::string("GattCharacteristicValueChanged std::exception: ") +
|
||||
ex.what());
|
||||
} catch (...) {
|
||||
NotifyConnectionException(bluetooth_address,
|
||||
"GattCharacteristicValueChanged unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace universal_ble
|
||||
} // namespace universal_ble
|
||||
|
||||
@@ -156,7 +156,12 @@ private:
|
||||
void OnDeviceInfoReceived(const DeviceInformation &device_info);
|
||||
void BluetoothLeDeviceConnectionStatusChanged(const BluetoothLEDevice &sender,
|
||||
const IInspectable &args);
|
||||
void NotifyConnectionChanged(uint64_t bluetooth_address, bool connected,
|
||||
std::optional<std::string> error = std::nullopt);
|
||||
void NotifyConnectionException(uint64_t bluetooth_address,
|
||||
const std::string &error_message);
|
||||
void CleanConnection(uint64_t bluetooth_address);
|
||||
void ResetState();
|
||||
void
|
||||
DisposeServices(const std::unique_ptr<BluetoothDeviceAgent> &device_agent);
|
||||
|
||||
@@ -217,4 +222,4 @@ private:
|
||||
|
||||
} // namespace universal_ble
|
||||
|
||||
#endif // FLUTTER_PLUGIN_UNIVERSAL_BLE_PLUGIN_H_
|
||||
#endif // FLUTTER_PLUGIN_UNIVERSAL_BLE_PLUGIN_H_
|
||||
|
||||
Reference in New Issue
Block a user