Files
universal_ble/windows/src/universal_ble_plugin.h
T
Raphael Smith f4b197f8ae Add requestConnectionPriority support (#221)
* feat: add requestConnectionPriority

- Add BleConnectionPriority enum (balanced, highPerformance, lowPower)
- Add BleCapabilities.supportsConnectionPriorityApi flag
- Implement on Android via BluetoothGatt.requestConnectionPriority()
- Throw notSupported on iOS, macOS, Windows, Linux, and Web
- Update pigeon definition and regenerate platform channel files
- Bump version 1.2.0 -> 1.3.0

* fix: improve requestConnectionPriority implementation

- Remove redundant BleCapabilities guard in Dart layer (native handles it)
- Add BleConnectionPriority Kotlin enum mirroring Dart enum
- Map to explicit Android SDK constants via when expression
- Add firstOrNull with ILLEGAL_ARGUMENT for unknown priority values
- Add catch (e: Exception) block for broader error handling
- Fix enum doc comment (throws belongs on method, not enum)
- Remove stale capability check guidance from docs and README

---------

Co-authored-by: aqeel-bmec-co <aqeel@bmec.co>
Co-authored-by: aqeel-bmec-co <85945830+aqeel-bmec-co@users.noreply.github.com>
2026-04-01 21:15:39 +02:00

229 lines
9.8 KiB
C++

#ifndef FLUTTER_PLUGIN_UNIVERSAL_BLE_PLUGIN_H_
#define FLUTTER_PLUGIN_UNIVERSAL_BLE_PLUGIN_H_
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <windows.h>
#include <winrt/Windows.Devices.Bluetooth.Advertisement.h>
#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
#include <winrt/Windows.Devices.Bluetooth.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Devices.Radios.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/base.h>
#include "generated/universal_ble.g.h"
#include "helper/universal_ble_base.h"
#include "helper/universal_enum.h"
#include "helper/utils.h"
#include "ui_thread_handler.hpp"
#include "universal_ble_thread_safe.h"
#include <memory>
namespace universal_ble {
struct GattCharacteristicObject {
GattCharacteristic obj = nullptr;
std::optional<event_token> subscription_token;
};
struct GattServiceObject {
GattDeviceService obj = nullptr;
std::unordered_map<std::string, GattCharacteristicObject> characteristics;
};
struct BluetoothDeviceAgent {
BluetoothLEDevice device;
event_token connection_status_changed_token;
std::unordered_map<std::string, GattServiceObject> gatt_map;
BluetoothDeviceAgent(
const BluetoothLEDevice &device,
const event_token connection_status_changed_token,
const std::unordered_map<std::string, GattServiceObject> &gatt_map)
: device(device),
connection_status_changed_token(connection_status_changed_token),
gatt_map(gatt_map) {}
~BluetoothDeviceAgent() { device = nullptr; }
GattCharacteristicObject &
FetchCharacteristic(const std::string &service_uuid,
const std::string &characteristic_uuid) {
if (gatt_map.count(service_uuid) == 0) {
throw create_flutter_error(UniversalBleErrorCode::kServiceNotFound,
"Service not found");
}
if (gatt_map[service_uuid].characteristics.count(characteristic_uuid) ==
0) {
throw create_flutter_error(UniversalBleErrorCode::kCharacteristicNotFound,
"Characteristic not found");
}
return gatt_map[service_uuid].characteristics.at(characteristic_uuid);
}
};
class UniversalBlePlugin : public flutter::Plugin,
public UniversalBlePlatformChannel {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
UniversalBlePlugin(flutter::PluginRegistrarWindows *registrar);
~UniversalBlePlugin();
// Disallow copy and assign.
UniversalBlePlugin(const UniversalBlePlugin &) = delete;
UniversalBlePlugin &operator=(const UniversalBlePlugin &) = delete;
private:
static void SuccessCallback() {}
static void ErrorCallback(const FlutterError &error) {
// Ignore ChannelConnection Error, This might occur because of HotReload
if (error.code() != "channel-error") {
std::cout << "ErrorCode: " << error.code()
<< " Message: " << error.message() << std::endl;
}
}
static int64_t GetCurrentTimestampMillis() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
flutter::PluginRegistrarWindows *registrar_;
bool initialized_ = false;
UniversalBleUiThreadHandler ui_thread_handler_;
Radio bluetooth_radio_{nullptr};
RadioState old_radio_state_ = RadioState::Unknown;
BluetoothLEAdvertisementWatcher bluetooth_le_watcher_{nullptr};
DeviceWatcher device_watcher_{nullptr};
std::unordered_map<uint64_t, std::unique_ptr<BluetoothDeviceAgent>>
connected_devices_{};
ThreadSafeMap<std::string, DeviceInformation> device_watcher_devices_{};
ThreadSafeMap<std::string, UniversalBleScanResult> scan_results_{};
// Maps DeviceInformation.Id() -> MAC address string used as key in
// device_watcher_devices_
ThreadSafeMap<std::string, std::string> device_watcher_id_to_mac_{};
event_token bluetooth_le_watcher_received_token_;
event_token device_watcher_added_token_;
event_token device_watcher_updated_token_;
event_token device_watcher_removed_token_;
event_token device_watcher_enumeration_completed_token_;
event_token device_watcher_stopped_token_;
event_revoker<IRadio> radio_state_changed_revoker_;
fire_and_forget InitializeAsync();
fire_and_forget ConnectAsync(uint64_t bluetooth_address);
fire_and_forget SetNotifiableAsync(
const std::string &device_id, const std::string &service,
const std::string &characteristic, int64_t ble_input_property,
std::function<void(std::optional<FlutterError> reply)> result);
fire_and_forget PairAsync(const std::string &device_id,
std::function<void(ErrorOr<bool> reply)> result);
fire_and_forget
CustomPairAsync(const std::string &device_id,
std::function<void(ErrorOr<bool> reply)> result);
static fire_and_forget GetSystemDevicesAsync(
std::vector<std::string> with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
static fire_and_forget
IsPairedAsync(const std::string &device_id,
std::function<void(ErrorOr<bool> reply)> result);
fire_and_forget DiscoverServicesAsync(
const std::string &device_id, bool with_descriptors,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
void
PairingRequestedHandler(DeviceInformationCustomPairing sender,
const DevicePairingRequestedEventArgs &event_args);
void RadioStateChanged(const Radio &sender, const IInspectable &);
void SetupDeviceWatcher();
void DisposeDeviceWatcher();
void PushUniversalScanResult(UniversalBleScanResult scan_result,
bool is_connectable);
static std::string ExpandServiceUuid(const std::vector<uint8_t>& uuid_bytes,
uint8_t uuid_type);
void BluetoothLeWatcherReceived(
const BluetoothLEAdvertisementWatcher &sender,
const BluetoothLEAdvertisementReceivedEventArgs &args);
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);
void GattCharacteristicValueChanged(const GattCharacteristic &sender,
const GattValueChangedEventArgs &args);
// UniversalBlePlatformChannel implementation.
void GetBluetoothAvailabilityState(
std::function<void(ErrorOr<int64_t> reply)> result) override;
void
EnableBluetooth(std::function<void(ErrorOr<bool> reply)> result) override;
void
DisableBluetooth(std::function<void(ErrorOr<bool> reply)> result) override;
ErrorOr<int64_t> GetConnectionState(const std::string &device_id) override;
std::optional<FlutterError>
SetLogLevel(const UniversalBleLogLevel &log_level) override;
std::optional<FlutterError>
StartScan(const UniversalScanFilter *filter, const UniversalScanConfig *config) override;
std::optional<FlutterError> StopScan() override;
ErrorOr<bool> IsScanning() override;
std::optional<FlutterError> Connect(const std::string &device_id, const bool *auto_connect) override;
std::optional<FlutterError> Disconnect(const std::string &device_id) override;
ErrorOr<bool> HasPermissions(bool with_android_fine_location) override;
void RequestPermissions(
bool with_android_fine_location,
std::function<void(std::optional<FlutterError> reply)> result) override;
void
DiscoverServices(const std::string &device_id, bool with_descriptors,
std::function<void(ErrorOr<flutter::EncodableList> reply)>
result) override;
void SetNotifiable(
const std::string &device_id, const std::string &service,
const std::string &characteristic, int64_t ble_input_property,
std::function<void(std::optional<FlutterError> reply)> result) override;
void ReadValue(
const std::string &device_id, const std::string &service,
const std::string &characteristic,
std::function<void(ErrorOr<std::vector<uint8_t>> reply)> result) override;
void WriteValue(
const std::string &device_id, const std::string &service,
const std::string &characteristic, const std::vector<uint8_t> &value,
int64_t ble_output_property,
std::function<void(std::optional<FlutterError> reply)> result) override;
void RequestMtu(const std::string &device_id, int64_t expected_mtu,
std::function<void(ErrorOr<int64_t> reply)> result) override;
void RequestConnectionPriority(
const std::string &device_id, int64_t priority,
std::function<void(std::optional<FlutterError> reply)> result) override;
void ReadRssi(const std::string &device_id,
std::function<void(ErrorOr<int64_t> reply)> result) override;
void IsPaired(const std::string &device_id,
std::function<void(ErrorOr<bool> reply)> result) override;
void Pair(const std::string &device_id,
std::function<void(ErrorOr<bool> reply)> result) override;
std::optional<FlutterError> UnPair(const std::string &device_id) override;
void
GetSystemDevices(const flutter::EncodableList &with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)>
result) override;
};
} // namespace universal_ble
#endif // FLUTTER_PLUGIN_UNIVERSAL_BLE_PLUGIN_H_