From 186becc53de1e876116dce2e2e8a0db050cfb1f7 Mon Sep 17 00:00:00 2001 From: mos Date: Mon, 15 May 2023 09:46:00 +0200 Subject: [PATCH 001/193] Basic support for bonding (missing CreateBond() and RemoveBond()) --- Source/Plugin.BLE/Android/Adapter.cs | 29 ++++++++++++------- Source/Plugin.BLE/Apple/Adapter.cs | 5 ++++ Source/Plugin.BLE/Shared/AdapterBase.cs | 24 +++++++++++++++ .../Plugin.BLE/Shared/Contracts/IAdapter.cs | 10 +++++++ Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs | 5 ++++ Source/Plugin.BLE/Windows/Adapter.cs | 5 ++++ 6 files changed, 68 insertions(+), 10 deletions(-) diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index 8007cc58..61b30275 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -3,12 +3,15 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Android.App; using Android.Bluetooth; using Android.Bluetooth.LE; +using Android.Content; using Android.OS; using Java.Util; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; +using Plugin.BLE.BroadcastReceivers; using Plugin.BLE.Extensions; using Object = Java.Lang.Object; using Trace = Plugin.BLE.Abstractions.Trace; @@ -27,17 +30,16 @@ public Adapter(BluetoothManager bluetoothManager) _bluetoothManager = bluetoothManager; _bluetoothAdapter = bluetoothManager.Adapter; + //bonding + var bondStatusBroadcastReceiver = new BondStatusBroadcastReceiver(); + Application.Context.RegisterReceiver(bondStatusBroadcastReceiver, + new IntentFilter(BluetoothDevice.ActionBondStateChanged)); - // TODO: bonding - //var bondStatusBroadcastReceiver = new BondStatusBroadcastReceiver(); - //Application.Context.RegisterReceiver(bondStatusBroadcastReceiver, - // new IntentFilter(BluetoothDevice.ActionBondStateChanged)); - - ////forward events from broadcast receiver - //bondStatusBroadcastReceiver.BondStateChanged += (s, args) => - //{ - // //DeviceBondStateChanged(this, args); - //}; + //forward events from broadcast receiver + bondStatusBroadcastReceiver.BondStateChanged += (s, args) => + { + HandleDeviceBondStateChanged(args); + }; if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop) { @@ -274,6 +276,13 @@ public override IReadOnlyList GetKnownDevicesByIds(Guid[] ids) return devices.Where(item => ids.Contains(item.Id)).ToList(); } + protected override IReadOnlyList GetBondedDevices() + { + var bondedDevices = _bluetoothAdapter.BondedDevices.Where(d => d.Type == BluetoothDeviceType.Le || d.Type == BluetoothDeviceType.Dual); + + return bondedDevices.Select(d => new Device(this, d, null, 0)).Cast().ToList(); + } + public override bool supportsExtendedAdvertising() { #if NET6_0_OR_GREATER diff --git a/Source/Plugin.BLE/Apple/Adapter.cs b/Source/Plugin.BLE/Apple/Adapter.cs index 98b18a5f..f9e1302d 100644 --- a/Source/Plugin.BLE/Apple/Adapter.cs +++ b/Source/Plugin.BLE/Apple/Adapter.cs @@ -292,6 +292,11 @@ public override IReadOnlyList GetKnownDevicesByIds(Guid[] ids) return nativeDevices.Select(d => new Device(this, d, _bleCentralManagerDelegate)).Cast().ToList(); } + protected override IReadOnlyList GetBondedDevices() + { + return null; // not supported + } + #if NET6_0_OR_GREATER || MACCATALYST private async Task WaitForState(CBManagerState state, CancellationToken cancellationToken, bool configureAwait = false) #else diff --git a/Source/Plugin.BLE/Shared/AdapterBase.cs b/Source/Plugin.BLE/Shared/AdapterBase.cs index 8ea957af..12d03db8 100644 --- a/Source/Plugin.BLE/Shared/AdapterBase.cs +++ b/Source/Plugin.BLE/Shared/AdapterBase.cs @@ -46,6 +46,10 @@ public abstract class AdapterBase : IAdapter /// public event EventHandler DeviceConnectionError; /// + /// Occurs when the bonding state of a device changed. + /// + public event EventHandler DeviceBondStateChanged; + /// /// Occurs when the scan has been stopped due the timeout after ms. /// public event EventHandler ScanTimeoutElapsed; @@ -97,6 +101,11 @@ public bool IsScanning /// public IReadOnlyList ConnectedDevices => ConnectedDeviceRegistry.Values.ToList(); + /// + /// List of all bonded devices (or null if the device does not support this information). + /// + public IReadOnlyList BondedDevices => GetBondedDevices(); + /// /// Starts scanning for BLE devices that fulfill the . /// DeviceDiscovered will only be called, if returns true for the discovered device. @@ -327,6 +336,15 @@ public void HandleConnectionFail(IDevice device, string errorMessage) }); } + /// + /// Handle bond state changed information. + /// + /// + protected void HandleDeviceBondStateChanged(DeviceBondStateChangedEventArgs args) + { + DeviceBondStateChanged?.Invoke(this, args); + } + /// /// Native implementation of StartScanningForDevicesAsync. /// @@ -356,6 +374,12 @@ public void HandleConnectionFail(IDevice device, string errorMessage) /// Returns a list of paired BLE devices for the given UUIDs. /// public abstract IReadOnlyList GetKnownDevicesByIds(Guid[] ids); + /// + /// Returns all BLE device bonded to the system. + /// + /// + protected abstract IReadOnlyList GetBondedDevices(); + /// /// Indicates whether extended advertising (BLE5) is supported. diff --git a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs index 492f39f8..42d2a77d 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs @@ -38,6 +38,10 @@ public interface IAdapter /// event EventHandler DeviceConnectionError; /// + /// Occurs when the bonding state of a device changed + /// + event EventHandler DeviceBondStateChanged; + /// /// Occurs when the scan has been stopped due the timeout after ms. /// event EventHandler ScanTimeoutElapsed; @@ -76,6 +80,12 @@ public interface IAdapter /// IReadOnlyList ConnectedDevices { get; } + /// + /// List of currently bonded devices. + /// The property is null if the OS doesn't provide this information + /// + IReadOnlyList BondedDevices { get; } + /// /// Starts scanning for BLE devices that fulfill the . /// DeviceDiscovered will only be called, if returns true for the discovered device. diff --git a/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs b/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs index 79d2aa4a..c7282114 100644 --- a/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs +++ b/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs @@ -47,6 +47,11 @@ public override IReadOnlyList GetSystemConnectedOrPairedDevices(Guid[] return new List(); } + protected override IReadOnlyList GetBondedDevices() + { + return null; // not supported + } + public override IReadOnlyList GetKnownDevicesByIds(Guid[] ids) { TraceUnavailability(); diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 88e18ac9..2ba0af48 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -142,6 +142,11 @@ public override IReadOnlyList GetSystemConnectedOrPairedDevices(Guid[] return ConnectedDevices; } + protected override IReadOnlyList GetBondedDevices() + { + return null; // not supported + } + /// /// Parses a given advertisement for various stored properties /// Currently only parses the manufacturer specific data From 9c948fd4fa0cb734b489cccec96f97a3e3c13340 Mon Sep 17 00:00:00 2001 From: mos Date: Tue, 16 May 2023 14:48:48 +0200 Subject: [PATCH 002/193] Added BondState, CreateBond(), and ForgetBond() to IDevice. Fixed issue in BondStatusBroadcastReceiver to add the adapter to new devices provided to the event. --- Source/Plugin.BLE/Android/Adapter.cs | 2 +- .../BondStatusBroadcastReceiver.cs | 21 +++--- Source/Plugin.BLE/Android/Device.cs | 65 ++++++++++++++++++- Source/Plugin.BLE/Apple/Device.cs | 20 ++++++ Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 24 +++++++ Source/Plugin.BLE/Shared/DeviceBase.cs | 11 ++++ Source/Plugin.BLE/Shared/DeviceBondState.cs | 6 +- Source/Plugin.BLE/Windows/Device.cs | 19 ++++++ 8 files changed, 157 insertions(+), 11 deletions(-) diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index 61b30275..da77d0aa 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -31,7 +31,7 @@ public Adapter(BluetoothManager bluetoothManager) _bluetoothAdapter = bluetoothManager.Adapter; //bonding - var bondStatusBroadcastReceiver = new BondStatusBroadcastReceiver(); + var bondStatusBroadcastReceiver = new BondStatusBroadcastReceiver(this); Application.Context.RegisterReceiver(bondStatusBroadcastReceiver, new IntentFilter(BluetoothDevice.ActionBondStateChanged)); diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index 96bba489..9f777f92 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -12,26 +12,31 @@ public class BondStatusBroadcastReceiver : BroadcastReceiver { public event EventHandler BondStateChanged; - public override void OnReceive(Context context, Intent intent) + Adapter BroadCastAdapter; + + public BondStatusBroadcastReceiver(Adapter adapter) { - var bondState = (Bond)intent.GetIntExtra(BluetoothDevice.ExtraBondState, (int)Bond.None); - //ToDo - var device = new Device(null, (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice), null); - Console.WriteLine(bondState.ToString()); + BroadCastAdapter = adapter; + } + public override void OnReceive(Context context, Intent intent) + { if (BondStateChanged == null) return; + var bondState = (global::Android.Bluetooth.Bond)intent.GetIntExtra(BluetoothDevice.ExtraBondState, (int)global::Android.Bluetooth.Bond.None); + var bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); + var device = new Device(BroadCastAdapter, bluetoothDevice, null, 0); switch (bondState) { - case Bond.None: + case global::Android.Bluetooth.Bond.None: BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.NotBonded }); break; - case Bond.Bonding: + case global::Android.Bluetooth.Bond.Bonding: BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.Bonding }); break; - case Bond.Bonded: + case global::Android.Bluetooth.Bond.Bonded: BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.Bonded }); break; diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index efb8468a..a7f103de 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -415,8 +415,8 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv } } - public override bool IsConnectable { get; protected set; } + public override bool IsConnectable { get; protected set; } public override bool SupportsIsConnectable { get => @@ -427,5 +427,68 @@ public override bool SupportsIsConnectable { #endif } + + protected override DeviceBondState GetBondState() + { + if (NativeDevice == null) + { + Trace.Message($"[Warning]: Can't get bond state of {Name}. NativeDevice is null."); + return DeviceBondState.NotSupported; + } + var bondState = NativeDevice.BondState; + switch (bondState) + { + case global::Android.Bluetooth.Bond.None: + return DeviceBondState.NotBonded; + case global::Android.Bluetooth.Bond.Bonding: + return DeviceBondState.Bonding; + case global::Android.Bluetooth.Bond.Bonded: + return DeviceBondState.Bonded; + default: + return DeviceBondState.NotSupported; + } + } + + public override bool CreateBond() + { + if (NativeDevice == null) + { + Trace.Message($"[Warning]: Can't create bond for {Name}. BluetoothDevice is null."); + return false; + } + else + { + return NativeDevice.CreateBond(); + } + } + + public override bool ForgetBond() + { + if (NativeDevice == null) + { + Trace.Message($"[Warning]: Can't remove bond of {Name}. BluetoothDevice is null."); + return false; + } + + if (BondState == DeviceBondState.NotBonded) + { + Trace.Message($"Device {Name} is not bonded."); + return true; + } + + try + { + // removeBond is not part of the API but was always available together with CreateBond (just hidden). + var removeBond = NativeDevice.Class.GetMethod("removeBond"); + // calling removeBond will disconnect! + return (bool)removeBond.Invoke(NativeDevice); + } + catch (Exception ex) + { + Trace.Message($"RemoveBond of {Name} failed. {ex.Message}"); + return false; + } + } + } } diff --git a/Source/Plugin.BLE/Apple/Device.cs b/Source/Plugin.BLE/Apple/Device.cs index 246d2915..5f6b5e0a 100644 --- a/Source/Plugin.BLE/Apple/Device.cs +++ b/Source/Plugin.BLE/Apple/Device.cs @@ -174,9 +174,29 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv return false; } + public override bool IsConnectable { get; protected set; } public override bool SupportsIsConnectable { get => true; } + + protected override DeviceBondState GetBondState() + { + return DeviceBondState.NotSupported; + } + + public override bool CreateBond() + { + Trace.Message("Cannot initiate a bonding request on iOS."); + return false; + } + + public override bool ForgetBond() + { + Trace.Message("Cannot forget bonding on iOS."); + return false; + } + + } } diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index 89ddc700..908934ae 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -120,5 +120,29 @@ public interface IDevice : IDisposable /// True, if device supports IsConnectable else False /// bool SupportsIsConnectable { get; } + + + /// + /// Gets the bonding state of a device. + /// + DeviceBondState BondState { get; } + + /// + /// Initiates a bonding request. + /// To establish an additional security level in the commumication between server and client pairing or bonding is used. + /// Pairing does the key exchange and encryption/decryption for one connection between server and client. + /// Bonding does pairing and remembers the keys in a secure storage so that they can be used for the next connection. + /// You have to subscribe to Adapter.DeviceBondStateChanged to get the current state. Typically first bonding and later bonded. + /// Important: + /// On iOS: Initiating a bonding request is not supported by iOS. The function simply returns false. + /// + /// True if bonding could be requested. On iOS it will always return false. + bool CreateBond(); + + /// + /// Forgets the bonding between server and client. + /// + /// + bool ForgetBond(); } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Shared/DeviceBase.cs b/Source/Plugin.BLE/Shared/DeviceBase.cs index 2bacad88..b48ddfb6 100644 --- a/Source/Plugin.BLE/Shared/DeviceBase.cs +++ b/Source/Plugin.BLE/Shared/DeviceBase.cs @@ -254,5 +254,16 @@ public override int GetHashCode() public abstract bool IsConnectable { get; protected set; } public abstract bool SupportsIsConnectable { get; } + + + protected abstract DeviceBondState GetBondState(); + + public DeviceBondState BondState => GetBondState(); + + public abstract bool CreateBond(); + + public abstract bool ForgetBond(); + + } } diff --git a/Source/Plugin.BLE/Shared/DeviceBondState.cs b/Source/Plugin.BLE/Shared/DeviceBondState.cs index e84b5213..40ec2db1 100644 --- a/Source/Plugin.BLE/Shared/DeviceBondState.cs +++ b/Source/Plugin.BLE/Shared/DeviceBondState.cs @@ -20,6 +20,10 @@ public enum DeviceBondState /// Indicates the remote device is bonded (paired), /// see https://developer.android.com/reference/android/bluetooth/BluetoothDevice#BOND_BONDED /// - Bonded + Bonded, + /// + /// Indicates that the device does not support information about the bonding state. + /// + NotSupported = -1, } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 904cdc98..02045579 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -105,5 +105,24 @@ public override void Dispose() public override bool IsConnectable { get; protected set; } public override bool SupportsIsConnectable { get => true; } + + + protected override DeviceBondState GetBondState() + { + return DeviceBondState.NotSupported; + } + + public override bool CreateBond() + { + Trace.Message("Cannot initiate a bonding request on iOS."); + return false; + } + + public override bool ForgetBond() + { + Trace.Message("Cannot forget bonding on iOS."); + return false; + } + } } From 9ac8425a7df92726123793c6edb2f52784d9bb3b Mon Sep 17 00:00:00 2001 From: mos Date: Tue, 16 May 2023 17:02:29 +0200 Subject: [PATCH 003/193] Improved CreateBond() comment with hints to the required permissions. --- Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index 908934ae..e498a926 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using static Android.OS.Build; +using static Java.Text.Normalizer; namespace Plugin.BLE.Abstractions.Contracts { @@ -134,7 +136,13 @@ public interface IDevice : IDisposable /// Bonding does pairing and remembers the keys in a secure storage so that they can be used for the next connection. /// You have to subscribe to Adapter.DeviceBondStateChanged to get the current state. Typically first bonding and later bonded. /// Important: - /// On iOS: Initiating a bonding request is not supported by iOS. The function simply returns false. + /// On iOS: + /// Initiating a bonding request is not supported by iOS. The function simply returns false. + /// On Android: + /// Android system services will handle the necessary user interactions to confirm and complete the bonding process. + /// For apps targeting Build.VERSION_CODES#R or lower, this requires the Manifest.permission#BLUETOOTH_ADMIN permission + /// which can be gained with a simple ßuses-permissionß manifest tag. For apps targeting Build.VERSION_CODES#S or or higher, + /// this requires the Manifest.permission#BLUETOOTH_CONNECT permission which can be gained with Activity.requestPermissions(String[], int). /// /// True if bonding could be requested. On iOS it will always return false. bool CreateBond(); From d5eb560970629050cbe7f730c5bc83c2cef523b8 Mon Sep 17 00:00:00 2001 From: mos Date: Tue, 16 May 2023 17:33:59 +0200 Subject: [PATCH 004/193] Removed some usings added by VS2022 for whatever reason, which resulted in compilation errors. --- Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index e498a926..758ca96e 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using static Android.OS.Build; -using static Java.Text.Normalizer; namespace Plugin.BLE.Abstractions.Contracts { From 9135392ae35925b08ab1a8ce5a73800ef4bde7ea Mon Sep 17 00:00:00 2001 From: mos Date: Sun, 21 May 2023 07:34:02 +0200 Subject: [PATCH 005/193] Simplified BondStatusBroadcastReceiver. --- .../BondStatusBroadcastReceiver.cs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index 9f777f92..b37a4b25 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -23,24 +23,18 @@ public override void OnReceive(Context context, Intent intent) { if (BondStateChanged == null) return; - var bondState = (global::Android.Bluetooth.Bond)intent.GetIntExtra(BluetoothDevice.ExtraBondState, (int)global::Android.Bluetooth.Bond.None); + var extraBondState = (Bond)intent.GetIntExtra(BluetoothDevice.ExtraBondState, (int)Bond.None); var bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); var device = new Device(BroadCastAdapter, bluetoothDevice, null, 0); - switch (bondState) + DeviceBondState bondState = DeviceBondState.NotSupported; + switch (extraBondState) { - case global::Android.Bluetooth.Bond.None: - BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.NotBonded }); - break; - - case global::Android.Bluetooth.Bond.Bonding: - BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.Bonding }); - break; - - case global::Android.Bluetooth.Bond.Bonded: - BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.Bonded }); - break; - + case Bond.None: bondState = DeviceBondState.NotBonded; break; + case Bond.Bonding: bondState = DeviceBondState.Bonding; break; + case Bond.Bonded: bondState = DeviceBondState.Bonded; break; } + BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = bondState }); ; } + } } \ No newline at end of file From 92874a4d0489be3ee0a8560e5c10f3e13aabd249 Mon Sep 17 00:00:00 2001 From: mos Date: Sun, 21 May 2023 10:26:47 +0200 Subject: [PATCH 006/193] Created DeviceBondStateExtension to host method to map from the native bond state to DeviceBondState --- .../BondStatusBroadcastReceiver.cs | 9 ++----- Source/Plugin.BLE/Android/Device.cs | 14 ++-------- .../Extensions/DeviceBondStateExtension.cs | 27 +++++++++++++++++++ Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 2 +- 4 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index b37a4b25..79eeab8e 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -4,6 +4,7 @@ using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.EventArgs; using Plugin.BLE.Android; +using Plugin.BLE.Extensions; namespace Plugin.BLE.BroadcastReceivers { @@ -26,13 +27,7 @@ public override void OnReceive(Context context, Intent intent) var extraBondState = (Bond)intent.GetIntExtra(BluetoothDevice.ExtraBondState, (int)Bond.None); var bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); var device = new Device(BroadCastAdapter, bluetoothDevice, null, 0); - DeviceBondState bondState = DeviceBondState.NotSupported; - switch (extraBondState) - { - case Bond.None: bondState = DeviceBondState.NotBonded; break; - case Bond.Bonding: bondState = DeviceBondState.Bonding; break; - case Bond.Bonded: bondState = DeviceBondState.Bonded; break; - } + DeviceBondState bondState = extraBondState.FromNative(); BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = bondState }); ; } diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index a7f103de..4a7c8614 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -13,6 +13,7 @@ using Trace = Plugin.BLE.Abstractions.Trace; using System.Threading; using Java.Util; +using Plugin.BLE.Extensions; namespace Plugin.BLE.Android { @@ -435,18 +436,7 @@ protected override DeviceBondState GetBondState() Trace.Message($"[Warning]: Can't get bond state of {Name}. NativeDevice is null."); return DeviceBondState.NotSupported; } - var bondState = NativeDevice.BondState; - switch (bondState) - { - case global::Android.Bluetooth.Bond.None: - return DeviceBondState.NotBonded; - case global::Android.Bluetooth.Bond.Bonding: - return DeviceBondState.Bonding; - case global::Android.Bluetooth.Bond.Bonded: - return DeviceBondState.Bonded; - default: - return DeviceBondState.NotSupported; - } + return NativeDevice.BondState.FromNative(); } public override bool CreateBond() diff --git a/Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs b/Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs new file mode 100644 index 00000000..0bd444c0 --- /dev/null +++ b/Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs @@ -0,0 +1,27 @@ +using Android.Bluetooth; +using Plugin.BLE.Abstractions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Plugin.BLE.Extensions +{ + internal static class DeviceBondStateExtension + { + public static DeviceBondState FromNative(this Bond bondState) + { + switch (bondState) + { + case Bond.None: + return DeviceBondState.NotBonded; + case Bond.Bonding: + return DeviceBondState.Bonding; + case Bond.Bonded: + return DeviceBondState.Bonded; + default: + return DeviceBondState.NotSupported; + } + } + + } +} diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index 758ca96e..6ae3999b 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -136,7 +136,7 @@ public interface IDevice : IDisposable /// Important: /// On iOS: /// Initiating a bonding request is not supported by iOS. The function simply returns false. - /// On Android: + /// On Android: Added in API level 19. /// Android system services will handle the necessary user interactions to confirm and complete the bonding process. /// For apps targeting Build.VERSION_CODES#R or lower, this requires the Manifest.permission#BLUETOOTH_ADMIN permission /// which can be gained with a simple ßuses-permissionß manifest tag. For apps targeting Build.VERSION_CODES#S or or higher, From d427d5d2792fab6875d7fd12d33d34783e30ef30 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 3 Jul 2023 21:47:32 +0200 Subject: [PATCH 007/193] dotnet format whitespace Source/BLE.sln --- .../ViewModels/DescriptorListViewModel.cs | 4 ++-- .../BLE.Client/ViewModels/DeviceListViewModel.cs | 6 +++--- Source/Plugin.BLE.Tests/CharacteristicBaseTests.cs | 2 +- Source/Plugin.BLE/Android/Adapter.cs | 6 +++--- .../BluetoothStatusBroadcastReceiver.cs | 2 +- Source/Plugin.BLE/Android/Device.cs | 5 +++-- .../Android/Extensions/ScanMatchModeExtension.cs | 2 +- Source/Plugin.BLE/Android/GattCallback.cs | 2 +- Source/Plugin.BLE/Android/Service.cs | 2 +- .../Plugin.BLE/Apple/BleCentralManagerDelegate.cs | 8 ++++---- Source/Plugin.BLE/Apple/Characteristic.cs | 13 +++++++------ Source/Plugin.BLE/Apple/Descriptor.cs | 2 +- Source/Plugin.BLE/Apple/Service.cs | 2 +- Source/Plugin.BLE/Shared/AdapterBase.cs | 6 +++--- Source/Plugin.BLE/Shared/CharacteristicWriteType.cs | 2 +- Source/Plugin.BLE/Shared/ConnectionInterval.cs | 2 +- Source/Plugin.BLE/Shared/Contracts/IAdapter.cs | 2 +- .../Plugin.BLE/Shared/Contracts/ICharacteristic.cs | 2 +- Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 2 +- .../Shared/Extensions/AdapterExtension.cs | 4 ++-- 20 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Source/BLE.Client/BLE.Client/ViewModels/DescriptorListViewModel.cs b/Source/BLE.Client/BLE.Client/ViewModels/DescriptorListViewModel.cs index 8cb2d1c4..bdc7cb64 100644 --- a/Source/BLE.Client/BLE.Client/ViewModels/DescriptorListViewModel.cs +++ b/Source/BLE.Client/BLE.Client/ViewModels/DescriptorListViewModel.cs @@ -10,7 +10,7 @@ public class DescriptorListViewModel : BaseViewModel private readonly IMvxNavigationService _navigation; private ICharacteristic _characteristic; - public IReadOnlyList Descriptors { get; private set;} + public IReadOnlyList Descriptors { get; private set; } public DescriptorListViewModel(IAdapter adapter, IMvxNavigationService navigation) : base(adapter) { @@ -53,7 +53,7 @@ public IDescriptor SelectedDescriptor { var bundle = new MvxBundle(new Dictionary(Bundle.Data) { { DescriptorIdKey, value.Id.ToString() } }); - _navigation.Navigate(bundle); + _navigation.Navigate(bundle); } RaisePropertyChanged(); diff --git a/Source/BLE.Client/BLE.Client/ViewModels/DeviceListViewModel.cs b/Source/BLE.Client/BLE.Client/ViewModels/DeviceListViewModel.cs index da8e89dc..844ae516 100644 --- a/Source/BLE.Client/BLE.Client/ViewModels/DeviceListViewModel.cs +++ b/Source/BLE.Client/BLE.Client/ViewModels/DeviceListViewModel.cs @@ -38,8 +38,8 @@ public Guid PreviousGuid } } - public MvxCommand RefreshCommand => new MvxCommand(() => TryStartScanning(refresh:true,filter:false)); - public MvxCommand RefreshFilteredScanCommand => new MvxCommand(() => TryStartScanning(refresh:true,filter:true)); + public MvxCommand RefreshCommand => new MvxCommand(() => TryStartScanning(refresh: true, filter: false)); + public MvxCommand RefreshFilteredScanCommand => new MvxCommand(() => TryStartScanning(refresh: true, filter: true)); public MvxCommand EmptyDevicesCommand => new MvxCommand(() => { @@ -349,7 +349,7 @@ private async Task ScanForDevicesFiltered() scanFilterOptions.ServiceUuids = list.ToArray(); } - await Adapter.StartScanningForDevicesAsync(scanFilterOptions,_cancellationTokenSource.Token); + await Adapter.StartScanningForDevicesAsync(scanFilterOptions, _cancellationTokenSource.Token); } private async Task UpdateConnectedDevices() diff --git a/Source/Plugin.BLE.Tests/CharacteristicBaseTests.cs b/Source/Plugin.BLE.Tests/CharacteristicBaseTests.cs index 010f6467..3fc01805 100644 --- a/Source/Plugin.BLE.Tests/CharacteristicBaseTests.cs +++ b/Source/Plugin.BLE.Tests/CharacteristicBaseTests.cs @@ -6,7 +6,7 @@ using Xunit; namespace Plugin.BLE.Tests -{ +{ public class CharacteristicBaseTests { [Theory(DisplayName = "Setting WriteType to not supported type throws InvalidOperationException")] diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index 8007cc58..6c5bd595 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -132,7 +132,7 @@ private void StartScanningNew(ScanFilterOptions scanFilterOptions) { Trace.Message($"Device address {deviceAddress} is invalid. The correct format is \"01:02:03:AB:CD:EF\""); } - + } } if (scanFilterOptions.HasDeviceNames) @@ -144,7 +144,7 @@ private void StartScanningNew(ScanFilterOptions scanFilterOptions) scanFilters.Add(sfb.Build()); } } - + } var ssb = new ScanSettings.Builder(); @@ -167,7 +167,7 @@ private void StartScanningNew(ScanFilterOptions scanFilterOptions) Trace.Message("Using ScanMatchMode Agressive"); } } - + #if NET6_0_OR_GREATER if (OperatingSystem.IsAndroidVersionAtLeast(26)) #else diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BluetoothStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BluetoothStatusBroadcastReceiver.cs index 641f7d86..443ed38d 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BluetoothStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BluetoothStatusBroadcastReceiver.cs @@ -30,7 +30,7 @@ public override void OnReceive(Context context, Intent intent) return; } - var btState = (State) state; + var btState = (State)state; _stateChangedHandler?.Invoke(btState.ToBluetoothState()); } } diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index efb8468a..d2853298 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -40,7 +40,7 @@ public class Device : DeviceBase /// public ConnectParameters ConnectParameters { get; private set; } - public Device(Adapter adapter, BluetoothDevice nativeDevice, BluetoothGatt gatt, int rssi = 0, byte[] advertisementData = null, bool isConnectable = true) + public Device(Adapter adapter, BluetoothDevice nativeDevice, BluetoothGatt gatt, int rssi = 0, byte[] advertisementData = null, bool isConnectable = true) : base(adapter, nativeDevice) { Update(nativeDevice, gatt); @@ -418,7 +418,8 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv public override bool IsConnectable { get; protected set; } - public override bool SupportsIsConnectable { + public override bool SupportsIsConnectable + { get => #if NET6_0_OR_GREATER OperatingSystem.IsAndroidVersionAtLeast(26); diff --git a/Source/Plugin.BLE/Android/Extensions/ScanMatchModeExtension.cs b/Source/Plugin.BLE/Android/Extensions/ScanMatchModeExtension.cs index ff71abad..6bad16de 100644 --- a/Source/Plugin.BLE/Android/Extensions/ScanMatchModeExtension.cs +++ b/Source/Plugin.BLE/Android/Extensions/ScanMatchModeExtension.cs @@ -8,7 +8,7 @@ internal static class ScanMatchModeExtension { public static BluetoothScanMatchMode ToNative(this ScanMatchMode matchMode) { - switch(matchMode) + switch (matchMode) { case ScanMatchMode.AGRESSIVE: return BluetoothScanMatchMode.Aggressive; diff --git a/Source/Plugin.BLE/Android/GattCallback.cs b/Source/Plugin.BLE/Android/GattCallback.cs index e55ebdf2..b73fed22 100644 --- a/Source/Plugin.BLE/Android/GattCallback.cs +++ b/Source/Plugin.BLE/Android/GattCallback.cs @@ -88,7 +88,7 @@ public override void OnConnectionStateChange(BluetoothGatt gatt, GattStatus stat } } else - { + { //connection must have been lost, because the callback was not triggered by calling disconnect Trace.Message($"Disconnected '{_device.Name}' by lost connection"); diff --git a/Source/Plugin.BLE/Android/Service.cs b/Source/Plugin.BLE/Android/Service.cs index 05f089a9..c0709e0c 100644 --- a/Source/Plugin.BLE/Android/Service.cs +++ b/Source/Plugin.BLE/Android/Service.cs @@ -16,7 +16,7 @@ public class Service : ServiceBase public override Guid Id => Guid.ParseExact(NativeService.Uuid.ToString(), "d"); public override bool IsPrimary => NativeService.Type == GattServiceType.Primary; - public Service(BluetoothGattService nativeService, BluetoothGatt gatt, IGattCallback gattCallback, IDevice device) + public Service(BluetoothGattService nativeService, BluetoothGatt gatt, IGattCallback gattCallback, IDevice device) : base(device, nativeService) { _gatt = gatt; diff --git a/Source/Plugin.BLE/Apple/BleCentralManagerDelegate.cs b/Source/Plugin.BLE/Apple/BleCentralManagerDelegate.cs index 408e3b3b..9c413706 100644 --- a/Source/Plugin.BLE/Apple/BleCentralManagerDelegate.cs +++ b/Source/Plugin.BLE/Apple/BleCentralManagerDelegate.cs @@ -20,7 +20,7 @@ public interface IBleCentralManagerDelegate public class BleCentralManagerDelegate : CBCentralManagerDelegate, IBleCentralManagerDelegate { -#region IBleCentralManagerDelegate events + #region IBleCentralManagerDelegate events private event EventHandler _willRestoreState; @@ -91,9 +91,9 @@ event EventHandler IBleCentralManagerDelegate.ConnectedPe remove => _connectedPeripheral -= value; } -#endregion + #endregion -#region Event wiring + #region Event wiring public override void WillRestoreState(CBCentralManager central, NSDictionary dict) { @@ -139,6 +139,6 @@ public override void ConnectedPeripheral(CBCentralManager central, CBPeripheral _connectedPeripheral?.Invoke(this, new CBPeripheralEventArgs(peripheral)); } -#endregion + #endregion } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Apple/Characteristic.cs b/Source/Plugin.BLE/Apple/Characteristic.cs index df16d63b..bb565481 100644 --- a/Source/Plugin.BLE/Apple/Characteristic.cs +++ b/Source/Plugin.BLE/Apple/Characteristic.cs @@ -34,14 +34,14 @@ public override byte[] Value { return new byte[0]; } - + return value.ToArray(); } - } + } public override CharacteristicPropertyType Properties => (CharacteristicPropertyType)(int)NativeCharacteristic.Properties; - public Characteristic(CBCharacteristic nativeCharacteristic, CBPeripheral parentDevice, IService service, IBleCentralManagerDelegate bleCentralManagerDelegate) + public Characteristic(CBCharacteristic nativeCharacteristic, CBPeripheral parentDevice, IService service, IBleCentralManagerDelegate bleCentralManagerDelegate) : base(service, nativeCharacteristic) { _parentDevice = parentDevice; @@ -134,7 +134,7 @@ protected override Task WriteNativeAsync(byte[] data, CharacteristicWriteTy if (writeType.ToNative() == CBCharacteristicWriteType.WithResponse) { task = TaskBuilder.FromEvent, EventHandler>( - execute: () => + execute: () => { if (_parentDevice.State != CBPeripheralState.Connected) throw exception; @@ -190,7 +190,8 @@ protected override Task WriteNativeAsync(byte[] data, CharacteristicWriteTy unsubscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral -= handler); } - else { + else + { task = Task.FromResult(0); } } @@ -240,7 +241,7 @@ protected override Task StartUpdatesNativeAsync(CancellationToken cancellationTo reject(new Exception($"Device {Service.Device.Id} disconnected while starting updates for characteristic with {Id}.")); }), subscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral += handler, - unsubscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral -= handler, + unsubscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral -= handler, token: cancellationToken); } diff --git a/Source/Plugin.BLE/Apple/Descriptor.cs b/Source/Plugin.BLE/Apple/Descriptor.cs index bc243eaa..190420cc 100644 --- a/Source/Plugin.BLE/Apple/Descriptor.cs +++ b/Source/Plugin.BLE/Apple/Descriptor.cs @@ -35,7 +35,7 @@ public override byte[] Value private readonly CBPeripheral _parentDevice; private readonly IBleCentralManagerDelegate _bleCentralManagerDelegate; - public Descriptor(CBDescriptor nativeDescriptor, CBPeripheral parentDevice, ICharacteristic characteristic, IBleCentralManagerDelegate bleCentralManagerDelegate) + public Descriptor(CBDescriptor nativeDescriptor, CBPeripheral parentDevice, ICharacteristic characteristic, IBleCentralManagerDelegate bleCentralManagerDelegate) : base(characteristic, nativeDescriptor) { _parentDevice = parentDevice; diff --git a/Source/Plugin.BLE/Apple/Service.cs b/Source/Plugin.BLE/Apple/Service.cs index 47055263..7c515ccc 100644 --- a/Source/Plugin.BLE/Apple/Service.cs +++ b/Source/Plugin.BLE/Apple/Service.cs @@ -17,7 +17,7 @@ public class Service : ServiceBase public override Guid Id => NativeService.UUID.GuidFromUuid(); public override bool IsPrimary => NativeService.Primary; - public Service(CBService nativeService, IDevice device, IBleCentralManagerDelegate bleCentralManagerDelegate) + public Service(CBService nativeService, IDevice device, IBleCentralManagerDelegate bleCentralManagerDelegate) : base(device, nativeService) { _device = device.NativeDevice as CBPeripheral; diff --git a/Source/Plugin.BLE/Shared/AdapterBase.cs b/Source/Plugin.BLE/Shared/AdapterBase.cs index 8ea957af..b4562b9b 100644 --- a/Source/Plugin.BLE/Shared/AdapterBase.cs +++ b/Source/Plugin.BLE/Shared/AdapterBase.cs @@ -101,9 +101,9 @@ public bool IsScanning /// Starts scanning for BLE devices that fulfill the . /// DeviceDiscovered will only be called, if returns true for the discovered device. /// - public async Task StartScanningForDevicesAsync(ScanFilterOptions scanFilterOptions, - Func deviceFilter = null, - bool allowDuplicatesKey = false, + public async Task StartScanningForDevicesAsync(ScanFilterOptions scanFilterOptions, + Func deviceFilter = null, + bool allowDuplicatesKey = false, CancellationToken cancellationToken = default) { if (IsScanning) diff --git a/Source/Plugin.BLE/Shared/CharacteristicWriteType.cs b/Source/Plugin.BLE/Shared/CharacteristicWriteType.cs index ca3ed866..1f3b73d5 100644 --- a/Source/Plugin.BLE/Shared/CharacteristicWriteType.cs +++ b/Source/Plugin.BLE/Shared/CharacteristicWriteType.cs @@ -9,7 +9,7 @@ public enum CharacteristicWriteType /// Value should be written with response if supported, else without response. /// Default, - + /// /// Value should be written with response. /// diff --git a/Source/Plugin.BLE/Shared/ConnectionInterval.cs b/Source/Plugin.BLE/Shared/ConnectionInterval.cs index 6e1da3ed..2c487f90 100644 --- a/Source/Plugin.BLE/Shared/ConnectionInterval.cs +++ b/Source/Plugin.BLE/Shared/ConnectionInterval.cs @@ -24,6 +24,6 @@ public enum ConnectionInterval /// This is mapped to CONNECTION_PRIORITY_LOW_POWER, /// see https://developer.android.com/reference/android/bluetooth/BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER /// - Low = 2 + Low = 2 } } diff --git a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs index 492f39f8..7b46c442 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs @@ -157,7 +157,7 @@ public interface IAdapter /// IMPORTANT: Only considered by iOS due to platform limitations. Filters devices by advertised services. SET THIS VALUE FOR ANY RESULTS /// List of IDevices connected to the OS. In case of no devices the list is empty. IReadOnlyList GetSystemConnectedOrPairedDevices(Guid[] services = null); - + /// /// Returns a list of paired BLE devices for the given UUIDs. /// diff --git a/Source/Plugin.BLE/Shared/Contracts/ICharacteristic.cs b/Source/Plugin.BLE/Shared/Contracts/ICharacteristic.cs index a6b37eb7..34907fc7 100644 --- a/Source/Plugin.BLE/Shared/Contracts/ICharacteristic.cs +++ b/Source/Plugin.BLE/Shared/Contracts/ICharacteristic.cs @@ -69,7 +69,7 @@ public interface ICharacteristic /// Indicates wheter the characteristic supports notify or not. /// bool CanUpdate { get; } - + /// /// Returns the parent service. Use this to access the device. /// diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index 89ddc700..76ff49a0 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/Source/Plugin.BLE/Shared/Extensions/AdapterExtension.cs b/Source/Plugin.BLE/Shared/Extensions/AdapterExtension.cs index 8e8db5b8..1f4fd175 100644 --- a/Source/Plugin.BLE/Shared/Extensions/AdapterExtension.cs +++ b/Source/Plugin.BLE/Shared/Extensions/AdapterExtension.cs @@ -34,7 +34,7 @@ public static Task StartScanningForDevicesAsync(this IAdapter adapter, Cancellat /// A task that represents the asynchronous read operation. The Task will finish after the scan has ended. public static Task StartScanningForDevicesAsync(this IAdapter adapter, Guid[] serviceUuids, CancellationToken cancellationToken = default) { - return adapter.StartScanningForDevicesAsync(new ScanFilterOptions(){ServiceUuids = serviceUuids}, null, cancellationToken: cancellationToken); + return adapter.StartScanningForDevicesAsync(new ScanFilterOptions() { ServiceUuids = serviceUuids }, null, cancellationToken: cancellationToken); } /// @@ -115,7 +115,7 @@ public static async Task DiscoverDeviceAsync(this IAdapter adapter, Fun /// Thrown if the device connection fails. public static Task ConnectToDeviceAsync(this IAdapter adapter, IDevice device, ConnectParameters connectParameters, CancellationToken cancellationToken) { - return adapter.ConnectToDeviceAsync(device, connectParameters:connectParameters, cancellationToken: cancellationToken); + return adapter.ConnectToDeviceAsync(device, connectParameters: connectParameters, cancellationToken: cancellationToken); } } } \ No newline at end of file From 9dd6d65bb7d5b924a7b87b6b19c7faec53199bfb Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 3 Jul 2023 21:50:37 +0200 Subject: [PATCH 008/193] IAdapter: rename 'supportsExtendedAdvertising' and 'supportsCodedPHY' * use Pasal case, according to common C# coding style (see e.g. https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/coding-style.md) --- Source/Plugin.BLE/Android/Adapter.cs | 4 ++-- Source/Plugin.BLE/Apple/Adapter.cs | 2 +- Source/Plugin.BLE/Shared/AdapterBase.cs | 4 ++-- Source/Plugin.BLE/Shared/Contracts/IAdapter.cs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index 6c5bd595..e2ecb128 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -274,7 +274,7 @@ public override IReadOnlyList GetKnownDevicesByIds(Guid[] ids) return devices.Where(item => ids.Contains(item.Id)).ToList(); } - public override bool supportsExtendedAdvertising() + public override bool SupportsExtendedAdvertising() { #if NET6_0_OR_GREATER if (OperatingSystem.IsAndroidVersionAtLeast(26)) @@ -290,7 +290,7 @@ public override bool supportsExtendedAdvertising() } } - public override bool supportsCodedPHY() + public override bool SupportsCodedPHY() { #if NET6_0_OR_GREATER if (OperatingSystem.IsAndroidVersionAtLeast(26)) diff --git a/Source/Plugin.BLE/Apple/Adapter.cs b/Source/Plugin.BLE/Apple/Adapter.cs index 98b18a5f..bfe85544 100644 --- a/Source/Plugin.BLE/Apple/Adapter.cs +++ b/Source/Plugin.BLE/Apple/Adapter.cs @@ -428,7 +428,7 @@ public static List ParseAdvertismentData(NSDictionary adver } #if NET6_0_OR_GREATER || __IOS__ - public override bool supportsExtendedAdvertising() + public override bool SupportsExtendedAdvertising() { #if NET6_0_OR_GREATER if (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13) || OperatingSystem.IsMacCatalystVersionAtLeast(13)) diff --git a/Source/Plugin.BLE/Shared/AdapterBase.cs b/Source/Plugin.BLE/Shared/AdapterBase.cs index b4562b9b..869ee23a 100644 --- a/Source/Plugin.BLE/Shared/AdapterBase.cs +++ b/Source/Plugin.BLE/Shared/AdapterBase.cs @@ -360,11 +360,11 @@ public void HandleConnectionFail(IDevice device, string errorMessage) /// /// Indicates whether extended advertising (BLE5) is supported. /// - public virtual bool supportsExtendedAdvertising() => false; + public virtual bool SupportsExtendedAdvertising() => false; /// /// Indicates whether the Coded PHY feature (BLE5) is supported. /// - public virtual bool supportsCodedPHY() => false; + public virtual bool SupportsCodedPHY() => false; } } diff --git a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs index 7b46c442..325b8d41 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs @@ -170,12 +170,12 @@ public interface IAdapter /// Indicates whether extended advertising (BLE5) is supported. /// /// true if extended advertising is supported, otherwise false. - bool supportsExtendedAdvertising(); + bool SupportsExtendedAdvertising(); /// /// Indicates whether the Coded PHY feature (BLE5) is supported. /// /// true if extended advertising is supported, otherwise false. - bool supportsCodedPHY(); + bool SupportsCodedPHY(); } } \ No newline at end of file From 0613a91553ee35fab1159650e51fde39191cec1a Mon Sep 17 00:00:00 2001 From: vitalii-vov <71486507+vitalii-vov@users.noreply.github.com> Date: Sun, 23 Jul 2023 11:23:37 +0300 Subject: [PATCH 009/193] Fixed crash with incorrect data in advertisement data on android (#719) * catch exceptions when parsing and print debug output * show device address in output * return the list of correctly parsed records even in case of errors --------- Co-authored-by: vitvov --- Source/Plugin.BLE/Android/Device.cs | 94 ++++++++++++++++------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index d2853298..d9773b1c 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -13,6 +13,7 @@ using Trace = Plugin.BLE.Abstractions.Trace; using System.Threading; using Java.Util; +using Plugin.BLE.Abstractions.Extensions; namespace Plugin.BLE.Android { @@ -255,64 +256,71 @@ private Guid ParseDeviceId() return new Guid(deviceGuid); } - public static List ParseScanRecord(byte[] scanRecord) + public List ParseScanRecord(byte[] scanRecord) { var records = new List(); if (scanRecord == null) return records; - int index = 0; - while (index < scanRecord.Length) + try { - byte length = scanRecord[index++]; - //Done once we run out of records - // 1 byte for type and length-1 bytes for data - if (length == 0) break; - - int type = scanRecord[index]; - //Done if our record isn't a valid type - if (type == 0) break; - - if (!Enum.IsDefined(typeof(AdvertisementRecordType), type)) + int index = 0; + while (index < scanRecord.Length) { - Trace.Message("Advertisment record type not defined: {0}", type); - break; - } + byte length = scanRecord[index++]; + //Done once we run out of records + // 1 byte for type and length-1 bytes for data + if (length == 0) break; - //data length is length -1 because type takes the first byte - byte[] data = new byte[length - 1]; - Array.Copy(scanRecord, index + 1, data, 0, length - 1); + int type = scanRecord[index]; + //Done if our record isn't a valid type + if (type == 0) break; - // don't forget that data is little endian so reverse - // Supplement to Bluetooth Core Specification 1 - // NOTE: all relevant devices are already little endian, so this is not necessary for any type except UUIDs - //var record = new AdvertisementRecord((AdvertisementRecordType)type, data.Reverse().ToArray()); - - switch ((AdvertisementRecordType)type) - { - case AdvertisementRecordType.ServiceDataUuid32Bit: - case AdvertisementRecordType.SsUuids128Bit: - case AdvertisementRecordType.SsUuids16Bit: - case AdvertisementRecordType.SsUuids32Bit: - case AdvertisementRecordType.UuidCom32Bit: - case AdvertisementRecordType.UuidsComplete128Bit: - case AdvertisementRecordType.UuidsComplete16Bit: - case AdvertisementRecordType.UuidsIncomple16Bit: - case AdvertisementRecordType.UuidsIncomplete128Bit: - Array.Reverse(data); + if (!Enum.IsDefined(typeof(AdvertisementRecordType), type)) + { + Trace.Message("Advertisment record type not defined: {0}", type); break; - } - var record = new AdvertisementRecord((AdvertisementRecordType)type, data); + } - Trace.Message(record.ToString()); + //data length is length -1 because type takes the first byte + byte[] data = new byte[length - 1]; + Array.Copy(scanRecord, index + 1, data, 0, length - 1); - records.Add(record); + // don't forget that data is little endian so reverse + // Supplement to Bluetooth Core Specification 1 + // NOTE: all relevant devices are already little endian, so this is not necessary for any type except UUIDs + //var record = new AdvertisementRecord((AdvertisementRecordType)type, data.Reverse().ToArray()); - //Advance - index += length; + switch ((AdvertisementRecordType)type) + { + case AdvertisementRecordType.ServiceDataUuid32Bit: + case AdvertisementRecordType.SsUuids128Bit: + case AdvertisementRecordType.SsUuids16Bit: + case AdvertisementRecordType.SsUuids32Bit: + case AdvertisementRecordType.UuidCom32Bit: + case AdvertisementRecordType.UuidsComplete128Bit: + case AdvertisementRecordType.UuidsComplete16Bit: + case AdvertisementRecordType.UuidsIncomple16Bit: + case AdvertisementRecordType.UuidsIncomplete128Bit: + Array.Reverse(data); + break; + } + var record = new AdvertisementRecord((AdvertisementRecordType)type, data); + + Trace.Message(record.ToString()); + + records.Add(record); + + //Advance + index += length; + } + } + catch(Exception) + { + //There may be a situation where scanRecord contains incorrect data. + Trace.Message("Failed to parse advertisementData. Device address: {0}, Data: {1}", NativeDevice.Address, scanRecord.ToHexString()); } - return records; } From 1ee84f4a8d23d3c8a309f2912eeeef053ea27699 Mon Sep 17 00:00:00 2001 From: travis012 Date: Mon, 24 Jul 2023 17:15:42 -0600 Subject: [PATCH 010/193] Fix windows connect/disconnect issues #528, #536, #423 --- Source/Plugin.BLE/Windows/Adapter.cs | 50 ++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 88e18ac9..43bdbd44 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -19,6 +19,7 @@ using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; +using System.Collections.Concurrent; namespace Plugin.BLE.UWP { @@ -28,6 +29,12 @@ public class Adapter : AdapterBase private BluetoothLEAdvertisementWatcher _bleWatcher; private DispatcherQueue _dq; + /// + /// Registry used to store device instances for pending operations : disconnect + /// Helps to detect connection lost events. + /// + private readonly IDictionary _deviceOperationRegistry = new ConcurrentDictionary(); + public Adapter(BluetoothLEHelper bluetoothHelper) { _bluetoothHelper = bluetoothHelper; @@ -82,7 +89,36 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect ConnectedDeviceRegistry[device.Id.ToString()] = device; + // TODO: ObservableBluetoothLEDevice.ConnectAsync needs updated to include a cancelation token param + // currently it is hardcoded to 5000ms. On windows users should not use cancellation tokens with + // timeouts that are <= 5000ms await nativeDevice.ConnectAsync(); + + if (nativeDevice.BluetoothLEDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) + { + // use DisconnectDeviceNative to clean up resources otherwise windows won't disconnect the device + // after a subsequent successful connection (#528, #536, #423) + DisconnectDeviceNative(device); + + // fire a connection failed event + HandleConnectionFail(device, "Failed connecting to device."); + + // this is normally done in Device_ConnectionStatusChanged but since nothing actually connected + // or disconnect, ConnectionStatusChanged will not fire. + ConnectedDeviceRegistry.TryRemove(device.Id.ToString(), out _); + } + else if (cancellationToken.IsCancellationRequested) + { + // connection attempt succeeded but was cancelled before it could be completed + // see TODO above. + + // cleanup resources + DisconnectDeviceNative(device); + } + else + { + _deviceOperationRegistry[device.Id.ToString()] = device; + } } private void Device_ConnectionStatusChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) @@ -106,7 +142,16 @@ private void Device_ConnectionStatusChanged(object sender, PropertyChangedEventA if (!nativeDevice.IsConnected && ConnectedDeviceRegistry.TryRemove(address, out var disconnectedDevice)) { - HandleDisconnectedDevice(true, disconnectedDevice); + bool isNormalDisconnect = !_deviceOperationRegistry.Remove(disconnectedDevice.Id.ToString()); + if (!isNormalDisconnect) + { + // device was powered off or went out of range. Call DisconnectDeviceNative to cleanup + // resources otherwise windows will not disconnect on a subsequent connect-disconnect. + DisconnectDeviceNative(disconnectedDevice); + } + + // fire the correct event (DeviceDisconnected or DeviceConnectionLost) + HandleDisconnectedDevice(isNormalDisconnect, disconnectedDevice); } } @@ -117,8 +162,9 @@ protected override void DisconnectDeviceNative(IDevice device) if (device.NativeDevice is ObservableBluetoothLEDevice) { + _deviceOperationRegistry.Remove(device.Id.ToString()); ((Device)device).ClearServices(); - device.Dispose(); + device.Dispose(); } } From e984ecc14c2d5c136f52beb4d4aca478c201000d Mon Sep 17 00:00:00 2001 From: travis012 Date: Tue, 25 Jul 2023 12:16:34 -0600 Subject: [PATCH 011/193] Don't use device.Dispose() on windows in DisconnectDeviceAsync to free resources. Fixes #620 --- Source/Plugin.BLE/Windows/Adapter.cs | 5 ++++- Source/Plugin.BLE/Windows/Device.cs | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 43bdbd44..f79826de 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -164,7 +164,10 @@ protected override void DisconnectDeviceNative(IDevice device) { _deviceOperationRegistry.Remove(device.Id.ToString()); ((Device)device).ClearServices(); - device.Dispose(); + + // [TR 07-25-23] don't actually dispose the device. Dispose has special meaning on Windows. + // Once an object is "Disposed" it cannot be accessed in any way. + ((Device)device).FreeResources(); } } diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 904cdc98..89e17eda 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -11,6 +11,7 @@ using CommunityToolkit.WinUI.Connectivity; #endif using Windows.Devices.Bluetooth; +using Windows.Devices.Enumeration; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; @@ -92,13 +93,29 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv public override void Dispose() { - NativeDevice.Services.ToList().ForEach(s => + FreeResources(false); + } + + internal void FreeResources(bool recreateNativeDevice = true) + { + NativeDevice?.Services?.ToList().ForEach(s => { s?.Service?.Session?.Dispose(); s?.Service?.Dispose(); }); - NativeDevice.BluetoothLEDevice?.Dispose(); + // save these so we can re-create ObservableBluetoothLEDevice if needed + var tempDevInfo = NativeDevice?.DeviceInfo; + var tempDq = NativeDevice?.DispatcherQueue; + + NativeDevice?.BluetoothLEDevice?.Dispose(); + + // the ObservableBluetoothLEDevice doesn't really support the BluetoothLEDevice + // being disposed so we need to recreate it. What we really need is to be able + // to set NativeDevice?.BluetoothLEDevice = null; + if (recreateNativeDevice) + NativeDevice = new ObservableBluetoothLEDevice(tempDevInfo, tempDq); + GC.Collect(); } From 75e0ced4eb5b6ff189ba6e16dc663ac246318848 Mon Sep 17 00:00:00 2001 From: Jeff Haynes Date: Sun, 30 Jul 2023 16:49:32 -0400 Subject: [PATCH 012/193] bonding working --- Source/Plugin.BLE/Android/Adapter.cs | 60 ++++++++++++++++--- .../BondStatusBroadcastReceiver.cs | 35 +++++------ Source/Plugin.BLE/Android/Device.cs | 19 ++++++ Source/Plugin.BLE/Apple/Adapter.cs | 5 ++ Source/Plugin.BLE/Shared/AdapterBase.cs | 2 + .../Plugin.BLE/Shared/Contracts/IAdapter.cs | 2 + .../DeviceBondStateChangedEventArgs.cs | 2 +- Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs | 5 ++ Source/Plugin.BLE/Windows/Adapter.cs | 5 ++ 9 files changed, 108 insertions(+), 27 deletions(-) diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index e2ecb128..83e5c7cf 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -3,12 +3,15 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Android.App; using Android.Bluetooth; using Android.Bluetooth.LE; +using Android.Content; using Android.OS; using Java.Util; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; +using Plugin.BLE.BroadcastReceivers; using Plugin.BLE.Extensions; using Object = Java.Lang.Object; using Trace = Plugin.BLE.Abstractions.Trace; @@ -22,22 +25,37 @@ public class Adapter : AdapterBase private readonly Api18BleScanCallback _api18ScanCallback; private readonly Api21BleScanCallback _api21ScanCallback; + private readonly Dictionary> _bondingTcsForAddress = new(); + public Adapter(BluetoothManager bluetoothManager) { _bluetoothManager = bluetoothManager; _bluetoothAdapter = bluetoothManager.Adapter; + + var bondStatusBroadcastReceiver = new BondStatusBroadcastReceiver(); + Application.Context.RegisterReceiver(bondStatusBroadcastReceiver, + new IntentFilter(BluetoothDevice.ActionBondStateChanged)); + + //forward events from broadcast receiver + bondStatusBroadcastReceiver.BondStateChanged += (s, args) => + { + if (!_bondingTcsForAddress.TryGetValue(args.Address, out var tcs)) + { + return; + } + if (args.State == DeviceBondState.Bonding) + { + return; + } - // TODO: bonding - //var bondStatusBroadcastReceiver = new BondStatusBroadcastReceiver(); - //Application.Context.RegisterReceiver(bondStatusBroadcastReceiver, - // new IntentFilter(BluetoothDevice.ActionBondStateChanged)); + if (args.State == DeviceBondState.Bonded) + { + tcs.TrySetResult(true); + } - ////forward events from broadcast receiver - //bondStatusBroadcastReceiver.BondStateChanged += (s, args) => - //{ - // //DeviceBondStateChanged(this, args); - //}; + tcs.TrySetException(new Exception("Bonding failed.")); + }; if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop) { @@ -49,6 +67,30 @@ public Adapter(BluetoothManager bluetoothManager) } } + public override Task BondAsync(IDevice device) + { + if (device == null) + throw new ArgumentNullException(nameof(device)); + + if (!(device.NativeDevice is BluetoothDevice nativeDevice)) + throw new ArgumentException("Invalid device type"); + + if (nativeDevice.BondState == Bond.Bonded) + return Task.CompletedTask; + + var tcs = new TaskCompletionSource(); + + _bondingTcsForAddress.Add(nativeDevice.Address!, tcs); + + if (!nativeDevice.CreateBond()) + { + _bondingTcsForAddress.Remove(nativeDevice.Address); + throw new Exception("Bonding failed"); + } + + return tcs.Task; + } + protected override Task StartScanningForDevicesNativeAsync(ScanFilterOptions scanFilterOptions, bool allowDuplicatesKey, CancellationToken scanCancellationToken) { if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop) diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index 96bba489..68dae876 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -3,11 +3,9 @@ using Android.Content; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.EventArgs; -using Plugin.BLE.Android; namespace Plugin.BLE.BroadcastReceivers { - //[BroadcastReceiver] public class BondStatusBroadcastReceiver : BroadcastReceiver { public event EventHandler BondStateChanged; @@ -15,27 +13,30 @@ public class BondStatusBroadcastReceiver : BroadcastReceiver public override void OnReceive(Context context, Intent intent) { var bondState = (Bond)intent.GetIntExtra(BluetoothDevice.ExtraBondState, (int)Bond.None); - //ToDo - var device = new Device(null, (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice), null); - Console.WriteLine(bondState.ToString()); - if (BondStateChanged == null) return; + var device = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); - switch (bondState) + var address = device?.Address; + + if (address == null) { - case Bond.None: - BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.NotBonded }); - break; + return; + } - case Bond.Bonding: - BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.Bonding }); - break; + var state = Convert(bondState); - case Bond.Bonded: - BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Device = device, State = DeviceBondState.Bonded }); - break; + BondStateChanged?.Invoke(this, new DeviceBondStateChangedEventArgs { Address = address, State = state }); + } - } + private static DeviceBondState Convert(Bond state) + { + return state switch + { + Bond.None => DeviceBondState.NotBonded, + Bond.Bonding => DeviceBondState.Bonding, + Bond.Bonded => DeviceBondState.Bonded, + _ => DeviceBondState.NotBonded + }; } } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index d9773b1c..a8d29c9f 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -35,6 +35,8 @@ public class Device : DeviceBase /// the registration must be disposed to avoid disconnecting after a connection /// private CancellationTokenRegistration _connectCancellationTokenRegistration; + + private TaskCompletionSource _bondCompleteTaskCompletionSource; /// /// the connect paramaters used when connecting to this device @@ -144,6 +146,23 @@ public void Connect(ConnectParameters connectParameters, CancellationToken cance } } + public Task BondAsync() + { + _bondCompleteTaskCompletionSource = new TaskCompletionSource(); + NativeDevice.CreateBond(); + return _bondCompleteTaskCompletionSource.Task; + } + + internal void SetBondState(DeviceBondState state) + { + if (state != DeviceBondState.Bonded) + { + return; + } + + _bondCompleteTaskCompletionSource?.TrySetResult(true); + } + private void ConnectToGattForceBleTransportAPI(bool autoconnect, CancellationToken cancellationToken) { //This parameter is present from API 18 but only public from API 23 diff --git a/Source/Plugin.BLE/Apple/Adapter.cs b/Source/Plugin.BLE/Apple/Adapter.cs index bfe85544..7561f4fe 100644 --- a/Source/Plugin.BLE/Apple/Adapter.cs +++ b/Source/Plugin.BLE/Apple/Adapter.cs @@ -149,6 +149,11 @@ public Adapter(CBCentralManager centralManager, IBleCentralManagerDelegate bleCe }; } + public override Task BondAsync(IDevice device) + { + throw new NotSupportedException(); + } + protected override async Task StartScanningForDevicesNativeAsync(ScanFilterOptions scanFilterOptions, bool allowDuplicatesKey, CancellationToken scanCancellationToken) { #if NET6_0_OR_GREATER || MACCATALYST diff --git a/Source/Plugin.BLE/Shared/AdapterBase.cs b/Source/Plugin.BLE/Shared/AdapterBase.cs index 869ee23a..478771d3 100644 --- a/Source/Plugin.BLE/Shared/AdapterBase.cs +++ b/Source/Plugin.BLE/Shared/AdapterBase.cs @@ -327,6 +327,8 @@ public void HandleConnectionFail(IDevice device, string errorMessage) }); } + public abstract Task BondAsync(IDevice device); + /// /// Native implementation of StartScanningForDevicesAsync. /// diff --git a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs index 325b8d41..4d5ca142 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs @@ -76,6 +76,8 @@ public interface IAdapter /// IReadOnlyList ConnectedDevices { get; } + public Task BondAsync(IDevice device); + /// /// Starts scanning for BLE devices that fulfill the . /// DeviceDiscovered will only be called, if returns true for the discovered device. diff --git a/Source/Plugin.BLE/Shared/EventArgs/DeviceBondStateChangedEventArgs.cs b/Source/Plugin.BLE/Shared/EventArgs/DeviceBondStateChangedEventArgs.cs index ff582c8c..4b596e3f 100644 --- a/Source/Plugin.BLE/Shared/EventArgs/DeviceBondStateChangedEventArgs.cs +++ b/Source/Plugin.BLE/Shared/EventArgs/DeviceBondStateChangedEventArgs.cs @@ -10,7 +10,7 @@ public class DeviceBondStateChangedEventArgs : System.EventArgs /// /// The device. /// - public IDevice Device { get; set; } + public string Address { get; set; } /// /// The bond state. /// diff --git a/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs b/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs index 79d2aa4a..d8589f3b 100644 --- a/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs +++ b/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs @@ -14,6 +14,11 @@ public override Task ConnectToKnownDeviceAsync(Guid deviceGuid, Connect return Task.FromResult(null); } + public override Task BondAsync(IDevice device) + { + return Task.FromResult(0); + } + protected override Task StartScanningForDevicesNativeAsync(ScanFilterOptions scanFilterOptions, bool allowDuplicatesKey, CancellationToken scanCancellationToken) { TraceUnavailability(); diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 88e18ac9..17836fb9 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -34,6 +34,11 @@ public Adapter(BluetoothLEHelper bluetoothHelper) _dq = DispatcherQueue.GetForCurrentThread(); } + public override Task BondAsync(IDevice device) + { + throw new NotImplementedException(); + } + protected override Task StartScanningForDevicesNativeAsync(ScanFilterOptions scanFilterOptions, bool allowDuplicatesKey, CancellationToken scanCancellationToken) { var serviceUuids = scanFilterOptions?.ServiceUuids; From 24d04942ccbe59ddd3501e966fc64492a7a79d1d Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 5 Aug 2023 15:58:08 +0200 Subject: [PATCH 013/193] Release 3.0.0-beta.5 * update version in csproj files * amend changelog.md --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- doc/changelog.md | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index f60b297b..07d6596c 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid10.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE - 3.0.0-beta.4 + 3.0.0-beta.5 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil MvvmCross.Plugin.BLE diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index bc9a933c..f114d371 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid10.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 Plugin.BLE Plugin.BLE - 3.0.0-beta.4 + 3.0.0-beta.5 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil Plugin.BLE diff --git a/doc/changelog.md b/doc/changelog.md index e80d09a7..54eadac9 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,11 @@ ## 3.0 MAUI +#### 3.0.0-beta.5 +- #721 Fix Windows connect/disconnect issues (fixes #423, #528, #536, #620) +- #719 Fixed crash with incorrect data in advertisement data on Android (fixes #567, #713) +- #708 Coding style fixes (fixes #693) + #### 3.0.0-beta.4 - #669 Return error codes to application - #679 Querying adapter capabilities: extended advertisements & coded PHY From 2f71abc49c2dc6baae7a270eaf626a0855d5111f Mon Sep 17 00:00:00 2001 From: travis012 Date: Sat, 5 Aug 2023 10:02:04 -0600 Subject: [PATCH 014/193] Add WIndows support for RequestMtuAsync. #727 --- Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 1 + Source/Plugin.BLE/Windows/Device.cs | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index 76ff49a0..19537772 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -87,6 +87,7 @@ public interface IDevice : IDisposable /// Important: /// On Android: This function will only work with API level 21 and higher. Other API level will get an default value as function result. /// On iOS: Requesting MTU sizes is not supported by iOS. The function will return the current negotiated MTU between master / slave. + /// On Windows: Requesting MTU sizes is not directly supported. Windows will always try and negotiate the maximum MTU between master / slave. The function will return the current negotiated MTU between master / slave. /// /// /// A task that represents the asynchronous operation. The result contains the negotiated MTU size between master and slave diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 89e17eda..660ebefc 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -79,10 +79,11 @@ protected override DeviceState GetState() return NativeDevice.IsPaired ? DeviceState.Limited : DeviceState.Disconnected; } - protected override Task RequestMtuNativeAsync(int requestValue) + protected override async Task RequestMtuNativeAsync(int requestValue) { - Trace.Message("Request MTU not supported in UWP"); - return Task.FromResult(-1); + var devId = BluetoothDeviceId.FromId(NativeDevice.BluetoothLEDevice.DeviceId); + using var gattSession = await Windows.Devices.Bluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); + return gattSession.MaxPduSize; } protected override bool UpdateConnectionIntervalNative(ConnectionInterval interval) From 6c527470defa02f120cd40b80635daa43c487190 Mon Sep 17 00:00:00 2001 From: mos Date: Sun, 6 Aug 2023 18:10:52 +0200 Subject: [PATCH 015/193] Removed CreateBond and ForgetBond for the moment --- Source/Plugin.BLE/Android/Device.cs | 41 ------------------- Source/Plugin.BLE/Apple/Device.cs | 13 ------ Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 23 ----------- Source/Plugin.BLE/Shared/DeviceBase.cs | 5 --- Source/Plugin.BLE/Windows/Device.cs | 12 ------ 5 files changed, 94 deletions(-) diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index 7f8b5940..c3d01ee6 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -440,46 +440,5 @@ protected override DeviceBondState GetBondState() return NativeDevice.BondState.FromNative(); } - public override bool CreateBond() - { - if (NativeDevice == null) - { - Trace.Message($"[Warning]: Can't create bond for {Name}. BluetoothDevice is null."); - return false; - } - else - { - return NativeDevice.CreateBond(); - } - } - - public override bool ForgetBond() - { - if (NativeDevice == null) - { - Trace.Message($"[Warning]: Can't remove bond of {Name}. BluetoothDevice is null."); - return false; - } - - if (BondState == DeviceBondState.NotBonded) - { - Trace.Message($"Device {Name} is not bonded."); - return true; - } - - try - { - // removeBond is not part of the API but was always available together with CreateBond (just hidden). - var removeBond = NativeDevice.Class.GetMethod("removeBond"); - // calling removeBond will disconnect! - return (bool)removeBond.Invoke(NativeDevice); - } - catch (Exception ex) - { - Trace.Message($"RemoveBond of {Name} failed. {ex.Message}"); - return false; - } - } - } } diff --git a/Source/Plugin.BLE/Apple/Device.cs b/Source/Plugin.BLE/Apple/Device.cs index 5f6b5e0a..3f92af0c 100644 --- a/Source/Plugin.BLE/Apple/Device.cs +++ b/Source/Plugin.BLE/Apple/Device.cs @@ -185,18 +185,5 @@ protected override DeviceBondState GetBondState() return DeviceBondState.NotSupported; } - public override bool CreateBond() - { - Trace.Message("Cannot initiate a bonding request on iOS."); - return false; - } - - public override bool ForgetBond() - { - Trace.Message("Cannot forget bonding on iOS."); - return false; - } - - } } diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index 4b2d0321..aecec1cb 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -127,28 +127,5 @@ public interface IDevice : IDisposable /// DeviceBondState BondState { get; } - /// - /// Initiates a bonding request. - /// To establish an additional security level in the commumication between server and client pairing or bonding is used. - /// Pairing does the key exchange and encryption/decryption for one connection between server and client. - /// Bonding does pairing and remembers the keys in a secure storage so that they can be used for the next connection. - /// You have to subscribe to Adapter.DeviceBondStateChanged to get the current state. Typically first bonding and later bonded. - /// Important: - /// On iOS: - /// Initiating a bonding request is not supported by iOS. The function simply returns false. - /// On Android: Added in API level 19. - /// Android system services will handle the necessary user interactions to confirm and complete the bonding process. - /// For apps targeting Build.VERSION_CODES#R or lower, this requires the Manifest.permission#BLUETOOTH_ADMIN permission - /// which can be gained with a simple ßuses-permissionß manifest tag. For apps targeting Build.VERSION_CODES#S or or higher, - /// this requires the Manifest.permission#BLUETOOTH_CONNECT permission which can be gained with Activity.requestPermissions(String[], int). - /// - /// True if bonding could be requested. On iOS it will always return false. - bool CreateBond(); - - /// - /// Forgets the bonding between server and client. - /// - /// - bool ForgetBond(); } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Shared/DeviceBase.cs b/Source/Plugin.BLE/Shared/DeviceBase.cs index b48ddfb6..98c0bd5a 100644 --- a/Source/Plugin.BLE/Shared/DeviceBase.cs +++ b/Source/Plugin.BLE/Shared/DeviceBase.cs @@ -260,10 +260,5 @@ public override int GetHashCode() public DeviceBondState BondState => GetBondState(); - public abstract bool CreateBond(); - - public abstract bool ForgetBond(); - - } } diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 02045579..3e1bb651 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -112,17 +112,5 @@ protected override DeviceBondState GetBondState() return DeviceBondState.NotSupported; } - public override bool CreateBond() - { - Trace.Message("Cannot initiate a bonding request on iOS."); - return false; - } - - public override bool ForgetBond() - { - Trace.Message("Cannot forget bonding on iOS."); - return false; - } - } } From 7fd39192f6692ffda7f7f458eafe4f94a50bbaa4 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Wed, 9 Aug 2023 14:31:40 +0200 Subject: [PATCH 016/193] Add better summary --- Source/Plugin.BLE/Shared/Contracts/IAdapter.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs index 54353e34..b26f5c7f 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs @@ -80,6 +80,21 @@ public interface IAdapter /// IReadOnlyList ConnectedDevices { get; } + /// + /// Initiates a bonding request. + /// To establish an additional security level in the commumication between server and client pairing or bonding is used. + /// Pairing does the key exchange and encryption/decryption for one connection between server and client. + /// Bonding does pairing and remembers the keys in a secure storage so that they can be used for the next connection. + /// You have to subscribe to Adapter.DeviceBondStateChanged to get the current state. Typically first bonding and later bonded. + /// Important: + /// On iOS: + /// Initiating a bonding request is not supported by iOS. The function simply returns false. + /// On Android: Added in API level 19. + /// Android system services will handle the necessary user interactions to confirm and complete the bonding process. + /// For apps targeting Build.VERSION_CODES#R or lower, this requires the Manifest.permission#BLUETOOTH_ADMIN permission + /// which can be gained with a simple 'uses-permission' manifest tag. For apps targeting Build.VERSION_CODES#S or or higher, + /// this requires the Manifest.permission#BLUETOOTH_CONNECT permission which can be gained with Activity.requestPermissions(String[], int). + /// public Task BondAsync(IDevice device); /// @@ -190,4 +205,4 @@ public interface IAdapter /// true if extended advertising is supported, otherwise false. bool SupportsCodedPHY(); } -} \ No newline at end of file +} From fe5c893c36cef7e960d9fa3eb0938d57860acd88 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Thu, 10 Aug 2023 00:09:33 +0200 Subject: [PATCH 017/193] Code styling --- Source/Plugin.BLE/Android/Adapter.cs | 15 +++++++++----- Source/Plugin.BLE/Android/Device.cs | 4 +--- Source/Plugin.BLE/Apple/Device.cs | 7 ++----- Source/Plugin.BLE/Shared/AdapterBase.cs | 8 +++----- .../Plugin.BLE/Shared/Contracts/IAdapter.cs | 4 ++-- Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 7 ++----- Source/Plugin.BLE/Shared/DeviceBase.cs | 20 ++++++++++++++----- Source/Plugin.BLE/Windows/Device.cs | 4 +--- 8 files changed, 36 insertions(+), 33 deletions(-) diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index 712f3b3f..c897fffa 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -76,13 +76,19 @@ public Adapter(BluetoothManager bluetoothManager) public override Task BondAsync(IDevice device) { if (device == null) - throw new ArgumentNullException(nameof(device)); + { + throw new ArgumentNullException(nameof(device)); + } if (!(device.NativeDevice is BluetoothDevice nativeDevice)) - throw new ArgumentException("Invalid device type"); + { + throw new ArgumentException("Invalid device type"); + } if (nativeDevice.BondState == Bond.Bonded) - return Task.CompletedTask; + { + return Task.CompletedTask; + } var tcs = new TaskCompletionSource(); @@ -445,7 +451,7 @@ public override void OnScanResult(ScanCallbackType callbackType, ScanResult resu (Build.VERSION.SdkInt >= BuildVersionCodes.O) #endif ? result.IsConnectable : true - ); ; + ); //Device device; //if (result.ScanRecord.ManufacturerSpecificData.Size() > 0) @@ -463,7 +469,6 @@ public override void OnScanResult(ScanCallbackType callbackType, ScanResult resu //} _adapter.HandleDiscoveredDevice(device); - } } } diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index 66019594..14e77534 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -455,8 +455,7 @@ public override bool SupportsIsConnectable (Build.VERSION.SdkInt >= BuildVersionCodes.O); #endif } - - + protected override DeviceBondState GetBondState() { if (NativeDevice == null) @@ -466,6 +465,5 @@ protected override DeviceBondState GetBondState() } return NativeDevice.BondState.FromNative(); } - } } diff --git a/Source/Plugin.BLE/Apple/Device.cs b/Source/Plugin.BLE/Apple/Device.cs index 3f92af0c..c16e3e53 100644 --- a/Source/Plugin.BLE/Apple/Device.cs +++ b/Source/Plugin.BLE/Apple/Device.cs @@ -173,17 +173,14 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv Trace.Message("Cannot update connection inteval on iOS."); return false; } - - + public override bool IsConnectable { get; protected set; } public override bool SupportsIsConnectable { get => true; } - - + protected override DeviceBondState GetBondState() { return DeviceBondState.NotSupported; } - } } diff --git a/Source/Plugin.BLE/Shared/AdapterBase.cs b/Source/Plugin.BLE/Shared/AdapterBase.cs index 37868436..cf25c3cf 100644 --- a/Source/Plugin.BLE/Shared/AdapterBase.cs +++ b/Source/Plugin.BLE/Shared/AdapterBase.cs @@ -74,8 +74,7 @@ public bool IsScanning /// Default: /// public ScanMode ScanMode { get; set; } = ScanMode.LowPower; - - + /// /// Scan match mode defines how agressively we look for adverts /// @@ -335,7 +334,8 @@ public void HandleConnectionFail(IDevice device, string errorMessage) ErrorMessage = errorMessage }); } - + + /// public abstract Task BondAsync(IDevice device); /// @@ -379,10 +379,8 @@ protected void HandleDeviceBondStateChanged(DeviceBondStateChangedEventArgs args /// /// Returns all BLE device bonded to the system. /// - /// protected abstract IReadOnlyList GetBondedDevices(); - /// /// Indicates whether extended advertising (BLE5) is supported. /// diff --git a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs index b26f5c7f..bab0553d 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IAdapter.cs @@ -82,7 +82,7 @@ public interface IAdapter /// /// Initiates a bonding request. - /// To establish an additional security level in the commumication between server and client pairing or bonding is used. + /// To establish an additional security level in the communication between server and client pairing or bonding is used. /// Pairing does the key exchange and encryption/decryption for one connection between server and client. /// Bonding does pairing and remembers the keys in a secure storage so that they can be used for the next connection. /// You have to subscribe to Adapter.DeviceBondStateChanged to get the current state. Typically first bonding and later bonded. @@ -165,7 +165,7 @@ public interface IAdapter /// /// Connection parameters. Contains platform specific parameters needed to achieved connection. The default value is None. /// The token to monitor for cancellation requests. The default value is None. - /// + /// The connected device. Task ConnectToKnownDeviceAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default); /// diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index aecec1cb..cd7398d3 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -107,7 +107,6 @@ public interface IDevice : IDisposable /// The requested interval (High/Low/Normal) bool UpdateConnectionInterval(ConnectionInterval interval); - /// /// Gets the information if the device has hinted during advertising that the device is connectable. /// This information is not pat of an advertising record. It's determined from the PDU header. @@ -120,12 +119,10 @@ public interface IDevice : IDisposable /// True, if device supports IsConnectable else False /// bool SupportsIsConnectable { get; } - - + /// /// Gets the bonding state of a device. /// DeviceBondState BondState { get; } - } -} \ No newline at end of file +} diff --git a/Source/Plugin.BLE/Shared/DeviceBase.cs b/Source/Plugin.BLE/Shared/DeviceBase.cs index 98c0bd5a..a3910392 100644 --- a/Source/Plugin.BLE/Shared/DeviceBase.cs +++ b/Source/Plugin.BLE/Shared/DeviceBase.cs @@ -202,8 +202,7 @@ public virtual void Dispose() public void ClearServices() { this.CancelEverythingAndReInitialize(); - - + lock (KnownServices) { foreach (var service in KnownServices) @@ -251,14 +250,25 @@ public override int GetHashCode() return Id.GetHashCode(); } + /// + /// Reflects if the device is connectable. + /// Only supported if is true. + /// public abstract bool IsConnectable { get; protected set; } + /// + /// Shows whether the device supports the . + /// public abstract bool SupportsIsConnectable { get; } - - + + /// + /// Gets the of the device. + /// protected abstract DeviceBondState GetBondState(); + /// + /// Gets the of the device. + /// public DeviceBondState BondState => GetBondState(); - } } diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index b22675ed..9869bc18 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -122,12 +122,10 @@ internal void FreeResources(bool recreateNativeDevice = true) public override bool IsConnectable { get; protected set; } public override bool SupportsIsConnectable { get => true; } - - + protected override DeviceBondState GetBondState() { return DeviceBondState.NotSupported; } - } } From 8cfbba4dad07efdbb37df189a44c3e3a8b7377f2 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Thu, 10 Aug 2023 00:25:34 +0200 Subject: [PATCH 018/193] Use simple type --- .../Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index b151b346..cde9d50c 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -34,7 +34,7 @@ public override void OnReceive(Context context, Intent intent) return; } - DeviceBondState bondState = extraBondState.FromNative(); + var bondState = extraBondState.FromNative(); BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Address = address, Device = device, State = bondState }); ; } From 8db129675e57e3713362b9b24f05623bc5759aaa Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Thu, 10 Aug 2023 00:26:55 +0200 Subject: [PATCH 019/193] Remove unused method --- Source/Plugin.BLE/Android/Device.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index 14e77534..97f55b2e 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -154,16 +154,6 @@ public Task BondAsync() return _bondCompleteTaskCompletionSource.Task; } - internal void SetBondState(DeviceBondState state) - { - if (state != DeviceBondState.Bonded) - { - return; - } - - _bondCompleteTaskCompletionSource?.TrySetResult(true); - } - private void ConnectToGattForceBleTransportAPI(bool autoconnect, CancellationToken cancellationToken) { //This parameter is present from API 18 but only public from API 23 From c91c21325ade5ff9f78c8c6efad0d58aaf2e4c96 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Thu, 10 Aug 2023 01:01:11 +0200 Subject: [PATCH 020/193] Use non-obsoleted method if possible --- .../BondStatusBroadcastReceiver.cs | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index cde9d50c..1a03d1e0 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -1,7 +1,7 @@ using System; using Android.Bluetooth; using Android.Content; -using Plugin.BLE.Abstractions; +using Android.OS; using Plugin.BLE.Abstractions.EventArgs; using Plugin.BLE.Android; using Plugin.BLE.Extensions; @@ -10,33 +10,56 @@ namespace Plugin.BLE.BroadcastReceivers { public class BondStatusBroadcastReceiver : BroadcastReceiver { - public event EventHandler BondStateChanged; + private readonly Adapter _broadcastAdapter; - Adapter BroadCastAdapter; + public event EventHandler BondStateChanged; public BondStatusBroadcastReceiver(Adapter adapter) { - BroadCastAdapter = adapter; + _broadcastAdapter = adapter; } public override void OnReceive(Context context, Intent intent) { - if (BondStateChanged == null) return; + if (BondStateChanged == null) + { + return; + } var extraBondState = (Bond)intent.GetIntExtra(BluetoothDevice.ExtraBondState, (int)Bond.None); - var bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); - var device = new Device(BroadCastAdapter, bluetoothDevice, null, 0); - - var address = bluetoothDevice?.Address; - if (address == null) + BluetoothDevice bluetoothDevice; + + // As older versions of .NET (pre7) don't include the Tiramisu version code, handle it manually. +#if NET7_0_OR_GREATER + if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu) +#else + if ((int)Build.VERSION.SdkInt >= 33) +#endif { - return; +#if NET7_0_OR_GREATER +#pragma warning disable CA1416 + bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice, Java.Lang.Class.FromType(typeof(BluetoothDevice))); +#pragma warning restore CA1416 +#else +#pragma warning disable CA1422 + bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); +#pragma warning restore CA1422 +#endif } + else + { +#pragma warning disable CA1422 + bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); +#pragma warning restore CA1422 + } + + var device = new Device(_broadcastAdapter, bluetoothDevice, null); + + var address = bluetoothDevice?.Address ?? string.Empty; var bondState = extraBondState.FromNative(); - BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Address = address, Device = device, State = bondState }); ; + BondStateChanged(this, new DeviceBondStateChangedEventArgs() { Address = address, Device = device, State = bondState }); } - } } From 8e74fcfdb7e62f90b754f4a0deb7069585122f88 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Thu, 10 Aug 2023 08:49:37 +0200 Subject: [PATCH 021/193] Remove warning disable --- .../BroadcastReceivers/BondStatusBroadcastReceiver.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index 1a03d1e0..1c96b214 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -38,20 +38,14 @@ public override void OnReceive(Context context, Intent intent) #endif { #if NET7_0_OR_GREATER -#pragma warning disable CA1416 bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice, Java.Lang.Class.FromType(typeof(BluetoothDevice))); -#pragma warning restore CA1416 #else -#pragma warning disable CA1422 bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); -#pragma warning restore CA1422 #endif } else { -#pragma warning disable CA1422 bluetoothDevice = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice); -#pragma warning restore CA1422 } var device = new Device(_broadcastAdapter, bluetoothDevice, null); From d13cdf3b1eb636579885c6d55e03da7a856b1698 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Thu, 10 Aug 2023 08:50:01 +0200 Subject: [PATCH 022/193] Use OperatingSystem.IsAndroidVersionAtLeast Instead of self-implemented check --- .../Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs index 1c96b214..bc4a0ac5 100644 --- a/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs +++ b/Source/Plugin.BLE/Android/BroadcastReceivers/BondStatusBroadcastReceiver.cs @@ -34,7 +34,7 @@ public override void OnReceive(Context context, Intent intent) #if NET7_0_OR_GREATER if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu) #else - if ((int)Build.VERSION.SdkInt >= 33) + if (OperatingSystem.IsAndroidVersionAtLeast(33)) #endif { #if NET7_0_OR_GREATER From 6e8ae76eeffc725f63ecf38742063b40cfb1bb61 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Thu, 10 Aug 2023 10:06:34 +0200 Subject: [PATCH 023/193] Fix DisconnectAndClose method when Device is null See #725 --- Source/Plugin.BLE/Android/Device.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index 97f55b2e..37cfe97f 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -107,6 +107,11 @@ protected override async Task GetServiceNativeAsync(Guid id) private async Task> DiscoverServicesInternal() { + if (_gatt == null) + { + Trace.Message("[Warning]: Can't discover services {0}. Gatt is null.", Name); + } + return await TaskBuilder .FromEvent, EventHandler, EventHandler>( execute: () => @@ -118,7 +123,14 @@ private async Task> DiscoverServicesInternal() }, getCompleteHandler: (complete, reject) => ((sender, args) => { - complete(_gatt.Services.Select(service => new Service(service, _gatt, _gattCallback, this)).ToList()); + if (_gatt.Services == null) + { + complete(new List()); + } + else + { + complete(_gatt.Services.Select(service => new Service(service, _gatt, _gattCallback, this)).ToList()); + } }), subscribeComplete: handler => _gattCallback.ServicesDiscovered += handler, unsubscribeComplete: handler => _gattCallback.ServicesDiscovered -= handler, @@ -194,8 +206,8 @@ private void ConnectToGattForceBleTransportAPI(bool autoconnect, CancellationTok private void DisconnectAndClose(BluetoothGatt gatt) { - gatt.Disconnect(); - gatt.Close(); + gatt?.Disconnect(); + gatt?.Close(); } /// From 7654a6ec24ce0fa4145c2fcb28a16af76be1fe74 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Fri, 11 Aug 2023 02:45:07 +0200 Subject: [PATCH 024/193] Remove duplicated usings --- Source/Plugin.BLE/Android/Adapter.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index c897fffa..5180ea97 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -4,17 +4,14 @@ using System.Threading; using System.Threading.Tasks; using Android.App; -using Android.App; using Android.Bluetooth; using Android.Bluetooth.LE; using Android.Content; -using Android.Content; using Android.OS; using Java.Util; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.BroadcastReceivers; -using Plugin.BLE.BroadcastReceivers; using Plugin.BLE.Extensions; using Object = Java.Lang.Object; using Trace = Plugin.BLE.Abstractions.Trace; From 76f38571099a0673f30468c496ec9c9161a8568a Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Fri, 11 Aug 2023 02:45:13 +0200 Subject: [PATCH 025/193] Remove unused usings --- .../Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs b/Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs index 0bd444c0..b9d73c0c 100644 --- a/Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs +++ b/Source/Plugin.BLE/Android/Extensions/DeviceBondStateExtension.cs @@ -1,8 +1,5 @@ using Android.Bluetooth; using Plugin.BLE.Abstractions; -using System; -using System.Collections.Generic; -using System.Text; namespace Plugin.BLE.Extensions { From e8636a2a4883e410c1428109908121cc29cc76c2 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Fri, 11 Aug 2023 02:46:03 +0200 Subject: [PATCH 026/193] Fix ci check warning (XML comment not on valid element) --- Source/Plugin.BLE/Shared/AdvertisementRecord.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Source/Plugin.BLE/Shared/AdvertisementRecord.cs b/Source/Plugin.BLE/Shared/AdvertisementRecord.cs index 6372fa07..8245731f 100644 --- a/Source/Plugin.BLE/Shared/AdvertisementRecord.cs +++ b/Source/Plugin.BLE/Shared/AdvertisementRecord.cs @@ -173,13 +173,7 @@ public enum AdvertisementRecordType /// /// «Manufacturer Specific Data» Bluetooth Core Specification: /// - ManufacturerSpecificData = 0xFF, - - /// - /// The is connectable flag. This is only reliable for the ios imlementation. The android stack does not expose this in the client. - /// - // obsolete - // IsConnectable = 0xAA + ManufacturerSpecificData = 0xFF } /// @@ -213,4 +207,4 @@ public override string ToString() return string.Format("Adv rec [Type {0}; Data {1}]", Type, Data.ToHexString()); } } -} \ No newline at end of file +} From 47ecaecfb8647cc4eba016760a337644bcebc823 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Fri, 11 Aug 2023 02:58:03 +0200 Subject: [PATCH 027/193] Add missing summaries --- Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs b/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs index d75c87cd..065f1757 100644 --- a/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs +++ b/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs @@ -4,6 +4,9 @@ namespace Plugin.BLE.Abstractions.Utils { + /// + /// Builder class to create event driven Tasks that may be marshalled onto the main thread. + /// public static class TaskBuilder { /// @@ -21,6 +24,9 @@ public static class TaskBuilder private static readonly SemaphoreSlim QueueSemaphore = new SemaphoreSlim(1); + /// + /// Creates an event driven chain of Actions. + /// public static async Task FromEvent( Action execute, Func, Action, TEventHandler> getCompleteHandler, @@ -55,6 +61,9 @@ public static async Task FromEvent + /// Queues the given onto the main thread and executes it. + /// public static Task EnqueueOnMainThreadAsync(Action execute, CancellationToken token = default) => SafeEnqueueAndExecute(execute, token); @@ -108,4 +117,4 @@ private static async Task SafeEnqueueAndExecute(Action execute return await (tcs?.Task ?? Task.FromResult(default(TReturn))); } } -} \ No newline at end of file +} From 60c128ffae28b31a3009804fd939c586acc8d7f5 Mon Sep 17 00:00:00 2001 From: "Kerkering, Tobias" Date: Sun, 13 Aug 2023 19:42:24 +0200 Subject: [PATCH 028/193] Cleanup dictionary --- Source/Plugin.BLE/Android/Adapter.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/Plugin.BLE/Android/Adapter.cs b/Source/Plugin.BLE/Android/Adapter.cs index 5180ea97..c0cfcee0 100644 --- a/Source/Plugin.BLE/Android/Adapter.cs +++ b/Source/Plugin.BLE/Android/Adapter.cs @@ -86,10 +86,17 @@ public override Task BondAsync(IDevice device) { return Task.CompletedTask; } - - var tcs = new TaskCompletionSource(); - - _bondingTcsForAddress.Add(nativeDevice.Address!, tcs); + + var deviceAddress = nativeDevice.Address!; + if (_bondingTcsForAddress.TryGetValue(deviceAddress, out var tcs)) + { + tcs.TrySetException(new Exception("Bonding failed on old try.")); + _bondingTcsForAddress.Remove(deviceAddress); + } + + var taskCompletionSource = new TaskCompletionSource(); + + _bondingTcsForAddress.Add(nativeDevice.Address!, taskCompletionSource); if (!nativeDevice.CreateBond()) { @@ -97,7 +104,7 @@ public override Task BondAsync(IDevice device) throw new Exception("Bonding failed"); } - return tcs.Task; + return taskCompletionSource.Task; } protected override Task StartScanningForDevicesNativeAsync(ScanFilterOptions scanFilterOptions, bool allowDuplicatesKey, CancellationToken scanCancellationToken) From ee8905fca57110607065bbbbad17f4dc0cd28bcf Mon Sep 17 00:00:00 2001 From: IanBUK <78267962+IanBUK@users.noreply.github.com> Date: Sat, 19 Aug 2023 16:26:42 +0100 Subject: [PATCH 029/193] Add Maui client Builds, runs and works on Samsung A8 tablet, crashes at startup on MacOS (M1 MacBook Air Ventura 13.5) --- .gitignore | 1 + Source/BLE.Client/BLE.Client.Maui/App.xaml | 15 + Source/BLE.Client/BLE.Client.Maui/App.xaml.cs | 21 + .../BLE.Client/BLE.Client.Maui/AppShell.xaml | 14 + .../BLE.Client.Maui/AppShell.xaml.cs | 10 + .../BLE.Client.Maui/BLE.Client.Maui.csproj | 85 ++++ .../Helpers/IPlatformHelpers.cs | 7 + .../BLE.Client/BLE.Client.Maui/MainPage.xaml | 47 ++ .../BLE.Client.Maui/MainPage.xaml.cs | 37 ++ .../BLE.Client/BLE.Client.Maui/MauiProgram.cs | 40 ++ .../BLE.Client.Maui/Models/BLEDevice.cs | 20 + .../BLE.Client.Maui/Models/BLEDevices.cs | 10 + .../BLE.Client.Maui/Models/DeviceState.cs | 30 ++ .../Platforms/Android/AndroidManifest.xml | 19 + .../Platforms/Android/DroidPlatformHelpers.cs | 48 +++ .../Platforms/Android/MainActivity.cs | 11 + .../Platforms/Android/MainApplication.cs | 16 + .../Android/Resources/values/colors.xml | 7 + .../Platforms/MacCatalyst/AppDelegate.cs | 10 + .../Platforms/MacCatalyst/Info.plist | 30 ++ .../Platforms/MacCatalyst/Program.cs | 16 + .../BLE.Client.Maui/Platforms/Tizen/Main.cs | 17 + .../Platforms/Tizen/tizen-manifest.xml | 15 + .../Platforms/Windows/App.xaml | 9 + .../Platforms/Windows/App.xaml.cs | 25 ++ .../Platforms/Windows/Package.appxmanifest | 47 ++ .../Platforms/Windows/app.manifest | 16 + .../Platforms/iOS/AppDelegate.cs | 10 + .../BLE.Client.Maui/Platforms/iOS/Info.plist | 32 ++ .../BLE.Client.Maui/Platforms/iOS/Program.cs | 16 + .../Properties/launchSettings.json | 8 + .../Resources/AppIcon/appicon.svg | 5 + .../Resources/AppIcon/appiconfg.svg | 8 + .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 107184 bytes .../Resources/Fonts/OpenSans-Semibold.ttf | Bin 0 -> 111076 bytes .../Resources/Images/dotnet_bot.svg | 95 ++++ .../Resources/Images/scanning.gif | Bin 0 -> 1687672 bytes .../Resources/Images/spinner.gif | Bin 0 -> 562943 bytes .../Resources/Raw/AboutAssets.txt | 17 + .../Resources/Splash/splash.svg | 9 + .../Resources/Styles/Colors.xaml | 44 ++ .../Resources/Styles/Styles.xaml | 406 ++++++++++++++++++ .../BLE.Client.Maui/Services/AlertService.cs | 48 +++ .../BLE.Client.Maui/Services/IAlertService.cs | 17 + .../ViewModels/BLEDeviceViewModel.cs | 134 ++++++ .../ViewModels/BLEScannerViewModel.cs | 337 +++++++++++++++ .../BLE.Client.Maui/Views/BLEScanner.xaml | 52 +++ .../BLE.Client.Maui/Views/BLEScanner.xaml.cs | 15 + Source/BLE.Mac.Maui.sln | 324 ++++++++++++++ .../MvvmCross.Plugins.BLE.csproj | 2 +- 50 files changed, 2201 insertions(+), 1 deletion(-) create mode 100644 Source/BLE.Client/BLE.Client.Maui/App.xaml create mode 100644 Source/BLE.Client/BLE.Client.Maui/App.xaml.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/AppShell.xaml create mode 100644 Source/BLE.Client/BLE.Client.Maui/AppShell.xaml.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj create mode 100644 Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/MainPage.xaml create mode 100644 Source/BLE.Client/BLE.Client.Maui/MainPage.xaml.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Models/BLEDevice.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Models/BLEDevices.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Models/DeviceState.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Android/AndroidManifest.xml create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Android/DroidPlatformHelpers.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Android/MainActivity.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Android/MainApplication.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Android/Resources/values/colors.xml create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/MacCatalyst/AppDelegate.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/MacCatalyst/Info.plist create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/MacCatalyst/Program.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Tizen/Main.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Tizen/tizen-manifest.xml create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/App.xaml create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/App.xaml.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/Package.appxmanifest create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/app.manifest create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/iOS/AppDelegate.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/iOS/Info.plist create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/iOS/Program.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Properties/launchSettings.json create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/AppIcon/appicon.svg create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/AppIcon/appiconfg.svg create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Fonts/OpenSans-Semibold.ttf create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Images/dotnet_bot.svg create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Images/scanning.gif create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Images/spinner.gif create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Raw/AboutAssets.txt create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Splash/splash.svg create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Styles/Colors.xaml create mode 100644 Source/BLE.Client/BLE.Client.Maui/Resources/Styles/Styles.xaml create mode 100644 Source/BLE.Client/BLE.Client.Maui/Services/AlertService.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Services/IAlertService.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEDeviceViewModel.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs create mode 100644 Source/BLE.Client/BLE.Client.Maui/Views/BLEScanner.xaml create mode 100644 Source/BLE.Client/BLE.Client.Maui/Views/BLEScanner.xaml.cs create mode 100644 Source/BLE.Mac.Maui.sln diff --git a/.gitignore b/.gitignore index c989a7f6..7d953152 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ bld/ FAKE* !FAKE*.cs .fake/ +.idea/ # Visual Studo 2015 cache/options directory .vs/ diff --git a/Source/BLE.Client/BLE.Client.Maui/App.xaml b/Source/BLE.Client/BLE.Client.Maui/App.xaml new file mode 100644 index 00000000..b1298e26 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/App.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/Source/BLE.Client/BLE.Client.Maui/App.xaml.cs b/Source/BLE.Client/BLE.Client.Maui/App.xaml.cs new file mode 100644 index 00000000..00fa7886 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/App.xaml.cs @@ -0,0 +1,21 @@ +using BLE.Client.Helpers; +using BLE.Client.Maui.Services; + +namespace BLE.Client.Maui; + +public partial class App : Application +{ + public static IServiceProvider Services; + public static IAlertService AlertSvc; + public static IPlatformHelpers PlatformHelper; + public App(IServiceProvider provider) + { + InitializeComponent(); + + Services = provider; + AlertSvc = Services.GetService(); + PlatformHelper = Services.GetService(); + MainPage = new AppShell(); + } +} + diff --git a/Source/BLE.Client/BLE.Client.Maui/AppShell.xaml b/Source/BLE.Client/BLE.Client.Maui/AppShell.xaml new file mode 100644 index 00000000..628beba1 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/AppShell.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/Source/BLE.Client/BLE.Client.Maui/AppShell.xaml.cs b/Source/BLE.Client/BLE.Client.Maui/AppShell.xaml.cs new file mode 100644 index 00000000..65d43bf8 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/AppShell.xaml.cs @@ -0,0 +1,10 @@ +namespace BLE.Client.Maui; + +public partial class AppShell : Shell +{ + public AppShell() + { + InitializeComponent(); + } +} + diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj new file mode 100644 index 00000000..c43078b2 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -0,0 +1,85 @@ + + + + net7.0-android;net7.0-ios;net7.0-maccatalyst + Exe + BLE.Client.Maui + true + true + enable + + + BLE.Client.Maui + + + com.companyname.ble.client.maui + a401d899-314c-4522-b345-bdf1731f57a5 + + + 1.0 + 1 + + 11.0 + 13.1 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + false + + + false + + + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs b/Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs new file mode 100644 index 00000000..d1130c7e --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs @@ -0,0 +1,7 @@ +namespace BLE.Client.Helpers +{ + public interface IPlatformHelpers + { + Task CheckAndRequestBluetoothPermissions(); + } +} diff --git a/Source/BLE.Client/BLE.Client.Maui/MainPage.xaml b/Source/BLE.Client/BLE.Client.Maui/MainPage.xaml new file mode 100644 index 00000000..294dbcdb --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/MainPage.xaml @@ -0,0 +1,47 @@ + + + + + + + + + /// The to known device async. /// Device GUID. - public override async Task ConnectToKnownDeviceAsync(Guid deviceGuid, ConnectParameters connectParameters = default(ConnectParameters), CancellationToken cancellationToken = default(CancellationToken)) + public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceGuid, ConnectParameters connectParameters = default(ConnectParameters), CancellationToken cancellationToken = default(CancellationToken)) { #if NET6_0_OR_GREATER || MACCATALYST await WaitForState(CBManagerState.PoweredOn, cancellationToken, true); @@ -230,7 +230,7 @@ private static Guid ParseDeviceGuid(CBPeripheral peripherial) #endif if (cancellationToken.IsCancellationRequested) - throw new TaskCanceledException("ConnectToKnownDeviceAsync cancelled"); + throw new TaskCanceledException("ConnectToKnownDeviceNativeAsync cancelled"); //FYI attempted to use tobyte array insetead of string but there was a problem with byte ordering Guid->NSUui var uuid = new NSUuid(deviceGuid.ToString()); @@ -256,7 +256,7 @@ private static Guid ParseDeviceGuid(CBPeripheral peripherial) ); if (peripherial == null) - throw new Exception($"[Adapter] Device {deviceGuid} not found."); + throw new Abstractions.Exceptions.DeviceConnectionException(deviceGuid, "", $"[Adapter] Device {deviceGuid} not found."); } diff --git a/Source/Plugin.BLE/Shared/AdapterBase.cs b/Source/Plugin.BLE/Shared/AdapterBase.cs index 869ee23a..239837d1 100644 --- a/Source/Plugin.BLE/Shared/AdapterBase.cs +++ b/Source/Plugin.BLE/Shared/AdapterBase.cs @@ -327,6 +327,24 @@ public void HandleConnectionFail(IDevice device, string errorMessage) }); } + /// + /// Connects to a device with a known GUID without scanning and if in range. Does not scan for devices. + /// + public async Task ConnectToKnownDeviceAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default) + { + if (DiscoveredDevicesRegistry.TryGetValue(deviceGuid, out IDevice discoveredDevice)) + { + await ConnectToDeviceAsync(discoveredDevice, connectParameters, cancellationToken); + return discoveredDevice; + } + + var connectedDevice = await ConnectToKnownDeviceNativeAsync(deviceGuid, connectParameters, cancellationToken); + if (!DiscoveredDevicesRegistry.ContainsKey(deviceGuid)) + DiscoveredDevicesRegistry.TryAdd(deviceGuid, connectedDevice); + + return connectedDevice; + } + /// /// Native implementation of StartScanningForDevicesAsync. /// @@ -345,9 +363,9 @@ public void HandleConnectionFail(IDevice device, string errorMessage) protected abstract void DisconnectDeviceNative(IDevice device); /// - /// Connects to a device with a known GUID without scanning and if in range. Does not scan for devices. + /// Native implementation of ConnectToKnownDeviceAsync. /// - public abstract Task ConnectToKnownDeviceAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default); + public abstract Task ConnectToKnownDeviceNativeAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default); /// /// Returns all BLE devices connected to the system. /// diff --git a/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs b/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs index 79d2aa4a..18f6003f 100644 --- a/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs +++ b/Source/Plugin.BLE/Shared/Utils/FakeAdapter.cs @@ -8,7 +8,7 @@ namespace Plugin.BLE.Abstractions.Utils { internal class FakeAdapter : AdapterBase { - public override Task ConnectToKnownDeviceAsync(Guid deviceGuid, ConnectParameters connectParameters, CancellationToken cancellationToken) + public override Task ConnectToKnownDeviceNativeAsync(Guid deviceGuid, ConnectParameters connectParameters, CancellationToken cancellationToken) { TraceUnavailability(); return Task.FromResult(null); diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index f79826de..10e19413 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -171,12 +171,15 @@ protected override void DisconnectDeviceNative(IDevice device) } } - public override async Task ConnectToKnownDeviceAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default) + public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default) { //convert GUID to string and take last 12 characters as MAC address var guidString = deviceGuid.ToString("N").Substring(20); var bluetoothAddress = Convert.ToUInt64(guidString, 16); var nativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bluetoothAddress); + if (nativeDevice == null) + throw new Abstractions.Exceptions.DeviceConnectionException(deviceGuid, "", $"[Adapter] Device {deviceGuid} not found."); + var knownDevice = new Device(this, nativeDevice, 0, deviceGuid, _dq); await ConnectToDeviceAsync(knownDevice, cancellationToken: cancellationToken); From a34f4b8359459fc03dc1dff5f159d6c1b62c337f Mon Sep 17 00:00:00 2001 From: IanBUK <78267962+IanBUK@users.noreply.github.com> Date: Wed, 30 Aug 2023 21:49:42 +0100 Subject: [PATCH 043/193] Fix colours, remove extraneous solution file. --- .../BLE.Client.Maui/Models/BLEDevice.cs | 11 +- .../ViewModels/BLEScannerViewModel.cs | 5 +- .../BLE.Client.Maui/Views/BLEScanner.xaml | 23 +- Source/BLE.Mac.Maui.sln | 324 ------------------ Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- 5 files changed, 34 insertions(+), 331 deletions(-) delete mode 100644 Source/BLE.Mac.Maui.sln diff --git a/Source/BLE.Client/BLE.Client.Maui/Models/BLEDevice.cs b/Source/BLE.Client/BLE.Client.Maui/Models/BLEDevice.cs index 2b8be584..e4bf7f59 100644 --- a/Source/BLE.Client/BLE.Client.Maui/Models/BLEDevice.cs +++ b/Source/BLE.Client/BLE.Client.Maui/Models/BLEDevice.cs @@ -14,7 +14,14 @@ public class BLEDevice public bool IsConnectable { get; set; } - public DeviceState State { get; set; } - } + public DeviceState State { get; set; } + + + public override string ToString() + { + return $"{Name}: {DeviceId}: {Rssi}: {State}"; + + } + } } diff --git a/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs b/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs index 59099880..50117460 100644 --- a/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs +++ b/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs @@ -14,7 +14,7 @@ namespace BLE.Client.Maui.ViewModels public class BLEScannerViewModel : INotifyPropertyChanged { - private IBluetoothLE _bluetoothManager; + private IBluetoothLE _bluetoothManager; protected IAdapter Adapter; public bool IsStateOn => _bluetoothManager.IsOn; public string StateText => GetStateText(); @@ -27,10 +27,9 @@ public class BLEScannerViewModel : INotifyPropertyChanged public IAsyncRelayCommand StartScan { get; } - private ObservableCollection _devices = new ObservableCollection(); + public ObservableCollection BLEDevices { get; private set;} = new ObservableCollection(); private ObservableCollection _messages = new ObservableCollection(); - public IList BLEDevices { get { DebugMessage("Getting BLEDevices"); return _devices; } } public IList Messages { get { DebugMessage("Getting messages"); return _messages; } } private string _lastMessage = string.Empty; diff --git a/Source/BLE.Client/BLE.Client.Maui/Views/BLEScanner.xaml b/Source/BLE.Client/BLE.Client.Maui/Views/BLEScanner.xaml index 1d76ff19..f61fea6f 100644 --- a/Source/BLE.Client/BLE.Client.Maui/Views/BLEScanner.xaml +++ b/Source/BLE.Client/BLE.Client.Maui/Views/BLEScanner.xaml @@ -45,7 +45,28 @@ /// The bluetooth advertisement watcher currently being used /// The advertisement recieved by the watcher - private async void DeviceFoundAsync(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs btAdv) + private void AdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs btAdv) { - var deviceId = ParseDeviceId(btAdv.BluetoothAddress); + var deviceId = btAdv.BluetoothAddress.ParseDeviceId(); if (DiscoveredDevicesRegistry.TryGetValue(deviceId, out var device)) { - Trace.Message("AdvertisdedPeripheral: {0} Id: {1}, Rssi: {2}", device.Name, device.Id, btAdv.RawSignalStrengthInDBm); + Trace.Message("AdvertisementReceived - Old Device: {0}", btAdv.ToDetailedString()); (device as Device)?.Update(btAdv.RawSignalStrengthInDBm, ParseAdvertisementData(btAdv.Advertisement)); this.HandleDiscoveredDevice(device); } else { - var bluetoothLeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress); + Trace.Message("AdvertisementReceived - New Device: {0}", btAdv.ToDetailedString()); + var bluetoothLeDevice = BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress).AsTask().Result; + //var bluetoothLeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress); if (bluetoothLeDevice != null) //make sure advertisement bluetooth address actually returns a device { - device = new Device(this, bluetoothLeDevice, btAdv.RawSignalStrengthInDBm, deviceId, _dq, ParseAdvertisementData(btAdv.Advertisement), btAdv.IsConnectable); - Trace.Message("DiscoveredPeripheral: {0} Id: {1}, Rssi: {2}", device.Name, device.Id, btAdv.RawSignalStrengthInDBm); + device = new Device( + this, + bluetoothLeDevice, + btAdv.RawSignalStrengthInDBm, + deviceId, + ParseAdvertisementData(btAdv.Advertisement), + btAdv.IsConnectable); this.HandleDiscoveredDevice(device); } } } - /// - /// Method to parse the bluetooth address as a hex string to a UUID - /// - /// BluetoothLEDevice native device address - /// a GUID that is padded left with 0 and the last 6 bytes are the bluetooth address - private static Guid ParseDeviceId(ulong bluetoothAddress) - { - var macWithoutColons = bluetoothAddress.ToString("x"); - macWithoutColons = macWithoutColons.PadLeft(12, '0'); //ensure valid length - var deviceGuid = new byte[16]; - Array.Clear(deviceGuid, 0, 16); - var macBytes = Enumerable.Range(0, macWithoutColons.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(macWithoutColons.Substring(x, 2), 16)) - .ToArray(); - macBytes.CopyTo(deviceGuid, 10); - return new Guid(deviceGuid); - } - public override IReadOnlyList GetKnownDevicesByIds(Guid[] ids) { // TODO: implement this diff --git a/Source/Plugin.BLE/Windows/BleImplementation.cs b/Source/Plugin.BLE/Windows/BleImplementation.cs index 94d2c986..b7c64b85 100644 --- a/Source/Plugin.BLE/Windows/BleImplementation.cs +++ b/Source/Plugin.BLE/Windows/BleImplementation.cs @@ -1,13 +1,10 @@ using Windows.Devices.Bluetooth; -#if WINDOWS_UWP -using Microsoft.Toolkit.Uwp.Connectivity; -#else -using CommunityToolkit.WinUI.Connectivity; -#endif - using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.UWP; +using System; +using System.Threading.Tasks; +using Windows.Devices.Radios; namespace Plugin.BLE { @@ -19,31 +16,37 @@ public class BleImplementation : BleImplementationBase public static BluetoothCacheMode CacheModeGetCharacteristics { get; set; } = BluetoothCacheMode.Cached; public static BluetoothCacheMode CacheModeGetServices { get; set; } = BluetoothCacheMode.Cached; - private BluetoothLEHelper _bluetoothHelper; - protected override IAdapter CreateNativeAdapter() { - return new Adapter(_bluetoothHelper); + return new Adapter(); } protected override BluetoothState GetInitialStateNative() { - //The only way to get the state of bluetooth through windows is by - //getting the radios for a device. This operation is asynchronous - //and thus cannot be called in this method. Thus, we are just - //returning "On" as long as the BluetoothLEHelper is initialized - if (_bluetoothHelper == null) + try + { + BluetoothAdapter btAdapter = BluetoothAdapter.GetDefaultAsync().AsTask().Result; + var radio = btAdapter.GetRadioAsync().AsTask().Result; + switch (radio.State) + { + case RadioState.On: + return BluetoothState.On; + case RadioState.Off: + return BluetoothState.Off; + default: + return BluetoothState.Unavailable; + } + } + catch (Exception ex) { + Trace.Message("GetInitialStateNativeAsync exception:{0}", ex.Message); return BluetoothState.Unavailable; } - return BluetoothState.On; } protected override void InitializeNative() { - //create local helper using the app context - var localHelper = BluetoothLEHelper.Context; - _bluetoothHelper = localHelper; + } } diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 660ebefc..112e47ba 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -2,26 +2,18 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; - -#if WINDOWS_UWP -using Windows.System; -using Microsoft.Toolkit.Uwp.Connectivity; -#else -using Microsoft.UI.Dispatching; -using CommunityToolkit.WinUI.Connectivity; -#endif using Windows.Devices.Bluetooth; -using Windows.Devices.Enumeration; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; namespace Plugin.BLE.UWP { - public class Device : DeviceBase + public class Device : DeviceBase { - public Device(Adapter adapter, BluetoothLEDevice nativeDevice, int rssi, Guid id, DispatcherQueue dq, IReadOnlyList advertisementRecords = null, bool isConnectable = true) - : base(adapter, new ObservableBluetoothLEDevice(nativeDevice.DeviceInformation, dq)) + public Device(Adapter adapter, BluetoothLEDevice nativeDevice, int rssi, Guid id, + IReadOnlyList advertisementRecords = null, bool isConnectable = true) + : base(adapter, nativeDevice) { Rssi = rssi; Id = id; @@ -48,10 +40,10 @@ public override Task UpdateRssiAsync() protected override async Task> GetServicesNativeAsync() { - if (NativeDevice?.BluetoothLEDevice == null) + if (NativeDevice == null) return new List(); - var result = await NativeDevice.BluetoothLEDevice.GetGattServicesAsync(BleImplementation.CacheModeGetServices); + var result = await NativeDevice.GetGattServicesAsync(BleImplementation.CacheModeGetServices); result?.ThrowIfError(); return result?.Services? @@ -62,7 +54,7 @@ protected override async Task> GetServicesNativeAsync() protected override async Task GetServiceNativeAsync(Guid id) { - var result = await NativeDevice.BluetoothLEDevice.GetGattServicesForUuidAsync(id, BleImplementation.CacheModeGetServices); + var result = await NativeDevice.GetGattServicesForUuidAsync(id, BleImplementation.CacheModeGetServices); result.ThrowIfError(); var nativeService = result.Services?.FirstOrDefault(); @@ -71,17 +63,16 @@ protected override async Task GetServiceNativeAsync(Guid id) protected override DeviceState GetState() { - if (NativeDevice.IsConnected) + if (NativeDevice.ConnectionStatus == BluetoothConnectionStatus.Connected) { return DeviceState.Connected; - } - - return NativeDevice.IsPaired ? DeviceState.Limited : DeviceState.Disconnected; + } + return NativeDevice.WasSecureConnectionUsedForPairing ? DeviceState.Limited : DeviceState.Disconnected; } protected override async Task RequestMtuNativeAsync(int requestValue) { - var devId = BluetoothDeviceId.FromId(NativeDevice.BluetoothLEDevice.DeviceId); + var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); using var gattSession = await Windows.Devices.Bluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); return gattSession.MaxPduSize; } @@ -93,32 +84,38 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv } public override void Dispose() - { - FreeResources(false); - } - - internal void FreeResources(bool recreateNativeDevice = true) - { - NativeDevice?.Services?.ToList().ForEach(s => + { + if (NativeDevice != null) { - s?.Service?.Session?.Dispose(); - s?.Service?.Dispose(); - }); - - // save these so we can re-create ObservableBluetoothLEDevice if needed - var tempDevInfo = NativeDevice?.DeviceInfo; - var tempDq = NativeDevice?.DispatcherQueue; - - NativeDevice?.BluetoothLEDevice?.Dispose(); + Trace.Message("Disposing {0} with id = {1}", Name, Id.ToString()); + NativeDevice.Dispose(); + NativeDevice = null; + } + //FreeResources(false); + } - // the ObservableBluetoothLEDevice doesn't really support the BluetoothLEDevice - // being disposed so we need to recreate it. What we really need is to be able - // to set NativeDevice?.BluetoothLEDevice = null; - if (recreateNativeDevice) - NativeDevice = new ObservableBluetoothLEDevice(tempDevInfo, tempDq); + //internal void FreeResources(bool recreateNativeDevice = true) + //{ + // NativeDevice?.Services?.ToList().ForEach(s => + // { + // s?.Service?.Session?.Dispose(); + // s?.Service?.Dispose(); + // }); + + // // save these so we can re-create ObservableBluetoothLEDevice if needed + // var tempDevInfo = NativeDevice?.DeviceInfo; + // var tempDq = NativeDevice?.DispatcherQueue; + + // NativeDevice?.BluetoothLEDevice?.Dispose(); + + // // the ObservableBluetoothLEDevice doesn't really support the BluetoothLEDevice + // // being disposed so we need to recreate it. What we really need is to be able + // // to set NativeDevice?.BluetoothLEDevice = null; + // if (recreateNativeDevice) + // NativeDevice = new ObservableBluetoothLEDevice(tempDevInfo, tempDq); - GC.Collect(); - } + // GC.Collect(); + //} public override bool IsConnectable { get; protected set; } diff --git a/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs b/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs new file mode 100644 index 00000000..4f96f51f --- /dev/null +++ b/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Windows.Devices.Bluetooth.GenericAttributeProfile; +namespace Plugin.BLE.Extensions; + +// +// This code has been copied from CommunityToolkit.WinUI.Connectivity +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +/// +/// Helper function when working with +/// +public static class GattProtocolErrorParser +{ + /// + /// Helper to convert an Gatt error value into a string + /// + /// the byte error value. + /// String representation of the error + public static string GetErrorString(this byte? errorValue) + { + var errorString = "Protocol Error"; + + if (errorValue.HasValue == false) + { + return errorString; + } + + if (errorValue == GattProtocolError.AttributeNotFound) + { + return "Attribute Not Found"; + } + + if (errorValue == GattProtocolError.AttributeNotLong) + { + return "Attribute Not Long"; + } + + if (errorValue == GattProtocolError.InsufficientAuthentication) + { + return "Insufficient Authentication"; + } + + if (errorValue == GattProtocolError.InsufficientAuthorization) + { + return "Insufficient Authorization"; + } + + if (errorValue == GattProtocolError.InsufficientEncryption) + { + return "Insufficient Encryption"; + } + + if (errorValue == GattProtocolError.InsufficientEncryptionKeySize) + { + return "Insufficient Encryption Key Size"; + } + + if (errorValue == GattProtocolError.InsufficientResources) + { + return "Insufficient Resources"; + } + + if (errorValue == GattProtocolError.InvalidAttributeValueLength) + { + return "Invalid Attribute Value Length"; + } + + if (errorValue == GattProtocolError.InvalidHandle) + { + return "Invalid Handle"; + } + + if (errorValue == GattProtocolError.InvalidOffset) + { + return "Invalid Offset"; + } + + if (errorValue == GattProtocolError.InvalidPdu) + { + return "Invalid Pdu"; + } + + if (errorValue == GattProtocolError.PrepareQueueFull) + { + return "Prepare Queue Full"; + } + + if (errorValue == GattProtocolError.ReadNotPermitted) + { + return "Read Not Permitted"; + } + + if (errorValue == GattProtocolError.RequestNotSupported) + { + return "Request Not Supported"; + } + + if (errorValue == GattProtocolError.UnlikelyError) + { + return "UnlikelyError"; + } + + if (errorValue == GattProtocolError.UnsupportedGroupType) + { + return "Unsupported Group Type"; + } + + if (errorValue == GattProtocolError.WriteNotPermitted) + { + return "Write Not Permitted"; + } + + return errorString; + } +} + diff --git a/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs b/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs index 658577f2..387161cc 100644 --- a/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs +++ b/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs @@ -2,11 +2,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Devices.Bluetooth.GenericAttributeProfile; -#if WINDOWS_UWP -using Microsoft.Toolkit.Uwp.Connectivity; -#else -using CommunityToolkit.WinUI.Connectivity; -#endif using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Exceptions; @@ -48,7 +43,7 @@ public static void ThrowIfError(this GattCommunicationStatus status, [CallerMemb } private static string GetErrorMessage(this GattCommunicationStatus status, string tag, byte? protocolError) - { + { switch (status) { //output trace message with status of update @@ -56,7 +51,7 @@ private static string GetErrorMessage(this GattCommunicationStatus status, strin Trace.Message($"[{tag}] success."); return null; case GattCommunicationStatus.ProtocolError when protocolError != null: - return $"[{tag}] failed with status: {status} and protocol error {protocolError.GetErrorString()}"; + return $"[{tag}] failed with status: {status} and protocol error {protocolError.GetErrorString()}"; case GattCommunicationStatus.AccessDenied: case GattCommunicationStatus.ProtocolError: case GattCommunicationStatus.Unreachable: diff --git a/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs new file mode 100644 index 00000000..323605e7 --- /dev/null +++ b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Versioning; +using System.Text; +using System.Threading.Tasks; +using Windows.Devices.Bluetooth; +using Windows.Devices.Bluetooth.Advertisement; + +namespace Plugin.BLE.Extensions; + +public static class TextHelpers +{ + public static string ToDetailedString(this BluetoothLEAdvertisementReceivedEventArgs btAdv) + { + string hexadr = btAdv.BluetoothAddress.ToHexBleAddress(); + StringBuilder sb = new StringBuilder(); + sb.Append(hexadr).Append(", Rssi:").Append(btAdv.RawSignalStrengthInDBm) + .Append(", Type: ").Append(btAdv.BluetoothAddressType); + if (btAdv.IsConnectable) + { + sb.Append(", Connectable"); + } + if (btAdv.IsScannable) + { + sb.Append(", Scannable"); + } + if (btAdv.IsScanResponse) + { + sb.Append(", ScanResponse"); + } + if (btAdv.IsDirected) + { + sb.Append(", Directed"); + } + return sb.ToString(); + } + + /// + /// Get a string of the BLE address: 48 bit = 6 bytes = 12 Hex chars + /// + /// + /// + public static string ToHexBleAddress(this Guid id) + { + return id.ToString("N").Substring(20); + //return id.ToString()[^12..].ToUpperInvariant(); //Not for netstandard2.0 + } + + + /// + /// Get a string of the BLE address: 48 bit = 6 bytes = 12 Hex chars + /// + /// + /// + public static string ToHexBleAddress(this ulong bluetoothAddress) + { + return bluetoothAddress.ToString("X12"); + } + + /// + /// Method to parse the bluetooth address as a hex string to a UUID + /// + /// BluetoothLEDevice native device address + /// a GUID that is padded left with 0 and the last 6 bytes are the bluetooth address + public static Guid ParseDeviceId(this ulong bluetoothAddress) + { + var macWithoutColons = bluetoothAddress.ToString("x"); + macWithoutColons = macWithoutColons.PadLeft(12, '0'); //ensure valid length + var deviceGuid = new byte[16]; + Array.Clear(deviceGuid, 0, 16); + var macBytes = Enumerable.Range(0, macWithoutColons.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(macWithoutColons.Substring(x, 2), 16)) + .ToArray(); + macBytes.CopyTo(deviceGuid, 10); + return new Guid(deviceGuid); + } +} From 7f43b1876f1d614bdd213ae41d6a1bdb3c44a773 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 19 Sep 2023 10:35:22 +0200 Subject: [PATCH 061/193] Revert changes to project files --- .../BLE.Client.Droid/BLE.Client.Droid.csproj | 4 +- Source/BLE.sln | 166 +++++++++--------- 2 files changed, 84 insertions(+), 86 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj index f011bf48..ecaf0c82 100644 --- a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj +++ b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj @@ -96,8 +96,6 @@ {83f1dffb-a653-45dd-be81-c02374c6db07} MvvmCross.Plugins.BLE - Android - false {951ed11d-d026-449c-90dc-5027bc41fb3b} @@ -141,4 +139,4 @@ - \ No newline at end of file + diff --git a/Source/BLE.sln b/Source/BLE.sln index b7510a80..80318c98 100644 --- a/Source/BLE.sln +++ b/Source/BLE.sln @@ -44,9 +44,9 @@ Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "BLE.Client.macOS", "BLE.Cli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLE.Client.UWP", "BLE.Client\BLE.Client.UWP\BLE.Client.UWP.csproj", "{25E04E05-F867-4F64-813D-AAFE0BA171B0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BLE.Client.Maui", "BLE.Client\BLE.Client.Maui\BLE.Client.Maui.csproj", "{D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLE.Client.Maui", "BLE.Client\BLE.Client.Maui\BLE.Client.Maui.csproj", "{D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BLE.Client.WinConsole", "BLE.Client\BLE.Client.WinConsole\BLE.Client.WinConsole.csproj", "{3F90E6DE-1311-4728-9184-63376F3C05DE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BLE.Client.WinConsole", "BLE.Client\BLE.Client.WinConsole\BLE.Client.WinConsole.csproj", "{39287F60-65DE-4077-90F2-57CF725E92B6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -418,34 +418,34 @@ Global {D0FBE2BA-799B-4071-A45D-8FB037006C9C}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator {D0FBE2BA-799B-4071-A45D-8FB037006C9C}.Release|x64.ActiveCfg = Release|iPhone {D0FBE2BA-799B-4071-A45D-8FB037006C9C}.Release|x86.ActiveCfg = Release|iPhone - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM.Build.0 = Release|Any CPU {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM.Build.0 = Debug|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x64.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|Any CPU.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM.Build.0 = Release|Any CPU {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM64.ActiveCfg = Debug|Any CPU {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x64.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x86.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x86.Build.0 = Debug|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhone.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x64.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x64.Build.0 = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x86.ActiveCfg = Release|Any CPU + {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x86.Build.0 = Release|Any CPU {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|Any CPU.Build.0 = Debug|Any CPU {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -600,62 +600,62 @@ Global {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x64.Build.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x86.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x86.Build.0 = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|ARM.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|ARM64.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|ARM64.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|x64.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|x64.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|x86.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.AppStore|x86.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|ARM.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|ARM.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|ARM64.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|iPhone.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|x64.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|x64.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|x86.ActiveCfg = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Debug|x86.Build.0 = Debug|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|Any CPU.Build.0 = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|ARM.ActiveCfg = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|ARM.Build.0 = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|ARM64.ActiveCfg = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|ARM64.Build.0 = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|iPhone.ActiveCfg = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|iPhone.Build.0 = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|x64.ActiveCfg = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|x64.Build.0 = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|x86.ActiveCfg = Release|Any CPU - {3F90E6DE-1311-4728-9184-63376F3C05DE}.Release|x86.Build.0 = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|ARM.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|ARM64.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|iPhone.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|x64.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|x64.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|x86.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.AppStore|x86.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|ARM.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|ARM.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|ARM64.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|iPhone.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|x64.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|x64.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|x86.ActiveCfg = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Debug|x86.Build.0 = Debug|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|Any CPU.Build.0 = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|ARM.ActiveCfg = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|ARM.Build.0 = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|ARM64.ActiveCfg = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|ARM64.Build.0 = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|iPhone.ActiveCfg = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|iPhone.Build.0 = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|x64.ActiveCfg = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|x64.Build.0 = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|x86.ActiveCfg = Release|Any CPU + {39287F60-65DE-4077-90F2-57CF725E92B6}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -667,7 +667,7 @@ Global {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B} = {A06042F5-F69D-4191-875E-5ADE9CF42075} {25E04E05-F867-4F64-813D-AAFE0BA171B0} = {A06042F5-F69D-4191-875E-5ADE9CF42075} {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E} = {A06042F5-F69D-4191-875E-5ADE9CF42075} - {3F90E6DE-1311-4728-9184-63376F3C05DE} = {A06042F5-F69D-4191-875E-5ADE9CF42075} + {39287F60-65DE-4077-90F2-57CF725E92B6} = {A06042F5-F69D-4191-875E-5ADE9CF42075} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B5D5105C-EA52-467B-8CCC-6777900C3B95} From a45e1f46bca527f5abba4e2d4d9d189ac83ce77d Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 19 Sep 2023 13:21:00 +0200 Subject: [PATCH 062/193] Improved console output Cleaned up the code in the adapter --- .../BLE.Client.WinConsole/BleDemo.cs | 2 +- .../BLE.Client.WinConsole/ConsoleTracer.cs | 34 +++++++++++-------- .../BLE.Client.WinConsole/Program.cs | 1 + Source/Plugin.BLE/Windows/Adapter.cs | 13 +++---- .../Windows/Extensions/TextHelpers.cs | 8 +++-- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs index d6aaeb30..f7abeb6d 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs @@ -30,7 +30,7 @@ private void Write(string format, params object[] args) writer?.Invoke(format, args); } - public async Task DoTheScanning(ScanMode scanMode = ScanMode.Passive, int time_ms = 2000) + public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ms = 2000) { if (!bluetoothLE.IsOn) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs index 7e87eb83..38633420 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs @@ -8,6 +8,9 @@ namespace BLE.Client.WinConsole { + /// + /// A class, which can log trace to the console without blocking the caller + /// public class ConsoleTracer { record Entry @@ -19,21 +22,25 @@ object[] Args private readonly DateTime time0; private readonly Stopwatch stopwatch; - private readonly object mutex; + private readonly Task worker; + private readonly object mutex; private readonly AutoResetEvent newEntry = new AutoResetEvent(false); ConcurrentQueue entries = new ConcurrentQueue(); - public ConsoleTracer() + public ConsoleTracer() { mutex = new object(); time0 = DateTime.Now; stopwatch = Stopwatch.StartNew(); - new Thread(WriteWorker) - { - IsBackground = true, - }.Start(); + worker = new Task(WriteWorker); + worker.Start(); } + /// + /// Trace something to the console - adding to queue - not blocking the caller + /// + /// + /// public void Trace(string format, params object[] args) { var time = GetTime(); @@ -43,20 +50,17 @@ public void Trace(string format, params object[] args) void WriteWorker() { - while (newEntry.WaitOne(1000)) + while (newEntry.WaitOne()) { - StringBuilder sb = new StringBuilder(); while (entries.TryDequeue(out Entry entry)) - { - sb.AppendLine(); - //Console.WriteLine(entry.Time.ToString("yyyy-MM-ddTHH:mm:ss.fff ") + string.Format(entry.Format, entry.Args) + " "); - Console.WriteLine(entry.Time.ToString("yyyy-MM-ddTHH:mm:ss.fff ") + entry.Format + " ", entry.Args); - } - //Console.Write(sb.ToString()); + { + //Console.WriteLine(entry.Time.ToString("yyyy-MM-ddTHH:mm:ss.fff ") + entry.Format + " ", entry.Args); + Console.WriteLine(entry.Time.ToString("HH:mm:ss.fff ") + entry.Format + " ", entry.Args); + } } } - private DateTime GetTime() + private DateTime GetTime() { return time0.AddTicks(stopwatch.ElapsedTicks); } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index 3bd820e8..a54387a4 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -9,3 +9,4 @@ await demo.ConnectTest("egoo"); + diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 177ff8bf..ae9aebb5 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -7,11 +7,11 @@ using System.Threading.Tasks; using Windows.Devices.Bluetooth; using Windows.Devices.Bluetooth.Advertisement; - using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; using System.Collections.Concurrent; +using Windows.Devices.Bluetooth.GenericAttributeProfile; namespace Plugin.BLE.UWP { @@ -70,13 +70,14 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect Trace.Message("ConnectToDeviceNativeAsync {0} Named: {1} Connected: {2}", device.Id.ToHexBleAddress(), device.Name, nativeDevice.ConnectionStatus); ConnectedDeviceRegistry[device.Id.ToString()] = device; - nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; - - var gats = await nativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); + nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; + nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; - Trace.Message("ConnectToDeviceNativeAsync DONE {0} Named: {1} Connected: {2}", device.Id.ToHexBleAddress(), device.Name, nativeDevice.ConnectionStatus); - if (nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) + var gattServiceResult = await nativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); + + if (gattServiceResult.Status != GattCommunicationStatus.Success + || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) { // use DisconnectDeviceNative to clean up resources otherwise windows won't disconnect the device // after a subsequent successful connection (#528, #536, #423) diff --git a/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs index 323605e7..c5bbc1a8 100644 --- a/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs +++ b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs @@ -15,8 +15,11 @@ public static string ToDetailedString(this BluetoothLEAdvertisementReceivedEvent { string hexadr = btAdv.BluetoothAddress.ToHexBleAddress(); StringBuilder sb = new StringBuilder(); - sb.Append(hexadr).Append(", Rssi:").Append(btAdv.RawSignalStrengthInDBm) - .Append(", Type: ").Append(btAdv.BluetoothAddressType); + sb.Append(hexadr) + .Append(", ").Append(btAdv.BluetoothAddressType) + .Append(", ").Append(btAdv.RawSignalStrengthInDBm) + .Append(", ").Append(btAdv.AdvertisementType); + if (btAdv.IsConnectable) { sb.Append(", Connectable"); @@ -47,7 +50,6 @@ public static string ToHexBleAddress(this Guid id) //return id.ToString()[^12..].ToUpperInvariant(); //Not for netstandard2.0 } - /// /// Get a string of the BLE address: 48 bit = 6 bytes = 12 Hex chars /// From 60d70b052ab41779389919651c23cfe259aa13e4 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 19 Sep 2023 15:09:34 +0200 Subject: [PATCH 063/193] Improved Advertisement output --- .../BLE.Client.WinConsole/BleDemo.cs | 37 ++++++++++++++----- .../BLE.Client.WinConsole/ConsoleTracer.cs | 14 +++++-- .../BLE.Client.WinConsole/Program.cs | 7 ++-- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs index f7abeb6d..7c462c9e 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs @@ -2,6 +2,7 @@ using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Abstractions.Extensions; +using Plugin.BLE.Extensions; using System; using System.Collections.Generic; using System.Linq; @@ -15,11 +16,11 @@ internal class BleDemo private readonly IBluetoothLE bluetoothLE; private readonly IAdapter adapter; private readonly Action? writer; - private readonly List foundDevices; + private readonly List discoveredDevices; public BleDemo(Action? writer = null) { - foundDevices = new List(); + discoveredDevices = new List(); bluetoothLE = CrossBluetoothLE.Current; adapter = CrossBluetoothLE.Current.Adapter; this.writer = writer; @@ -41,27 +42,45 @@ public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ Write("Bluetooth is on"); Write("Scanning now for " + time_ms + " ms..."); var cancellationTokenSource = new CancellationTokenSource(time_ms); - foundDevices.Clear(); + discoveredDevices.Clear(); adapter.DeviceDiscovered += (s, a) => { var dev = a.Device; - Write("Found: {0} with Name = {1} Connectable: {2}", dev.Id.ToString()[^12..], dev.Name, dev.IsConnectable); - foundDevices.Add(a.Device); + Write("DeviceDiscovered: {0} with Name = {1}", dev.Id.ToHexBleAddress(), dev.Name); + discoveredDevices.Add(a.Device); }; adapter.ScanMode = scanMode; await adapter.StartScanningForDevicesAsync(cancellationToken: cancellationTokenSource.Token); } - public async Task ConnectTest(string subname) + void WriteAdvertisementRecords(IDevice device) { - foreach(var device in foundDevices) + Write("Device.State: {0} with {1} AdvertisementRecords", device.State, device.AdvertisementRecords.Count); + foreach (var ar in device.AdvertisementRecords) { - if (device.Name.Contains(subname)) + switch (ar.Type) + { + case AdvertisementRecordType.CompleteLocalName: + Write(ar.ToString() + " = " + Encoding.UTF8.GetString(ar.Data)); + break; + default: + Write(ar.ToString()); + break; + } + } + } + + public async Task ConnectTest(string name) + { + Thread.Sleep(10); + foreach(var device in discoveredDevices) + { + if (device.Name.Contains(name)) { await adapter.ConnectToDeviceAsync(device); - Write("Device.State: {0}", device.State); + WriteAdvertisementRecords(device); } } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs index 38633420..5c1e1b71 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs @@ -11,7 +11,7 @@ namespace BLE.Client.WinConsole /// /// A class, which can log trace to the console without blocking the caller /// - public class ConsoleTracer + public class ConsoleTracer : IDisposable { record Entry ( @@ -23,13 +23,12 @@ object[] Args private readonly DateTime time0; private readonly Stopwatch stopwatch; private readonly Task worker; - private readonly object mutex; + private bool disposing = false; private readonly AutoResetEvent newEntry = new AutoResetEvent(false); ConcurrentQueue entries = new ConcurrentQueue(); public ConsoleTracer() { - mutex = new object(); time0 = DateTime.Now; stopwatch = Stopwatch.StartNew(); worker = new Task(WriteWorker); @@ -50,7 +49,7 @@ public void Trace(string format, params object[] args) void WriteWorker() { - while (newEntry.WaitOne()) + while (!disposing && newEntry.WaitOne()) { while (entries.TryDequeue(out Entry entry)) { @@ -58,11 +57,18 @@ void WriteWorker() Console.WriteLine(entry.Time.ToString("HH:mm:ss.fff ") + entry.Format + " ", entry.Args); } } + Console.WriteLine("Bye"); } private DateTime GetTime() { return time0.AddTicks(stopwatch.ElapsedTicks); } + + public void Dispose() + { + disposing = true; + worker.Wait(1000); + } } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index a54387a4..04843ceb 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -1,12 +1,13 @@ using BLE.Client.WinConsole; using Plugin.BLE; +using Plugin.BLE.Abstractions.Contracts; Console.WriteLine("Hello, BLE World!"); var ct = new ConsoleTracer(); Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.Trace; var demo = new BleDemo(ct.Trace); -await demo.DoTheScanning(); -await demo.ConnectTest("egoo"); - +await demo.DoTheScanning(ScanMode.LowPower); +await demo.ConnectTest("Shure"); +ct.Dispose(); From e8898f6bc6da68ce89baf50ae7a7f23bbbac4aff Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Wed, 20 Sep 2023 09:41:47 +0200 Subject: [PATCH 064/193] Connect to known in console demo --- Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs index 7c462c9e..a447c2e6 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Windows.Devices.Bluetooth; namespace BLE.Client.WinConsole { @@ -31,6 +32,12 @@ private void Write(string format, params object[] args) writer?.Invoke(format, args); } + public void ConnectToKnown(Guid id) + { + IDevice dev = adapter.ConnectToKnownDeviceAsync(id).Result; + WriteAdvertisementRecords(dev); + } + public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ms = 2000) { @@ -57,7 +64,12 @@ public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ void WriteAdvertisementRecords(IDevice device) { - Write("Device.State: {0} with {1} AdvertisementRecords", device.State, device.AdvertisementRecords.Count); + if (device.AdvertisementRecords is null) + { + Write("{0} {1} has no AdvertisementRecords...", device.Name, device.State); + return; + } + Write("{0} {1} with {2} AdvertisementRecords", device.Name, device.State, device.AdvertisementRecords.Count); foreach (var ar in device.AdvertisementRecords) { switch (ar.Type) From c0142d9d30519a61cf941e7d5f909c4af4d5eb2c Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Thu, 21 Sep 2023 22:26:58 +0200 Subject: [PATCH 065/193] Release 3.0.0-rc.1 * update version in csproj files * amend changelog.md --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- doc/changelog.md | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 0c252573..3209e2c2 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE - 3.0.0-beta.6 + 3.0.0-rc.1 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil MvvmCross.Plugin.BLE diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index c973ccd1..d4e19970 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 Plugin.BLE Plugin.BLE - 3.0.0-beta.6 + 3.0.0-rc.1 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil Plugin.BLE diff --git a/doc/changelog.md b/doc/changelog.md index 8a896838..1f7e0d2d 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,10 @@ ## 3.0 MAUI +#### 3.0.0-rc.1 +- #730 Bonding on Android (fixes #690) +- #740 Target Android 13 everywhere + #### 3.0.0-beta.6 - #728 Add Windows support for RequestMtuAsync (fixes #727) - #735 Fix DisconnectAsync hangs on Windows and Android after scanning then connecting with ConnectToKnownDeviceAsync (fixes #734) From 3cd8a1bad15fb513d47bf615e7d91a69aba67731 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 26 Sep 2023 16:34:57 +0200 Subject: [PATCH 066/193] Improved Connect/Disconnect/Connect/Disconnect --- .../BLE.Client.WinConsole/BleDemo.cs | 19 +++++- .../BLE.Client.WinConsole/ConsoleTracer.cs | 2 +- Source/Plugin.BLE/Windows/Adapter.cs | 57 ++++++++++------- Source/Plugin.BLE/Windows/Device.cs | 61 ++++++++++--------- .../Windows/Extensions/TextHelpers.cs | 28 ++++++++- 5 files changed, 113 insertions(+), 54 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs index a447c2e6..5311616c 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs @@ -4,6 +4,7 @@ using Plugin.BLE.Abstractions.Extensions; using Plugin.BLE.Extensions; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; @@ -18,10 +19,12 @@ internal class BleDemo private readonly IAdapter adapter; private readonly Action? writer; private readonly List discoveredDevices; + private readonly IDictionary connectedDevices; public BleDemo(Action? writer = null) { discoveredDevices = new List(); + connectedDevices = new ConcurrentDictionary(); bluetoothLE = CrossBluetoothLE.Current; adapter = CrossBluetoothLE.Current.Adapter; this.writer = writer; @@ -32,10 +35,17 @@ private void Write(string format, params object[] args) writer?.Invoke(format, args); } - public void ConnectToKnown(Guid id) + public IDevice ConnectToKnown(Guid id) { IDevice dev = adapter.ConnectToKnownDeviceAsync(id).Result; - WriteAdvertisementRecords(dev); + connectedDevices[id] = dev; + return dev; + } + + public IDevice ConnectToKnown(string bleaddress) + { + var id = bleaddress.ToBleDeviceGuid(); + return ConnectToKnown(id); } public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ms = 2000) @@ -96,5 +106,10 @@ public async Task ConnectTest(string name) } } } + + internal Task Disconnect(IDevice dev) + { + return adapter.DisconnectDeviceAsync(dev); + } } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs index 5c1e1b71..1e64eb2b 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs @@ -57,7 +57,7 @@ void WriteWorker() Console.WriteLine(entry.Time.ToString("HH:mm:ss.fff ") + entry.Format + " ", entry.Args); } } - Console.WriteLine("Bye"); + Console.WriteLine("Bye bye says the Console Tracer."); } private DateTime GetTime() diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index ae9aebb5..d09ee389 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -17,13 +17,13 @@ namespace Plugin.BLE.UWP { public class Adapter : AdapterBase { - private BluetoothLEAdvertisementWatcher _bleWatcher; + private BluetoothLEAdvertisementWatcher _bleWatcher; /// /// Registry used to store device instances for pending operations : disconnect /// Helps to detect connection lost events. /// - private readonly IDictionary _deviceOperationRegistry = new ConcurrentDictionary(); + private readonly IDictionary _deviceOperationRegistry = new ConcurrentDictionary(); public Adapter() { @@ -65,7 +65,11 @@ protected override void StopScanNative() protected override async Task ConnectToDeviceNativeAsync(IDevice device, ConnectParameters connectParameters, CancellationToken cancellationToken) { - + var dev = device as Device; + if (dev.NativeDevice == null) + { + await dev.RecreateNativeDevice(); + } var nativeDevice = device.NativeDevice as BluetoothLEDevice; Trace.Message("ConnectToDeviceNativeAsync {0} Named: {1} Connected: {2}", device.Id.ToHexBleAddress(), device.Name, nativeDevice.ConnectionStatus); @@ -74,10 +78,13 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; - var gattServiceResult = await nativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); - - if (gattServiceResult.Status != GattCommunicationStatus.Success - || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) + // Calling the GetGattServicesAsync on the BluetoothLEDevice with uncached property causes the device to connect + BluetoothCacheMode restoremode = BleImplementation.CacheModeGetServices; + BleImplementation.CacheModeGetServices = BluetoothCacheMode.Uncached; + var services = device.GetServicesAsync(cancellationToken).Result; + BleImplementation.CacheModeGetServices = restoremode; + + if (!services.Any() || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) { // use DisconnectDeviceNative to clean up resources otherwise windows won't disconnect the device // after a subsequent successful connection (#528, #536, #423) @@ -101,12 +108,17 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect else { _deviceOperationRegistry[device.Id.ToString()] = device; - } + } } private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, object args) - { - var id = nativeDevice.BluetoothAddress.ParseDeviceId().ToString(); + { + Trace.Message("Device_ConnectionStatusChanged {0} {1} {2}", + nativeDevice.BluetoothAddress.ToHexBleAddress(), + nativeDevice.Name, + nativeDevice.ConnectionStatus); + var id = nativeDevice.BluetoothAddress.ParseDeviceId().ToString(); + if (nativeDevice.ConnectionStatus == BluetoothConnectionStatus.Connected && ConnectedDeviceRegistry.TryGetValue(id, out var connectedDevice)) { @@ -127,26 +139,29 @@ private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, obje // fire the correct event (DeviceDisconnected or DeviceConnectionLost) HandleDisconnectedDevice(isNormalDisconnect, disconnectedDevice); - } + if (isNormalDisconnect) + { + nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; + } + } } protected override void DisconnectDeviceNative(IDevice device) { // Windows doesn't support disconnecting, so currently just dispose of the device - Trace.Message($"Disconnected from device with ID: {device.Id}"); + Trace.Message($"DisconnectDeviceNative from device with ID: {device.Id.ToHexBleAddress()}"); if (device.NativeDevice is BluetoothLEDevice nativeDevice) - { - _deviceOperationRegistry.Remove(device.Id.ToString()); - nativeDevice.Dispose(); - } + { + _deviceOperationRegistry.Remove(device.Id.ToString()); + ((Device)device).ClearServices(); + ((Device)device).DisposeNativeDevice(); + } } public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default) - { - //convert GUID to string and take last 12 characters as MAC address - var guidString = deviceGuid.ToString("N").Substring(20); - var bluetoothAddress = Convert.ToUInt64(guidString, 16); - var nativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bluetoothAddress); + { + var bleAddress = deviceGuid.ToBleAddress(); + var nativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bleAddress); if (nativeDevice == null) throw new Abstractions.Exceptions.DeviceConnectionException(deviceGuid, "", $"[Adapter] Device {deviceGuid} not found."); diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 112e47ba..ddde18fa 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -6,11 +6,17 @@ using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; +using System.Threading; +using System.Collections.Concurrent; namespace Plugin.BLE.UWP { public class Device : DeviceBase - { + { + private ConcurrentBag asyncOperations = new(); + private readonly Mutex opMutex = new Mutex(false); + private readonly SemaphoreSlim opSemaphore = new SemaphoreSlim(1); + public Device(Adapter adapter, BluetoothLEDevice nativeDevice, int rssi, Guid id, IReadOnlyList advertisementRecords = null, bool isConnectable = true) : base(adapter, nativeDevice) @@ -33,11 +39,27 @@ public override Task UpdateRssiAsync() //No current method to update the Rssi of a device //In future implementations, maybe listen for device's advertisements - Trace.Message("Request RSSI not supported in UWP"); + Trace.Message("Request RSSI not supported in UWP"); return Task.FromResult(true); } + public void DisposeNativeDevice() + { + if (NativeDevice is not null) + { + NativeDevice.Dispose(); + NativeDevice = null; + } + } + + public async Task RecreateNativeDevice() + { + DisposeNativeDevice(); + var bleAddress = Id.ToBleAddress(); + NativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bleAddress); + } + protected override async Task> GetServicesNativeAsync() { if (NativeDevice == null) @@ -50,6 +72,7 @@ protected override async Task> GetServicesNativeAsync() .Select(nativeService => new Service(nativeService, this)) .Cast() .ToList() ?? new List(); + } protected override async Task GetServiceNativeAsync(Guid id) @@ -63,6 +86,10 @@ protected override async Task GetServiceNativeAsync(Guid id) protected override DeviceState GetState() { + if (NativeDevice is null) + { + return DeviceState.Disconnected; + } if (NativeDevice.ConnectionStatus == BluetoothConnectionStatus.Connected) { return DeviceState.Connected; @@ -81,42 +108,18 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv { Trace.Message("Update Connection Interval not supported in UWP"); return false; - } + } public override void Dispose() { if (NativeDevice != null) { - Trace.Message("Disposing {0} with id = {1}", Name, Id.ToString()); + Trace.Message("Disposing {0} with name = {1}", Id.ToHexBleAddress(), Name); NativeDevice.Dispose(); - NativeDevice = null; + NativeDevice = null; } - //FreeResources(false); } - //internal void FreeResources(bool recreateNativeDevice = true) - //{ - // NativeDevice?.Services?.ToList().ForEach(s => - // { - // s?.Service?.Session?.Dispose(); - // s?.Service?.Dispose(); - // }); - - // // save these so we can re-create ObservableBluetoothLEDevice if needed - // var tempDevInfo = NativeDevice?.DeviceInfo; - // var tempDq = NativeDevice?.DispatcherQueue; - - // NativeDevice?.BluetoothLEDevice?.Dispose(); - - // // the ObservableBluetoothLEDevice doesn't really support the BluetoothLEDevice - // // being disposed so we need to recreate it. What we really need is to be able - // // to set NativeDevice?.BluetoothLEDevice = null; - // if (recreateNativeDevice) - // NativeDevice = new ObservableBluetoothLEDevice(tempDevInfo, tempDq); - - // GC.Collect(); - //} - public override bool IsConnectable { get; protected set; } public override bool SupportsIsConnectable { get => true; } diff --git a/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs index c5bbc1a8..1544420b 100644 --- a/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs +++ b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs @@ -46,7 +46,7 @@ public static string ToDetailedString(this BluetoothLEAdvertisementReceivedEvent /// public static string ToHexBleAddress(this Guid id) { - return id.ToString("N").Substring(20); + return id.ToString("N").Substring(20).ToUpperInvariant(); //return id.ToString()[^12..].ToUpperInvariant(); //Not for netstandard2.0 } @@ -78,4 +78,30 @@ public static Guid ParseDeviceId(this ulong bluetoothAddress) macBytes.CopyTo(deviceGuid, 10); return new Guid(deviceGuid); } + + /// + /// Covert 12 chars hex string = 6 bytes = 48 bits to Guid used in this plugin + /// + /// + /// + public static Guid ToBleDeviceGuid(this string macWithoutColons) + { + macWithoutColons = macWithoutColons.PadLeft(12, '0'); //ensure valid length + var deviceGuid = new byte[16]; + Array.Clear(deviceGuid, 0, 16); + var macBytes = Enumerable.Range(0, macWithoutColons.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(macWithoutColons.Substring(x, 2), 16)) + .ToArray(); + macBytes.CopyTo(deviceGuid, 10); + return new Guid(deviceGuid); + } + + public static ulong ToBleAddress(this Guid deviceGuid) + { + //convert GUID to string and take last 12 characters as MAC address + var guidString = deviceGuid.ToString("N").Substring(20); + var bluetoothAddress = Convert.ToUInt64(guidString, 16); + return bluetoothAddress; + } } From ea78b31896573ae71ee003832e663c1cf25bf8ae Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 7 Oct 2023 22:07:59 +0200 Subject: [PATCH 067/193] update MvvmCross to 9.1.1 * for .NET 6 upwards * the Xamarin targets stay at 8.0.2 (because they are not supported by v9) --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 3209e2c2..c2fddfbf 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -82,7 +82,7 @@ - + @@ -93,7 +93,7 @@ - + @@ -105,7 +105,7 @@ - + @@ -113,7 +113,7 @@ - + From 8b138ae76d6e4de52a1688c9a0f758d9f8ec4b88 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 8 Oct 2023 13:02:14 +0200 Subject: [PATCH 068/193] Release 3.0.0 * update version in csproj files * amend changelog.md --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- doc/changelog.md | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index c2fddfbf..ab3afe17 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE - 3.0.0-rc.1 + 3.0.0 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil MvvmCross.Plugin.BLE diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index d4e19970..82544821 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 Plugin.BLE Plugin.BLE - 3.0.0-rc.1 + 3.0.0 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil Plugin.BLE diff --git a/doc/changelog.md b/doc/changelog.md index 1f7e0d2d..5fc7b495 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,11 @@ ## 3.0 MAUI +#### 3.0.0 +- Add support for MAUI (.NET 6 and 7), while keeping compatibility with Xamarin +- Add support for Windows (UWP & WinUI, experimental) +- Various improvements and bugfixes (see beta- and pre-releases) + #### 3.0.0-rc.1 - #730 Bonding on Android (fixes #690) - #740 Target Android 13 everywhere From 0b92d0eab2cafc20c84051595876aad18e5ac962 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 9 Oct 2023 12:37:28 +0200 Subject: [PATCH 069/193] Improved BLE adapter detection --- Source/Plugin.BLE/Windows/BleImplementation.cs | 8 ++++++-- .../Windows/Extensions/GattProtocolErrorParser.cs | 2 +- .../Plugin.BLE/Windows/Extensions/GattResultExtensions.cs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/Plugin.BLE/Windows/BleImplementation.cs b/Source/Plugin.BLE/Windows/BleImplementation.cs index b7c64b85..f9883045 100644 --- a/Source/Plugin.BLE/Windows/BleImplementation.cs +++ b/Source/Plugin.BLE/Windows/BleImplementation.cs @@ -26,10 +26,14 @@ protected override BluetoothState GetInitialStateNative() try { BluetoothAdapter btAdapter = BluetoothAdapter.GetDefaultAsync().AsTask().Result; - var radio = btAdapter.GetRadioAsync().AsTask().Result; + if (!btAdapter.IsLowEnergySupported) + { + return BluetoothState.Unavailable; + } + var radio = btAdapter.GetRadioAsync().AsTask().Result; switch (radio.State) { - case RadioState.On: + case RadioState.On: return BluetoothState.On; case RadioState.Off: return BluetoothState.Off; diff --git a/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs b/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs index 4f96f51f..e988b7e5 100644 --- a/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs +++ b/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs @@ -18,7 +18,7 @@ namespace Plugin.BLE.Extensions; /// /// Helper function when working with /// -public static class GattProtocolErrorParser +internal static class GattProtocolErrorParser { /// /// Helper to convert an Gatt error value into a string diff --git a/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs b/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs index 387161cc..9f8112c3 100644 --- a/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs +++ b/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs @@ -7,7 +7,7 @@ namespace Plugin.BLE.Extensions { - public static class GattResultExtensions + internal static class GattResultExtensions { public static void ThrowIfError(this GattWriteResult result, [CallerMemberName]string tag = null) => result.Status.ThrowIfError(tag, result.ProtocolError); From 498c1e84a8725e1f7cf3498e6a9032fc3e283a67 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Fri, 20 Oct 2023 21:48:33 +0200 Subject: [PATCH 070/193] BLE.Client.Maui: add a target framework for Windows10 --- Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index 81f3e115..a0b23afd 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -2,6 +2,7 @@ net7.0-android33.0;net7.0-ios;net7.0-maccatalyst + $(TargetFrameworks);net7.0-windows10.0.19041 Exe BLE.Client.Maui true From 16f6d774246308a50b38ee8d6eb8226f20963762 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Fri, 20 Oct 2023 21:59:59 +0200 Subject: [PATCH 071/193] BLE.sln: deploy project BLE.Client.Maui * so that it can be debugged --- Source/BLE.sln | 128 ++++++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 59 deletions(-) diff --git a/Source/BLE.sln b/Source/BLE.sln index 93ec24bb..15cc01f0 100644 --- a/Source/BLE.sln +++ b/Source/BLE.sln @@ -40,11 +40,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLE.Client.Droid", "BLE.Cli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLE.Client.iOS", "BLE.Client\BLE.Client.iOS\BLE.Client.iOS.csproj", "{D0FBE2BA-799B-4071-A45D-8FB037006C9C}" EndProject -Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "BLE.Client.macOS", "BLE.Client\BLE.Client.macOS\BLE.Client.macOS.csproj", "{08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLE.Client.macOS", "BLE.Client\BLE.Client.macOS\BLE.Client.macOS.csproj", "{B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLE.Client.UWP", "BLE.Client\BLE.Client.UWP\BLE.Client.UWP.csproj", "{25E04E05-F867-4F64-813D-AAFE0BA171B0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLE.Client.Maui", "BLE.Client\BLE.Client.Maui\BLE.Client.Maui.csproj", "{D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BLE.Client.Maui", "BLE.Client\BLE.Client.Maui\BLE.Client.Maui.csproj", "{D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -416,62 +416,62 @@ Global {D0FBE2BA-799B-4071-A45D-8FB037006C9C}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator {D0FBE2BA-799B-4071-A45D-8FB037006C9C}.Release|x64.ActiveCfg = Release|iPhone {D0FBE2BA-799B-4071-A45D-8FB037006C9C}.Release|x86.ActiveCfg = Release|iPhone - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x64.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Ad-Hoc|x86.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM64.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|ARM64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhone.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x64.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x64.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x86.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.AppStore|x86.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|ARM.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|ARM.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|ARM64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|iPhone.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|x64.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|x64.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|x86.ActiveCfg = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Debug|x86.Build.0 = Debug|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|Any CPU.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|ARM.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|ARM.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|ARM64.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|ARM64.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|iPhone.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|iPhone.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|x64.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|x64.Build.0 = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|x86.ActiveCfg = Release|Any CPU - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B}.Release|x86.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|ARM.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|x64.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|Any CPU.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|ARM.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|ARM.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|ARM64.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|iPhone.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|x64.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|x64.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|x86.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.AppStore|x86.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|ARM.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|ARM64.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|iPhone.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|x64.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|x64.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|x86.ActiveCfg = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Debug|x86.Build.0 = Debug|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|Any CPU.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|ARM.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|ARM.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|ARM64.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|ARM64.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|iPhone.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|iPhone.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|x64.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|x64.Build.0 = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|x86.ActiveCfg = Release|Any CPU + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED}.Release|x86.Build.0 = Release|Any CPU {25E04E05-F867-4F64-813D-AAFE0BA171B0}.Ad-Hoc|Any CPU.ActiveCfg = Release|x64 {25E04E05-F867-4F64-813D-AAFE0BA171B0}.Ad-Hoc|Any CPU.Build.0 = Release|x64 {25E04E05-F867-4F64-813D-AAFE0BA171B0}.Ad-Hoc|Any CPU.Deploy.0 = Release|x64 @@ -572,32 +572,42 @@ Global {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.AppStore|x86.Build.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|ARM.ActiveCfg = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|ARM.Build.0 = Debug|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|ARM.Deploy.0 = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|ARM64.ActiveCfg = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|ARM64.Build.0 = Debug|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|ARM64.Deploy.0 = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|iPhone.ActiveCfg = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|iPhone.Build.0 = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|x64.ActiveCfg = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|x64.Build.0 = Debug|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|x64.Deploy.0 = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|x86.ActiveCfg = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|x86.Build.0 = Debug|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Debug|x86.Deploy.0 = Debug|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|Any CPU.Build.0 = Release|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|Any CPU.Deploy.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|ARM.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|ARM.Build.0 = Release|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|ARM.Deploy.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|ARM64.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|ARM64.Build.0 = Release|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|ARM64.Deploy.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|iPhone.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|iPhone.Build.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x64.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x64.Build.0 = Release|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x64.Deploy.0 = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x86.ActiveCfg = Release|Any CPU {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x86.Build.0 = Release|Any CPU + {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E}.Release|x86.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -606,7 +616,7 @@ Global {DFE97BE0-070B-43AD-BF37-50FD42B542D1} = {A06042F5-F69D-4191-875E-5ADE9CF42075} {7B0E4EFD-EEB8-4CE9-9C8D-7A4BF734E9A7} = {A06042F5-F69D-4191-875E-5ADE9CF42075} {D0FBE2BA-799B-4071-A45D-8FB037006C9C} = {A06042F5-F69D-4191-875E-5ADE9CF42075} - {08364CFC-8328-4CE0-9A15-91CB3EAA9B0B} = {A06042F5-F69D-4191-875E-5ADE9CF42075} + {B202A1F9-B9B8-4F5F-A011-C8EA5E485CED} = {A06042F5-F69D-4191-875E-5ADE9CF42075} {25E04E05-F867-4F64-813D-AAFE0BA171B0} = {A06042F5-F69D-4191-875E-5ADE9CF42075} {D04F2D04-A33F-4E09-9EE2-29D7B2A6CD4E} = {A06042F5-F69D-4191-875E-5ADE9CF42075} EndGlobalSection From 42db04dc04ee62c48b49b2928d70709f7b4dee3d Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Fri, 20 Oct 2023 22:18:06 +0200 Subject: [PATCH 072/193] BLE.Client.Maui: implement IPlatformHelpers on Windows --- Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs | 2 ++ .../Platforms/Windows/WindowsPlatformHelpers.cs | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs diff --git a/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs b/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs index 9a4ef7dd..d2286027 100644 --- a/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs +++ b/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs @@ -42,6 +42,8 @@ private static void AddPlatformSpecificItems(MauiAppBuilder builder) builder.Services.AddSingleton(); #elif MACCATALYST builder.Services.AddSingleton(); +#elif WINDOWS + builder.Services.AddSingleton(); #endif } } diff --git a/Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs b/Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs new file mode 100644 index 00000000..349fcda5 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs @@ -0,0 +1,10 @@ +namespace BLE.Client.Helpers +{ + public class WindowsPlatformHelpers : IPlatformHelpers + { + public Task CheckAndRequestBluetoothPermissions() + { + return Task.FromResult(PermissionStatus.Granted); + } + } +} From 4a5f987296f6e6cabc91b2951ab1223607eadb22 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 21 Oct 2023 09:21:56 +0200 Subject: [PATCH 073/193] BLE.Client.Maui: fix a runtime exception on Windows System.Runtime.InteropServices.COMException: 'The application called an interface that was marshalled for a different thread. (0x8001010E (RPC_E_WRONG_THREAD))' --- .../BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs b/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs index 52b9fe6b..5547f033 100644 --- a/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs +++ b/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs @@ -215,7 +215,8 @@ private void AddOrUpdateDevice(IDevice device) else { DebugMessage($"Add Device: {device.Id}"); - BLEDevices.Add(new BLEDeviceViewModel(device)); + vm = new BLEDeviceViewModel(device); + MainThread.BeginInvokeOnMainThread(() => BLEDevices.Add(vm)); OnPropertyChanged(nameof(BLEDevices)); } DebugMessage($"Device Found: '{device.Id}' done"); From 79802286faec73a813f3f60015b970c04629985c Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 21 Oct 2023 14:23:31 +0200 Subject: [PATCH 074/193] GHA: build BLE.Client.Maui on Mac * and remove the "setup-nuget" action (unnecessary) --- .github/workflows/dotnet.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 40662c8a..fa6f41a2 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -75,7 +75,6 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: nuget/setup-nuget@v1 - name: Setup .NET uses: actions/setup-dotnet@v3 with: @@ -89,3 +88,5 @@ jobs: run: dotnet build ./Source/Plugin.BLE/Plugin.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false - name: Build MVVMCross.Plugins.BLE NuGet run: dotnet build ./Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false + - name: Build MAUI sample + run: dotnet build ./Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj /p:Configuration=Release /t:restore,build /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false From 2590003758ea9cdaf674e287e61238bd1f925c2f Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 21 Oct 2023 16:11:59 +0200 Subject: [PATCH 075/193] GHA: run on Mac OS 13 * to fix strange warnings and errors in BLE.Client.Maui (see below) * macos-latest is currently at 12.7 ILLINK : warning MT0079: The recommended Xcode version for Microsoft.iOS 16.4.7107 is Xcode 14.3 or later. The current Xcode version (found in /Applications/Xcode_14.2.app/Contents/Developer) is 14.2. [/Users/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj::TargetFramework=net7.0-ios] ILLINK : warning MT0079: The recommended Xcode version for Microsoft.MacCatalyst 16.4.7107 is Xcode 14.3 or later. The current Xcode version (found in /Applications/Xcode_14.2.app/Contents/Developer) is 14.2. [/Users/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj::TargetFramework=net7.0-maccatalyst] ILLink : unknown error IL7000: An error occured while executing the custom linker steps. Please review the build log for more information. [/Users/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj::TargetFramework=net7.0-ios] ILLINK : error MT0180: This version of Microsoft.iOS requires the iOS 16.4 SDK (shipped with Xcode 14.3). Either upgrade Xcode to get the required header files or set the managed linker behaviour to Link Framework SDKs Only in your project's iOS Build Options > Linker Behavior (to try to avoid the new APIs). [/Users/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj::TargetFramework=net7.0-ios] ILLINK : error MT2301: The linker step 'Setup' failed during processing: This version of Microsoft.iOS requires the iOS 16.4 SDK (shipped with Xcode 14.3). Either upgrade Xcode to get the required header files or set the managed linker behaviour to Link Framework SDKs Only in your project's iOS Build Options > Linker Behavior (to try to avoid the new APIs). [/Users/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj::TargetFramework=net7.0-ios] Error: /Users/runner/.dotnet/sdk/7.0.402/Sdks/Microsoft.NET.ILLink.Tasks/build/Microsoft.NET.ILLink.targets(86,5): error NETSDK1144: Optimizing assemblies for size failed. Optimization can be disabled by setting the PublishTrimmed property to false. [/Users/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj::TargetFramework=net7.0-ios] --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index fa6f41a2..c086a327 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -70,7 +70,7 @@ jobs: path: ./Source/*/nuget/*Plugin.BLE*.nupkg macBuild: - runs-on: macos-latest + runs-on: macos-13 steps: - uses: actions/checkout@v3 with: From a9353003ba3936cce5eb3feb773cbbc8e5d7d143 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 21 Oct 2023 21:45:47 +0200 Subject: [PATCH 076/193] Plugin.BLE.Tests: update Microsoft.NET.Test.Sdk and xunit --- Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj b/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj index 111927c8..e037de93 100644 --- a/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj +++ b/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj @@ -4,13 +4,13 @@ false - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From ea4e53dfa1a222e2b49859e0a1c032b60b1b6b0d Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 21 Oct 2023 22:01:50 +0200 Subject: [PATCH 077/193] BLE.Client: update Xamarin.Forms to 5.0.0.2612 --- Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj | 2 +- Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj | 4 ++-- Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj | 4 ++-- Source/BLE.Client/BLE.Client.macOS/BLE.Client.macOS.csproj | 2 +- Source/BLE.Client/BLE.Client/BLE.Client.csproj | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj index ecaf0c82..56646670 100644 --- a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj +++ b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj @@ -123,7 +123,7 @@ 0.2.0.64 - 5.0.0.2478 + 5.0.0.2612 1.2.5.4 diff --git a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj index fb0c1de0..668fc015 100644 --- a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj +++ b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj @@ -192,7 +192,7 @@ 2.0.0 - 5.0.0.2478 + 5.0.0.2612 @@ -217,4 +217,4 @@ --> - + \ No newline at end of file diff --git a/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj b/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj index f4589537..4c2bc096 100644 --- a/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj +++ b/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj @@ -198,9 +198,9 @@ 0.2.0.64 - 5.0.0.2478 + 5.0.0.2612 - + \ No newline at end of file diff --git a/Source/BLE.Client/BLE.Client.macOS/BLE.Client.macOS.csproj b/Source/BLE.Client/BLE.Client.macOS/BLE.Client.macOS.csproj index e5bd9132..d46482af 100644 --- a/Source/BLE.Client/BLE.Client.macOS/BLE.Client.macOS.csproj +++ b/Source/BLE.Client/BLE.Client.macOS/BLE.Client.macOS.csproj @@ -105,7 +105,7 @@ 8.0.2 - 5.0.0.2478 + 5.0.0.2612 diff --git a/Source/BLE.Client/BLE.Client/BLE.Client.csproj b/Source/BLE.Client/BLE.Client/BLE.Client.csproj index 4b035576..37c04c08 100644 --- a/Source/BLE.Client/BLE.Client/BLE.Client.csproj +++ b/Source/BLE.Client/BLE.Client/BLE.Client.csproj @@ -14,7 +14,7 @@ - + From 0249457b7266cd25765c7425323ea2d1c422332a Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 21 Oct 2023 22:15:48 +0200 Subject: [PATCH 078/193] BLE.Client: update Xamarin.Essentials to 1.8.0 --- Source/BLE.Client/BLE.Client/BLE.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/BLE.Client/BLE.Client/BLE.Client.csproj b/Source/BLE.Client/BLE.Client/BLE.Client.csproj index 37c04c08..b2dbf771 100644 --- a/Source/BLE.Client/BLE.Client/BLE.Client.csproj +++ b/Source/BLE.Client/BLE.Client/BLE.Client.csproj @@ -15,7 +15,7 @@ - + From 6d74b9ffbd69cdfb70f6cbada582cf058803ef91 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 22 Oct 2023 10:26:15 +0200 Subject: [PATCH 079/193] BLE.Client: update Microsoft.Extensions.Logging to 7.0.0 --- Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj | 2 +- Source/BLE.Client/BLE.Client/BLE.Client.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj index 668fc015..999a4246 100644 --- a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj +++ b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj @@ -171,7 +171,7 @@ 7.1.0.514 - 6.0.0 + 7.0.0 6.2.14 diff --git a/Source/BLE.Client/BLE.Client/BLE.Client.csproj b/Source/BLE.Client/BLE.Client/BLE.Client.csproj index b2dbf771..a566de6f 100644 --- a/Source/BLE.Client/BLE.Client/BLE.Client.csproj +++ b/Source/BLE.Client/BLE.Client/BLE.Client.csproj @@ -11,7 +11,7 @@ - + From 3dfbda8a7c3cc73b62814d4c13fa9e4803f090d6 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 22 Oct 2023 10:52:24 +0200 Subject: [PATCH 080/193] BLE.Client: update Serilog packages --- Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj | 4 ++-- Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj | 4 ++-- Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj index 56646670..bd387c8e 100644 --- a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj +++ b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj @@ -117,10 +117,10 @@ 8.0.2 - 3.1.0 + 7.0.0 - 0.2.0.64 + 1.0.0 5.0.0.2612 diff --git a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj index 999a4246..2cc3a446 100644 --- a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj +++ b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj @@ -183,10 +183,10 @@ 8.0.2 - 2.11.0 + 3.0.1 - 3.1.0 + 7.0.0 2.0.0 diff --git a/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj b/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj index 4c2bc096..b8570709 100644 --- a/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj +++ b/Source/BLE.Client/BLE.Client.iOS/BLE.Client.iOS.csproj @@ -192,10 +192,10 @@ 8.0.2 - 3.1.0 + 7.0.0 - 0.2.0.64 + 1.0.0 5.0.0.2612 From 2e6ecde002f2a7873a2a77bc50d9c0d9b61c4c72 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 22 Oct 2023 11:12:53 +0200 Subject: [PATCH 081/193] BLE.Client.Droid: update AndroidX packages --- .../BLE.Client.Droid/BLE.Client.Droid.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj index bd387c8e..6aea4786 100644 --- a/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj +++ b/Source/BLE.Client/BLE.Client.Droid/BLE.Client.Droid.csproj @@ -126,17 +126,17 @@ 5.0.0.2612 - 1.2.5.4 + 1.6.0.1 - 1.0.0.12 + 1.0.0.21 - 1.2.1.5 + 1.3.1.2 - 1.1.1.13 + 1.2.1.2 - + \ No newline at end of file From 74d4f89052746326c1c21380a10d1747b9dda9d9 Mon Sep 17 00:00:00 2001 From: Aidan Riley Date: Wed, 25 Oct 2023 12:08:21 +0100 Subject: [PATCH 082/193] ReadAsync updates characteristic value on Windows --- Source/Plugin.BLE/Windows/Characteristic.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Windows/Characteristic.cs b/Source/Plugin.BLE/Windows/Characteristic.cs index acac08bf..c6e726d0 100644 --- a/Source/Plugin.BLE/Windows/Characteristic.cs +++ b/Source/Plugin.BLE/Windows/Characteristic.cs @@ -50,7 +50,7 @@ protected override async Task> GetDescriptorsNativeAs protected override async Task<(byte[] data, int resultCode)> ReadNativeAsync() { var readResult = await NativeCharacteristic.ReadValueAsync(BleImplementation.CacheModeCharacteristicRead); - var _value = readResult.GetValueOrThrowIfError(); + _value = readResult.GetValueOrThrowIfError(); return (_value, (int)readResult.Status); } From 63554278a1f8075c97e73f3377c874b0b2d64618 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Wed, 25 Oct 2023 21:37:01 +0200 Subject: [PATCH 083/193] Update README.md --- README.md | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6cf6636c..588a0f8e 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,9 @@ Install-Package MvvmCross.Plugin.BLE Install-Package MvvmCross.Plugin.BLE -Pre ``` -**Android** +## Permissions + +### Android Add these permissions to AndroidManifest.xml. For Marshmallow and above, please follow [Requesting Runtime Permissions in Android Marshmallow](https://devblogs.microsoft.com/xamarin/requesting-runtime-permissions-in-android-marshmallow/) and don't forget to prompt the user for the location permission. @@ -60,45 +62,46 @@ Android 12 and above may require one or more of the following additional runtime - ``` - Add this line to your manifest if you want to declare that your app is available to BLE-capable devices **only**: ```xml ```` -**iOS** +### iOS On iOS you must add the following keys to your `Info.plist` - UIBackgroundModes - - - bluetooth-central - - - bluetooth-peripheral - - - - NSBluetoothPeripheralUsageDescription - YOUR CUSTOM MESSAGE - - - NSBluetoothAlwaysUsageDescription - YOUR CUSTOM MESSAGE +```xml +UIBackgroundModes + + + bluetooth-central + + bluetooth-peripheral + + + +NSBluetoothPeripheralUsageDescription +YOUR CUSTOM MESSAGE + + +NSBluetoothAlwaysUsageDescription +YOUR CUSTOM MESSAGE +```` -**MacOS** +### MacOS On MacOS (version 11 and above) you must add the following keys to your `Info.plist`: - - NSBluetoothAlwaysUsageDescription - YOUR CUSTOM MESSAGE +```xml + +NSBluetoothAlwaysUsageDescription +YOUR CUSTOM MESSAGE +```` -**UWP** +### UWP Add this line to the Package Manifest (.appxmanifest): From 4fca5e389052ecc988f85d3bb939f1bfd84ad72e Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sat, 28 Oct 2023 13:51:11 +0200 Subject: [PATCH 084/193] Improve logging when receiving advertisements --- .../BLE.Client/BLE.Client.WinConsole/BleDemo.cs | 12 +++++++++++- Source/Plugin.BLE/Windows/Adapter.cs | 15 ++++++++++----- .../Plugin.BLE/Windows/Extensions/TextHelpers.cs | 15 ++++++++++++--- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs index 5311616c..65001504 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs @@ -72,7 +72,7 @@ public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ } - void WriteAdvertisementRecords(IDevice device) + private void WriteAdvertisementRecords(IDevice device) { if (device.AdvertisementRecords is null) { @@ -107,6 +107,16 @@ public async Task ConnectTest(string name) } } + public void ShowGetSystemConnectedOrPairedDevices() + { + IReadOnlyList devs = adapter.GetSystemConnectedOrPairedDevices(); + Write("GetSystemConnectedOrPairedDevices found {0} devices.", devs.Count); + foreach(var dev in devs) + { + Write("{0}: {1}", dev.Id.ToHexBleAddress(), dev.Name); + } + } + internal Task Disconnect(IDevice dev) { return adapter.DisconnectDeviceAsync(dev); diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 7a55195d..0e63b4de 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -12,6 +12,7 @@ using Plugin.BLE.Extensions; using System.Collections.Concurrent; using Windows.Devices.Bluetooth.GenericAttributeProfile; +using Windows.Devices.Enumeration; namespace Plugin.BLE.UWP { @@ -210,17 +211,15 @@ private void AdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, Blue { var deviceId = btAdv.BluetoothAddress.ParseDeviceId(); - if (DiscoveredDevicesRegistry.TryGetValue(deviceId, out var device)) + if (DiscoveredDevicesRegistry.TryGetValue(deviceId, out var device) && device != null) { - Trace.Message("AdvertisementReceived - Old Device: {0}", btAdv.ToDetailedString()); + Trace.Message("AdvReceived - Old: {0}", btAdv.ToDetailedString(device.Name)); (device as Device)?.Update(btAdv.RawSignalStrengthInDBm, ParseAdvertisementData(btAdv.Advertisement)); this.HandleDiscoveredDevice(device); } - else + if (device == null) { - Trace.Message("AdvertisementReceived - New Device: {0}", btAdv.ToDetailedString()); var bluetoothLeDevice = BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress).AsTask().Result; - //var bluetoothLeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress); if (bluetoothLeDevice != null) //make sure advertisement bluetooth address actually returns a device { device = new Device( @@ -230,8 +229,14 @@ private void AdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, Blue deviceId, ParseAdvertisementData(btAdv.Advertisement), btAdv.IsConnectable); + Trace.Message("AdvReceived - New: {0}", btAdv.ToDetailedString(device.Name)); + _ = DiscoveredDevicesRegistry.TryRemove(deviceId, out _); this.HandleDiscoveredDevice(device); } + else + { + DiscoveredDevicesRegistry[deviceId] = null; + } } } diff --git a/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs index 1544420b..83cbfda4 100644 --- a/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs +++ b/Source/Plugin.BLE/Windows/Extensions/TextHelpers.cs @@ -11,11 +11,12 @@ namespace Plugin.BLE.Extensions; public static class TextHelpers { - public static string ToDetailedString(this BluetoothLEAdvertisementReceivedEventArgs btAdv) + public static string ToDetailedString(this BluetoothLEAdvertisementReceivedEventArgs btAdv, string name = "na") { string hexadr = btAdv.BluetoothAddress.ToHexBleAddress(); StringBuilder sb = new StringBuilder(); sb.Append(hexadr) + .Append(", ").Append(name) .Append(", ").Append(btAdv.BluetoothAddressType) .Append(", ").Append(btAdv.RawSignalStrengthInDBm) .Append(", ").Append(btAdv.AdvertisementType); @@ -37,7 +38,7 @@ public static string ToDetailedString(this BluetoothLEAdvertisementReceivedEvent sb.Append(", Directed"); } return sb.ToString(); - } + } /// /// Get a string of the BLE address: 48 bit = 6 bytes = 12 Hex chars @@ -80,7 +81,7 @@ public static Guid ParseDeviceId(this ulong bluetoothAddress) } /// - /// Covert 12 chars hex string = 6 bytes = 48 bits to Guid used in this plugin + /// Convert 12 chars hex string = 6 bytes = 48 bits to Guid used in this plugin /// /// /// @@ -97,6 +98,14 @@ public static Guid ToBleDeviceGuid(this string macWithoutColons) return new Guid(deviceGuid); } + public static Guid ToBleDeviceGuidFromId(this string idWithColons) + { + //example: Bluetooth#Bluetoothe4:aa:ea:cd:28:00-70:bf:92:06:e1:9e + var nocolons = idWithColons.Replace(":", ""); + return ToBleDeviceGuid(nocolons.Substring(nocolons.Length-12, 12)); + } + + public static ulong ToBleAddress(this Guid deviceGuid) { //convert GUID to string and take last 12 characters as MAC address From a77893cadba3977ef4d85feee82a897f5d4af843 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sat, 28 Oct 2023 14:44:27 +0200 Subject: [PATCH 085/193] Added comments in the GattProtocolErrorParser.cs file --- .../Extensions/GattProtocolErrorParser.cs | 223 +++++++++--------- 1 file changed, 111 insertions(+), 112 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs b/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs index e988b7e5..d17f9216 100644 --- a/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs +++ b/Source/Plugin.BLE/Windows/Extensions/GattProtocolErrorParser.cs @@ -1,125 +1,124 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Windows.Devices.Bluetooth.GenericAttributeProfile; -namespace Plugin.BLE.Extensions; - -// -// This code has been copied from CommunityToolkit.WinUI.Connectivity -// // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/// -/// Helper function when working with -/// -internal static class GattProtocolErrorParser +// This file has been copied from CommunityToolkit.WinUI.Connectivity +// https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.Connectivity/BluetoothLEHelper/GattProtocolErrorParser.cs +// Date: 2023-10-28 with these changes applied: +// 1. These comments are added +// 2. namespace Microsoft.Toolkit.Uwp.Connectivity -> Plugin.BLE.Extensions +// 3. public static class GattProtocolErrorParser -> internal static class GattProtocolErrorParser + +using Windows.Devices.Bluetooth.GenericAttributeProfile; + +namespace Plugin.BLE.Extensions { /// - /// Helper to convert an Gatt error value into a string + /// Helper function when working with /// - /// the byte error value. - /// String representation of the error - public static string GetErrorString(this byte? errorValue) + internal static class GattProtocolErrorParser { - var errorString = "Protocol Error"; + /// + /// Helper to convert an Gatt error value into a string + /// + /// the byte error value. + /// String representation of the error + public static string GetErrorString(this byte? errorValue) + { + var errorString = "Protocol Error"; + + if (errorValue.HasValue == false) + { + return errorString; + } + + if (errorValue == GattProtocolError.AttributeNotFound) + { + return "Attribute Not Found"; + } + + if (errorValue == GattProtocolError.AttributeNotLong) + { + return "Attribute Not Long"; + } + + if (errorValue == GattProtocolError.InsufficientAuthentication) + { + return "Insufficient Authentication"; + } + + if (errorValue == GattProtocolError.InsufficientAuthorization) + { + return "Insufficient Authorization"; + } + + if (errorValue == GattProtocolError.InsufficientEncryption) + { + return "Insufficient Encryption"; + } + + if (errorValue == GattProtocolError.InsufficientEncryptionKeySize) + { + return "Insufficient Encryption Key Size"; + } + + if (errorValue == GattProtocolError.InsufficientResources) + { + return "Insufficient Resources"; + } + + if (errorValue == GattProtocolError.InvalidAttributeValueLength) + { + return "Invalid Attribute Value Length"; + } + + if (errorValue == GattProtocolError.InvalidHandle) + { + return "Invalid Handle"; + } + + if (errorValue == GattProtocolError.InvalidOffset) + { + return "Invalid Offset"; + } + + if (errorValue == GattProtocolError.InvalidPdu) + { + return "Invalid Pdu"; + } + + if (errorValue == GattProtocolError.PrepareQueueFull) + { + return "Prepare Queue Full"; + } + + if (errorValue == GattProtocolError.ReadNotPermitted) + { + return "Read Not Permitted"; + } + + if (errorValue == GattProtocolError.RequestNotSupported) + { + return "Request Not Supported"; + } + + if (errorValue == GattProtocolError.UnlikelyError) + { + return "UnlikelyError"; + } + + if (errorValue == GattProtocolError.UnsupportedGroupType) + { + return "Unsupported Group Type"; + } + + if (errorValue == GattProtocolError.WriteNotPermitted) + { + return "Write Not Permitted"; + } - if (errorValue.HasValue == false) - { return errorString; } - - if (errorValue == GattProtocolError.AttributeNotFound) - { - return "Attribute Not Found"; - } - - if (errorValue == GattProtocolError.AttributeNotLong) - { - return "Attribute Not Long"; - } - - if (errorValue == GattProtocolError.InsufficientAuthentication) - { - return "Insufficient Authentication"; - } - - if (errorValue == GattProtocolError.InsufficientAuthorization) - { - return "Insufficient Authorization"; - } - - if (errorValue == GattProtocolError.InsufficientEncryption) - { - return "Insufficient Encryption"; - } - - if (errorValue == GattProtocolError.InsufficientEncryptionKeySize) - { - return "Insufficient Encryption Key Size"; - } - - if (errorValue == GattProtocolError.InsufficientResources) - { - return "Insufficient Resources"; - } - - if (errorValue == GattProtocolError.InvalidAttributeValueLength) - { - return "Invalid Attribute Value Length"; - } - - if (errorValue == GattProtocolError.InvalidHandle) - { - return "Invalid Handle"; - } - - if (errorValue == GattProtocolError.InvalidOffset) - { - return "Invalid Offset"; - } - - if (errorValue == GattProtocolError.InvalidPdu) - { - return "Invalid Pdu"; - } - - if (errorValue == GattProtocolError.PrepareQueueFull) - { - return "Prepare Queue Full"; - } - - if (errorValue == GattProtocolError.ReadNotPermitted) - { - return "Read Not Permitted"; - } - - if (errorValue == GattProtocolError.RequestNotSupported) - { - return "Request Not Supported"; - } - - if (errorValue == GattProtocolError.UnlikelyError) - { - return "UnlikelyError"; - } - - if (errorValue == GattProtocolError.UnsupportedGroupType) - { - return "Unsupported Group Type"; - } - - if (errorValue == GattProtocolError.WriteNotPermitted) - { - return "Write Not Permitted"; - } - - return errorString; } -} - +} \ No newline at end of file From c40b9fbd1be12781002856b54db1d5b202e63bb1 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sat, 28 Oct 2023 14:54:09 +0200 Subject: [PATCH 086/193] Formatted GattResultExtensions.cs --- .../Windows/Extensions/GattResultExtensions.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs b/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs index 9f8112c3..2d27d34b 100644 --- a/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs +++ b/Source/Plugin.BLE/Windows/Extensions/GattResultExtensions.cs @@ -9,20 +9,19 @@ namespace Plugin.BLE.Extensions { internal static class GattResultExtensions { - public static void ThrowIfError(this GattWriteResult result, [CallerMemberName]string tag = null) + public static void ThrowIfError(this GattWriteResult result, [CallerMemberName] string tag = null) => result.Status.ThrowIfError(tag, result.ProtocolError); - public static void ThrowIfError(this GattCharacteristicsResult result, [CallerMemberName]string tag = null) + public static void ThrowIfError(this GattCharacteristicsResult result, [CallerMemberName] string tag = null) => result.Status.ThrowIfError(tag, result.ProtocolError); - public static void ThrowIfError(this GattDescriptorsResult result, [CallerMemberName]string tag = null) + public static void ThrowIfError(this GattDescriptorsResult result, [CallerMemberName] string tag = null) => result.Status.ThrowIfError(tag, result.ProtocolError); - public static void ThrowIfError(this GattDeviceServicesResult result, [CallerMemberName]string tag = null) + public static void ThrowIfError(this GattDeviceServicesResult result, [CallerMemberName] string tag = null) => result.Status.ThrowIfError(tag, result.ProtocolError); - - public static byte[] GetValueOrThrowIfError(this GattReadResult result, [CallerMemberName]string tag = null) + public static byte[] GetValueOrThrowIfError(this GattReadResult result, [CallerMemberName] string tag = null) { var errorMessage = result.Status.GetErrorMessage(tag, result.ProtocolError); if (!string.IsNullOrEmpty(errorMessage)) @@ -33,7 +32,7 @@ public static byte[] GetValueOrThrowIfError(this GattReadResult result, [CallerM return result.Value?.ToArray() ?? new byte[0]; } - public static void ThrowIfError(this GattCommunicationStatus status, [CallerMemberName]string tag = null, byte? protocolError = null) + public static void ThrowIfError(this GattCommunicationStatus status, [CallerMemberName] string tag = null, byte? protocolError = null) { var errorMessage = status.GetErrorMessage(tag, protocolError); if (!string.IsNullOrEmpty(errorMessage)) @@ -43,7 +42,7 @@ public static void ThrowIfError(this GattCommunicationStatus status, [CallerMemb } private static string GetErrorMessage(this GattCommunicationStatus status, string tag, byte? protocolError) - { + { switch (status) { //output trace message with status of update From ca458e5a293ed5c815027e26e64db38fe6d9a0fc Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sat, 28 Oct 2023 17:59:02 +0200 Subject: [PATCH 087/193] namespace Plugin.BLE.Windows --- Source/Plugin.BLE/Windows/Adapter.cs | 5 +---- Source/Plugin.BLE/Windows/BleImplementation.cs | 2 +- Source/Plugin.BLE/Windows/Characteristic.cs | 2 +- Source/Plugin.BLE/Windows/Descriptor.cs | 2 +- Source/Plugin.BLE/Windows/Device.cs | 8 ++++---- Source/Plugin.BLE/Windows/Service.cs | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 0e63b4de..650be2c7 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading; @@ -11,10 +10,8 @@ using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; using System.Collections.Concurrent; -using Windows.Devices.Bluetooth.GenericAttributeProfile; -using Windows.Devices.Enumeration; -namespace Plugin.BLE.UWP +namespace Plugin.BLE.Windows { public class Adapter : AdapterBase { diff --git a/Source/Plugin.BLE/Windows/BleImplementation.cs b/Source/Plugin.BLE/Windows/BleImplementation.cs index f9883045..32deeb73 100644 --- a/Source/Plugin.BLE/Windows/BleImplementation.cs +++ b/Source/Plugin.BLE/Windows/BleImplementation.cs @@ -1,7 +1,7 @@ using Windows.Devices.Bluetooth; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; -using Plugin.BLE.UWP; +using Plugin.BLE.Windows; using System; using System.Threading.Tasks; using Windows.Devices.Radios; diff --git a/Source/Plugin.BLE/Windows/Characteristic.cs b/Source/Plugin.BLE/Windows/Characteristic.cs index acac08bf..07c899bb 100644 --- a/Source/Plugin.BLE/Windows/Characteristic.cs +++ b/Source/Plugin.BLE/Windows/Characteristic.cs @@ -11,7 +11,7 @@ using Plugin.BLE.Abstractions.EventArgs; using Plugin.BLE.Extensions; -namespace Plugin.BLE.UWP +namespace Plugin.BLE.Windows { public class Characteristic : CharacteristicBase { diff --git a/Source/Plugin.BLE/Windows/Descriptor.cs b/Source/Plugin.BLE/Windows/Descriptor.cs index d4220825..f42114d0 100644 --- a/Source/Plugin.BLE/Windows/Descriptor.cs +++ b/Source/Plugin.BLE/Windows/Descriptor.cs @@ -6,7 +6,7 @@ using Windows.Security.Cryptography; using Plugin.BLE.Extensions; -namespace Plugin.BLE.UWP +namespace Plugin.BLE.Windows { public class Descriptor : DescriptorBase { diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index e11dbe8c..ec7b3b52 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -9,7 +9,7 @@ using System.Threading; using System.Collections.Concurrent; -namespace Plugin.BLE.UWP +namespace Plugin.BLE.Windows { public class Device : DeviceBase { @@ -39,7 +39,7 @@ public override Task UpdateRssiAsync() //No current method to update the Rssi of a device //In future implementations, maybe listen for device's advertisements - Trace.Message("Request RSSI not supported in UWP"); + Trace.Message("Request RSSI not supported in Windows"); return Task.FromResult(true); } @@ -100,13 +100,13 @@ protected override DeviceState GetState() protected override async Task RequestMtuNativeAsync(int requestValue) { var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - using var gattSession = await Windows.Devices.Bluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); + using var gattSession = await global::Windows.Devices.Bluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); return gattSession.MaxPduSize; } protected override bool UpdateConnectionIntervalNative(ConnectionInterval interval) { - Trace.Message("Update Connection Interval not supported in UWP"); + Trace.Message("Update Connection Interval not supported in Windows"); return false; } diff --git a/Source/Plugin.BLE/Windows/Service.cs b/Source/Plugin.BLE/Windows/Service.cs index c85e0d9b..ec8ce0c2 100644 --- a/Source/Plugin.BLE/Windows/Service.cs +++ b/Source/Plugin.BLE/Windows/Service.cs @@ -7,7 +7,7 @@ using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; -namespace Plugin.BLE.UWP +namespace Plugin.BLE.Windows { public class Service : ServiceBase { From 46383d2f68bb93d97ab3a2d4261ae873b8b85bcc Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sat, 28 Oct 2023 18:05:25 +0200 Subject: [PATCH 088/193] using WBluetooth = global::Windows.Devices.Bluetooth --- Source/Plugin.BLE/Windows/Device.cs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index ec7b3b52..deca8605 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -8,18 +8,19 @@ using Plugin.BLE.Extensions; using System.Threading; using System.Collections.Concurrent; +using WBluetooth = global::Windows.Devices.Bluetooth; namespace Plugin.BLE.Windows { public class Device : DeviceBase - { + { private ConcurrentBag asyncOperations = new(); private readonly Mutex opMutex = new Mutex(false); private readonly SemaphoreSlim opSemaphore = new SemaphoreSlim(1); public Device(Adapter adapter, BluetoothLEDevice nativeDevice, int rssi, Guid id, - IReadOnlyList advertisementRecords = null, bool isConnectable = true) - : base(adapter, nativeDevice) + IReadOnlyList advertisementRecords = null, bool isConnectable = true) + : base(adapter, nativeDevice) { Rssi = rssi; Id = id; @@ -39,7 +40,7 @@ public override Task UpdateRssiAsync() //No current method to update the Rssi of a device //In future implementations, maybe listen for device's advertisements - Trace.Message("Request RSSI not supported in Windows"); + Trace.Message("Request RSSI not supported in Windows"); return Task.FromResult(true); } @@ -47,7 +48,7 @@ public override Task UpdateRssiAsync() public void DisposeNativeDevice() { if (NativeDevice is not null) - { + { NativeDevice.Dispose(); NativeDevice = null; } @@ -93,14 +94,14 @@ protected override DeviceState GetState() if (NativeDevice.ConnectionStatus == BluetoothConnectionStatus.Connected) { return DeviceState.Connected; - } + } return NativeDevice.WasSecureConnectionUsedForPairing ? DeviceState.Limited : DeviceState.Disconnected; } protected override async Task RequestMtuNativeAsync(int requestValue) { var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - using var gattSession = await global::Windows.Devices.Bluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); + using var gattSession = await WBluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); return gattSession.MaxPduSize; } @@ -108,22 +109,22 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv { Trace.Message("Update Connection Interval not supported in Windows"); return false; - } + } public override void Dispose() - { + { if (NativeDevice != null) { - Trace.Message("Disposing {0} with name = {1}", Id.ToHexBleAddress(), Name); + Trace.Message("Disposing {0} with name = {1}", Id.ToHexBleAddress(), Name); NativeDevice.Dispose(); - NativeDevice = null; + NativeDevice = null; } } public override bool IsConnectable { get; protected set; } public override bool SupportsIsConnectable { get => true; } - + protected override DeviceBondState GetBondState() { return DeviceBondState.NotSupported; From 0d4bd0e36e99f57b10ac572d4a79aac1f9c74a11 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 30 Oct 2023 05:50:28 +0100 Subject: [PATCH 089/193] Implemented GetSystemConnectedOrPairedDevices --- Source/Plugin.BLE/Windows/Adapter.cs | 64 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 650be2c7..1358c96f 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -10,18 +10,19 @@ using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; using System.Collections.Concurrent; +using Windows.Devices.Enumeration; namespace Plugin.BLE.Windows { public class Adapter : AdapterBase - { + { private BluetoothLEAdvertisementWatcher _bleWatcher; /// /// Registry used to store device instances for pending operations : disconnect /// Helps to detect connection lost events. /// - private readonly IDictionary _deviceOperationRegistry = new ConcurrentDictionary(); + private readonly IDictionary _deviceOperationRegistry = new ConcurrentDictionary(); public Adapter() { @@ -61,7 +62,7 @@ protected override void StopScanNative() if (_bleWatcher != null) { Trace.Message("Stopping the scan for devices"); - _bleWatcher.Stop(); + _bleWatcher.Stop(); _bleWatcher = null; } } @@ -83,7 +84,7 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect // Calling the GetGattServicesAsync on the BluetoothLEDevice with uncached property causes the device to connect BluetoothCacheMode restoremode = BleImplementation.CacheModeGetServices; - BleImplementation.CacheModeGetServices = BluetoothCacheMode.Uncached; + BleImplementation.CacheModeGetServices = BluetoothCacheMode.Uncached; var services = device.GetServicesAsync(cancellationToken).Result; BleImplementation.CacheModeGetServices = restoremode; @@ -111,25 +112,25 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect else { _deviceOperationRegistry[device.Id.ToString()] = device; - } + } } private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, object args) - { + { Trace.Message("Device_ConnectionStatusChanged {0} {1} {2}", nativeDevice.BluetoothAddress.ToHexBleAddress(), nativeDevice.Name, nativeDevice.ConnectionStatus); - var id = nativeDevice.BluetoothAddress.ParseDeviceId().ToString(); + var id = nativeDevice.BluetoothAddress.ParseDeviceId().ToString(); - if (nativeDevice.ConnectionStatus == BluetoothConnectionStatus.Connected + if (nativeDevice.ConnectionStatus == BluetoothConnectionStatus.Connected && ConnectedDeviceRegistry.TryGetValue(id, out var connectedDevice)) { HandleConnectedDevice(connectedDevice); return; } - if (nativeDevice.ConnectionStatus == BluetoothConnectionStatus.Disconnected + if (nativeDevice.ConnectionStatus == BluetoothConnectionStatus.Disconnected && ConnectedDeviceRegistry.TryRemove(id, out var disconnectedDevice)) { bool isNormalDisconnect = !_deviceOperationRegistry.Remove(disconnectedDevice.Id.ToString()); @@ -146,7 +147,7 @@ private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, obje { nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; } - } + } } protected override void DisconnectDeviceNative(IDevice device) @@ -154,16 +155,16 @@ protected override void DisconnectDeviceNative(IDevice device) // Windows doesn't support disconnecting, so currently just dispose of the device Trace.Message($"DisconnectDeviceNative from device with ID: {device.Id.ToHexBleAddress()}"); if (device.NativeDevice is BluetoothLEDevice nativeDevice) - { + { _deviceOperationRegistry.Remove(device.Id.ToString()); ((Device)device).ClearServices(); - ((Device)device).DisposeNativeDevice(); - } + ((Device)device).DisposeNativeDevice(); + } } public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default) - { - var bleAddress = deviceGuid.ToBleAddress(); + { + var bleAddress = deviceGuid.ToBleAddress(); var nativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bleAddress); if (nativeDevice == null) throw new Abstractions.Exceptions.DeviceConnectionException(deviceGuid, "", $"[Adapter] Device {deviceGuid} not found."); @@ -176,10 +177,35 @@ public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceG public override IReadOnlyList GetSystemConnectedOrPairedDevices(Guid[] services = null) { - //currently no way to retrieve paired and connected devices on windows without using an - //async method. - Trace.Message("Returning devices connected by this app only"); - return ConnectedDevices; + string pairedSelector = BluetoothDevice.GetDeviceSelectorFromPairingState(true); + DeviceInformationCollection pairedDevices = DeviceInformation.FindAllAsync(pairedSelector).GetAwaiter().GetResult(); + List devlist = ConnectedDevices.ToList(); + List ids = ConnectedDevices.Select(d => d.Id).ToList(); + foreach (var dev in pairedDevices) + { + Guid id = dev.Id.ToBleDeviceGuidFromId(); + ulong bleaddress = id.ToBleAddress(); + if (!ids.Contains(id)) + { + var bluetoothLeDevice = BluetoothLEDevice.FromBluetoothAddressAsync(bleaddress).AsTask().Result; + if (bluetoothLeDevice != null) + { + var device = new Device( + this, + bluetoothLeDevice, + 0, id); + devlist.Add(device); + ids.Add(id); + Trace.Message("GetSystemConnectedOrPairedDevices: {0}: {1}", dev.Id, dev.Name); + } + else + { + Trace.Message("GetSystemConnectedOrPairedDevices: {0}: {1}, BluetoothLEDevice == null", dev.Id, dev.Name); + } + + } + } + return devlist; } protected override IReadOnlyList GetBondedDevices() From baaf4bcfdd799f1d53c1a826ea2be86c314d4d6b Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Fri, 3 Nov 2023 22:12:44 +0100 Subject: [PATCH 090/193] remove the net6.0 targets for android, ios and maccatalyst * this gets rid of the following warnings: warning NETSDK1202: The workload 'android' is out of support and will not receive security updates in the future. Please refer to https://aka.ms/maui-support-policy for more information about the support policy. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\Plugin.BLE\Plugin.BLE.csproj::TargetFramework=net6.0-android33.0] warning NETSDK1202: The workload 'ios' is out of support and will not receive security updates in the future. Please refer to https://aka.ms/maui-support-policy for more information about the support policy. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\Plugin.BLE\Plugin.BLE.csproj::TargetFramework=net6.0-ios] warning NETSDK1202: The workload 'maccatalyst' is out of support and will not receive security updates in the future. Please refer to https://aka.ms/maui-support-policy for more information about the support policy. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\Plugin.BLE\Plugin.BLE.csproj::TargetFramework=net6.0-maccatalyst] * .NET MAUI 6 is out of support since May 8, 2023, see: https://dotnet.microsoft.com/en-us/platform/support/policy/maui --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index ab3afe17..461ec956 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -1,6 +1,6 @@  - net6.0-android33.0;net6.0-ios;net6.0-maccatalyst;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst + net7.0-android33.0;net7.0-ios;net7.0-maccatalyst $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 46b0533a..1667d998 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -1,6 +1,6 @@  - netstandard2.0;net6.0-android33.0;net6.0-ios;net6.0-maccatalyst;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst + netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 Plugin.BLE Plugin.BLE @@ -75,11 +75,11 @@ - + - + From 091142fdb84cd6ebf159ef0f5ef83347748b22e4 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 4 Nov 2023 23:13:06 +0100 Subject: [PATCH 091/193] remove the net6.0 target for windows * in principle .NET 6 is still supported for another year: https://dotnet.microsoft.com/en-us/platform/support/policy * but since the other .NET (MAUI) targets are out of support, there is not much reason to keep this one --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 461ec956..c05effb1 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -1,7 +1,7 @@  net7.0-android33.0;net7.0-ios;net7.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE 3.0.0 diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 1667d998..f089e81b 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -1,7 +1,7 @@  netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net6.0-windows10.0.19041;net7.0-windows10.0.19041 + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041 Plugin.BLE Plugin.BLE 3.0.0 From c90dcd2a378c3579786dc6993a3ae334ea22c526 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 5 Nov 2023 10:17:44 +0100 Subject: [PATCH 092/193] replace references to 'UWP' by 'Windows' * where applicable * the library does not only support UWP, but Windows in general by now --- README.md | 2 +- .../MvvmCross.Plugins.BLE.csproj | 6 +++--- Source/Plugin.BLE/Plugin.BLE.csproj | 7 +++---- Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs | 2 +- doc/characteristics.md | 12 ++++++------ doc/scanmode_mapping.md | 4 ++-- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 588a0f8e..497aea84 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Build status: [![Build status](https://github.com/dotnet-bluetooth-le/dotnet-blu | Xamarin.iOS | 7.0 | | | Xamarin.Mac | 10.9 (Mavericks) | >= 2.1.0 | | Xamarin.UWP | 1709 - 10.0.16299 | >= 2.2.0 | -| MAUI (all 4 OS) | | >= 3.0.0 | +| MAUI (Android, iOS, Mac, WinUI) | | >= 3.0.0 | ## Nuget Packages diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index c05effb1..9132ae0a 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -14,13 +14,13 @@ Apache-2.0 Adrian Seceleanu, Sven-Michael Stübe, Janus Weil https://github.com/dotnet-bluetooth-le/dotnet-bluetooth-le/ - MVVMCross Plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and UWP. + MVVMCross Plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and Windows. - xamarin, maui, pcl, xam.pcl, uwp, bluetooth, ble, .net maui, xamarin.forms, ios + xamarin, maui, pcl, xam.pcl, bluetooth, ble, .net maui, xamarin.forms, android, ios MVVMCross Bluetooth Low Energy (BLE) Plugin for .NET MAUI and Xamarin - MVVMCross Plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and UWP. + MVVMCross Plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and Windows. Read the full documentation on the projects page. Apache-2.0 diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index f089e81b..63235fc3 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -14,14 +14,13 @@ Apache-2.0 Adrian Seceleanu, Sven-Michael Stübe, Janus Weil https://github.com/dotnet-bluetooth-le/dotnet-bluetooth-le/ - Plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and UWP. + Plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and Windows. - xamarin, maui, pcl, xam.pcl, uwp, bluetooth, ble, .net maui, xamarin.forms, ios + xamarin, maui, pcl, xam.pcl, bluetooth, ble, .net maui, xamarin.forms, android, ios Bluetooth Low Energy (BLE) Plugin for .NET MAUI and Xamarin - Xamarin and MAUI plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and UWP. - Read the full documentation on the projects page. + Xamarin and MAUI plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and Windows. Apache-2.0 https://github.com/dotnet-bluetooth-le/dotnet-bluetooth-le/ diff --git a/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs b/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs index 065f1757..e2e7b976 100644 --- a/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs +++ b/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs @@ -18,7 +18,7 @@ public static class TaskBuilder /// Platform specific main thread invocation. Useful to avoid GATT 133 errors on Android. /// Set this to NULL in order to disable main thread queued invocations. /// Android: already implemented and set by default - /// UWP, iOS, macOS: NULL by default - not needed, turning this on is redundant as it's already handled internaly by the platform + /// Windows, iOS, macOS: NULL by default - not needed, turning this on is redundant as it's already handled internaly by the platform /// public static Action MainThreadInvoker { get; set; } diff --git a/doc/characteristics.md b/doc/characteristics.md index a420b28f..3630c463 100644 --- a/doc/characteristics.md +++ b/doc/characteristics.md @@ -7,7 +7,7 @@ Plugin iOS Android - UWP + Windows 0 @@ -66,10 +66,10 @@ -Specification: [Core 4.2 Vol.3 3.3.1.1](https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439) -UWP: [GattCharacteristicProperties](https://msdn.microsoft.com/en-in/library/windows/apps/windows.devices.bluetooth.genericattributeprofile.gattcharacteristicproperties) -Android: [GattProperty](https://developer.xamarin.com/api/type/Android.Bluetooth.GattProperty/) -iOS: [CBCharacteristicProperties](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCharacteristic_Class/#//apple_ref/c/tdef/CBCharacteristicProperties) +- Specification: [Core 4.2 Vol.3 3.3.1.1](https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439) +- Windows: [GattCharacteristicProperties](https://msdn.microsoft.com/en-in/library/windows/apps/windows.devices.bluetooth.genericattributeprofile.gattcharacteristicproperties) +- Android: [GattProperty](https://developer.xamarin.com/api/type/Android.Bluetooth.GattProperty/) +- iOS: [CBCharacteristicProperties](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCharacteristic_Class/#//apple_ref/c/tdef/CBCharacteristicProperties) From 1 to 128 all platforms are using the values from the specification. -iOS and UWP are using the values 256, and 512. On UWP they are mapped to extended properties (Core 4.2 §3.3.3.1). iOS is using it for non standard (4.2) values. +iOS and Windows are using the values 256, and 512. On Windows they are mapped to extended properties (Core 4.2 §3.3.3.1). iOS is using it for non standard (4.2) values. diff --git a/doc/scanmode_mapping.md b/doc/scanmode_mapping.md index 2d1dfa89..1017277b 100644 --- a/doc/scanmode_mapping.md +++ b/doc/scanmode_mapping.md @@ -5,7 +5,7 @@ Value iOS/macOS Android - UWP + Windows Passive @@ -33,4 +33,4 @@ - iOS: Not supported. [Passive Mode will be used automatically in background mode](https://lists.apple.com/archives/bluetooth-dev/2012/May/msg00041.html) (caution: very old information!)) - Android: [ScanSettings](https://developer.android.com/reference/android/bluetooth/le/ScanSettings.html). Falls back to system default for all scan modes, if Android < Lollipop. -- UWP: [BluetoothLEScanningMode](https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothlescanningmode) +- Windows: [BluetoothLEScanningMode](https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothlescanningmode) From 62b9c111e3428b429d37fbaaa7a20063c5e66dea Mon Sep 17 00:00:00 2001 From: Jan Marek Date: Wed, 8 Nov 2023 16:12:09 +0100 Subject: [PATCH 093/193] Added BroadcastName to AdvertisementRecordType to parse data correctly #772 --- Source/Plugin.BLE/Shared/AdvertisementRecord.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Plugin.BLE/Shared/AdvertisementRecord.cs b/Source/Plugin.BLE/Shared/AdvertisementRecord.cs index 8245731f..83a1824d 100644 --- a/Source/Plugin.BLE/Shared/AdvertisementRecord.cs +++ b/Source/Plugin.BLE/Shared/AdvertisementRecord.cs @@ -165,6 +165,11 @@ public enum AdvertisementRecordType /// SecureConnectionsRandomValue = 0x23, + /// + /// «Broadcast Name» Public Broadcast Profile v1.0 or later + /// + BroadcastName = 0x30, + /// /// «3D Information Data» ​3D Synchronization Profile, v1.0 or later /// From 75a4bf14aea701bc373b9c42745d40ab6054e5c9 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Wed, 8 Nov 2023 21:57:18 +0100 Subject: [PATCH 094/193] disable MainThreadInvoker in Adapter.ConnectToDeviceAsync --- Source/Plugin.BLE/Shared/AdapterBase.cs | 2 +- Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/Plugin.BLE/Shared/AdapterBase.cs b/Source/Plugin.BLE/Shared/AdapterBase.cs index e8d33a37..d2d3ab45 100644 --- a/Source/Plugin.BLE/Shared/AdapterBase.cs +++ b/Source/Plugin.BLE/Shared/AdapterBase.cs @@ -218,7 +218,7 @@ await TaskBuilder.FromEvent, EventHandler DeviceConnectionError += handler, unsubscribeReject: handler => DeviceConnectionError -= handler, - token: cts.Token); + token: cts.Token, mainthread: false); } } diff --git a/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs b/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs index e2e7b976..43bdc8db 100644 --- a/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs +++ b/Source/Plugin.BLE/Shared/Utils/TaskBuilder.cs @@ -35,7 +35,8 @@ public static async Task FromEvent, TRejectHandler> getRejectHandler, Action subscribeReject, Action unsubscribeReject, - CancellationToken token = default) + CancellationToken token = default, + bool mainthread = true) { var tcs = new TaskCompletionSource(); void Complete(TReturn args) => tcs.TrySetResult(args); @@ -51,7 +52,7 @@ public static async Task FromEvent tcs.TrySetCanceled(), false)) { - return await SafeEnqueueAndExecute(execute, token, tcs); + return await SafeEnqueueAndExecute(execute, token, tcs, mainthread); } } finally @@ -64,13 +65,13 @@ public static async Task FromEvent /// Queues the given onto the main thread and executes it. /// - public static Task EnqueueOnMainThreadAsync(Action execute, CancellationToken token = default) - => SafeEnqueueAndExecute(execute, token); + public static Task EnqueueOnMainThreadAsync(Action execute, CancellationToken token = default, bool mainthread = true) + => SafeEnqueueAndExecute(execute, token, mainthread: mainthread); - private static async Task SafeEnqueueAndExecute(Action execute, CancellationToken token, TaskCompletionSource tcs = null) + private static async Task SafeEnqueueAndExecute(Action execute, CancellationToken token, TaskCompletionSource tcs = null, bool mainthread = true) { - if (MainThreadInvoker != null) + if (MainThreadInvoker != null && mainthread) { var shouldReleaseSemaphore = false; var shouldCompleteTask = tcs == null; From 90caa8a8e5dfcdf72b2ee4bea7efbafae8950332 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sat, 11 Nov 2023 06:51:07 +0100 Subject: [PATCH 095/193] Implemented StateChanged for Windows --- .../Plugin.BLE/Windows/BleImplementation.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Source/Plugin.BLE/Windows/BleImplementation.cs b/Source/Plugin.BLE/Windows/BleImplementation.cs index 32deeb73..793f976d 100644 --- a/Source/Plugin.BLE/Windows/BleImplementation.cs +++ b/Source/Plugin.BLE/Windows/BleImplementation.cs @@ -30,16 +30,9 @@ protected override BluetoothState GetInitialStateNative() { return BluetoothState.Unavailable; } - var radio = btAdapter.GetRadioAsync().AsTask().Result; - switch (radio.State) - { - case RadioState.On: - return BluetoothState.On; - case RadioState.Off: - return BluetoothState.Off; - default: - return BluetoothState.Unavailable; - } + var radio = btAdapter.GetRadioAsync().AsTask().Result; + radio.StateChanged += Radio_StateChanged; + return ToBluetoothState(radio.State); } catch (Exception ex) { @@ -48,6 +41,24 @@ protected override BluetoothState GetInitialStateNative() } } + private static BluetoothState ToBluetoothState(RadioState radioState) + { + switch (radioState) + { + case RadioState.On: + return BluetoothState.On; + case RadioState.Off: + return BluetoothState.Off; + default: + return BluetoothState.Unavailable; + } + } + + private void Radio_StateChanged(Radio radio, object args) + { + State = ToBluetoothState(radio.State); + } + protected override void InitializeNative() { From 024b8a9f22957c6db12d12f384de01cca7a0b9e6 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Tue, 14 Nov 2023 18:45:35 +0100 Subject: [PATCH 096/193] add net8.0 targets to Plugin.BLE and MvvmCross.Plugins.BLE --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 4 ++-- Source/Plugin.BLE/Plugin.BLE.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 9132ae0a..3b9097c5 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -1,7 +1,7 @@  - net7.0-android33.0;net7.0-ios;net7.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041 + net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android33.0;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE 3.0.0 diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 63235fc3..9d614db8 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -1,7 +1,7 @@  - netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041 + netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android33.0;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 Plugin.BLE Plugin.BLE 3.0.0 From 465d0fe153c5a34eff7bd4821c2698fa8b4eed5c Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Tue, 14 Nov 2023 18:53:50 +0100 Subject: [PATCH 097/193] GHA: update .NET version to 8.0 --- .github/workflows/dotnet.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index c086a327..934218dc 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -20,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Install .NET MAUI shell: pwsh run: | @@ -78,7 +78,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Install .NET MAUI run: | dotnet nuget locals all --clear From 1e7d5584751d7d522cc56f30767ea8f79be29fa8 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Tue, 14 Nov 2023 19:54:39 +0100 Subject: [PATCH 098/193] .NET 8: update Android target to SDK version 34 /Users/runner/.dotnet/sdk/8.0.100/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1181: Error getting pack version: Pack 'Microsoft.Android.Ref.33' was not present in workload manifests. [/Users/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/Plugin.BLE/Plugin.BLE.csproj::TargetFramework=net8.0-android33.0] --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 3b9097c5..62af1d05 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -1,6 +1,6 @@  - net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android33.0;net8.0-ios;net8.0-maccatalyst + net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android34.0;net8.0-ios;net8.0-maccatalyst $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 9d614db8..651351a5 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -1,6 +1,6 @@  - netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android33.0;net8.0-ios;net8.0-maccatalyst + netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android34.0;net8.0-ios;net8.0-maccatalyst $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 Plugin.BLE Plugin.BLE From 3a074aa3149b70bde53347c4689f33656121ed58 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Tue, 14 Nov 2023 20:42:31 +0100 Subject: [PATCH 099/193] try to fix build errors on Windows Error: C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1083: The specified RuntimeIdentifier 'win10-arm' is not recognized. See https://aka.ms/netsdk1083 for more information. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj::TargetFramework=net8.0-windows10.0.19041] Error: C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1083: The specified RuntimeIdentifier 'win10-arm-aot' is not recognized. See https://aka.ms/netsdk1083 for more information. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj::TargetFramework=net8.0-windows10.0.19041] Error: C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1083: The specified RuntimeIdentifier 'win10-arm64-aot' is not recognized. See https://aka.ms/netsdk1083 for more information. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj::TargetFramework=net8.0-windows10.0.19041] Error: C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1083: The specified RuntimeIdentifier 'win10-x86' is not recognized. See https://aka.ms/netsdk1083 for more information. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj::TargetFramework=net8.0-windows10.0.19041] Error: C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1083: The specified RuntimeIdentifier 'win10-x86-aot' is not recognized. See https://aka.ms/netsdk1083 for more information. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj::TargetFramework=net8.0-windows10.0.19041] Error: C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1083: The specified RuntimeIdentifier 'win10-x64' is not recognized. See https://aka.ms/netsdk1083 for more information. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj::TargetFramework=net8.0-windows10.0.19041] Error: C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1083: The specified RuntimeIdentifier 'win10-x64-aot' is not recognized. See https://aka.ms/netsdk1083 for more information. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj::TargetFramework=net8.0-windows10.0.19041] --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 62af1d05..4a5fd0ba 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -52,6 +52,11 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + win10-x64;win10-x86;win10-arm64 + true + + all From 5ba225d5d55bf5b3bbeb9c02b1278c90fb079bab Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Tue, 14 Nov 2023 22:48:08 +0100 Subject: [PATCH 100/193] update Plugin.BLE.Tests to net8.0 --- Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj b/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj index e037de93..e5e7bdc4 100644 --- a/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj +++ b/Source/Plugin.BLE.Tests/Plugin.BLE.Tests.csproj @@ -1,6 +1,6 @@  - net7.0 + net8.0 false @@ -21,4 +21,4 @@ - \ No newline at end of file + From db0dbcbc78492e6c0e3ed24313f7a41e7f55a655 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Wed, 15 Nov 2023 08:49:37 +0100 Subject: [PATCH 101/193] update BLE.Client.Maui to net8.0 * targeting Android SDK level 34, because .NET 8 does not support 33 --- .../BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index a0b23afd..fb1287e6 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -1,8 +1,8 @@  - net7.0-android33.0;net7.0-ios;net7.0-maccatalyst - $(TargetFrameworks);net7.0-windows10.0.19041 + net8.0-android34.0;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);net8.0-windows10.0.19041 Exe BLE.Client.Maui true @@ -28,16 +28,13 @@ 6.5 - - false - - + false 4 - + false Mac Developer 3rd Party Mac Developer Installer From edf2f86ec7a9b9230d10a7a9d9218debe7b9ecfb Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Wed, 15 Nov 2023 18:06:22 +0100 Subject: [PATCH 102/193] GHA: use XCode 15 --- .github/workflows/dotnet.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 934218dc..b7dda90a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -79,6 +79,10 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x + - name: Setup XCode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15' - name: Install .NET MAUI run: | dotnet nuget locals all --clear From 8619d1e15a058fc2b466760c7d9e1ca5f1d8c82c Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Thu, 16 Nov 2023 08:45:41 +0100 Subject: [PATCH 103/193] Plugin.BLE.csproj: clean up some Windows-specific config * copy from MvvmCross.Plugins.BLE.csproj --- Source/Plugin.BLE/Plugin.BLE.csproj | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 651351a5..046f225e 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -43,14 +43,21 @@ 10.0.17763.0 - - true - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - - - - + + + true + + true + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + + + all + runtime; build; native; contentfiles; analyzers + + true From d40f99fd1c30013e1f691db021e562f706732d89 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Fri, 17 Nov 2023 20:49:47 +0100 Subject: [PATCH 104/193] GHA: use Java 11 on Windows * the current Windows image seems to use Java 8 by default * the build failed with these errors: error XA0031: Java SDK 11.0 or above is required when using .NET 6 or higher. Download the latest JDK at: https://aka.ms/msopenjdk [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\Plugin.BLE\Plugin.BLE.csproj::TargetFramework=net7.0-android33.0] error XA0031: Java SDK 11.0 or above is required when using $(TargetFrameworkVersion) v13.0. [D:\a\dotnet-bluetooth-le\dotnet-bluetooth-le\Source\Plugin.BLE\Plugin.BLE.csproj::TargetFramework=MonoAndroid13.0] --- .github/workflows/dotnet.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b7dda90a..11d19d78 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -21,6 +21,11 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' - name: Install .NET MAUI shell: pwsh run: | From b3fae9e137de45a63b5f61ce59bf1bc8fd690dc9 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Fri, 10 Nov 2023 22:30:14 +0100 Subject: [PATCH 105/193] Demo Number of services after reconnect is not working as expected for windows --- .../BLE.Client.WinConsole/BleDemo.cs | 66 +++++++++++++++---- .../BLE.Client.WinConsole/ConsoleTracer.cs | 17 ++++- .../BLE.Client.WinConsole/Program.cs | 13 ++-- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs index 65001504..af174100 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs @@ -3,6 +3,7 @@ using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Abstractions.Extensions; using Plugin.BLE.Extensions; +using Plugin.BLE.Windows; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -16,17 +17,18 @@ namespace BLE.Client.WinConsole internal class BleDemo { private readonly IBluetoothLE bluetoothLE; - private readonly IAdapter adapter; + public IAdapter Adapter { get; } private readonly Action? writer; private readonly List discoveredDevices; private readonly IDictionary connectedDevices; + private bool scanningDone = false; public BleDemo(Action? writer = null) { discoveredDevices = new List(); connectedDevices = new ConcurrentDictionary(); bluetoothLE = CrossBluetoothLE.Current; - adapter = CrossBluetoothLE.Current.Adapter; + Adapter = CrossBluetoothLE.Current.Adapter; this.writer = writer; } @@ -37,7 +39,7 @@ private void Write(string format, params object[] args) public IDevice ConnectToKnown(Guid id) { - IDevice dev = adapter.ConnectToKnownDeviceAsync(id).Result; + IDevice dev = Adapter.ConnectToKnownDeviceAsync(id).Result; connectedDevices[id] = dev; return dev; } @@ -61,15 +63,15 @@ public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ var cancellationTokenSource = new CancellationTokenSource(time_ms); discoveredDevices.Clear(); - adapter.DeviceDiscovered += (s, a) => + Adapter.DeviceDiscovered += (s, a) => { var dev = a.Device; Write("DeviceDiscovered: {0} with Name = {1}", dev.Id.ToHexBleAddress(), dev.Name); discoveredDevices.Add(a.Device); }; - adapter.ScanMode = scanMode; - await adapter.StartScanningForDevicesAsync(cancellationToken: cancellationTokenSource.Token); - + Adapter.ScanMode = scanMode; + await Adapter.StartScanningForDevicesAsync(cancellationToken: cancellationTokenSource.Token); + scanningDone = true; } private void WriteAdvertisementRecords(IDevice device) @@ -94,22 +96,34 @@ private void WriteAdvertisementRecords(IDevice device) } } - public async Task ConnectTest(string name) + /// + /// Connect to a device with a specific name + /// Assumes that DoTheScanning has been called and that the device is advertising + /// + /// + /// + public async Task ConnectTest(string name) { + if (!scanningDone) + { + Write("ConnectTest({0}) Failed - Call the DoTheScanning() method first!"); + return null; + } Thread.Sleep(10); foreach(var device in discoveredDevices) { if (device.Name.Contains(name)) { - await adapter.ConnectToDeviceAsync(device); - WriteAdvertisementRecords(device); + await Adapter.ConnectToDeviceAsync(device); + return device; } } + return null; } public void ShowGetSystemConnectedOrPairedDevices() { - IReadOnlyList devs = adapter.GetSystemConnectedOrPairedDevices(); + IReadOnlyList devs = Adapter.GetSystemConnectedOrPairedDevices(); Write("GetSystemConnectedOrPairedDevices found {0} devices.", devs.Count); foreach(var dev in devs) { @@ -117,9 +131,37 @@ public void ShowGetSystemConnectedOrPairedDevices() } } + /// + /// This demonstrates a bug where the known services is not cleared at disconnect (2023-11-03) + /// + /// 12 hex char ble address + public async Task ShowNumberOfServices(string bleaddress) + { + Write("Connecting to device with address = {0}", bleaddress); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(bleaddress.ToBleDeviceGuid()) ?? throw new Exception("null"); + string name = dev.Name; + Write("Connected to {0} {1} {2}", name, dev.Id.ToHexBleAddress(), dev.State); + Write("Calling dev.GetServicesAsync()..."); + var services = await dev.GetServicesAsync(); + Write("Found {0} services", services.Count); + Thread.Sleep(1000); + Write("Disconnecting from {0} {1}", name, dev.Id.ToHexBleAddress()); + await Adapter.DisconnectDeviceAsync(dev); + Thread.Sleep(1000); + Write("ReConnecting to device {0} {1}...", name, dev.Id.ToHexBleAddress()); + await Adapter.ConnectToDeviceAsync(dev); + Write("Connect Done."); + Thread.Sleep(1000); + Write("Calling dev.GetServicesAsync()..."); + services = await dev.GetServicesAsync(); + Write("Found {0} services", services.Count); + await Adapter.DisconnectDeviceAsync(dev); + Thread.Sleep(1000); + } + internal Task Disconnect(IDevice dev) { - return adapter.DisconnectDeviceAsync(dev); + return Adapter.DisconnectDeviceAsync(dev); } } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs index 1e64eb2b..6d34efd0 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs @@ -47,6 +47,16 @@ public void Trace(string format, params object[] args) newEntry.Set(); } + /// + /// Get a tracer with a prefix + /// + /// + /// + public Action GetPrefixedTrace(string prefix) + { + return new Action((format, args) => Trace(prefix + " - " + format, args)); + } + void WriteWorker() { while (!disposing && newEntry.WaitOne()) @@ -57,7 +67,7 @@ void WriteWorker() Console.WriteLine(entry.Time.ToString("HH:mm:ss.fff ") + entry.Format + " ", entry.Args); } } - Console.WriteLine("Bye bye says the Console Tracer."); + Console.WriteLine("Console Tracer is Finished."); } private DateTime GetTime() @@ -67,8 +77,9 @@ private DateTime GetTime() public void Dispose() { - disposing = true; - worker.Wait(1000); + disposing = true; + newEntry.Set(); + worker.Wait(100); } } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index 04843ceb..48e20cbd 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -1,13 +1,14 @@ using BLE.Client.WinConsole; using Plugin.BLE; using Plugin.BLE.Abstractions.Contracts; +using System; Console.WriteLine("Hello, BLE World!"); -var ct = new ConsoleTracer(); -Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.Trace; -var demo = new BleDemo(ct.Trace); -await demo.DoTheScanning(ScanMode.LowPower); -await demo.ConnectTest("Shure"); -ct.Dispose(); +using (var ct = new ConsoleTracer()) +{ + Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.GetPrefixedTrace("Plugin.BLE"); + var demo = new BleDemo(ct.GetPrefixedTrace(" DEMO")); + await demo.ShowNumberOfServices("40CBC0DD37E2"); +} From 71d4ede3dc2cd2f5bbb1b461e254af7bb7c3bd93 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Fri, 10 Nov 2023 22:35:11 +0100 Subject: [PATCH 106/193] Fixed ConnectToDeviceNativeAsync --- Source/Plugin.BLE/Windows/Adapter.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 1358c96f..6cd0e353 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -11,6 +11,7 @@ using Plugin.BLE.Extensions; using System.Collections.Concurrent; using Windows.Devices.Enumeration; +using WBluetooth = global::Windows.Devices.Bluetooth; namespace Plugin.BLE.Windows { @@ -83,12 +84,9 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; // Calling the GetGattServicesAsync on the BluetoothLEDevice with uncached property causes the device to connect - BluetoothCacheMode restoremode = BleImplementation.CacheModeGetServices; - BleImplementation.CacheModeGetServices = BluetoothCacheMode.Uncached; - var services = device.GetServicesAsync(cancellationToken).Result; - BleImplementation.CacheModeGetServices = restoremode; - - if (!services.Any() || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) + var servicesResult = await dev.NativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); + if (servicesResult.Status != WBluetooth.GenericAttributeProfile.GattCommunicationStatus.Success + || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) { // use DisconnectDeviceNative to clean up resources otherwise windows won't disconnect the device // after a subsequent successful connection (#528, #536, #423) From 1109c8c065d8f26780af123476f3ec85d1fed301 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sun, 12 Nov 2023 19:43:12 +0100 Subject: [PATCH 107/193] Comment added in ConnectToDeviceNativeAsync for Windows --- Source/Plugin.BLE/Windows/Adapter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 6cd0e353..23a5720b 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -84,6 +84,10 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; // Calling the GetGattServicesAsync on the BluetoothLEDevice with uncached property causes the device to connect + // ref https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice.frombluetoothaddressasync + // Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. + // To initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery + // method on BluetoothLEDevice, or perform a read/write operation against the device. var servicesResult = await dev.NativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); if (servicesResult.Status != WBluetooth.GenericAttributeProfile.GattCommunicationStatus.Success || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) From bf0bc38cb74a2d6280046a87dab2cfdfa516e5e8 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sun, 12 Nov 2023 21:02:27 +0100 Subject: [PATCH 108/193] Improved comments in ConnectToDeviceNativeAsync for Windows --- Source/Plugin.BLE/Windows/Adapter.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 23a5720b..93fda586 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -84,11 +84,21 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; // Calling the GetGattServicesAsync on the BluetoothLEDevice with uncached property causes the device to connect + // // ref https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice.frombluetoothaddressasync // Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. // To initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery // method on BluetoothLEDevice, or perform a read/write operation against the device. + // + // Remark from Ask Bojesen 2023-11-12: + // Below three lines reflect the different approach with GattSession, but I could not get this working properly + // + // var deviceId = BluetoothDeviceId.FromId(dev.NativeDevice.DeviceId); + // var genericProfileGattSession = await GattSession.FromDeviceIdAsync(deviceId); + // bool success = genericProfileGattSession.MaintainConnection = true; + var servicesResult = await dev.NativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); + if (servicesResult.Status != WBluetooth.GenericAttributeProfile.GattCommunicationStatus.Success || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) { From 6a802be72b6084e4526309498be45cf5abf04881 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 14 Nov 2023 13:55:12 +0100 Subject: [PATCH 109/193] Using GattSession.FromDeviceIdAsync to connect --- Source/Plugin.BLE/Windows/Adapter.cs | 12 ++--- Source/Plugin.BLE/Windows/Device.cs | 73 ++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 93fda586..de81ef9e 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -97,10 +97,11 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect // var genericProfileGattSession = await GattSession.FromDeviceIdAsync(deviceId); // bool success = genericProfileGattSession.MaintainConnection = true; - var servicesResult = await dev.NativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); + //var servicesResult = await dev.NativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); + //bool success = servicesResult.Status == WBluetooth.GenericAttributeProfile.GattCommunicationStatus.Success; + bool success = await dev.ConnectInternal(connectParameters, cancellationToken); - if (servicesResult.Status != WBluetooth.GenericAttributeProfile.GattCommunicationStatus.Success - || nativeDevice.ConnectionStatus != BluetoothConnectionStatus.Connected) + if (!success) { // use DisconnectDeviceNative to clean up resources otherwise windows won't disconnect the device // after a subsequent successful connection (#528, #536, #423) @@ -165,12 +166,11 @@ private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, obje protected override void DisconnectDeviceNative(IDevice device) { // Windows doesn't support disconnecting, so currently just dispose of the device - Trace.Message($"DisconnectDeviceNative from device with ID: {device.Id.ToHexBleAddress()}"); + Trace.Message($"DisconnectDeviceNative from device with ID: {device.Id.ToHexBleAddress()}"); if (device.NativeDevice is BluetoothLEDevice nativeDevice) { _deviceOperationRegistry.Remove(device.Id.ToString()); - ((Device)device).ClearServices(); - ((Device)device).DisposeNativeDevice(); + ((Device)device).DisconnectInternal(); } } diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index deca8605..8231114c 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Collections.Concurrent; using WBluetooth = global::Windows.Devices.Bluetooth; +using static System.Net.Mime.MediaTypeNames; +using Windows.Devices.Bluetooth.GenericAttributeProfile; namespace Plugin.BLE.Windows { @@ -17,6 +19,9 @@ public class Device : DeviceBase private ConcurrentBag asyncOperations = new(); private readonly Mutex opMutex = new Mutex(false); private readonly SemaphoreSlim opSemaphore = new SemaphoreSlim(1); + private ConnectParameters connectParameters; + private GattSession gattSession = null; + private bool isDisposed = false; public Device(Adapter adapter, BluetoothLEDevice nativeDevice, int rssi, Guid id, IReadOnlyList advertisementRecords = null, bool isConnectable = true) @@ -101,7 +106,7 @@ protected override DeviceState GetState() protected override async Task RequestMtuNativeAsync(int requestValue) { var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - using var gattSession = await WBluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); + using var gattSession = await WBluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); return gattSession.MaxPduSize; } @@ -111,14 +116,72 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv return false; } + public async Task ConnectInternal(ConnectParameters connectParameters, CancellationToken cancellationToken) + { + this.connectParameters = connectParameters; + if (NativeDevice is null) + { + Trace.Message("ConnectInternal says: Cannot connect since NativeDevice is null"); + return false; + } + try + { + var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); + gattSession = await WBluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); + gattSession.SessionStatusChanged += GattSession_SessionStatusChanged; + gattSession.MaintainConnection = true; + } catch (Exception ex) + { + Trace.Message("WARNING ConnectInternal failed: {0}", ex.Message); + DisposeGattSession(); + return false; + } + bool success = gattSession != null; + return success; + } + + private void DisposeGattSession() + { + if (gattSession != null) + { + gattSession.MaintainConnection = false; + gattSession.SessionStatusChanged -= GattSession_SessionStatusChanged; + gattSession.Dispose(); + gattSession = null; + } + } + + private void GattSession_SessionStatusChanged(GattSession sender, GattSessionStatusChangedEventArgs args) + { + Trace.Message("GattSession_SessionStatusChanged: " + args.Status); + } + + public void DisconnectInternal() + { + DisposeGattSession(); + ClearServices(); + DisposeNativeDevice(); + } + public override void Dispose() { - if (NativeDevice != null) + if (isDisposed) { - Trace.Message("Disposing {0} with name = {1}", Id.ToHexBleAddress(), Name); - NativeDevice.Dispose(); - NativeDevice = null; + return; + } + isDisposed = true; + try + { + DisposeGattSession(); + ClearServices(); + DisposeNativeDevice(); } + catch { } + } + + ~Device() + { + DisposeGattSession(); } public override bool IsConnectable { get; protected set; } From 44db17a6444ce0aed5ab2e038c201bec516db9ff Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 14 Nov 2023 14:01:05 +0100 Subject: [PATCH 110/193] To initiate a connection, set GattSession.MaintainConnection to true --- Source/Plugin.BLE/Windows/Adapter.cs | 17 ----------------- Source/Plugin.BLE/Windows/Device.cs | 8 ++++++-- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index de81ef9e..47577584 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -11,7 +11,6 @@ using Plugin.BLE.Extensions; using System.Collections.Concurrent; using Windows.Devices.Enumeration; -using WBluetooth = global::Windows.Devices.Bluetooth; namespace Plugin.BLE.Windows { @@ -83,22 +82,6 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; - // Calling the GetGattServicesAsync on the BluetoothLEDevice with uncached property causes the device to connect - // - // ref https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice.frombluetoothaddressasync - // Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. - // To initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery - // method on BluetoothLEDevice, or perform a read/write operation against the device. - // - // Remark from Ask Bojesen 2023-11-12: - // Below three lines reflect the different approach with GattSession, but I could not get this working properly - // - // var deviceId = BluetoothDeviceId.FromId(dev.NativeDevice.DeviceId); - // var genericProfileGattSession = await GattSession.FromDeviceIdAsync(deviceId); - // bool success = genericProfileGattSession.MaintainConnection = true; - - //var servicesResult = await dev.NativeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); - //bool success = servicesResult.Status == WBluetooth.GenericAttributeProfile.GattCommunicationStatus.Success; bool success = await dev.ConnectInternal(connectParameters, cancellationToken); if (!success) diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 8231114c..db5614fd 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -117,7 +117,11 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv } public async Task ConnectInternal(ConnectParameters connectParameters, CancellationToken cancellationToken) - { + { + // ref https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice.frombluetoothaddressasync + // Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. + // To initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery + // method on BluetoothLEDevice, or perform a read/write operation against the device. this.connectParameters = connectParameters; if (NativeDevice is null) { @@ -127,7 +131,7 @@ public async Task ConnectInternal(ConnectParameters connectParameters, Can try { var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - gattSession = await WBluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); + gattSession = await GattSession.FromDeviceIdAsync(devId); gattSession.SessionStatusChanged += GattSession_SessionStatusChanged; gattSession.MaintainConnection = true; } catch (Exception ex) From 1b4cfebba2ba5adbce6c1f78091af197c58e2e5b Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 14 Nov 2023 14:13:09 +0100 Subject: [PATCH 111/193] Writing gattSession.MaxPduSize in log. --- Source/Plugin.BLE/Windows/Device.cs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index db5614fd..60a6ce72 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -105,8 +105,13 @@ protected override DeviceState GetState() protected override async Task RequestMtuNativeAsync(int requestValue) { - var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - using var gattSession = await WBluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(devId); + // Ref https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.genericattributeprofile.gattsession.maxpdusize + // There are no means in windows to request a change, but we can read the current value + if (gattSession is null) + { + Trace.Message("WARNING RequestMtuNativeAsync failed since gattSession is null"); + return -1; + } return gattSession.MaxPduSize; } @@ -131,10 +136,12 @@ public async Task ConnectInternal(ConnectParameters connectParameters, Can try { var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - gattSession = await GattSession.FromDeviceIdAsync(devId); + gattSession = await GattSession.FromDeviceIdAsync(devId); gattSession.SessionStatusChanged += GattSession_SessionStatusChanged; + gattSession.MaxPduSizeChanged += GattSession_MaxPduSizeChanged; gattSession.MaintainConnection = true; - } catch (Exception ex) + } + catch (Exception ex) { Trace.Message("WARNING ConnectInternal failed: {0}", ex.Message); DisposeGattSession(); @@ -160,6 +167,11 @@ private void GattSession_SessionStatusChanged(GattSession sender, GattSessionSta Trace.Message("GattSession_SessionStatusChanged: " + args.Status); } + private void GattSession_MaxPduSizeChanged(GattSession sender, object args) + { + Trace.Message("GattSession_MaxPduSizeChanged: {0}", sender.MaxPduSize); + } + public void DisconnectInternal() { DisposeGattSession(); @@ -183,9 +195,9 @@ public override void Dispose() catch { } } - ~Device() - { - DisposeGattSession(); + ~Device() + { + DisposeGattSession(); } public override bool IsConnectable { get; protected set; } From 45e06c159afd8becafc616c1d8f3856ac8dafe2c Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Fri, 17 Nov 2023 11:57:34 +0100 Subject: [PATCH 112/193] Formatted document --- Source/Plugin.BLE/Windows/Adapter.cs | 4 ++-- Source/Plugin.BLE/Windows/Device.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 47577584..5a91616f 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -84,7 +84,7 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect bool success = await dev.ConnectInternal(connectParameters, cancellationToken); - if (!success) + if (!success) { // use DisconnectDeviceNative to clean up resources otherwise windows won't disconnect the device // after a subsequent successful connection (#528, #536, #423) @@ -149,7 +149,7 @@ private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, obje protected override void DisconnectDeviceNative(IDevice device) { // Windows doesn't support disconnecting, so currently just dispose of the device - Trace.Message($"DisconnectDeviceNative from device with ID: {device.Id.ToHexBleAddress()}"); + Trace.Message($"DisconnectDeviceNative from device with ID: {device.Id.ToHexBleAddress()}"); if (device.NativeDevice is BluetoothLEDevice nativeDevice) { _deviceOperationRegistry.Remove(device.Id.ToString()); diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 60a6ce72..e0cd189b 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -110,7 +110,7 @@ protected override async Task RequestMtuNativeAsync(int requestValue) if (gattSession is null) { Trace.Message("WARNING RequestMtuNativeAsync failed since gattSession is null"); - return -1; + return -1; } return gattSession.MaxPduSize; } From 656f1d1ec91c514ddeeff09f4979df1a2b132877 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Fri, 17 Nov 2023 22:29:39 +0100 Subject: [PATCH 113/193] Release 3.1.0-beta.1 * update version in csproj files * amend changelog.md --- .../MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- doc/changelog.md | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 4a5fd0ba..0d25a548 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE - 3.0.0 + 3.1.0-beta.1 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil MvvmCross.Plugin.BLE diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 046f225e..87d4facc 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -4,7 +4,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 Plugin.BLE Plugin.BLE - 3.0.0 + 3.1.0-beta.1 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil Plugin.BLE diff --git a/doc/changelog.md b/doc/changelog.md index 5fc7b495..0bd1dbbc 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -1,5 +1,18 @@ # Changelog +## 3.1 + +#### 3.1.0-beta.1 +- #746 Windows.Devices.Bluetooth Only +- #764 ReadAsync updates characteristic value on Windows +- #770 Remove .NET 6 +- #773 Added BroadcastName to AdvertisementRecordType (fixes #772) +- #774 disable MainThreadInvoker in Adapter.ConnectToDeviceAsync (fixes #757) +- #776 Improve Connect In Windows +- #777 StateChanged for Windows +- #778 Add support for .NET 8 + + ## 3.0 MAUI #### 3.0.0 @@ -40,6 +53,7 @@ - #614 Upgrade to .NET 6 - #638 GitHub Actions: update to .NET 7 (fixes #622, #626, #630) + ## 2.2 UWP #### 2.2.0-pre5 @@ -65,6 +79,7 @@ #### 2.2.0-pre.1 UWP - UWP support pre-release + ## 2.1 MacOS ### 2.1.3 From 243d2194f140ff9ff6daa9347d89a9fe5fd7ced8 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Wed, 29 Nov 2023 11:14:34 +0100 Subject: [PATCH 114/193] TrySetStateAsync --- .../Plugin.BLE/Windows/BleImplementation.cs | 55 ++++++++++++++----- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/Source/Plugin.BLE/Windows/BleImplementation.cs b/Source/Plugin.BLE/Windows/BleImplementation.cs index 793f976d..017d33c0 100644 --- a/Source/Plugin.BLE/Windows/BleImplementation.cs +++ b/Source/Plugin.BLE/Windows/BleImplementation.cs @@ -10,6 +10,10 @@ namespace Plugin.BLE { public class BleImplementation : BleImplementationBase { + private BluetoothAdapter btAdapter; + private Radio radio; + private bool isInitialized = false; + public static BluetoothCacheMode CacheModeCharacteristicRead { get; set; } = BluetoothCacheMode.Uncached; public static BluetoothCacheMode CacheModeDescriptorRead { get; set; } = BluetoothCacheMode.Uncached; public static BluetoothCacheMode CacheModeGetDescriptors { get; set; } = BluetoothCacheMode.Cached; @@ -23,22 +27,15 @@ protected override IAdapter CreateNativeAdapter() protected override BluetoothState GetInitialStateNative() { - try - { - BluetoothAdapter btAdapter = BluetoothAdapter.GetDefaultAsync().AsTask().Result; - if (!btAdapter.IsLowEnergySupported) - { - return BluetoothState.Unavailable; - } - var radio = btAdapter.GetRadioAsync().AsTask().Result; - radio.StateChanged += Radio_StateChanged; - return ToBluetoothState(radio.State); + if (!isInitialized) + { + return BluetoothState.Unknown; } - catch (Exception ex) + if (!btAdapter.IsLowEnergySupported) { - Trace.Message("GetInitialStateNativeAsync exception:{0}", ex.Message); return BluetoothState.Unavailable; } + return ToBluetoothState(radio.State); } private static BluetoothState ToBluetoothState(RadioState radioState) @@ -61,7 +58,39 @@ private void Radio_StateChanged(Radio radio, object args) protected override void InitializeNative() { - + try + { + btAdapter = BluetoothAdapter.GetDefaultAsync().AsTask().Result; + radio = btAdapter.GetRadioAsync().AsTask().Result; + radio.StateChanged += Radio_StateChanged; + isInitialized = true; + } + catch (Exception ex) + { + Trace.Message("InitializeNative exception:{0}", ex.Message); + } + } + + /// + /// Try set the state of the Bluetooth on/off in Windows + /// + /// + /// true if the the method executed with success otherwice false + public async Task TrySetStateAsync(bool on) + { + if (!isInitialized) + { + return false; + } + try + { + return await radio.SetStateAsync(on ? RadioState.On : RadioState.Off) == RadioAccessStatus.Allowed; + } + catch (Exception ex) + { + Trace.Message("TrySetStateAsync exception:{0}", ex.Message); + return false; + } } } From 480be6cab955b598b8b62724c1f254a8a4d1040e Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sun, 10 Dec 2023 10:14:35 +0100 Subject: [PATCH 115/193] Implemented BluetoothLEPreferredConnectionParameters --- .../BLE.Client.WinConsole.csproj | 2 +- .../BLE.Client/BLE.Client.WinConsole/Demo.cs | 12 ++ .../{BleDemo.cs => PluginDemos.cs} | 12 +- .../BLE.Client.WinConsole/Program.cs | 42 ++++++- .../BLE.Client.WinConsole/WindowsDemos.cs | 106 ++++++++++++++++++ Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- Source/Plugin.BLE/Shared/ConnectParameter.cs | 12 +- .../Shared/ConnectionParameterSet.cs | 26 +++++ Source/Plugin.BLE/Windows/Adapter.cs | 8 +- Source/Plugin.BLE/Windows/Device.cs | 41 ++++++- 10 files changed, 247 insertions(+), 16 deletions(-) create mode 100644 Source/BLE.Client/BLE.Client.WinConsole/Demo.cs rename Source/BLE.Client/BLE.Client.WinConsole/{BleDemo.cs => PluginDemos.cs} (93%) create mode 100644 Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs create mode 100644 Source/Plugin.BLE/Shared/ConnectionParameterSet.cs diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj b/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj index d19a9352..014f3859 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj +++ b/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj @@ -2,7 +2,7 @@ Exe - net7.0-windows10.0.19041 + net8.0-windows10.0.22621.0 enable enable diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Demo.cs b/Source/BLE.Client/BLE.Client.WinConsole/Demo.cs new file mode 100644 index 00000000..84d043f1 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.WinConsole/Demo.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +public record Demo +( + string Description, + Func Method +); + diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs similarity index 93% rename from Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs rename to Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index af174100..544f2703 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BleDemo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -14,7 +14,7 @@ namespace BLE.Client.WinConsole { - internal class BleDemo + internal class PluginDemos { private readonly IBluetoothLE bluetoothLE; public IAdapter Adapter { get; } @@ -23,7 +23,7 @@ internal class BleDemo private readonly IDictionary connectedDevices; private bool scanningDone = false; - public BleDemo(Action? writer = null) + public PluginDemos(Action? writer = null) { discoveredDevices = new List(); connectedDevices = new ConcurrentDictionary(); @@ -44,10 +44,14 @@ public IDevice ConnectToKnown(Guid id) return dev; } - public IDevice ConnectToKnown(string bleaddress) + public async Task Test_Connect_Disconnect(string bleaddress) { var id = bleaddress.ToBleDeviceGuid(); - return ConnectToKnown(id); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); + connectedDevices[id] = dev; + await Task.Delay(4000); + await Adapter.DisconnectDeviceAsync(dev); + dev.Dispose(); } public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ms = 2000) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index 48e20cbd..122360e5 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -2,13 +2,51 @@ using Plugin.BLE; using Plugin.BLE.Abstractions.Contracts; using System; +using Windows.Media.Capture; Console.WriteLine("Hello, BLE World!"); using (var ct = new ConsoleTracer()) { + const string bleaddress = "8C4B14C8602A"; Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.GetPrefixedTrace("Plugin.BLE"); - var demo = new BleDemo(ct.GetPrefixedTrace(" DEMO")); - await demo.ShowNumberOfServices("40CBC0DD37E2"); + var ppemos = new PluginDemos(ct.GetPrefixedTrace(" DEMO")); + var wdemos = new WindowsDemos(ct.GetPrefixedTrace(" DEMO")); + var demoDict = new Dictionary + { + {ConsoleKey.D8, + new Demo("Plugin: Test Connect -> Disconnect", ppemos.Test_Connect_Disconnect) }, + {ConsoleKey.D9, + new Demo("Windows: Test Connect -> Disconnect", wdemos.Test_Connect_Disconnect) }, + }; + Console.WriteLine("Using BLE Address: " + bleaddress); + Console.WriteLine(); + Console.WriteLine("List of tests to run for key:"); + Console.WriteLine(ConsoleKey.Escape + " -> Quit!"); + foreach (var demo in demoDict) + { + Console.WriteLine(demo.Key + ": " + demo.Value.Description); + } + while (true) + { + var key = Console.ReadKey(); + if (key.Key == ConsoleKey.Escape) + { + break; + } + if (demoDict.TryGetValue(key.Key, out Demo? chosendemo)) + { + Console.WriteLine(key.Key + " -> Running: " + chosendemo.Description); + Console.WriteLine("---------------------------------------------"); + if (chosendemo is null) + { + throw new Exception("No such demo!"); + } + await chosendemo.Method(bleaddress); + } else + { + Console.WriteLine(key.Key + " -> No such test. Remember " + ConsoleKey.Escape + " -> Quit!"); + } + } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs new file mode 100644 index 00000000..3c0a2391 --- /dev/null +++ b/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs @@ -0,0 +1,106 @@ +using Plugin.BLE.Abstractions.Contracts; +using Plugin.BLE.Windows; +using Plugin.BLE; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Diagnostics; +using Windows.Devices.Bluetooth.GenericAttributeProfile; +using Windows.Devices.Bluetooth; +using WBluetooth = Windows.Devices.Bluetooth; +using Plugin.BLE.Extensions; + +namespace BLE.Client.WinConsole +{ + /// + /// The purpose of this demonstration class is to show/test Windows BluetoothLEDevice without using the Plugin + /// + public class WindowsDemos + { + private readonly Action? writer; + ManualResetEvent disconnectedSignal = new ManualResetEvent(false); + ManualResetEvent connectedSignal = new ManualResetEvent(false); + + public WindowsDemos(Action? writer = null) + { + this.writer = writer; + } + + private void Write(string format, params object[] args) + { + writer?.Invoke(format, args); + } + + public async Task Test_Connect_Disconnect(string blehexaddress) + { + ulong bleaddress = blehexaddress.ToBleDeviceGuid().ToBleAddress(); + WBluetooth.BluetoothLEDevice dev = await WBluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(bleaddress); + dev.RequestPreferredConnectionParameters(BluetoothLEPreferredConnectionParameters.ThroughputOptimized); + dev.ConnectionStatusChanged += Dev_ConnectionStatusChanged; + var devId = BluetoothDeviceId.FromId(dev.DeviceId); + Write("Connecting..."); + var stopwatch = Stopwatch.StartNew(); + GattSession gattSession = await GattSession.FromDeviceIdAsync(devId); + gattSession = await GattSession.FromDeviceIdAsync(devId); + gattSession.MaintainConnection = true; + gattSession.SessionStatusChanged += GattSession_SessionStatusChanged; + gattSession.MaxPduSizeChanged += GattSession_MaxPduSizeChanged; + if (!connectedSignal.WaitOne(10000)) + { + Write("Not Connected!!!"); + return; + } + Write("Connected in {0} ms", stopwatch.ElapsedMilliseconds); + var conpar = dev.GetConnectionParameters(); + Write($"Connected with Latency = {conpar.ConnectionLatency}, " + + $"Interval = {conpar.ConnectionInterval}, Timeout = {conpar.LinkTimeout}, MaxPdu = {gattSession.MaxPduSize}"); + + Thread.Sleep(100); + Write("Now Sleeing 4 secs..."); + Thread.Sleep(4000); + disconnectedSignal.Reset(); + Write("Disconnecting..."); + stopwatch = Stopwatch.StartNew(); + + gattSession.MaintainConnection = false; + gattSession.Dispose(); + dev.Dispose(); + if (!disconnectedSignal.WaitOne(10000)) + { + Write("Not Disconnected!!!"); + return; + } + Write("Disconnected in {0} ms", stopwatch.ElapsedMilliseconds); + } + + private void GattSession_MaxPduSizeChanged(GattSession sender, object args) + { + Write("MaxPduSizeChanged: {0}", sender.MaxPduSize); + } + + private void GattSession_SessionStatusChanged(GattSession sender, GattSessionStatusChangedEventArgs args) + { + Write("SessionStatusChanged: {0}", args.Status); + } + + private void Dev_ConnectionStatusChanged(BluetoothLEDevice sender, object args) + { + Write("ConnectionStatusChanged:{0}", sender.ConnectionStatus); + switch(sender.ConnectionStatus) + { + case BluetoothConnectionStatus.Disconnected: + disconnectedSignal.Set(); + break; + case BluetoothConnectionStatus.Connected: + connectedSignal.Set(); + break; + default: + Write("Unknown BluetoothConnectionStatus: {0}", sender.ConnectionStatus); + break; + } + } + } +} diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 87d4facc..373aaa2a 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -1,7 +1,7 @@  netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android34.0;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041;net8.0-windows10.0.22621.0 Plugin.BLE Plugin.BLE 3.1.0-beta.1 diff --git a/Source/Plugin.BLE/Shared/ConnectParameter.cs b/Source/Plugin.BLE/Shared/ConnectParameter.cs index 1574f5c6..2625748d 100644 --- a/Source/Plugin.BLE/Shared/ConnectParameter.cs +++ b/Source/Plugin.BLE/Shared/ConnectParameter.cs @@ -16,6 +16,12 @@ public struct ConnectParameters /// public bool ForceBleTransport { get; } + /// + /// Windows only, mapped to: + /// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothlepreferredconnectionparameters + /// + public ConnectionParameterSet ConnectionParameterSet { get; } + /// /// Default-constructed connection parameters (all parameters set to false). /// @@ -26,10 +32,14 @@ public struct ConnectParameters /// /// Android only: Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true). The default is false. /// Android only: For Dual Mode device, force transport mode to LE. The default is false. - public ConnectParameters(bool autoConnect = false, bool forceBleTransport = false) + public ConnectParameters( + bool autoConnect = false, + bool forceBleTransport = false, + ConnectionParameterSet connectionParameterSet = ConnectionParameterSet.None) { AutoConnect = autoConnect; ForceBleTransport = forceBleTransport; + ConnectionParameterSet = connectionParameterSet; } } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Shared/ConnectionParameterSet.cs b/Source/Plugin.BLE/Shared/ConnectionParameterSet.cs new file mode 100644 index 00000000..24faf0ed --- /dev/null +++ b/Source/Plugin.BLE/Shared/ConnectionParameterSet.cs @@ -0,0 +1,26 @@ +namespace Plugin.BLE.Abstractions +{ + /// + /// Only supported in Windows. Mapped to this + /// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothlepreferredconnectionparameters + /// + public enum ConnectionParameterSet + { + /// + /// Not setting any preffered connection type + /// + None, + /// + /// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothlepreferredconnectionparameters.balanced + /// + Balanced, + /// + /// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothlepreferredconnectionparameters.poweroptimized + /// + PowerOptimized, + /// + /// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothlepreferredconnectionparameters.throughputoptimized + /// + ThroughputOptimized + } +} diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 5a91616f..02e0e47a 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -62,7 +62,7 @@ protected override void StopScanNative() if (_bleWatcher != null) { Trace.Message("Stopping the scan for devices"); - _bleWatcher.Stop(); + _bleWatcher.Stop(); _bleWatcher = null; } } @@ -122,6 +122,12 @@ private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, obje if (nativeDevice.ConnectionStatus == BluetoothConnectionStatus.Connected && ConnectedDeviceRegistry.TryGetValue(id, out var connectedDevice)) { +#if WINDOWS10_0_22000_0_OR_GREATER + var conpar = nativeDevice.GetConnectionParameters(); + Trace.Message( + $"Connected with Latency = {conpar.ConnectionLatency}, " + + $"Interval = {conpar.ConnectionInterval}, Timeout = {conpar.LinkTimeout}"); +#endif HandleConnectedDevice(connectedDevice); return; } diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index e0cd189b..6445059f 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -31,7 +31,7 @@ public Device(Adapter adapter, BluetoothLEDevice nativeDevice, int rssi, Guid id Id = id; Name = nativeDevice.Name; AdvertisementRecords = advertisementRecords; - IsConnectable = isConnectable; + IsConnectable = isConnectable; } internal void Update(short btAdvRawSignalStrengthInDBm, IReadOnlyList advertisementData) @@ -121,25 +121,53 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv return false; } + + + static void MaybeRequestPreferredConnectionParameters(BluetoothLEDevice device, ConnectParameters connectParameters) + { +#if WINDOWS10_0_22000_0_OR_GREATER + BluetoothLEPreferredConnectionParameters parameters = null; + switch(connectParameters.ConnectionParameterSet) + { + case ConnectionParameterSet.Balanced: + parameters = BluetoothLEPreferredConnectionParameters.Balanced; + break; + case ConnectionParameterSet.PowerOptimized: + parameters = BluetoothLEPreferredConnectionParameters.PowerOptimized; + break; + case ConnectionParameterSet.ThroughputOptimized: + parameters = BluetoothLEPreferredConnectionParameters.ThroughputOptimized; + break; + default: + parameters = BluetoothLEPreferredConnectionParameters.ThroughputOptimized; + break; + } + if (parameters is not null) + { + var conreq = device.RequestPreferredConnectionParameters(parameters); + Trace.Message($"RequestPreferredConnectionParameters({connectParameters.ConnectionParameterSet}): {conreq.Status}"); + } +#endif + } public async Task ConnectInternal(ConnectParameters connectParameters, CancellationToken cancellationToken) { // ref https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice.frombluetoothaddressasync // Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. // To initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery - // method on BluetoothLEDevice, or perform a read/write operation against the device. - this.connectParameters = connectParameters; + // method on BluetoothLEDevice, or perform a read/write operation against the device. if (NativeDevice is null) { Trace.Message("ConnectInternal says: Cannot connect since NativeDevice is null"); return false; } try - { + { + MaybeRequestPreferredConnectionParameters(NativeDevice, connectParameters); var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - gattSession = await GattSession.FromDeviceIdAsync(devId); + gattSession = await GattSession.FromDeviceIdAsync(devId); + gattSession.MaintainConnection = true; gattSession.SessionStatusChanged += GattSession_SessionStatusChanged; gattSession.MaxPduSizeChanged += GattSession_MaxPduSizeChanged; - gattSession.MaintainConnection = true; } catch (Exception ex) { @@ -156,6 +184,7 @@ private void DisposeGattSession() if (gattSession != null) { gattSession.MaintainConnection = false; + gattSession.MaxPduSizeChanged -= GattSession_MaxPduSizeChanged; gattSession.SessionStatusChanged -= GattSession_SessionStatusChanged; gattSession.Dispose(); gattSession = null; From 0ffd6b87e39735cff40cde04bc90ae02bfeca1b1 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Wed, 27 Dec 2023 14:11:01 +0100 Subject: [PATCH 116/193] GHA: add a Linux build --- .github/workflows/dotnet.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 11d19d78..5dba5729 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -99,3 +99,22 @@ jobs: run: dotnet build ./Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false - name: Build MAUI sample run: dotnet build ./Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj /p:Configuration=Release /t:restore,build /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false + + linuxBuild: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Install workloads + run: dotnet workload install android wasm-tools maui-android + - name: Build Plugin.BLE NuGet + run: dotnet build ./Source/Plugin.BLE/Plugin.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false + - name: Build MVVMCross.Plugins.BLE NuGet + run: dotnet build ./Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false + - name: Build MAUI sample + run: dotnet build ./Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj /p:Configuration=Release /t:restore,build /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false From 408037fe8f74e539de409848318b9a47680ee0d1 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Wed, 27 Dec 2023 14:22:36 +0100 Subject: [PATCH 117/193] do not build iOS & Mac targets on Linux --- Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 5 +++-- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 5 +++-- Source/Plugin.BLE/Plugin.BLE.csproj | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index fb1287e6..529aabbc 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -1,8 +1,9 @@  - net8.0-android34.0;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);net8.0-windows10.0.19041 + net8.0-android34.0 + $(TargetFrameworks);net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);net8.0-windows10.0.19041 Exe BLE.Client.Maui true diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 0d25a548..6d48f213 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -1,7 +1,8 @@  - net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android34.0;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 + net7.0-android33.0;net8.0-android34.0 + $(TargetFrameworks);net7.0-ios;net7.0-maccatalyst;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE 3.1.0-beta.1 diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 87d4facc..69648eb9 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -1,7 +1,8 @@  - netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android34.0;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 + netstandard2.0;net7.0-android33.0;net8.0-android34.0 + $(TargetFrameworks);net7.0-ios;net7.0-maccatalyst;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 Plugin.BLE Plugin.BLE 3.1.0-beta.1 From b0373baf8858f23d9f1b1f69c3313bc78b7e5331 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 2 Jan 2024 11:06:06 +0100 Subject: [PATCH 118/193] Added TrySetStateAsync to Shared Interface --- Source/Plugin.BLE/Android/BleImplementation.cs | 7 +++++++ Source/Plugin.BLE/Apple/BleImplementation.cs | 7 +++++++ Source/Plugin.BLE/Shared/BleImplementationBase.cs | 7 ++++++- Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs | 8 ++++++++ Source/Plugin.BLE/Windows/BleImplementation.cs | 9 ++------- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Source/Plugin.BLE/Android/BleImplementation.cs b/Source/Plugin.BLE/Android/BleImplementation.cs index a840a103..c0f79d6e 100644 --- a/Source/Plugin.BLE/Android/BleImplementation.cs +++ b/Source/Plugin.BLE/Android/BleImplementation.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Android.App; using Android.Bluetooth; using Android.Content; @@ -81,5 +82,11 @@ protected override BluetoothState GetInitialStateNative() protected override IAdapter CreateNativeAdapter() => new Adapter(_bluetoothManager); + + public override Task TrySetStateAsync(bool on) + { + Abstractions.Trace.Message("WARNING TrySetStateAsync is not implemented for Android"); + return Task.FromResult(false); + } } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Apple/BleImplementation.cs b/Source/Plugin.BLE/Apple/BleImplementation.cs index 2c110015..a2ef56b0 100644 --- a/Source/Plugin.BLE/Apple/BleImplementation.cs +++ b/Source/Plugin.BLE/Apple/BleImplementation.cs @@ -4,6 +4,7 @@ using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Extensions; using Plugin.BLE.iOS; +using System.Threading.Tasks; namespace Plugin.BLE { @@ -61,5 +62,11 @@ private CBCentralInitOptions CreateInitOptions() ShowPowerAlert = _showPowerAlert }; } + + public override Task TrySetStateAsync(bool on) + { + Trace.Message("WARNING TrySetStateAsync is not implemented for Apple"); + return Task.FromResult(false); + } } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Shared/BleImplementationBase.cs b/Source/Plugin.BLE/Shared/BleImplementationBase.cs index 69385d87..11c2e2a6 100644 --- a/Source/Plugin.BLE/Shared/BleImplementationBase.cs +++ b/Source/Plugin.BLE/Shared/BleImplementationBase.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Abstractions.EventArgs; using Plugin.BLE.Abstractions.Utils; @@ -71,19 +72,23 @@ private IAdapter CreateAdapter() return new FakeAdapter(); return CreateNativeAdapter(); - } + } /// /// Native implementation of Initialize. /// protected abstract void InitializeNative(); + /// /// Get initial state of native adapter. /// protected abstract BluetoothState GetInitialStateNative(); + /// /// Create the native adapter. /// protected abstract IAdapter CreateNativeAdapter(); + + public abstract Task TrySetStateAsync(bool on); } } \ No newline at end of file diff --git a/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs b/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs index 08da6cdc..21455ded 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Plugin.BLE.Abstractions.EventArgs; namespace Plugin.BLE.Abstractions.Contracts @@ -29,6 +30,13 @@ public interface IBluetoothLE /// bool IsOn { get; } + /// + /// Try set the state of the Bluetooth on/off + /// + /// + /// true if the the method executed with success otherwice false + Task TrySetStateAsync(bool on); + /// /// Adapter to that provides access to the physical bluetooth adapter. /// diff --git a/Source/Plugin.BLE/Windows/BleImplementation.cs b/Source/Plugin.BLE/Windows/BleImplementation.cs index 017d33c0..f5795ab0 100644 --- a/Source/Plugin.BLE/Windows/BleImplementation.cs +++ b/Source/Plugin.BLE/Windows/BleImplementation.cs @@ -71,12 +71,7 @@ protected override void InitializeNative() } } - /// - /// Try set the state of the Bluetooth on/off in Windows - /// - /// - /// true if the the method executed with success otherwice false - public async Task TrySetStateAsync(bool on) + public override async Task TrySetStateAsync(bool on) { if (!isInitialized) { @@ -88,7 +83,7 @@ public async Task TrySetStateAsync(bool on) } catch (Exception ex) { - Trace.Message("TrySetStateAsync exception:{0}", ex.Message); + Trace.Message("TrySetStateAsync exception: {0}", ex.Message); return false; } } From 143995c1942f553d5aea3414a8694ff51d4cdafc Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 2 Jan 2024 11:10:19 +0100 Subject: [PATCH 119/193] Fixed formatting --- Source/Plugin.BLE/Shared/BleImplementationBase.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Source/Plugin.BLE/Shared/BleImplementationBase.cs b/Source/Plugin.BLE/Shared/BleImplementationBase.cs index 11c2e2a6..4adde41f 100644 --- a/Source/Plugin.BLE/Shared/BleImplementationBase.cs +++ b/Source/Plugin.BLE/Shared/BleImplementationBase.cs @@ -18,7 +18,6 @@ public abstract class BleImplementationBase : IBluetoothLE /// Occurs when the state of the Bluetooth adapter changes. /// public event EventHandler StateChanged; - /// /// Indicates whether the device supports BLE. /// @@ -72,23 +71,28 @@ private IAdapter CreateAdapter() return new FakeAdapter(); return CreateNativeAdapter(); - } + } /// /// Native implementation of Initialize. /// protected abstract void InitializeNative(); - + /// /// Get initial state of native adapter. /// protected abstract BluetoothState GetInitialStateNative(); - + /// /// Create the native adapter. /// protected abstract IAdapter CreateNativeAdapter(); - + + /// + /// Try set the state of the Bluetooth on/off + /// + /// + /// true if the the method executed with success otherwice false public abstract Task TrySetStateAsync(bool on); } } \ No newline at end of file From d037c8f9001a5f8ec0dd1d83defae06fdb4ef0d5 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Sun, 14 Jan 2024 21:50:56 +0100 Subject: [PATCH 120/193] Comment added to method: Only supported in Windows --- Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs b/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs index 21455ded..0bd46a14 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs @@ -32,6 +32,7 @@ public interface IBluetoothLE /// /// Try set the state of the Bluetooth on/off + /// 2024-01-14: Only supported in Windows /// /// /// true if the the method executed with success otherwice false From 370ed5ca6569dcf6bee95932bda90bb1a20bbf17 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 15 Jan 2024 20:57:20 +0100 Subject: [PATCH 121/193] Improved WIndows Demo Client --- .../BLE.Client.WinConsole.csproj | 4 +- .../BLE.Client.WinConsole/ConsoleTracer.cs | 1 + .../BLE.Client.WinConsole/PluginDemos.cs | 67 ++++++++++++++++--- .../BLE.Client.WinConsole/Program.cs | 31 +++++---- .../BLE.Client.WinConsole/WindowsDemos.cs | 24 ++++++- 5 files changed, 102 insertions(+), 25 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj b/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj index 014f3859..c72432fa 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj +++ b/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj @@ -3,12 +3,12 @@ Exe net8.0-windows10.0.22621.0 - enable + disable enable - + diff --git a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs index 6d34efd0..db7344fb 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/ConsoleTracer.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace BLE.Client.WinConsole diff --git a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index 544f2703..f453613d 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -9,8 +9,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Windows.Devices.Bluetooth; +using Windows.Devices.Enumeration; namespace BLE.Client.WinConsole { @@ -20,17 +22,27 @@ internal class PluginDemos public IAdapter Adapter { get; } private readonly Action? writer; private readonly List discoveredDevices; - private readonly IDictionary connectedDevices; private bool scanningDone = false; public PluginDemos(Action? writer = null) { discoveredDevices = new List(); - connectedDevices = new ConcurrentDictionary(); bluetoothLE = CrossBluetoothLE.Current; Adapter = CrossBluetoothLE.Current.Adapter; + Adapter.DeviceConnected += Adapter_DeviceConnected; + Adapter.DeviceDisconnected += Adapter_DeviceDisconnected; this.writer = writer; - } + } + + private void Adapter_DeviceDisconnected(object? sender, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs e) + { + Write($"Adapter_DeviceDisconnected {e.Device.Id}"); + } + + private void Adapter_DeviceConnected(object? sender, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs e) + { + Write($"Adapter_DeviceConnected {e.Device.Id}"); + } private void Write(string format, params object[] args) { @@ -39,19 +51,58 @@ private void Write(string format, params object[] args) public IDevice ConnectToKnown(Guid id) { - IDevice dev = Adapter.ConnectToKnownDeviceAsync(id).Result; - connectedDevices[id] = dev; + IDevice dev = Adapter.ConnectToKnownDeviceAsync(id).Result; return dev; } - public async Task Test_Connect_Disconnect(string bleaddress) + public async Task Connect_Disconnect(string bleaddress) { var id = bleaddress.ToBleDeviceGuid(); - IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); - connectedDevices[id] = dev; + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); + Write("Waiting 4 secs"); await Task.Delay(4000); + Write("Disconnecting"); + await Adapter.DisconnectDeviceAsync(dev); + dev.Dispose(); + Write("Test_Connect_Disconnect done"); + } + + public async Task Pair_Connect_Disconnect(string bleaddress) + { + var id = bleaddress.ToBleDeviceGuid(); + ulong bleAddressulong = id.ToBleAddress(); + using (BluetoothLEDevice nativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bleAddressulong)) + { + nativeDevice.ConnectionStatusChanged += NativeDevice_ConnectionStatusChanged; + var deviceInformation = await DeviceInformation.CreateFromIdAsync(nativeDevice.DeviceId); + var pairing = deviceInformation.Pairing; + pairing.Custom.PairingRequested += Custom_PairingRequested; + Write("Pairing"); + DevicePairingResult result = await pairing.Custom.PairAsync(DevicePairingKinds.ConfirmOnly, DevicePairingProtectionLevel.Encryption); + Write("Pairing result: " + result.Status); + } + Write("Waiting 5 sec"); + await Task.Delay(5000); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); + await Task.Delay(1000); + await dev.RequestMtuAsync(517); + Write("Waiting 3 secs"); + await Task.Delay(3000); + Write("Disconnecting"); await Adapter.DisconnectDeviceAsync(dev); dev.Dispose(); + Write("Pair_Connect_Disconnect done"); + } + + private void NativeDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args) + { + Write($"NativeDevice_ConnectionStatusChanged({sender.ConnectionStatus})"); + } + + private void Custom_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args) + { + Write("Custom_PairingRequested -> Accept"); + args.Accept(); } public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ms = 2000) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index 122360e5..e211cd9f 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -2,50 +2,55 @@ using Plugin.BLE; using Plugin.BLE.Abstractions.Contracts; using System; +using System.Collections.Generic; using Windows.Media.Capture; Console.WriteLine("Hello, BLE World!"); using (var ct = new ConsoleTracer()) { - const string bleaddress = "8C4B14C8602A"; + const string bleaddress = "8C4B14C9C68A"; Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.GetPrefixedTrace("Plugin.BLE"); var ppemos = new PluginDemos(ct.GetPrefixedTrace(" DEMO")); var wdemos = new WindowsDemos(ct.GetPrefixedTrace(" DEMO")); var demoDict = new Dictionary { - {ConsoleKey.D8, - new Demo("Plugin: Test Connect -> Disconnect", ppemos.Test_Connect_Disconnect) }, - {ConsoleKey.D9, - new Demo("Windows: Test Connect -> Disconnect", wdemos.Test_Connect_Disconnect) }, + {ConsoleKey.D1, new Demo("Plugin: Connect -> Disconnect", ppemos.Connect_Disconnect) }, + {ConsoleKey.D2, new Demo("Plugin: Pair -> Connect -> Disconnect", ppemos.Pair_Connect_Disconnect) }, + {ConsoleKey.D8, new Demo("Windows: Connect -> Disconnect", wdemos.Connect_Disconnect) }, + {ConsoleKey.D9, new Demo("Windows: Unpair all BLE devices", wdemos.UnPairAllBleDevices) }, }; + Console.WriteLine("Using BLE Address: " + bleaddress); Console.WriteLine(); Console.WriteLine("List of tests to run for key:"); Console.WriteLine(ConsoleKey.Escape + " -> Quit!"); - foreach (var demo in demoDict) - { - Console.WriteLine(demo.Key + ": " + demo.Value.Description); - } while (true) { - var key = Console.ReadKey(); + foreach (var demo in demoDict) + { + Console.WriteLine(demo.Key + ": " + demo.Value.Description); + } + + var key = Console.ReadKey(); if (key.Key == ConsoleKey.Escape) { break; } if (demoDict.TryGetValue(key.Key, out Demo? chosendemo)) { - Console.WriteLine(key.Key + " -> Running: " + chosendemo.Description); - Console.WriteLine("---------------------------------------------"); + Console.WriteLine(); + Console.WriteLine("Running: " + chosendemo.Description); if (chosendemo is null) { throw new Exception("No such demo!"); } await chosendemo.Method(bleaddress); - } else + } + else { Console.WriteLine(key.Key + " -> No such test. Remember " + ConsoleKey.Escape + " -> Quit!"); } + Console.WriteLine("---------------------------------------------"); } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs index 3c0a2391..6ec1d2ce 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs @@ -12,6 +12,8 @@ using Windows.Devices.Bluetooth; using WBluetooth = Windows.Devices.Bluetooth; using Plugin.BLE.Extensions; +using System.Threading; +using Windows.Devices.Enumeration; namespace BLE.Client.WinConsole { @@ -34,7 +36,7 @@ private void Write(string format, params object[] args) writer?.Invoke(format, args); } - public async Task Test_Connect_Disconnect(string blehexaddress) + public async Task Connect_Disconnect(string blehexaddress) { ulong bleaddress = blehexaddress.ToBleDeviceGuid().ToBleAddress(); WBluetooth.BluetoothLEDevice dev = await WBluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(bleaddress); @@ -76,6 +78,24 @@ public async Task Test_Connect_Disconnect(string blehexaddress) Write("Disconnected in {0} ms", stopwatch.ElapsedMilliseconds); } + public async Task UnPairAllBleDevices(string bleaddress = "") + { + string aqsFilter = BluetoothLEDevice.GetDeviceSelector(); + var collection = await DeviceInformation.FindAllAsync(aqsFilter); + foreach (DeviceInformation di in collection) + { + try + { + Write($"Unpairing {di.Name}"); + _ = di.Pairing.UnpairAsync(); + } + catch (Exception ex) + { + Write($"Exception when unpairing {di.Name}: {ex.Message}"); + } + } + } + private void GattSession_MaxPduSizeChanged(GattSession sender, object args) { Write("MaxPduSizeChanged: {0}", sender.MaxPduSize); @@ -89,7 +109,7 @@ private void GattSession_SessionStatusChanged(GattSession sender, GattSessionSta private void Dev_ConnectionStatusChanged(BluetoothLEDevice sender, object args) { Write("ConnectionStatusChanged:{0}", sender.ConnectionStatus); - switch(sender.ConnectionStatus) + switch (sender.ConnectionStatus) { case BluetoothConnectionStatus.Disconnected: disconnectedSignal.Set(); From ac723239bc2cefb26f9014225d5ea9d6187aa83f Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 15 Jan 2024 20:58:01 +0100 Subject: [PATCH 122/193] Improved WinConsole Demo --- .../BLE.Client.WinConsole/BLE.Client.WinConsole.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj b/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj index c72432fa..45e83446 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj +++ b/Source/BLE.Client/BLE.Client.WinConsole/BLE.Client.WinConsole.csproj @@ -8,7 +8,7 @@ - + From adc7bba199bfedb8e0238c517df95452e169acfd Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 15 Jan 2024 21:03:40 +0100 Subject: [PATCH 123/193] Fixed MaybeRequestPreferredConnectionParameters for None --- Source/Plugin.BLE/Windows/Device.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 6445059f..b53756ce 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -138,8 +138,8 @@ static void MaybeRequestPreferredConnectionParameters(BluetoothLEDevice device, case ConnectionParameterSet.ThroughputOptimized: parameters = BluetoothLEPreferredConnectionParameters.ThroughputOptimized; break; - default: - parameters = BluetoothLEPreferredConnectionParameters.ThroughputOptimized; + case ConnectionParameterSet.None: + default: break; } if (parameters is not null) From 9a98eee892dcc58cff46e30cd359d97868dbf86b Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 15 Jan 2024 21:17:05 +0100 Subject: [PATCH 124/193] Merge from master --- Source/Plugin.BLE/Plugin.BLE.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 373aaa2a..69648eb9 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -1,7 +1,8 @@  - netstandard2.0;net7.0-android33.0;net7.0-ios;net7.0-maccatalyst;net8.0-android34.0;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041;net8.0-windows10.0.22621.0 + netstandard2.0;net7.0-android33.0;net8.0-android34.0 + $(TargetFrameworks);net7.0-ios;net7.0-maccatalyst;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 Plugin.BLE Plugin.BLE 3.1.0-beta.1 From 3f5f61cfa1009fb389d3663d5441b6c2608f03d6 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 15 Jan 2024 21:19:55 +0100 Subject: [PATCH 125/193] Added net8.0-windows10.0.22621.0 --- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 69648eb9..d465901a 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -2,7 +2,7 @@ netstandard2.0;net7.0-android33.0;net8.0-android34.0 $(TargetFrameworks);net7.0-ios;net7.0-maccatalyst;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041;net8.0-windows10.0.22621.0 Plugin.BLE Plugin.BLE 3.1.0-beta.1 From e07c2ae509e1b1af4b47029224b900f8f7655b51 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Wed, 17 Jan 2024 22:58:31 +0100 Subject: [PATCH 126/193] GHA: fix Linux build errors * by installing the necessary Android tools * the errors were caused by a recent change in the runner image, see https://github.com/actions/runner-images/issues/8952 --- .github/workflows/dotnet.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 5dba5729..333bb744 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -100,7 +100,7 @@ jobs: - name: Build MAUI sample run: dotnet build ./Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj /p:Configuration=Release /t:restore,build /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false - linuxBuild: + linuxBuild: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -112,9 +112,13 @@ jobs: dotnet-version: 8.0.x - name: Install workloads run: dotnet workload install android wasm-tools maui-android + - name: Install Android tools + run: | + ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager \ + --sdk_root=$ANDROID_SDK_ROOT "platform-tools" - name: Build Plugin.BLE NuGet - run: dotnet build ./Source/Plugin.BLE/Plugin.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false + run: dotnet build ./Source/Plugin.BLE/Plugin.BLE.csproj -p:Configuration=Release -t:restore,build,pack -p:Version=$(git describe) -p:ContinuousIntegrationBuild=true -p:DeterministicSourcePaths=false - name: Build MVVMCross.Plugins.BLE NuGet - run: dotnet build ./Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false + run: dotnet build ./Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj -p:Configuration=Release -t:restore,build,pack -p:Version=$(git describe) -p:ContinuousIntegrationBuild=true -p:DeterministicSourcePaths=false - name: Build MAUI sample - run: dotnet build ./Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj /p:Configuration=Release /t:restore,build /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false + run: dotnet build ./Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj -p:Configuration=Release -t:restore,build -p:Version=$(git describe) -p:ContinuousIntegrationBuild=true -p:DeterministicSourcePaths=false From 257fedf7bcea4e202e25ab24a4b61cb3e4ed6d3c Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 22 Jan 2024 10:44:56 +0100 Subject: [PATCH 127/193] Work In Progress --- .../BLE.Client.WinConsole/PluginDemos.cs | 23 +++++++++++-------- .../BLE.Client.WinConsole/Program.cs | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- Source/Plugin.BLE/Windows/Device.cs | 2 -- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index f453613d..ba1c9645 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -71,19 +71,22 @@ public async Task Pair_Connect_Disconnect(string bleaddress) { var id = bleaddress.ToBleDeviceGuid(); ulong bleAddressulong = id.ToBleAddress(); + DeviceInformation? deviceInformation = null; using (BluetoothLEDevice nativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(bleAddressulong)) - { + { + nativeDevice.RequestPreferredConnectionParameters(BluetoothLEPreferredConnectionParameters.ThroughputOptimized); nativeDevice.ConnectionStatusChanged += NativeDevice_ConnectionStatusChanged; - var deviceInformation = await DeviceInformation.CreateFromIdAsync(nativeDevice.DeviceId); - var pairing = deviceInformation.Pairing; - pairing.Custom.PairingRequested += Custom_PairingRequested; - Write("Pairing"); - DevicePairingResult result = await pairing.Custom.PairAsync(DevicePairingKinds.ConfirmOnly, DevicePairingProtectionLevel.Encryption); - Write("Pairing result: " + result.Status); + deviceInformation = await DeviceInformation.CreateFromIdAsync(nativeDevice.DeviceId); } - Write("Waiting 5 sec"); - await Task.Delay(5000); - IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); + deviceInformation.Pairing.Custom.PairingRequested += Custom_PairingRequested; + Write("Pairing"); + DevicePairingResult result = await deviceInformation.Pairing.Custom.PairAsync(DevicePairingKinds.ConfirmOnly, DevicePairingProtectionLevel.Encryption); + Write("Pairing result: " + result.Status); + //Write("Waiting 10 sec after pairing before connecting"); + //await Task.Delay(2*5000); + Write("Calling Adapter.ConnectToKnownDeviceAsync"); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); + Write($"Calling Adapter.ConnectToKnownDeviceAsync done with {dev.Name}"); await Task.Delay(1000); await dev.RequestMtuAsync(517); Write("Waiting 3 secs"); diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index e211cd9f..1305a4c6 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -8,7 +8,7 @@ Console.WriteLine("Hello, BLE World!"); using (var ct = new ConsoleTracer()) { - const string bleaddress = "8C4B14C9C68A"; + const string bleaddress = "8C4B14C86266"; Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.GetPrefixedTrace("Plugin.BLE"); var ppemos = new PluginDemos(ct.GetPrefixedTrace(" DEMO")); var wdemos = new WindowsDemos(ct.GetPrefixedTrace(" DEMO")); diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index d465901a..762f6fdd 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -2,7 +2,7 @@ netstandard2.0;net7.0-android33.0;net8.0-android34.0 $(TargetFrameworks);net7.0-ios;net7.0-maccatalyst;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041;net8.0-windows10.0.22621.0 + $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041;net8.0-windows10.0.22000.0 Plugin.BLE Plugin.BLE 3.1.0-beta.1 diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index b53756ce..2e3471fb 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -121,8 +121,6 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv return false; } - - static void MaybeRequestPreferredConnectionParameters(BluetoothLEDevice device, ConnectParameters connectParameters) { #if WINDOWS10_0_22000_0_OR_GREATER From 8c7759a88b9bf046223ccd66387db65e677e331d Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 22 Jan 2024 10:59:03 +0100 Subject: [PATCH 128/193] Fixed formatting --- Source/Plugin.BLE/Shared/ConnectParameter.cs | 2 +- Source/Plugin.BLE/Windows/Adapter.cs | 2 +- Source/Plugin.BLE/Windows/Device.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Plugin.BLE/Shared/ConnectParameter.cs b/Source/Plugin.BLE/Shared/ConnectParameter.cs index 2625748d..9a6e57db 100644 --- a/Source/Plugin.BLE/Shared/ConnectParameter.cs +++ b/Source/Plugin.BLE/Shared/ConnectParameter.cs @@ -33,7 +33,7 @@ public struct ConnectParameters /// Android only: Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true). The default is false. /// Android only: For Dual Mode device, force transport mode to LE. The default is false. public ConnectParameters( - bool autoConnect = false, + bool autoConnect = false, bool forceBleTransport = false, ConnectionParameterSet connectionParameterSet = ConnectionParameterSet.None) { diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 02e0e47a..97c95d61 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -62,7 +62,7 @@ protected override void StopScanNative() if (_bleWatcher != null) { Trace.Message("Stopping the scan for devices"); - _bleWatcher.Stop(); + _bleWatcher.Stop(); _bleWatcher = null; } } diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 2e3471fb..2d43c320 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -31,7 +31,7 @@ public Device(Adapter adapter, BluetoothLEDevice nativeDevice, int rssi, Guid id Id = id; Name = nativeDevice.Name; AdvertisementRecords = advertisementRecords; - IsConnectable = isConnectable; + IsConnectable = isConnectable; } internal void Update(short btAdvRawSignalStrengthInDBm, IReadOnlyList advertisementData) @@ -159,10 +159,10 @@ public async Task ConnectInternal(ConnectParameters connectParameters, Can return false; } try - { + { MaybeRequestPreferredConnectionParameters(NativeDevice, connectParameters); var devId = BluetoothDeviceId.FromId(NativeDevice.DeviceId); - gattSession = await GattSession.FromDeviceIdAsync(devId); + gattSession = await GattSession.FromDeviceIdAsync(devId); gattSession.MaintainConnection = true; gattSession.SessionStatusChanged += GattSession_SessionStatusChanged; gattSession.MaxPduSizeChanged += GattSession_MaxPduSizeChanged; From 0642cf7d893154c2b523f3f1249231a028e63dcb Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 22 Jan 2024 14:15:54 +0100 Subject: [PATCH 129/193] Fixed that connectParameters was not passed on at first connect --- Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs | 7 ++++--- Source/Plugin.BLE/Windows/Adapter.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index ba1c9645..bef7a227 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -58,9 +58,10 @@ public IDevice ConnectToKnown(Guid id) public async Task Connect_Disconnect(string bleaddress) { var id = bleaddress.ToBleDeviceGuid(); - IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); - Write("Waiting 4 secs"); - await Task.Delay(4000); + var connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.ThroughputOptimized); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters); + Write("Waiting 5 secs"); + await Task.Delay(5000); Write("Disconnecting"); await Adapter.DisconnectDeviceAsync(dev); dev.Dispose(); diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 97c95d61..1e19cd7e 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -172,7 +172,7 @@ public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceG var knownDevice = new Device(this, nativeDevice, 0, deviceGuid); - await ConnectToDeviceAsync(knownDevice, cancellationToken: cancellationToken); + await ConnectToDeviceAsync(knownDevice, connectParameters, cancellationToken: cancellationToken); return knownDevice; } From 0ed054e7095cecdccaf2e417fe75b7875301b963 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Tue, 30 Jan 2024 08:51:40 +0100 Subject: [PATCH 130/193] UpdateConnectionParameters added and implemented for Windows --- .../BLE.Client.WinConsole/PluginDemos.cs | 21 +++++++++++++++++++ .../BLE.Client.WinConsole/Program.cs | 3 ++- Source/Plugin.BLE/Android/Device.cs | 5 +++++ Source/Plugin.BLE/Apple/Device.cs | 5 +++++ Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 14 ++++++++++++- Source/Plugin.BLE/Shared/DeviceBase.cs | 11 ++++++++-- Source/Plugin.BLE/Windows/Device.cs | 14 +++++++++++-- 7 files changed, 67 insertions(+), 6 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index bef7a227..b41aab66 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -68,6 +68,27 @@ public async Task Connect_Disconnect(string bleaddress) Write("Test_Connect_Disconnect done"); } + public async Task Connect_Change_Parameters_Disconnect(string bleaddress) + { + var id = bleaddress.ToBleDeviceGuid(); + var connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.Balanced); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters); + Write("Waiting 5 secs"); + await Task.Delay(5000); + connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.ThroughputOptimized); + dev.UpdateConnectionParameters(connectParameters); + Write("Waiting 5 secs"); + await Task.Delay(5000); + connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.Balanced); + dev.UpdateConnectionParameters(connectParameters); + Write("Waiting 5 secs"); + await Task.Delay(5000); + Write("Disconnecting"); + await Adapter.DisconnectDeviceAsync(dev); + dev.Dispose(); + Write("Test_Connect_Disconnect done"); + } + public async Task Pair_Connect_Disconnect(string bleaddress) { var id = bleaddress.ToBleDeviceGuid(); diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index 1305a4c6..efbb057d 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -8,7 +8,7 @@ Console.WriteLine("Hello, BLE World!"); using (var ct = new ConsoleTracer()) { - const string bleaddress = "8C4B14C86266"; + const string bleaddress = "8C4B14C8602A"; Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.GetPrefixedTrace("Plugin.BLE"); var ppemos = new PluginDemos(ct.GetPrefixedTrace(" DEMO")); var wdemos = new WindowsDemos(ct.GetPrefixedTrace(" DEMO")); @@ -16,6 +16,7 @@ { {ConsoleKey.D1, new Demo("Plugin: Connect -> Disconnect", ppemos.Connect_Disconnect) }, {ConsoleKey.D2, new Demo("Plugin: Pair -> Connect -> Disconnect", ppemos.Pair_Connect_Disconnect) }, + {ConsoleKey.D3, new Demo("Plugin: Connect -> Change Parameters -> Disconnect", ppemos.Connect_Change_Parameters_Disconnect) }, {ConsoleKey.D8, new Demo("Windows: Connect -> Disconnect", wdemos.Connect_Disconnect) }, {ConsoleKey.D9, new Demo("Windows: Unpair all BLE devices", wdemos.UnPairAllBleDevices) }, }; diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index 37cfe97f..ad0f6d89 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -467,5 +467,10 @@ protected override DeviceBondState GetBondState() } return NativeDevice.BondState.FromNative(); } + + public override bool UpdateConnectionParameters(ConnectParameters connectParameters = default) + { + throw new NotImplementedException(); + } } } diff --git a/Source/Plugin.BLE/Apple/Device.cs b/Source/Plugin.BLE/Apple/Device.cs index c16e3e53..7ebe850d 100644 --- a/Source/Plugin.BLE/Apple/Device.cs +++ b/Source/Plugin.BLE/Apple/Device.cs @@ -182,5 +182,10 @@ protected override DeviceBondState GetBondState() { return DeviceBondState.NotSupported; } + + public override bool UpdateConnectionParameters(ConnectParameters connectParameters = default) + { + throw new NotImplementedException(); + } } } diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index 32492d54..b82c657b 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -120,10 +120,22 @@ public interface IDevice : IDisposable /// True, if device supports IsConnectable else False /// bool SupportsIsConnectable { get; } - + /// /// Gets the bonding state of a device. /// DeviceBondState BondState { get; } + + /// + /// Updates the connection paramaters if already connected + /// + /// + /// Only implemented for Windows + /// + /// Connection parameters. Contains platform specific parameters needed to achieved connection. The default value is None. + /// + /// The Result property will contain a boolean that inticates if the update was successful. + /// + bool UpdateConnectionParameters(ConnectParameters connectParameters = default); } } diff --git a/Source/Plugin.BLE/Shared/DeviceBase.cs b/Source/Plugin.BLE/Shared/DeviceBase.cs index a3910392..dc66014b 100644 --- a/Source/Plugin.BLE/Shared/DeviceBase.cs +++ b/Source/Plugin.BLE/Shared/DeviceBase.cs @@ -202,7 +202,7 @@ public virtual void Dispose() public void ClearServices() { this.CancelEverythingAndReInitialize(); - + lock (KnownServices) { foreach (var service in KnownServices) @@ -260,12 +260,19 @@ public override int GetHashCode() /// Shows whether the device supports the . /// public abstract bool SupportsIsConnectable { get; } - + /// /// Gets the of the device. /// protected abstract DeviceBondState GetBondState(); + /// + /// Updates the connection paramaters if already connected + /// + /// + /// + public abstract bool UpdateConnectionParameters(ConnectParameters connectParameters = default); + /// /// Gets the of the device. /// diff --git a/Source/Plugin.BLE/Windows/Device.cs b/Source/Plugin.BLE/Windows/Device.cs index 2d43c320..1798f471 100644 --- a/Source/Plugin.BLE/Windows/Device.cs +++ b/Source/Plugin.BLE/Windows/Device.cs @@ -121,7 +121,7 @@ protected override bool UpdateConnectionIntervalNative(ConnectionInterval interv return false; } - static void MaybeRequestPreferredConnectionParameters(BluetoothLEDevice device, ConnectParameters connectParameters) + static bool MaybeRequestPreferredConnectionParameters(BluetoothLEDevice device, ConnectParameters connectParameters) { #if WINDOWS10_0_22000_0_OR_GREATER BluetoothLEPreferredConnectionParameters parameters = null; @@ -144,8 +144,13 @@ static void MaybeRequestPreferredConnectionParameters(BluetoothLEDevice device, { var conreq = device.RequestPreferredConnectionParameters(parameters); Trace.Message($"RequestPreferredConnectionParameters({connectParameters.ConnectionParameterSet}): {conreq.Status}"); - } + return conreq.Status == BluetoothLEPreferredConnectionParametersRequestStatus.Success; + } + return true; +#else + return false; #endif + } public async Task ConnectInternal(ConnectParameters connectParameters, CancellationToken cancellationToken) { @@ -235,5 +240,10 @@ protected override DeviceBondState GetBondState() { return DeviceBondState.NotSupported; } + + public override bool UpdateConnectionParameters(ConnectParameters connectParameters = default) + { + return MaybeRequestPreferredConnectionParameters(NativeDevice, connectParameters); + } } } From 4f274baa7a20b5544a583bee32952f8ddf817431 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Fri, 2 Feb 2024 22:27:05 +0100 Subject: [PATCH 131/193] Corrected spelling and added a missing comment --- Source/Plugin.BLE/Android/Device.cs | 2 +- Source/Plugin.BLE/Shared/ConnectParameter.cs | 1 + Source/Plugin.BLE/Shared/ConnectionParameterSet.cs | 2 +- Source/Plugin.BLE/Shared/Contracts/IDevice.cs | 6 +++--- Source/Plugin.BLE/Shared/DeviceBase.cs | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Plugin.BLE/Android/Device.cs b/Source/Plugin.BLE/Android/Device.cs index ad0f6d89..431f7c62 100644 --- a/Source/Plugin.BLE/Android/Device.cs +++ b/Source/Plugin.BLE/Android/Device.cs @@ -40,7 +40,7 @@ public class Device : DeviceBase private TaskCompletionSource _bondCompleteTaskCompletionSource; /// - /// the connect paramaters used when connecting to this device + /// the connect parameters used when connecting to this device /// public ConnectParameters ConnectParameters { get; private set; } diff --git a/Source/Plugin.BLE/Shared/ConnectParameter.cs b/Source/Plugin.BLE/Shared/ConnectParameter.cs index 9a6e57db..d84f1374 100644 --- a/Source/Plugin.BLE/Shared/ConnectParameter.cs +++ b/Source/Plugin.BLE/Shared/ConnectParameter.cs @@ -32,6 +32,7 @@ public struct ConnectParameters /// /// Android only: Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true). The default is false. /// Android only: For Dual Mode device, force transport mode to LE. The default is false. + /// Windows only: Default is None, where this has no effect - use eg. ThroughputOptimized for firmware upload to a device public ConnectParameters( bool autoConnect = false, bool forceBleTransport = false, diff --git a/Source/Plugin.BLE/Shared/ConnectionParameterSet.cs b/Source/Plugin.BLE/Shared/ConnectionParameterSet.cs index 24faf0ed..d8dd6190 100644 --- a/Source/Plugin.BLE/Shared/ConnectionParameterSet.cs +++ b/Source/Plugin.BLE/Shared/ConnectionParameterSet.cs @@ -7,7 +7,7 @@ public enum ConnectionParameterSet { /// - /// Not setting any preffered connection type + /// Not setting any prefered connection type /// None, /// diff --git a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs index b82c657b..e29295f9 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IDevice.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IDevice.cs @@ -127,14 +127,14 @@ public interface IDevice : IDisposable DeviceBondState BondState { get; } /// - /// Updates the connection paramaters if already connected + /// Updates the connection parameters if already connected /// /// /// Only implemented for Windows /// - /// Connection parameters. Contains platform specific parameters needed to achieved connection. The default value is None. + /// Connection parameters. Contains platform specific parameters needed to achieve connection. The default value is None. /// - /// The Result property will contain a boolean that inticates if the update was successful. + /// The Result property will contain a boolean that indicates if the update was successful. /// bool UpdateConnectionParameters(ConnectParameters connectParameters = default); } diff --git a/Source/Plugin.BLE/Shared/DeviceBase.cs b/Source/Plugin.BLE/Shared/DeviceBase.cs index dc66014b..11a0b9a1 100644 --- a/Source/Plugin.BLE/Shared/DeviceBase.cs +++ b/Source/Plugin.BLE/Shared/DeviceBase.cs @@ -267,7 +267,7 @@ public override int GetHashCode() protected abstract DeviceBondState GetBondState(); /// - /// Updates the connection paramaters if already connected + /// Updates the connection parameters if already connected /// /// /// From 394768519855b73c875432f05f17e700b950613a Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Fri, 2 Feb 2024 23:41:26 +0100 Subject: [PATCH 132/193] build Xamarin.UWP client via cake --- .build/build.cake | 1 + 1 file changed, 1 insertion(+) diff --git a/.build/build.cake b/.build/build.cake index 1c691c2d..a140d546 100644 --- a/.build/build.cake +++ b/.build/build.cake @@ -72,6 +72,7 @@ Task("BuildClients") BuildProject("BLE.Client", "BLE.Client.Droid", Path.Combine("clients", "android")); BuildProject("BLE.Client", "BLE.Client.iOS", Path.Combine("clients", "ios")); BuildProject("BLE.Client", "BLE.Client.macOS", Path.Combine("clients", "macOS")); + BuildProject("BLE.Client", "BLE.Client.UWP", Path.Combine("clients", "uwp")); }); Task("Clean").Does (() => From 9b0887df912be991f2cb180dbc802d0b45169403 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 3 Feb 2024 00:18:13 +0100 Subject: [PATCH 133/193] amend .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 377e7b7f..761d1d47 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ FAKE* *.puibxml *.pdb *.DS_Store +.mono/ # Visual Studo 2015 cache/options directory .vs/ From 7a20453cb49b35f86d978db0adbfa8adad339d0c Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 3 Feb 2024 09:22:33 +0100 Subject: [PATCH 134/193] BLE.Client.UWP: remove references to BLE.Client.UWP_TemporaryKey.pfx * the file does not exist --- Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj index 2cc3a446..7a4127fc 100644 --- a/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj +++ b/Source/BLE.Client/BLE.Client.UWP/BLE.Client.UWP.csproj @@ -17,7 +17,6 @@ 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true - BLE.Client.UWP_TemporaryKey.pfx true @@ -105,7 +104,6 @@ Designer - @@ -217,4 +215,4 @@ --> - \ No newline at end of file + From d2365df74df939f9fa844a92e3f86e96555b3642 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 3 Feb 2024 12:07:12 +0100 Subject: [PATCH 135/193] build BLE.Client.WinConsole via cake --- .build/build.cake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.build/build.cake b/.build/build.cake index a140d546..3bac0394 100644 --- a/.build/build.cake +++ b/.build/build.cake @@ -68,11 +68,14 @@ Task("BuildLibs") Task("BuildClients") .Does(() => { + // Xamarin BuildProject("BLE.Client", "BLE.Client", Path.Combine("clients", "netstandard2.0")); BuildProject("BLE.Client", "BLE.Client.Droid", Path.Combine("clients", "android")); BuildProject("BLE.Client", "BLE.Client.iOS", Path.Combine("clients", "ios")); BuildProject("BLE.Client", "BLE.Client.macOS", Path.Combine("clients", "macOS")); BuildProject("BLE.Client", "BLE.Client.UWP", Path.Combine("clients", "uwp")); + // .NET 7/8 + BuildProject("BLE.Client", "BLE.Client.WinConsole", Path.Combine("clients", "wincon")); }); Task("Clean").Does (() => From 9a3e1cfb08cc2f2c701f09cda515c1a9c8c54237 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 3 Feb 2024 12:45:08 +0100 Subject: [PATCH 136/193] build BLE.Client.Maui via cake --- .build/build.cake | 1 + 1 file changed, 1 insertion(+) diff --git a/.build/build.cake b/.build/build.cake index 3bac0394..b3511442 100644 --- a/.build/build.cake +++ b/.build/build.cake @@ -76,6 +76,7 @@ Task("BuildClients") BuildProject("BLE.Client", "BLE.Client.UWP", Path.Combine("clients", "uwp")); // .NET 7/8 BuildProject("BLE.Client", "BLE.Client.WinConsole", Path.Combine("clients", "wincon")); + BuildProject("BLE.Client", "BLE.Client.Maui", Path.Combine("clients", "maui")); }); Task("Clean").Does (() => From 0cb1c114839cc1e88c4b3adce9a3c3a670822f3b Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 3 Feb 2024 22:35:18 +0100 Subject: [PATCH 137/193] BLE.Client.Maui: add runtime IDs for Mac and Windows * in order to fix CI errors like: Assets file doesn't have a target for 'net8.0-maccatalyst/maccatalyst-arm64'. Ensure that restore has run and that you have included 'net8.0-maccatalyst' in the TargetFrameworks for your project. You may also need to include 'maccatalyst-arm64' in your project's RuntimeIdentifiers. --- .../BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index 529aabbc..fd9ea374 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -36,10 +36,15 @@ 4 - false - Mac Developer - 3rd Party Mac Developer Installer + maccatalyst-arm64 + false + Mac Developer + 3rd Party Mac Developer Installer + + + win-x64 + From a9dec6eefe4e1c38b864f449e3500a19b5ef48a6 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 4 Feb 2024 17:33:30 +0100 Subject: [PATCH 138/193] BLE.Client.Maui: set PublishReadyToRun to false --- Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index fd9ea374..85147185 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -9,6 +9,7 @@ true true enable + false BLE.Client.Maui From 1cfb43dfe3ffd0860042453309e498ec4e04b793 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 4 Feb 2024 18:25:34 +0100 Subject: [PATCH 139/193] BLE.Client.Maui: set the Platform for Windows * as suggested here: https://stackoverflow.com/questions/72980822/how-to-fix-a-packaged-winui3-app-build-what-makes-trouble-with-processorarchite --- Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index 85147185..5747da41 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -43,6 +43,7 @@ 3rd Party Mac Developer Installer + x64 win-x64 From d4e54b5d60b2b9a3ad3a9b926d403e777c741dbb Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 4 Feb 2024 19:01:21 +0100 Subject: [PATCH 140/193] BLE.Client.Maui: add x64 and x86 configs for Windows * as suggested here: https://learn.microsoft.com/en-us/answers/questions/859591/the-runtimeidentifier-platform-win10-x64-and-the-p --- Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index 5747da41..1b4790da 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -42,9 +42,13 @@ Mac Developer 3rd Party Mac Developer Installer - + x64 - win-x64 + win10-x64 + + + x86 + win10-x86 From bb60c72234966ce5485a25f4b8612ce92ba9921c Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Mon, 5 Feb 2024 17:54:20 +0100 Subject: [PATCH 141/193] - Fixed GetSystemConnectedOrPairedDevices in Windows - Improved WinConsole Demo Client --- .../BleAddressSelector.cs | 63 +++++++++++++++++++ .../BLE.Client/BLE.Client.WinConsole/Demo.cs | 2 +- .../BLE.Client.WinConsole/PluginDemos.cs | 62 +++++++++++++----- .../BLE.Client.WinConsole/Program.cs | 42 +++++++++---- .../BLE.Client.WinConsole/WindowsDemos.cs | 10 +-- Source/Plugin.BLE/Windows/Adapter.cs | 2 +- 6 files changed, 145 insertions(+), 36 deletions(-) create mode 100644 Source/BLE.Client/BLE.Client.WinConsole/BleAddressSelector.cs diff --git a/Source/BLE.Client/BLE.Client.WinConsole/BleAddressSelector.cs b/Source/BLE.Client/BLE.Client.WinConsole/BleAddressSelector.cs new file mode 100644 index 00000000..8369099d --- /dev/null +++ b/Source/BLE.Client/BLE.Client.WinConsole/BleAddressSelector.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLE.Client.WinConsole +{ + public static class BleAddressSelector + { + static string bleaddressTxtPath = "bleaddress.txt"; + static string? bleaddress = null; + + public static bool DoesBleAddressExists() + { + if (File.Exists(bleaddressTxtPath)) + { + bleaddress = File.ReadAllText(bleaddressTxtPath); + return true; + } + return false; + } + + public static string GetBleAddress() + { + if (bleaddress is null) + { + if (File.Exists(bleaddressTxtPath)) + { + bleaddress = File.ReadAllText(bleaddressTxtPath); + } + else + { + NewBleAddress(); + } + } + if (bleaddress is null) + { + throw new Exception("BleAddressSelector says bleaddress is null"); + } + return bleaddress; + } + + public static void SetBleAddress(string? bleaddressIn) + { + if (bleaddressIn is null || bleaddressIn.Length != 12) + { + Console.WriteLine("Wrong BLE Address entered"); + throw new Exception("Wrong BLE Address entered"); + } + bleaddress = bleaddressIn.ToUpperInvariant(); + File.WriteAllText(bleaddressTxtPath, bleaddress); + } + + public static Task NewBleAddress() + { + Console.Write("Enter BLE Address (12 hex chars): "); + SetBleAddress(Console.ReadLine()); + return Task.CompletedTask; + } + } +} diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Demo.cs b/Source/BLE.Client/BLE.Client.WinConsole/Demo.cs index 84d043f1..16dc0353 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Demo.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Demo.cs @@ -7,6 +7,6 @@ public record Demo ( string Description, - Func Method + Func Method ); diff --git a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index b41aab66..78d4769c 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -51,15 +51,16 @@ private void Write(string format, params object[] args) public IDevice ConnectToKnown(Guid id) { - IDevice dev = Adapter.ConnectToKnownDeviceAsync(id).Result; + IDevice dev = Adapter.ConnectToKnownDeviceAsync(id).Result; return dev; } - public async Task Connect_Disconnect(string bleaddress) + public async Task Connect_Disconnect() { + string bleaddress = BleAddressSelector.GetBleAddress(); var id = bleaddress.ToBleDeviceGuid(); var connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.ThroughputOptimized); - IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters); Write("Waiting 5 secs"); await Task.Delay(5000); Write("Disconnecting"); @@ -68,8 +69,9 @@ public async Task Connect_Disconnect(string bleaddress) Write("Test_Connect_Disconnect done"); } - public async Task Connect_Change_Parameters_Disconnect(string bleaddress) + public async Task Connect_Change_Parameters_Disconnect() { + string bleaddress = BleAddressSelector.GetBleAddress(); var id = bleaddress.ToBleDeviceGuid(); var connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.Balanced); IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters); @@ -89,8 +91,9 @@ public async Task Connect_Change_Parameters_Disconnect(string bleaddress) Write("Test_Connect_Disconnect done"); } - public async Task Pair_Connect_Disconnect(string bleaddress) + public async Task Pair_Connect_Disconnect() { + string bleaddress = BleAddressSelector.GetBleAddress(); var id = bleaddress.ToBleDeviceGuid(); ulong bleAddressulong = id.ToBleAddress(); DeviceInformation? deviceInformation = null; @@ -139,14 +142,15 @@ public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ return; } Write("Bluetooth is on"); - Write("Scanning now for " + time_ms + " ms..."); + Write("Scanning now for " + time_ms + " ms..."); var cancellationTokenSource = new CancellationTokenSource(time_ms); discoveredDevices.Clear(); + int index = 1; Adapter.DeviceDiscovered += (s, a) => { var dev = a.Device; - Write("DeviceDiscovered: {0} with Name = {1}", dev.Id.ToHexBleAddress(), dev.Name); + Write($"{index++}: DeviceDiscovered: {0} with Name = {1}", dev.Id.ToHexBleAddress(), dev.Name); discoveredDevices.Add(a.Device); }; Adapter.ScanMode = scanMode; @@ -154,6 +158,26 @@ public async Task DoTheScanning(ScanMode scanMode = ScanMode.LowPower, int time_ scanningDone = true; } + internal async Task DiscoverAndSelect() + { + await DoTheScanning(); + int index = 1; + await Task.Delay(200); + Console.WriteLine(); + foreach (var dev in discoveredDevices) + { + Console.WriteLine($"{index++}: {dev.Id.ToHexBleAddress()} with Name = {dev.Name}"); + } + Console.WriteLine(); + Console.Write($"Select BLE address index with value {1} to {discoveredDevices.Count}: "); + if (int.TryParse(Console.ReadLine(), out int selectedIndex)) + { + IDevice selecteddev = discoveredDevices[selectedIndex - 1]; + Console.WriteLine($"Selected {selectedIndex}: {selecteddev.Id.ToHexBleAddress()} with Name = {selecteddev.Name}"); + BleAddressSelector.SetBleAddress(selecteddev.Id.ToHexBleAddress()); + } + } + private void WriteAdvertisementRecords(IDevice device) { if (device.AdvertisementRecords is null) @@ -190,34 +214,36 @@ private void WriteAdvertisementRecords(IDevice device) return null; } Thread.Sleep(10); - foreach(var device in discoveredDevices) + foreach (var device in discoveredDevices) { if (device.Name.Contains(name)) { - await Adapter.ConnectToDeviceAsync(device); + await Adapter.ConnectToDeviceAsync(device); return device; } } return null; } - public void ShowGetSystemConnectedOrPairedDevices() + public Task RunGetSystemConnectedOrPairedDevices() { - IReadOnlyList devs = Adapter.GetSystemConnectedOrPairedDevices(); - Write("GetSystemConnectedOrPairedDevices found {0} devices.", devs.Count); - foreach(var dev in devs) + IReadOnlyList devs = Adapter.GetSystemConnectedOrPairedDevices(); + Task.Delay(200); + Write($"GetSystemConnectedOrPairedDevices found {devs.Count} devices:"); + foreach (var dev in devs) { Write("{0}: {1}", dev.Id.ToHexBleAddress(), dev.Name); } + return Task.CompletedTask; } /// /// This demonstrates a bug where the known services is not cleared at disconnect (2023-11-03) - /// - /// 12 hex char ble address - public async Task ShowNumberOfServices(string bleaddress) + /// + public async Task ShowNumberOfServices() { - Write("Connecting to device with address = {0}", bleaddress); + string bleaddress = BleAddressSelector.GetBleAddress(); + Write("Connecting to device with address = {0}", bleaddress); IDevice dev = await Adapter.ConnectToKnownDeviceAsync(bleaddress.ToBleDeviceGuid()) ?? throw new Exception("null"); string name = dev.Name; Write("Connected to {0} {1} {2}", name, dev.Id.ToHexBleAddress(), dev.State); @@ -243,5 +269,7 @@ internal Task Disconnect(IDevice dev) { return Adapter.DisconnectDeviceAsync(dev); } + + } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index efbb057d..1fe8f9d2 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -3,30 +3,45 @@ using Plugin.BLE.Abstractions.Contracts; using System; using System.Collections.Generic; +using System.Threading.Tasks; using Windows.Media.Capture; Console.WriteLine("Hello, BLE World!"); using (var ct = new ConsoleTracer()) { - const string bleaddress = "8C4B14C8602A"; + Plugin.BLE.Abstractions.Trace.TraceImplementation = ct.GetPrefixedTrace("Plugin.BLE"); var ppemos = new PluginDemos(ct.GetPrefixedTrace(" DEMO")); var wdemos = new WindowsDemos(ct.GetPrefixedTrace(" DEMO")); var demoDict = new Dictionary { - {ConsoleKey.D1, new Demo("Plugin: Connect -> Disconnect", ppemos.Connect_Disconnect) }, - {ConsoleKey.D2, new Demo("Plugin: Pair -> Connect -> Disconnect", ppemos.Pair_Connect_Disconnect) }, - {ConsoleKey.D3, new Demo("Plugin: Connect -> Change Parameters -> Disconnect", ppemos.Connect_Change_Parameters_Disconnect) }, - {ConsoleKey.D8, new Demo("Windows: Connect -> Disconnect", wdemos.Connect_Disconnect) }, - {ConsoleKey.D9, new Demo("Windows: Unpair all BLE devices", wdemos.UnPairAllBleDevices) }, + + {ConsoleKey.D1, new Demo("Discover and set the BleAddress", ppemos.DiscoverAndSelect) }, + {ConsoleKey.D2, new Demo("Set the BleAddress", BleAddressSelector.NewBleAddress) }, + {ConsoleKey.D3, new Demo("Connect -> Disconnect", ppemos.Connect_Disconnect) }, + {ConsoleKey.D4, new Demo("Pair -> Connect -> Disconnect", ppemos.Pair_Connect_Disconnect) }, + {ConsoleKey.D5, new Demo("Connect -> Change Parameters -> Disconnect", ppemos.Connect_Change_Parameters_Disconnect) }, + {ConsoleKey.D6, new Demo("Run GetSystemConnectedOrPairedDevices", ppemos.RunGetSystemConnectedOrPairedDevices) }, + {ConsoleKey.A, new Demo("Pure Windows: Connect -> Disconnect", wdemos.Connect_Disconnect) }, + {ConsoleKey.S, new Demo("Pure Windows: Unpair all BLE devices", wdemos.UnPairAllBleDevices) }, }; - Console.WriteLine("Using BLE Address: " + bleaddress); - Console.WriteLine(); - Console.WriteLine("List of tests to run for key:"); - Console.WriteLine(ConsoleKey.Escape + " -> Quit!"); while (true) { + + Console.WriteLine(); + if (BleAddressSelector.DoesBleAddressExists()) + { + Console.WriteLine($"Using BLE Address: {BleAddressSelector.GetBleAddress()}"); + } + else + { + Console.WriteLine("No Ble address has been set - use key '1' or '2' to set the BLE address)"); + } + Console.WriteLine("List of tests to run for key:"); + Console.WriteLine(); + Console.WriteLine(ConsoleKey.Escape + ": Quit!"); + foreach (var demo in demoDict) { Console.WriteLine(demo.Key + ": " + demo.Value.Description); @@ -40,17 +55,18 @@ if (demoDict.TryGetValue(key.Key, out Demo? chosendemo)) { Console.WriteLine(); - Console.WriteLine("Running: " + chosendemo.Description); + Console.WriteLine($"Running: {chosendemo.Description}"); if (chosendemo is null) { throw new Exception("No such demo!"); } - await chosendemo.Method(bleaddress); + await chosendemo.Method(); } else { - Console.WriteLine(key.Key + " -> No such test. Remember " + ConsoleKey.Escape + " -> Quit!"); + Console.WriteLine($"{key} -> No such test. Remember {ConsoleKey.Escape} -> Quit!"); } + await Task.Delay(200); Console.WriteLine("---------------------------------------------"); } } diff --git a/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs index 6ec1d2ce..4b3ab973 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/WindowsDemos.cs @@ -36,10 +36,11 @@ private void Write(string format, params object[] args) writer?.Invoke(format, args); } - public async Task Connect_Disconnect(string blehexaddress) + public async Task Connect_Disconnect() { - ulong bleaddress = blehexaddress.ToBleDeviceGuid().ToBleAddress(); - WBluetooth.BluetoothLEDevice dev = await WBluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(bleaddress); + string bleaddress = BleAddressSelector.GetBleAddress(); + ulong bleaddressUl = bleaddress.ToBleDeviceGuid().ToBleAddress(); + WBluetooth.BluetoothLEDevice dev = await WBluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(bleaddressUl); dev.RequestPreferredConnectionParameters(BluetoothLEPreferredConnectionParameters.ThroughputOptimized); dev.ConnectionStatusChanged += Dev_ConnectionStatusChanged; var devId = BluetoothDeviceId.FromId(dev.DeviceId); @@ -78,8 +79,9 @@ public async Task Connect_Disconnect(string blehexaddress) Write("Disconnected in {0} ms", stopwatch.ElapsedMilliseconds); } - public async Task UnPairAllBleDevices(string bleaddress = "") + public async Task UnPairAllBleDevices() { + var bleaddress = BleAddressSelector.GetBleAddress(); string aqsFilter = BluetoothLEDevice.GetDeviceSelector(); var collection = await DeviceInformation.FindAllAsync(aqsFilter); foreach (DeviceInformation di in collection) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 1e19cd7e..7c9c9a46 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -178,7 +178,7 @@ public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceG public override IReadOnlyList GetSystemConnectedOrPairedDevices(Guid[] services = null) { - string pairedSelector = BluetoothDevice.GetDeviceSelectorFromPairingState(true); + string pairedSelector = BluetoothLEDevice.GetDeviceSelectorFromPairingState(true); DeviceInformationCollection pairedDevices = DeviceInformation.FindAllAsync(pairedSelector).GetAwaiter().GetResult(); List devlist = ConnectedDevices.ToList(); List ids = ConnectedDevices.Select(d => d.Id).ToList(); From 722b300a6949fea9e0fd10bf6462f77de098e334 Mon Sep 17 00:00:00 2001 From: James Hughes Date: Thu, 1 Feb 2024 21:33:01 +0000 Subject: [PATCH 142/193] Added services for citysports treadmill --- Source/Plugin.BLE/Shared/KnownCharacteristics.cs | 2 ++ Source/Plugin.BLE/Shared/KnownServices.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/Source/Plugin.BLE/Shared/KnownCharacteristics.cs b/Source/Plugin.BLE/Shared/KnownCharacteristics.cs index 86c38c9f..b760f198 100644 --- a/Source/Plugin.BLE/Shared/KnownCharacteristics.cs +++ b/Source/Plugin.BLE/Shared/KnownCharacteristics.cs @@ -109,6 +109,7 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("Time Update State", Guid.ParseExact("00002a17-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Time with DST", Guid.ParseExact("00002a11-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Time Zone", Guid.ParseExact("00002a0e-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Treadmill Control", Guid.ParseExact("00002ad9-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Tx Power Level", Guid.ParseExact("00002a07-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Unread Alert Status", Guid.ParseExact("00002a45-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Aerobic Heart Rate Lower Limit", Guid.ParseExact("00002a7e-0000-1000-8000-00805f9b34fb", "d")), @@ -174,6 +175,7 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("Weight Measurement", Guid.ParseExact("00002a9d-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Weight Scale Feature", Guid.ParseExact("00002a9e-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Wind Chill", Guid.ParseExact("00002a79-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Workout Status", Guid.ParseExact("00002acd-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Battery Level State", Guid.ParseExact("00002a1b-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Battery Power State", Guid.ParseExact("00002a1a-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Latitude", Guid.ParseExact("00002a2d-0000-1000-8000-00805f9b34fb", "d")), diff --git a/Source/Plugin.BLE/Shared/KnownServices.cs b/Source/Plugin.BLE/Shared/KnownServices.cs index 56d9ee95..9693be62 100644 --- a/Source/Plugin.BLE/Shared/KnownServices.cs +++ b/Source/Plugin.BLE/Shared/KnownServices.cs @@ -63,6 +63,7 @@ public static KnownService Lookup(Guid id) new KnownService("TI SensorTag Connection Control", Guid.ParseExact("f000ccc0-0451-4000-b000-000000000000", "d")), new KnownService("TI SensorTag OvertheAir Download", Guid.ParseExact("f000ffc0-0451-4000-b000-000000000000", "d")), new KnownService("TXRX_SERV_UUID RedBearLabs Biscuit Service", Guid.ParseExact("713d0000-503e-4c75-ba94-3148f18d941e", "d")), + new KnownService("CITYSPORTS Treadmill", Guid.ParseExact("00001826-0000-1000-8000-00805f9b34fb", "d")), }.AsReadOnly(); } } \ No newline at end of file From c63a29b7792a7769b803812bf460f73060b7838a Mon Sep 17 00:00:00 2001 From: James H Date: Sat, 3 Feb 2024 22:24:32 +0000 Subject: [PATCH 143/193] Adjusted name to represent assigned number --- Source/Plugin.BLE/Shared/KnownServices.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Plugin.BLE/Shared/KnownServices.cs b/Source/Plugin.BLE/Shared/KnownServices.cs index 9693be62..c6a04c44 100644 --- a/Source/Plugin.BLE/Shared/KnownServices.cs +++ b/Source/Plugin.BLE/Shared/KnownServices.cs @@ -63,7 +63,7 @@ public static KnownService Lookup(Guid id) new KnownService("TI SensorTag Connection Control", Guid.ParseExact("f000ccc0-0451-4000-b000-000000000000", "d")), new KnownService("TI SensorTag OvertheAir Download", Guid.ParseExact("f000ffc0-0451-4000-b000-000000000000", "d")), new KnownService("TXRX_SERV_UUID RedBearLabs Biscuit Service", Guid.ParseExact("713d0000-503e-4c75-ba94-3148f18d941e", "d")), - new KnownService("CITYSPORTS Treadmill", Guid.ParseExact("00001826-0000-1000-8000-00805f9b34fb", "d")), + new KnownService("Fitness Machine Service", Guid.ParseExact("00001826-0000-1000-8000-00805f9b34fb", "d")), }.AsReadOnly(); } } \ No newline at end of file From 8037e799e6796283529c3fecd7581733733ca8ed Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 5 Feb 2024 22:06:24 +0100 Subject: [PATCH 144/193] fix names of new characteristics * according to Assigned Numbers document: https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf * and enforce alphabetical ordering --- Source/Plugin.BLE/Shared/KnownCharacteristics.cs | 4 ++-- Source/Plugin.BLE/Shared/KnownServices.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Plugin.BLE/Shared/KnownCharacteristics.cs b/Source/Plugin.BLE/Shared/KnownCharacteristics.cs index b760f198..3fd4ae28 100644 --- a/Source/Plugin.BLE/Shared/KnownCharacteristics.cs +++ b/Source/Plugin.BLE/Shared/KnownCharacteristics.cs @@ -57,6 +57,7 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("DST Offset", Guid.ParseExact("00002a0d-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Exact Time 256", Guid.ParseExact("00002a0c-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Firmware Revision String", Guid.ParseExact("00002a26-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Fitness Machine Control Point", Guid.ParseExact("00002ad9-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Glucose Feature", Guid.ParseExact("00002a51-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Glucose Measurement", Guid.ParseExact("00002a18-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Glucose Measure Context", Guid.ParseExact("00002a34-0000-1000-8000-00805f9b34fb", "d")), @@ -109,7 +110,7 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("Time Update State", Guid.ParseExact("00002a17-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Time with DST", Guid.ParseExact("00002a11-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Time Zone", Guid.ParseExact("00002a0e-0000-1000-8000-00805f9b34fb", "d")), - new KnownCharacteristic("Treadmill Control", Guid.ParseExact("00002ad9-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Treadmill Data", Guid.ParseExact("00002acd-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Tx Power Level", Guid.ParseExact("00002a07-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Unread Alert Status", Guid.ParseExact("00002a45-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Aerobic Heart Rate Lower Limit", Guid.ParseExact("00002a7e-0000-1000-8000-00805f9b34fb", "d")), @@ -175,7 +176,6 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("Weight Measurement", Guid.ParseExact("00002a9d-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Weight Scale Feature", Guid.ParseExact("00002a9e-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Wind Chill", Guid.ParseExact("00002a79-0000-1000-8000-00805f9b34fb", "d")), - new KnownCharacteristic("Workout Status", Guid.ParseExact("00002acd-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Battery Level State", Guid.ParseExact("00002a1b-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Battery Power State", Guid.ParseExact("00002a1a-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Latitude", Guid.ParseExact("00002a2d-0000-1000-8000-00805f9b34fb", "d")), diff --git a/Source/Plugin.BLE/Shared/KnownServices.cs b/Source/Plugin.BLE/Shared/KnownServices.cs index c6a04c44..543f4784 100644 --- a/Source/Plugin.BLE/Shared/KnownServices.cs +++ b/Source/Plugin.BLE/Shared/KnownServices.cs @@ -37,6 +37,7 @@ public static KnownService Lookup(Guid id) new KnownService("Cycling Power", Guid.ParseExact("00001818-0000-1000-8000-00805f9b34fb", "d")), new KnownService("Cycling Speed and Cadence", Guid.ParseExact("00001816-0000-1000-8000-00805f9b34fb", "d")), new KnownService("Device Information", Guid.ParseExact("0000180a-0000-1000-8000-00805f9b34fb", "d")), + new KnownService("Fitness Machine Service", Guid.ParseExact("00001826-0000-1000-8000-00805f9b34fb", "d")), new KnownService("Generic Access", Guid.ParseExact("00001800-0000-1000-8000-00805f9b34fb", "d")), new KnownService("Generic Attribute", Guid.ParseExact("00001801-0000-1000-8000-00805f9b34fb", "d")), new KnownService("Glucose", Guid.ParseExact("00001808-0000-1000-8000-00805f9b34fb", "d")), @@ -63,7 +64,6 @@ public static KnownService Lookup(Guid id) new KnownService("TI SensorTag Connection Control", Guid.ParseExact("f000ccc0-0451-4000-b000-000000000000", "d")), new KnownService("TI SensorTag OvertheAir Download", Guid.ParseExact("f000ffc0-0451-4000-b000-000000000000", "d")), new KnownService("TXRX_SERV_UUID RedBearLabs Biscuit Service", Guid.ParseExact("713d0000-503e-4c75-ba94-3148f18d941e", "d")), - new KnownService("Fitness Machine Service", Guid.ParseExact("00001826-0000-1000-8000-00805f9b34fb", "d")), }.AsReadOnly(); } } \ No newline at end of file From d60553b586cf09a3d136556b8e9c6ae069c62395 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 5 Feb 2024 22:29:54 +0100 Subject: [PATCH 145/193] add more missing characteristics from #804 --- Source/Plugin.BLE/Shared/KnownCharacteristics.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Plugin.BLE/Shared/KnownCharacteristics.cs b/Source/Plugin.BLE/Shared/KnownCharacteristics.cs index 3fd4ae28..5baf48ec 100644 --- a/Source/Plugin.BLE/Shared/KnownCharacteristics.cs +++ b/Source/Plugin.BLE/Shared/KnownCharacteristics.cs @@ -58,6 +58,8 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("Exact Time 256", Guid.ParseExact("00002a0c-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Firmware Revision String", Guid.ParseExact("00002a26-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Fitness Machine Control Point", Guid.ParseExact("00002ad9-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Fitness Machine Feature", Guid.ParseExact("00002acc-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Fitness Machine Status", Guid.ParseExact("00002ada-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Glucose Feature", Guid.ParseExact("00002a51-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Glucose Measurement", Guid.ParseExact("00002a18-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Glucose Measure Context", Guid.ParseExact("00002a34-0000-1000-8000-00805f9b34fb", "d")), @@ -100,6 +102,7 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("Service Changed", Guid.ParseExact("00002a05-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Software Revision String", Guid.ParseExact("00002a28-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Supported New Alert Category", Guid.ParseExact("00002a47-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Supported Speed Range", Guid.ParseExact("00002ad4-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Supported Unread Alert Category", Guid.ParseExact("00002a48-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("System ID", Guid.ParseExact("00002a23-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Temperature Measurement", Guid.ParseExact("00002a1c-0000-1000-8000-00805f9b34fb", "d")), @@ -110,6 +113,7 @@ public static KnownCharacteristic Lookup(Guid id) new KnownCharacteristic("Time Update State", Guid.ParseExact("00002a17-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Time with DST", Guid.ParseExact("00002a11-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Time Zone", Guid.ParseExact("00002a0e-0000-1000-8000-00805f9b34fb", "d")), + new KnownCharacteristic("Training Status", Guid.ParseExact("00002ad3-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Treadmill Data", Guid.ParseExact("00002acd-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Tx Power Level", Guid.ParseExact("00002a07-0000-1000-8000-00805f9b34fb", "d")), new KnownCharacteristic("Unread Alert Status", Guid.ParseExact("00002a45-0000-1000-8000-00805f9b34fb", "d")), From ca2511e8c2c78cc440fe6d533e7bd480157636a8 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 5 Feb 2024 23:15:57 +0100 Subject: [PATCH 146/193] KnownServices: use GuidExtension.UuidFromPartial and improve formatting * for better readability --- Source/Plugin.BLE/Shared/KnownServices.cs | 67 ++++++++++++----------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/Source/Plugin.BLE/Shared/KnownServices.cs b/Source/Plugin.BLE/Shared/KnownServices.cs index 543f4784..5d38e964 100644 --- a/Source/Plugin.BLE/Shared/KnownServices.cs +++ b/Source/Plugin.BLE/Shared/KnownServices.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Plugin.BLE.Abstractions.Extensions; namespace Plugin.BLE.Abstractions { @@ -30,39 +31,41 @@ public static KnownService Lookup(Guid id) /// public static IReadOnlyList Services { get; } = new List { - new KnownService("Alert Notification Service", Guid.ParseExact("00001811-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Battery Service", Guid.ParseExact("0000180f-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Blood Pressure", Guid.ParseExact("00001810-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Current Time Service", Guid.ParseExact("00001805-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Cycling Power", Guid.ParseExact("00001818-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Cycling Speed and Cadence", Guid.ParseExact("00001816-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Device Information", Guid.ParseExact("0000180a-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Fitness Machine Service", Guid.ParseExact("00001826-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Generic Access", Guid.ParseExact("00001800-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Generic Attribute", Guid.ParseExact("00001801-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Glucose", Guid.ParseExact("00001808-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Health Thermometer", Guid.ParseExact("00001809-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Heart Rate", Guid.ParseExact("0000180d-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Human Interface Device", Guid.ParseExact("00001812-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Immediate Alert", Guid.ParseExact("00001802-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Link Loss", Guid.ParseExact("00001803-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Location and Navigation", Guid.ParseExact("00001819-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Next DST Change Service", Guid.ParseExact("00001807-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Phone Alert Status Service", Guid.ParseExact("0000180e-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Reference Time Update Service", Guid.ParseExact("00001806-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Running Speed and Cadence", Guid.ParseExact("00001814-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("Scan Parameters", Guid.ParseExact("00001813-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("TX Power", Guid.ParseExact("00001804-0000-1000-8000-00805f9b34fb", "d")), - new KnownService("TI SensorTag Smart Keys", Guid.ParseExact("0000ffe0-0000-1000-8000-00805f9b34fb", "d")), + new KnownService("Alert Notification Service", GuidExtension.UuidFromPartial(0x1811)), + new KnownService("Battery Service", GuidExtension.UuidFromPartial(0x180f)), + new KnownService("Blood Pressure", GuidExtension.UuidFromPartial(0x1810)), + new KnownService("Current Time Service", GuidExtension.UuidFromPartial(0x1805)), + new KnownService("Cycling Power", GuidExtension.UuidFromPartial(0x1818)), + new KnownService("Cycling Speed and Cadence", GuidExtension.UuidFromPartial(0x1816)), + new KnownService("Device Information", GuidExtension.UuidFromPartial(0x180a)), + new KnownService("Fitness Machine Service", GuidExtension.UuidFromPartial(0x1826)), + new KnownService("Generic Access", GuidExtension.UuidFromPartial(0x1800)), + new KnownService("Generic Attribute", GuidExtension.UuidFromPartial(0x1801)), + new KnownService("Glucose", GuidExtension.UuidFromPartial(0x1808)), + new KnownService("Health Thermometer", GuidExtension.UuidFromPartial(0x1809)), + new KnownService("Heart Rate", GuidExtension.UuidFromPartial(0x180d)), + new KnownService("Human Interface Device", GuidExtension.UuidFromPartial(0x1812)), + new KnownService("Immediate Alert", GuidExtension.UuidFromPartial(0x1802)), + new KnownService("Link Loss", GuidExtension.UuidFromPartial(0x1803)), + new KnownService("Location and Navigation", GuidExtension.UuidFromPartial(0x1819)), + new KnownService("Next DST Change Service", GuidExtension.UuidFromPartial(0x1807)), + new KnownService("Phone Alert Status Service", GuidExtension.UuidFromPartial(0x180e)), + new KnownService("Reference Time Update Service", GuidExtension.UuidFromPartial(0x1806)), + new KnownService("Running Speed and Cadence", GuidExtension.UuidFromPartial(0x1814)), + new KnownService("Scan Parameters", GuidExtension.UuidFromPartial(0x1813)), + new KnownService("TX Power", GuidExtension.UuidFromPartial(0x1804)), + + new KnownService("TI SensorTag Smart Keys", Guid.ParseExact("0000ffe0-0000-1000-8000-00805f9b34fb", "d")), new KnownService("TI SensorTag Infrared Thermometer", Guid.ParseExact("f000aa00-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag Accelerometer", Guid.ParseExact("f000aa10-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag Humidity", Guid.ParseExact("f000aa20-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag Magnometer", Guid.ParseExact("f000aa30-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag Barometer", Guid.ParseExact("f000aa40-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag Gyroscope", Guid.ParseExact("f000aa50-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag Test", Guid.ParseExact("f000aa60-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag Connection Control", Guid.ParseExact("f000ccc0-0451-4000-b000-000000000000", "d")), - new KnownService("TI SensorTag OvertheAir Download", Guid.ParseExact("f000ffc0-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag Accelerometer", Guid.ParseExact("f000aa10-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag Humidity", Guid.ParseExact("f000aa20-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag Magnometer", Guid.ParseExact("f000aa30-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag Barometer", Guid.ParseExact("f000aa40-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag Gyroscope", Guid.ParseExact("f000aa50-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag Test", Guid.ParseExact("f000aa60-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag Connection Control", Guid.ParseExact("f000ccc0-0451-4000-b000-000000000000", "d")), + new KnownService("TI SensorTag OvertheAir Download", Guid.ParseExact("f000ffc0-0451-4000-b000-000000000000", "d")), + new KnownService("TXRX_SERV_UUID RedBearLabs Biscuit Service", Guid.ParseExact("713d0000-503e-4c75-ba94-3148f18d941e", "d")), }.AsReadOnly(); } From ab570be1c15452b60ecab28251b87dcf370c9e6f Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 5 Feb 2024 23:29:23 +0100 Subject: [PATCH 147/193] KnownServices: remove the word "Service" from all service names * for consistency * most of them don't have it * since the names are used in the "KnownService" class, it should be clear that they refer to a service --- Source/Plugin.BLE/Shared/KnownServices.cs | 50 +++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Source/Plugin.BLE/Shared/KnownServices.cs b/Source/Plugin.BLE/Shared/KnownServices.cs index 5d38e964..724beb38 100644 --- a/Source/Plugin.BLE/Shared/KnownServices.cs +++ b/Source/Plugin.BLE/Shared/KnownServices.cs @@ -31,29 +31,29 @@ public static KnownService Lookup(Guid id) /// public static IReadOnlyList Services { get; } = new List { - new KnownService("Alert Notification Service", GuidExtension.UuidFromPartial(0x1811)), - new KnownService("Battery Service", GuidExtension.UuidFromPartial(0x180f)), - new KnownService("Blood Pressure", GuidExtension.UuidFromPartial(0x1810)), - new KnownService("Current Time Service", GuidExtension.UuidFromPartial(0x1805)), - new KnownService("Cycling Power", GuidExtension.UuidFromPartial(0x1818)), - new KnownService("Cycling Speed and Cadence", GuidExtension.UuidFromPartial(0x1816)), - new KnownService("Device Information", GuidExtension.UuidFromPartial(0x180a)), - new KnownService("Fitness Machine Service", GuidExtension.UuidFromPartial(0x1826)), - new KnownService("Generic Access", GuidExtension.UuidFromPartial(0x1800)), - new KnownService("Generic Attribute", GuidExtension.UuidFromPartial(0x1801)), - new KnownService("Glucose", GuidExtension.UuidFromPartial(0x1808)), - new KnownService("Health Thermometer", GuidExtension.UuidFromPartial(0x1809)), - new KnownService("Heart Rate", GuidExtension.UuidFromPartial(0x180d)), - new KnownService("Human Interface Device", GuidExtension.UuidFromPartial(0x1812)), - new KnownService("Immediate Alert", GuidExtension.UuidFromPartial(0x1802)), - new KnownService("Link Loss", GuidExtension.UuidFromPartial(0x1803)), - new KnownService("Location and Navigation", GuidExtension.UuidFromPartial(0x1819)), - new KnownService("Next DST Change Service", GuidExtension.UuidFromPartial(0x1807)), - new KnownService("Phone Alert Status Service", GuidExtension.UuidFromPartial(0x180e)), - new KnownService("Reference Time Update Service", GuidExtension.UuidFromPartial(0x1806)), - new KnownService("Running Speed and Cadence", GuidExtension.UuidFromPartial(0x1814)), - new KnownService("Scan Parameters", GuidExtension.UuidFromPartial(0x1813)), - new KnownService("TX Power", GuidExtension.UuidFromPartial(0x1804)), + new KnownService("Alert Notification", GuidExtension.UuidFromPartial(0x1811)), + new KnownService("Battery", GuidExtension.UuidFromPartial(0x180f)), + new KnownService("Blood Pressure", GuidExtension.UuidFromPartial(0x1810)), + new KnownService("Current Time", GuidExtension.UuidFromPartial(0x1805)), + new KnownService("Cycling Power", GuidExtension.UuidFromPartial(0x1818)), + new KnownService("Cycling Speed and Cadence", GuidExtension.UuidFromPartial(0x1816)), + new KnownService("Device Information", GuidExtension.UuidFromPartial(0x180a)), + new KnownService("Fitness Machine", GuidExtension.UuidFromPartial(0x1826)), + new KnownService("Generic Access", GuidExtension.UuidFromPartial(0x1800)), + new KnownService("Generic Attribute", GuidExtension.UuidFromPartial(0x1801)), + new KnownService("Glucose", GuidExtension.UuidFromPartial(0x1808)), + new KnownService("Health Thermometer", GuidExtension.UuidFromPartial(0x1809)), + new KnownService("Heart Rate", GuidExtension.UuidFromPartial(0x180d)), + new KnownService("Human Interface Device", GuidExtension.UuidFromPartial(0x1812)), + new KnownService("Immediate Alert", GuidExtension.UuidFromPartial(0x1802)), + new KnownService("Link Loss", GuidExtension.UuidFromPartial(0x1803)), + new KnownService("Location and Navigation", GuidExtension.UuidFromPartial(0x1819)), + new KnownService("Next DST Change", GuidExtension.UuidFromPartial(0x1807)), + new KnownService("Phone Alert Status", GuidExtension.UuidFromPartial(0x180e)), + new KnownService("Reference Time Update", GuidExtension.UuidFromPartial(0x1806)), + new KnownService("Running Speed and Cadence", GuidExtension.UuidFromPartial(0x1814)), + new KnownService("Scan Parameters", GuidExtension.UuidFromPartial(0x1813)), + new KnownService("TX Power", GuidExtension.UuidFromPartial(0x1804)), new KnownService("TI SensorTag Smart Keys", Guid.ParseExact("0000ffe0-0000-1000-8000-00805f9b34fb", "d")), new KnownService("TI SensorTag Infrared Thermometer", Guid.ParseExact("f000aa00-0451-4000-b000-000000000000", "d")), @@ -66,7 +66,7 @@ public static KnownService Lookup(Guid id) new KnownService("TI SensorTag Connection Control", Guid.ParseExact("f000ccc0-0451-4000-b000-000000000000", "d")), new KnownService("TI SensorTag OvertheAir Download", Guid.ParseExact("f000ffc0-0451-4000-b000-000000000000", "d")), - new KnownService("TXRX_SERV_UUID RedBearLabs Biscuit Service", Guid.ParseExact("713d0000-503e-4c75-ba94-3148f18d941e", "d")), + new KnownService("TXRX_SERV_UUID RedBearLabs Biscuit", Guid.ParseExact("713d0000-503e-4c75-ba94-3148f18d941e", "d")), }.AsReadOnly(); } -} \ No newline at end of file +} From 744d518187408be1240c0d5cfb743caa1c0211e4 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 5 Feb 2024 23:43:45 +0100 Subject: [PATCH 148/193] KnownServices: add more services from Assigned Numbers * all listed services up to 0x1826 are included now --- Source/Plugin.BLE/Shared/KnownServices.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Source/Plugin.BLE/Shared/KnownServices.cs b/Source/Plugin.BLE/Shared/KnownServices.cs index 724beb38..305443d5 100644 --- a/Source/Plugin.BLE/Shared/KnownServices.cs +++ b/Source/Plugin.BLE/Shared/KnownServices.cs @@ -32,28 +32,41 @@ public static KnownService Lookup(Guid id) public static IReadOnlyList Services { get; } = new List { new KnownService("Alert Notification", GuidExtension.UuidFromPartial(0x1811)), + new KnownService("Automation IO", GuidExtension.UuidFromPartial(0x1815)), new KnownService("Battery", GuidExtension.UuidFromPartial(0x180f)), new KnownService("Blood Pressure", GuidExtension.UuidFromPartial(0x1810)), + new KnownService("Body Composition", GuidExtension.UuidFromPartial(0x181b)), + new KnownService("Bond Management", GuidExtension.UuidFromPartial(0x181e)), + new KnownService("Continuous Glucose", GuidExtension.UuidFromPartial(0x181f)), new KnownService("Current Time", GuidExtension.UuidFromPartial(0x1805)), new KnownService("Cycling Power", GuidExtension.UuidFromPartial(0x1818)), new KnownService("Cycling Speed and Cadence", GuidExtension.UuidFromPartial(0x1816)), new KnownService("Device Information", GuidExtension.UuidFromPartial(0x180a)), + new KnownService("Environmental Sensing", GuidExtension.UuidFromPartial(0x181a)), new KnownService("Fitness Machine", GuidExtension.UuidFromPartial(0x1826)), new KnownService("Generic Access", GuidExtension.UuidFromPartial(0x1800)), new KnownService("Generic Attribute", GuidExtension.UuidFromPartial(0x1801)), new KnownService("Glucose", GuidExtension.UuidFromPartial(0x1808)), new KnownService("Health Thermometer", GuidExtension.UuidFromPartial(0x1809)), new KnownService("Heart Rate", GuidExtension.UuidFromPartial(0x180d)), + new KnownService("HTTP Proxy", GuidExtension.UuidFromPartial(0x1823)), new KnownService("Human Interface Device", GuidExtension.UuidFromPartial(0x1812)), new KnownService("Immediate Alert", GuidExtension.UuidFromPartial(0x1802)), + new KnownService("Indoor Positioning", GuidExtension.UuidFromPartial(0x1821)), + new KnownService("Internet Protocol Support", GuidExtension.UuidFromPartial(0x1820)), new KnownService("Link Loss", GuidExtension.UuidFromPartial(0x1803)), new KnownService("Location and Navigation", GuidExtension.UuidFromPartial(0x1819)), new KnownService("Next DST Change", GuidExtension.UuidFromPartial(0x1807)), + new KnownService("Object Transfer", GuidExtension.UuidFromPartial(0x1825)), new KnownService("Phone Alert Status", GuidExtension.UuidFromPartial(0x180e)), + new KnownService("Pulse Oximeter", GuidExtension.UuidFromPartial(0x1822)), new KnownService("Reference Time Update", GuidExtension.UuidFromPartial(0x1806)), new KnownService("Running Speed and Cadence", GuidExtension.UuidFromPartial(0x1814)), new KnownService("Scan Parameters", GuidExtension.UuidFromPartial(0x1813)), + new KnownService("Transport Discovery", GuidExtension.UuidFromPartial(0x1824)), new KnownService("TX Power", GuidExtension.UuidFromPartial(0x1804)), + new KnownService("User Data", GuidExtension.UuidFromPartial(0x181c)), + new KnownService("Weight Scale", GuidExtension.UuidFromPartial(0x181d)), new KnownService("TI SensorTag Smart Keys", Guid.ParseExact("0000ffe0-0000-1000-8000-00805f9b34fb", "d")), new KnownService("TI SensorTag Infrared Thermometer", Guid.ParseExact("f000aa00-0451-4000-b000-000000000000", "d")), From dd470ba24cc30c152216a3e513bc97bd76e28e98 Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Wed, 7 Feb 2024 17:22:16 +0100 Subject: [PATCH 149/193] Fixed ConnectionLost -> Connect --- .../BLE.Client.WinConsole/PluginDemos.cs | 124 +++++++++++++++++- .../BLE.Client.WinConsole/Program.cs | 3 + Source/Plugin.BLE/Windows/Adapter.cs | 7 +- 3 files changed, 127 insertions(+), 7 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index 78d4769c..e065b27b 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -31,17 +31,29 @@ public PluginDemos(Action? writer = null) Adapter = CrossBluetoothLE.Current.Adapter; Adapter.DeviceConnected += Adapter_DeviceConnected; Adapter.DeviceDisconnected += Adapter_DeviceDisconnected; + Adapter.DeviceConnectionLost += Adapter_DeviceConnectionLost; + Adapter.DeviceConnectionError += Adapter_DeviceConnectionError; this.writer = writer; } + private void Adapter_DeviceConnectionError(object? sender, Plugin.BLE.Abstractions.EventArgs.DeviceErrorEventArgs e) + { + Write($"Adapter_DeviceConnectionError {e.Device.Id.ToHexBleAddress()} with name: {e.Device.Name}"); + } + + private void Adapter_DeviceConnectionLost(object? sender, Plugin.BLE.Abstractions.EventArgs.DeviceErrorEventArgs e) + { + Write($"Adapter_DeviceConnectionLost {e.Device.Id.ToHexBleAddress()} with name: {e.Device.Name}"); + } + private void Adapter_DeviceDisconnected(object? sender, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs e) { - Write($"Adapter_DeviceDisconnected {e.Device.Id}"); + Write($"Adapter_DeviceDisconnected {e.Device.Id.ToHexBleAddress()} with name: {e.Device.Name}"); } private void Adapter_DeviceConnected(object? sender, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs e) { - Write($"Adapter_DeviceConnected {e.Device.Id}"); + Write($"Adapter_DeviceConnected {e.Device.Id.ToHexBleAddress()} with name: {e.Device.Name}"); } private void Write(string format, params object[] args) @@ -69,6 +81,114 @@ public async Task Connect_Disconnect() Write("Test_Connect_Disconnect done"); } + public async Task Connect_Read_Services_Disconnect_5X() + { + string bleaddress = BleAddressSelector.GetBleAddress(); + var id = bleaddress.ToBleDeviceGuid(); + var connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.Balanced); + + using (IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters)) + { + for (int i = 0; i < 5; i++) + { + await Task.Delay(100); + Write($"---------------- {i} ------------------"); + if (dev.State != DeviceState.Connected) + { + Write("Connecting"); + await Adapter.ConnectToDeviceAsync(dev); + } + Write("Reading services"); + + var services = await dev.GetServicesAsync(); + List charlist = new List(); + foreach (var service in services) + { + var characteristics = await service.GetCharacteristicsAsync(); + charlist.AddRange(characteristics); + } + + foreach (var service in services) + { + service.Dispose(); + } + charlist.Clear(); + Write("Waiting 3 secs"); + await Task.Delay(3000); + Write("Disconnecting"); + await Adapter.DisconnectDeviceAsync(dev); + Write("Test_Connect_Disconnect done"); + } + } + } + + public async Task Connect_Read_Services_Dispose_5X() + { + string bleaddress = BleAddressSelector.GetBleAddress(); + var id = bleaddress.ToBleDeviceGuid(); + var connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.Balanced); + for (int i = 0; i < 5; i++) + { + await Task.Delay(100); + Write($"---------------- {i} ------------------"); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters); + Write("Reading services"); + var services = await dev.GetServicesAsync(); + List charlist = new List(); + foreach (var service in services) + { + var characteristics = await service.GetCharacteristicsAsync(); + charlist.AddRange(characteristics); + } + + foreach (var service in services) + { + service.Dispose(); + } + charlist.Clear(); + Write("Waiting 3 secs"); + await Task.Delay(3000); + //await Adapter.DisconnectDeviceAsync(dev); + Write("Disposing"); + dev.Dispose(); + } + } + + public async Task Connect_ConnectionLost_Connect() + { + string bleaddress = BleAddressSelector.GetBleAddress(); + var id = bleaddress.ToBleDeviceGuid(); + var connectParameters = new ConnectParameters(connectionParameterSet: ConnectionParameterSet.Balanced); + ConsoleKey consoleKey = ConsoleKey.None; + using (IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id, connectParameters)) + { + while (consoleKey != ConsoleKey.Escape) + { + Write("Reading services"); + var services = await dev.GetServicesAsync(); + List charlist = new List(); + foreach (var service in services) + { + var characteristics = await service.GetCharacteristicsAsync(); + charlist.AddRange(characteristics); + } + await Task.Delay(1000); + Console.WriteLine(new string('-', 80)); + Console.WriteLine("Now powercycle the device... Hit any key when the device is booted up again (Escape to quit)"); + Console.WriteLine(new string('-', 80)); + consoleKey = Console.ReadKey().Key; + await Adapter.ConnectToDeviceAsync(dev, connectParameters); + Write("Waiting 3 secs"); + await Task.Delay(3000); + foreach (var service in services) + { + service.Dispose(); + } + charlist.Clear(); + } + } + } + public async Task Connect_Change_Parameters_Disconnect() { string bleaddress = BleAddressSelector.GetBleAddress(); diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index 1fe8f9d2..9bed9006 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -22,6 +22,9 @@ {ConsoleKey.D4, new Demo("Pair -> Connect -> Disconnect", ppemos.Pair_Connect_Disconnect) }, {ConsoleKey.D5, new Demo("Connect -> Change Parameters -> Disconnect", ppemos.Connect_Change_Parameters_Disconnect) }, {ConsoleKey.D6, new Demo("Run GetSystemConnectedOrPairedDevices", ppemos.RunGetSystemConnectedOrPairedDevices) }, + {ConsoleKey.D7, new Demo("5X: Connect -> Read services -> Disconnect", ppemos.Connect_Read_Services_Disconnect_5X) }, + {ConsoleKey.D8, new Demo("5X: Connect -> Read services -> Dispose", ppemos.Connect_Read_Services_Dispose_5X) }, + {ConsoleKey.D9, new Demo("Connect -> Loop: ConnectionLost -> Connect", ppemos.Connect_ConnectionLost_Connect) }, {ConsoleKey.A, new Demo("Pure Windows: Connect -> Disconnect", wdemos.Connect_Disconnect) }, {ConsoleKey.S, new Demo("Pure Windows: Unpair all BLE devices", wdemos.UnPairAllBleDevices) }, }; diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 7c9c9a46..4df77ed2 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -144,11 +144,8 @@ private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, obje } // fire the correct event (DeviceDisconnected or DeviceConnectionLost) - HandleDisconnectedDevice(isNormalDisconnect, disconnectedDevice); - if (isNormalDisconnect) - { - nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; - } + HandleDisconnectedDevice(isNormalDisconnect, disconnectedDevice); + nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; } } From f4866818bbd7183fe38fc87f79edca9a52ce3d9b Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Wed, 7 Feb 2024 22:52:31 +0100 Subject: [PATCH 150/193] Simplified adaptor to handle DeviceConnectionLost vs DeviceDisconnected --- Source/Plugin.BLE/Windows/Adapter.cs | 50 +++++++++++----------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index 4df77ed2..de1275bf 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -19,10 +19,10 @@ public class Adapter : AdapterBase private BluetoothLEAdvertisementWatcher _bleWatcher; /// - /// Registry used to store device instances for pending operations : disconnect + /// Registry used to store device instances for pending disconnect operations /// Helps to detect connection lost events. /// - private readonly IDictionary _deviceOperationRegistry = new ConcurrentDictionary(); + private readonly IDictionary disconnectingRegistry = new ConcurrentDictionary(); public Adapter() { @@ -77,14 +77,17 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect var nativeDevice = device.NativeDevice as BluetoothLEDevice; Trace.Message("ConnectToDeviceNativeAsync {0} Named: {1} Connected: {2}", device.Id.ToHexBleAddress(), device.Name, nativeDevice.ConnectionStatus); - ConnectedDeviceRegistry[device.Id.ToString()] = device; - - nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; - nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; bool success = await dev.ConnectInternal(connectParameters, cancellationToken); - - if (!success) + if (success) + { + if (!ConnectedDeviceRegistry.ContainsKey(device.Id.ToString())) + { + ConnectedDeviceRegistry[device.Id.ToString()] = device; + nativeDevice.ConnectionStatusChanged += Device_ConnectionStatusChanged; + } + } + else { // use DisconnectDeviceNative to clean up resources otherwise windows won't disconnect the device // after a subsequent successful connection (#528, #536, #423) @@ -94,21 +97,9 @@ protected override async Task ConnectToDeviceNativeAsync(IDevice device, Connect HandleConnectionFail(device, "Failed connecting to device."); // this is normally done in Device_ConnectionStatusChanged but since nothing actually connected - // or disconnect, ConnectionStatusChanged will not fire. + // or disconnect, ConnectionStatusChanged will not fire. ConnectedDeviceRegistry.TryRemove(device.Id.ToString(), out _); } - else if (cancellationToken.IsCancellationRequested) - { - // connection attempt succeeded but was cancelled before it could be completed - // see TODO above. - - // cleanup resources - DisconnectDeviceNative(device); - } - else - { - _deviceOperationRegistry[device.Id.ToString()] = device; - } } private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, object args) @@ -135,17 +126,17 @@ private void Device_ConnectionStatusChanged(BluetoothLEDevice nativeDevice, obje if (nativeDevice.ConnectionStatus == BluetoothConnectionStatus.Disconnected && ConnectedDeviceRegistry.TryRemove(id, out var disconnectedDevice)) { - bool isNormalDisconnect = !_deviceOperationRegistry.Remove(disconnectedDevice.Id.ToString()); - if (!isNormalDisconnect) + bool disconnectRequested = disconnectingRegistry.Remove(id); + if (!disconnectRequested) { // device was powered off or went out of range. Call DisconnectDeviceNative to cleanup // resources otherwise windows will not disconnect on a subsequent connect-disconnect. DisconnectDeviceNative(disconnectedDevice); } - - // fire the correct event (DeviceDisconnected or DeviceConnectionLost) - HandleDisconnectedDevice(isNormalDisconnect, disconnectedDevice); + ConnectedDeviceRegistry.Remove(id, out _); nativeDevice.ConnectionStatusChanged -= Device_ConnectionStatusChanged; + // fire the correct event (DeviceDisconnected or DeviceConnectionLost) + HandleDisconnectedDevice(disconnectRequested, disconnectedDevice); } } @@ -153,11 +144,8 @@ protected override void DisconnectDeviceNative(IDevice device) { // Windows doesn't support disconnecting, so currently just dispose of the device Trace.Message($"DisconnectDeviceNative from device with ID: {device.Id.ToHexBleAddress()}"); - if (device.NativeDevice is BluetoothLEDevice nativeDevice) - { - _deviceOperationRegistry.Remove(device.Id.ToString()); - ((Device)device).DisconnectInternal(); - } + disconnectingRegistry[device.Id.ToString()] = device; + ((Device)device).DisconnectInternal(); } public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceGuid, ConnectParameters connectParameters = default, CancellationToken cancellationToken = default) From fa6cdb522b2ae689b1b615a906ef8d78c3eaab76 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 11 Feb 2024 13:34:00 +0100 Subject: [PATCH 151/193] GHA: update actions 'checkout' and 'setup-dotnet' * in order to fix a warning: Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: actions/checkout@v3, actions/setup-dotnet@v3. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/. --- .github/workflows/dotnet.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 333bb744..5dbb9efa 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -13,12 +13,12 @@ jobs: winBuild: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: nuget/setup-nuget@v1 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - name: Set up JDK 11 @@ -77,11 +77,11 @@ jobs: macBuild: runs-on: macos-13 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - name: Setup XCode @@ -103,11 +103,11 @@ jobs: linuxBuild: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - name: Install workloads From e8c8aa113c67e92117c37d650b50d8a2998fceb5 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 11 Feb 2024 13:42:13 +0100 Subject: [PATCH 152/193] BLE.Client.Maui: add PackageReference to Microsoft.Maui.Controls * in order to fix a GHA warning: warning MA002: Starting with .NET 8, setting true does not automatically include NuGet package references in your project. Update your project by including this item: . You can skip this warning by setting true in your project file. [/home/runner/work/dotnet-bluetooth-le/dotnet-bluetooth-le/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj::TargetFramework=net8.0-android34.0] --- Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index 1b4790da..835a92f9 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -70,6 +70,7 @@ + From 30616a7c05a71bd5d2602cb7678c73c1df22b335 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 11 Feb 2024 13:54:48 +0100 Subject: [PATCH 153/193] BLE.Client.Maui: remove uses-sdk clause (with SDK versions) from AndroidManifest.xml * in order to fix a GHA warning: warning XA1006: The TargetFrameworkVersion (Android API level 34) is higher than the targetSdkVersion (33). Please increase the `android:targetSdkVersion` in the `AndroidManifest.xml` so that the API levels match. * the SDK versions in the AndroidManifest are not needed at all, since they are defined in BLE.Client.Maui.csproj (via TargetFrameworks and SupportedOSPlatformVersion) --- .../BLE.Client.Maui/Platforms/Android/AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.Maui/Platforms/Android/AndroidManifest.xml b/Source/BLE.Client/BLE.Client.Maui/Platforms/Android/AndroidManifest.xml index eedc2bf6..60a968bb 100644 --- a/Source/BLE.Client/BLE.Client.Maui/Platforms/Android/AndroidManifest.xml +++ b/Source/BLE.Client/BLE.Client.Maui/Platforms/Android/AndroidManifest.xml @@ -15,5 +15,4 @@ - - \ No newline at end of file + From b821ee9008b81b61f53037fb7d5a115699d11ea2 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 11 Feb 2024 14:24:27 +0100 Subject: [PATCH 154/193] BLE.Client.Maui: remove IPlatformHelpers * since the sample is based on Maui 8, we can use the standard Bluetooth permissions --- Source/BLE.Client/BLE.Client.Maui/App.xaml.cs | 6 +-- .../BLE.Client.Maui/BLE.Client.Maui.csproj | 2 - .../Helpers/IPlatformHelpers.cs | 7 --- .../BLE.Client/BLE.Client.Maui/MauiProgram.cs | 21 +------- .../Platforms/Android/DroidPlatformHelpers.cs | 48 ------------------- .../MacCatalyst/MacCatalystPlatformHelpers.cs | 16 ------- .../Windows/WindowsPlatformHelpers.cs | 10 ---- .../Platforms/iOS/iOSPlatformHelpers.cs | 10 ---- .../ViewModels/BLEScannerViewModel.cs | 4 +- 9 files changed, 6 insertions(+), 118 deletions(-) delete mode 100644 Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs delete mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Android/DroidPlatformHelpers.cs delete mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/MacCatalyst/MacCatalystPlatformHelpers.cs delete mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs delete mode 100644 Source/BLE.Client/BLE.Client.Maui/Platforms/iOS/iOSPlatformHelpers.cs diff --git a/Source/BLE.Client/BLE.Client.Maui/App.xaml.cs b/Source/BLE.Client/BLE.Client.Maui/App.xaml.cs index 00fa7886..3b2a0319 100644 --- a/Source/BLE.Client/BLE.Client.Maui/App.xaml.cs +++ b/Source/BLE.Client/BLE.Client.Maui/App.xaml.cs @@ -1,5 +1,4 @@ -using BLE.Client.Helpers; -using BLE.Client.Maui.Services; +using BLE.Client.Maui.Services; namespace BLE.Client.Maui; @@ -7,14 +6,13 @@ public partial class App : Application { public static IServiceProvider Services; public static IAlertService AlertSvc; - public static IPlatformHelpers PlatformHelper; + public App(IServiceProvider provider) { InitializeComponent(); Services = provider; AlertSvc = Services.GetService(); - PlatformHelper = Services.GetService(); MainPage = new AppShell(); } } diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index 835a92f9..47562a14 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -80,7 +80,6 @@ - @@ -89,7 +88,6 @@ - diff --git a/Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs b/Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs deleted file mode 100644 index d1130c7e..00000000 --- a/Source/BLE.Client/BLE.Client.Maui/Helpers/IPlatformHelpers.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace BLE.Client.Helpers -{ - public interface IPlatformHelpers - { - Task CheckAndRequestBluetoothPermissions(); - } -} diff --git a/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs b/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs index d2286027..800e401e 100644 --- a/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs +++ b/Source/BLE.Client/BLE.Client.Maui/MauiProgram.cs @@ -1,5 +1,4 @@ -using BLE.Client.Helpers; -using BLE.Client.Maui.Services; +using BLE.Client.Maui.Services; namespace BLE.Client.Maui; public static class MauiProgram @@ -16,10 +15,6 @@ public static MauiApp CreateMauiApp() }); builder.Services.AddSingleton(); - AddPlatformSpecificItems(builder); - - - #if DEBUG //builder.Logging.AddDebug(); #endif @@ -32,18 +27,4 @@ public static MauiApp CreateMauiApp() public static bool IsMacCatalyst => DeviceInfo.Current.Platform == DevicePlatform.MacCatalyst; public static bool IsMacOS => DeviceInfo.Current.Platform == DevicePlatform.macOS; - - - private static void AddPlatformSpecificItems(MauiAppBuilder builder) - { -#if ANDROID - builder.Services.AddSingleton(); -#elif IOS - builder.Services.AddSingleton(); -#elif MACCATALYST - builder.Services.AddSingleton(); -#elif WINDOWS - builder.Services.AddSingleton(); -#endif - } } diff --git a/Source/BLE.Client/BLE.Client.Maui/Platforms/Android/DroidPlatformHelpers.cs b/Source/BLE.Client/BLE.Client.Maui/Platforms/Android/DroidPlatformHelpers.cs deleted file mode 100644 index 938fdaa4..00000000 --- a/Source/BLE.Client/BLE.Client.Maui/Platforms/Android/DroidPlatformHelpers.cs +++ /dev/null @@ -1,48 +0,0 @@ -using BLE.Client.Helpers; -using static Microsoft.Maui.ApplicationModel.Permissions; - -//[assembly: Dependency(typeof(BLE.Client.Droid.Helpers.PlatformHelpers))] -namespace BLE.Client.Helpers -{ - public class DroidPlatformHelpers : IPlatformHelpers - { - public async Task CheckAndRequestBluetoothPermissions() - { - PermissionStatus status; - if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.S) - { - status = await Permissions.CheckStatusAsync(); - - if (status == PermissionStatus.Granted) - return status; - - status = await Permissions.RequestAsync(); - } - else - { - status = await Permissions.CheckStatusAsync(); - - if (status == PermissionStatus.Granted) - return status; - - - if (Permissions.ShouldShowRationale()) - { - await Application.Current.MainPage.DisplayAlert("Permission required", "Location permission is required for bluetooth scanning. We do not store or use your location at all.", "OK"); - } - - status = await Permissions.RequestAsync(); - } - return status; - } - } - - public class BluetoothSPermission :BasePlatformPermission - { - public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)> - { - (Android.Manifest.Permission.BluetoothScan, true), - (Android.Manifest.Permission.BluetoothConnect, true) - }.ToArray(); - } -} diff --git a/Source/BLE.Client/BLE.Client.Maui/Platforms/MacCatalyst/MacCatalystPlatformHelpers.cs b/Source/BLE.Client/BLE.Client.Maui/Platforms/MacCatalyst/MacCatalystPlatformHelpers.cs deleted file mode 100644 index 024ac784..00000000 --- a/Source/BLE.Client/BLE.Client.Maui/Platforms/MacCatalyst/MacCatalystPlatformHelpers.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using BLE.Client.Helpers; - -namespace BLE.Client.Helpers -{ - public class MacCatalystPlatformHelpers : IPlatformHelpers - { - public Task CheckAndRequestBluetoothPermissions() - { - System.Diagnostics.Debug.WriteLine("Into CheckAndRequestBluetoothPermissions "); - - return Task.FromResult(PermissionStatus.Granted); - } - } -} - diff --git a/Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs b/Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs deleted file mode 100644 index 349fcda5..00000000 --- a/Source/BLE.Client/BLE.Client.Maui/Platforms/Windows/WindowsPlatformHelpers.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BLE.Client.Helpers -{ - public class WindowsPlatformHelpers : IPlatformHelpers - { - public Task CheckAndRequestBluetoothPermissions() - { - return Task.FromResult(PermissionStatus.Granted); - } - } -} diff --git a/Source/BLE.Client/BLE.Client.Maui/Platforms/iOS/iOSPlatformHelpers.cs b/Source/BLE.Client/BLE.Client.Maui/Platforms/iOS/iOSPlatformHelpers.cs deleted file mode 100644 index 2784b8f5..00000000 --- a/Source/BLE.Client/BLE.Client.Maui/Platforms/iOS/iOSPlatformHelpers.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BLE.Client.Helpers -{ - public class iOSPlatformHelpers : IPlatformHelpers - { - public Task CheckAndRequestBluetoothPermissions() - { - return Task.FromResult(PermissionStatus.Granted); - } - } -} diff --git a/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs b/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs index 5547f033..40f4c91d 100644 --- a/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs +++ b/Source/BLE.Client/BLE.Client.Maui/ViewModels/BLEScannerViewModel.cs @@ -364,7 +364,9 @@ private async void StartScanForDevices() private async Task HasCorrectPermissions() { DebugMessage("Into HasCorrectPermissions"); - var permissionResult = await App.PlatformHelper.CheckAndRequestBluetoothPermissions(); + var permissionResult = await Permissions.CheckStatusAsync(); + if (permissionResult != PermissionStatus.Granted) + permissionResult = await Permissions.RequestAsync(); DebugMessage($"Back from await App.PlatformHelper: '{permissionResult}'"); if (permissionResult != PermissionStatus.Granted) { From 2c73525758df1a8522b40573cbcd1e8ee20f074f Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sun, 11 Feb 2024 17:43:40 +0100 Subject: [PATCH 155/193] GHA: update several actions in the Windows build * in order to fix a warning: Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: nuget/setup-nuget@v1, actions/setup-java@v3, microsoft/setup-msbuild@v1.1, cake-build/cake-action@v1.4.1, actions/upload-artifact@v3. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/. --- .github/workflows/dotnet.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 5dbb9efa..610e106a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,13 +16,13 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: nuget/setup-nuget@v1 + - uses: nuget/setup-nuget@v2 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '11' @@ -33,34 +33,34 @@ jobs: & dotnet workload install maui --source https://aka.ms/dotnet6/nuget/index.json --source https://api.nuget.org/v3/index.json & dotnet workload install android ios maccatalyst tvos macos maui wasm-tools maui-maccatalyst --source https://aka.ms/dotnet6/nuget/index.json --source https://api.nuget.org/v3/index.json - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Clean - uses: cake-build/cake-action@v1.4.1 + uses: cake-build/cake-action@v2 with: script-path: .build/build.cake target: Clean - name: Restore - uses: cake-build/cake-action@v1.4.1 + uses: cake-build/cake-action@v2 with: script-path: .build/build.cake target: Restore - name: Build Libs - uses: cake-build/cake-action@v1.4.1 + uses: cake-build/cake-action@v2 with: script-path: .build/build.cake target: BuildLibs - name: Build Clients - uses: cake-build/cake-action@v1.4.1 + uses: cake-build/cake-action@v2 with: script-path: .build/build.cake target: BuildClients - name: Builds Tests - uses: cake-build/cake-action@v1.4.1 + uses: cake-build/cake-action@v2 with: script-path: .build/build.cake target: BuildTests - name: Run Tests - uses: cake-build/cake-action@v1.4.1 + uses: cake-build/cake-action@v2 with: script-path: .build/build.cake target: RunTests @@ -69,7 +69,7 @@ jobs: - name: Build MVVMCross.Plugins.BLE NuGet run: msbuild .\Source\MvvmCross.Plugins.BLE\MvvmCross.Plugins.BLE.csproj /p:Configuration=Release /t:restore,build,pack /p:PackageOutputPath=./nuget /p:Version=$(git describe) /p:ContinuousIntegrationBuild=true /p:DeterministicSourcePaths=false - name: Upload packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: nupkg path: ./Source/*/nuget/*Plugin.BLE*.nupkg From 841c4a2b7549c6f6aafd5f2b9371d62f63ca3205 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 12 Feb 2024 21:50:17 +0100 Subject: [PATCH 156/193] Release 3.1.0-beta.2 * update version in csproj files * amend changelog.md --- Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj | 2 +- Source/Plugin.BLE/Plugin.BLE.csproj | 2 +- doc/changelog.md | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj index 6d48f213..68900e7f 100644 --- a/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj +++ b/Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj @@ -5,7 +5,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041 MVVMCross.Plugins.BLE MVVMCross.Plugins.BLE - 3.1.0-beta.1 + 3.1.0-beta.2 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil MvvmCross.Plugin.BLE diff --git a/Source/Plugin.BLE/Plugin.BLE.csproj b/Source/Plugin.BLE/Plugin.BLE.csproj index 762f6fdd..4dd95112 100644 --- a/Source/Plugin.BLE/Plugin.BLE.csproj +++ b/Source/Plugin.BLE/Plugin.BLE.csproj @@ -5,7 +5,7 @@ $(TargetFrameworks);MonoAndroid13.0;Xamarin.iOS10;Xamarin.Mac20;uap10.0.19041;net7.0-windows10.0.19041;net8.0-windows10.0.19041;net8.0-windows10.0.22000.0 Plugin.BLE Plugin.BLE - 3.1.0-beta.1 + 3.1.0-beta.2 $(AssemblyName) ($(TargetFramework)) Adrian Seceleanu, Sven-Michael Stübe, Janus Weil Plugin.BLE diff --git a/doc/changelog.md b/doc/changelog.md index 0bd1dbbc..1c847d8f 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,13 @@ ## 3.1 +#### 3.1.0-beta.2 +- #784 Turn Bluetooth Radio On/Off +- #801 Connection parameters for Windows +- #805 Added services for Citysports treadmill (fixes #804) +- #807 Fix GetSystemConnectedOrPairedDevices in Windows +- #809 Fix ConnectionLost -> Connect for Windows + #### 3.1.0-beta.1 - #746 Windows.Devices.Bluetooth Only - #764 ReadAsync updates characteristic value on Windows From 450dbc5cf96c2b2101b55ecf7d57002e9bbcba5e Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 17 Feb 2024 14:20:00 +0100 Subject: [PATCH 157/193] README.md: spelling fixes --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 497aea84..31b7df1d 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ Add this line to the Package Manifest (.appxmanifest): We provide a sample Xamarin.Forms app, that is a basic bluetooth LE scanner. With this app, it's possible to -- check the ble status +- check the BLE status - discover devices - connect/disconnect - discover the services @@ -325,14 +325,14 @@ The BLE API implementation (especially on **Android**) has the following limitat } ``` -- **Avoid caching of Characteristic or Service instances between connection sessions**. This includes saving a reference to them in your class between connection sessions etc. After a device has been disconnected all Service & Characteristic instances become **invalid**. Allways **use GetServiceAsync and GetCharacteristicAsync to get a valid instance**. +- **Avoid caching of Characteristic or Service instances between connection sessions**. This includes saving a reference to them in your class between connection sessions etc. After a device has been disconnected all Service & Characteristic instances become **invalid**. Always **use GetServiceAsync and GetCharacteristicAsync to get a valid instance**. ### General BLE iOS, Android -- Scanning: Avoid performing ble device operations like Connect, Read, Write etc while scanning for devices. Scanning is battery-intensive. - - try to stop scanning before performing device operations (connect/read/write/etc) - - try to stop scanning as soon as you find the desired device - - never scan on a loop, and set a time limit on your scan +- Scanning: Avoid performing BLE device operations like Connect, Read, Write etc while scanning for devices. Scanning is battery-intensive. + - Try to stop scanning before performing device operations (connect/read/write/etc). + - Try to stop scanning as soon as you find the desired device. + - Never scan on a loop, and set a time limit on your scan. ## How to build the nuget package From a0c726387023eff256dc7939675632fa798dcef9 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 17 Feb 2024 22:33:24 +0100 Subject: [PATCH 158/193] BLE.Client.Maui: update to MAUI 8.0.7 * in order to fix build errors with latest SDK (8.0.201): error NU1605: Warning As Error: Detected package downgrade: Microsoft.Maui.Controls from 8.0.6 to 8.0.3. Reference the package directly from the project to select a different version. error NU1605: BLE.Client.Maui -> Microsoft.Maui.Controls.Compatibility 8.0.6 -> Microsoft.Maui.Controls (>= 8.0.6) error NU1605: BLE.Client.Maui -> Microsoft.Maui.Controls (>= 8.0.3) --- Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj index 47562a14..8a3da5cd 100644 --- a/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj +++ b/Source/BLE.Client/BLE.Client.Maui/BLE.Client.Maui.csproj @@ -70,7 +70,7 @@ - + From 112a1eaf4bde674842626abdcd4247d583a0e221 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 17 Feb 2024 22:52:34 +0100 Subject: [PATCH 159/193] add a FUNDING.yml file --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..84dbdffb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: janusw From 135bda84515e0a8b5d73330ed98ae6b883a427fc Mon Sep 17 00:00:00 2001 From: Ask Bojesen Date: Thu, 29 Feb 2024 11:04:44 +0100 Subject: [PATCH 160/193] Implemented and tested: - Adapter.BondAsync - Adapter.BondedDevices --- .../BLE.Client.WinConsole/PluginDemos.cs | 18 +++++++ .../BLE.Client.WinConsole/Program.cs | 2 + Source/Plugin.BLE/Windows/Adapter.cs | 54 ++++++++++++++++--- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs index e065b27b..3c14db01 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/PluginDemos.cs @@ -211,6 +211,24 @@ public async Task Connect_Change_Parameters_Disconnect() Write("Test_Connect_Disconnect done"); } + public async Task BondAsync() + { + string bleaddress = BleAddressSelector.GetBleAddress(); + var id = bleaddress.ToBleDeviceGuid(); + IDevice dev = await Adapter.ConnectToKnownDeviceAsync(id); + await Adapter.BondAsync(dev); + } + + public Task GetBondedDevices() + { + int idx = 0; + foreach(var dev in Adapter.BondedDevices) + { + Write($"{idx++} Bonded device: {dev.Name} : {dev.Id}"); + } + return Task.FromResult(true); + } + public async Task Pair_Connect_Disconnect() { string bleaddress = BleAddressSelector.GetBleAddress(); diff --git a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs index 9bed9006..1f34b72b 100644 --- a/Source/BLE.Client/BLE.Client.WinConsole/Program.cs +++ b/Source/BLE.Client/BLE.Client.WinConsole/Program.cs @@ -25,6 +25,8 @@ {ConsoleKey.D7, new Demo("5X: Connect -> Read services -> Disconnect", ppemos.Connect_Read_Services_Disconnect_5X) }, {ConsoleKey.D8, new Demo("5X: Connect -> Read services -> Dispose", ppemos.Connect_Read_Services_Dispose_5X) }, {ConsoleKey.D9, new Demo("Connect -> Loop: ConnectionLost -> Connect", ppemos.Connect_ConnectionLost_Connect) }, + {ConsoleKey.Q, new Demo("Adapter.BondAsync", ppemos.BondAsync) }, + {ConsoleKey.W, new Demo("Adapter.BondedDevices", ppemos.GetBondedDevices) }, {ConsoleKey.A, new Demo("Pure Windows: Connect -> Disconnect", wdemos.Connect_Disconnect) }, {ConsoleKey.S, new Demo("Pure Windows: Unpair all BLE devices", wdemos.UnPairAllBleDevices) }, }; diff --git a/Source/Plugin.BLE/Windows/Adapter.cs b/Source/Plugin.BLE/Windows/Adapter.cs index de1275bf..1ad0baa2 100644 --- a/Source/Plugin.BLE/Windows/Adapter.cs +++ b/Source/Plugin.BLE/Windows/Adapter.cs @@ -28,9 +28,27 @@ public Adapter() { } - public override Task BondAsync(IDevice device) + public override async Task BondAsync(IDevice device) { - throw new NotImplementedException(); + var bleDevice = device.NativeDevice as BluetoothLEDevice; + if (bleDevice is null) + { + Trace.Message($"BondAsync failed since NativeDevice is null with: {device.Name}: {device.Id} "); + return; + } + DeviceInformation deviceInformation = await DeviceInformation.CreateFromIdAsync(bleDevice.DeviceId); + if (deviceInformation.Pairing.IsPaired) + { + Trace.Message($"BondAsync is already paired with: {device.Name}: {device.Id}"); + return; + } + if (!deviceInformation.Pairing.CanPair) + { + Trace.Message($"BondAsync cannot pair with: {device.Name}: {device.Id}"); + return; + } + DevicePairingResult result = await deviceInformation.Pairing.PairAsync(); + Trace.Message($"BondAsync pairing result was {result.Status} with: {device.Name}: {device.Id}"); } protected override Task StartScanningForDevicesNativeAsync(ScanFilterOptions scanFilterOptions, bool allowDuplicatesKey, CancellationToken scanCancellationToken) @@ -161,6 +179,33 @@ public override async Task ConnectToKnownDeviceNativeAsync(Guid deviceG return knownDevice; } + protected override IReadOnlyList GetBondedDevices() + { + string pairedSelector = BluetoothLEDevice.GetDeviceSelectorFromPairingState(true); + DeviceInformationCollection pairedDevices = DeviceInformation.FindAllAsync(pairedSelector).GetAwaiter().GetResult(); + List devlist = new List(); + foreach (var dev in pairedDevices) + { + Guid id = dev.Id.ToBleDeviceGuidFromId(); + ulong bleaddress = id.ToBleAddress(); + var bluetoothLeDevice = BluetoothLEDevice.FromBluetoothAddressAsync(bleaddress).AsTask().Result; + if (bluetoothLeDevice != null) + { + var device = new Device( + this, + bluetoothLeDevice, + 0, id); + devlist.Add(device); + Trace.Message("GetBondedDevices: {0}: {1}", dev.Id, dev.Name); + } + else + { + Trace.Message("GetBondedDevices: {0}: {1}, BluetoothLEDevice == null", dev.Id, dev.Name); + } + } + return devlist; + } + public override IReadOnlyList GetSystemConnectedOrPairedDevices(Guid[] services = null) { string pairedSelector = BluetoothLEDevice.GetDeviceSelectorFromPairingState(true); @@ -194,11 +239,6 @@ public override IReadOnlyList GetSystemConnectedOrPairedDevices(Guid[] return devlist; } - protected override IReadOnlyList GetBondedDevices() - { - return null; // not supported - } - /// /// Parses a given advertisement for various stored properties /// Currently only parses the manufacturer specific data From e5facce6d0b8a1e1f9eb47eea44e34da30830b55 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 2 Mar 2024 21:17:22 +0100 Subject: [PATCH 161/193] implement BleImplementation.TrySetStateAsync on Android * at least the enable part --- Source/Plugin.BLE/Android/BleImplementation.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Plugin.BLE/Android/BleImplementation.cs b/Source/Plugin.BLE/Android/BleImplementation.cs index c0f79d6e..38e8d297 100644 --- a/Source/Plugin.BLE/Android/BleImplementation.cs +++ b/Source/Plugin.BLE/Android/BleImplementation.cs @@ -85,8 +85,18 @@ protected override IAdapter CreateNativeAdapter() public override Task TrySetStateAsync(bool on) { - Abstractions.Trace.Message("WARNING TrySetStateAsync is not implemented for Android"); - return Task.FromResult(false); + if (on) + { + var enable = new Intent(BluetoothAdapter.ActionRequestEnable); + enable.SetFlags(ActivityFlags.NewTask); + Application.Context.StartActivity(enable); + return Task.FromResult(true); + } + else + { + Abstractions.Trace.Message("WARNING TrySetStateAsync(false) is not implemented for Android"); + return Task.FromResult(false); + } } } } \ No newline at end of file From 2442bce36adc3c6597ee7ff669f57ab9f87b2e03 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 4 Mar 2024 15:26:19 +0100 Subject: [PATCH 162/193] BleImplementation.TrySetStateAsync: also implement the disable part on Android * this is somehow undocumented, though --- Source/Plugin.BLE/Android/BleImplementation.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Source/Plugin.BLE/Android/BleImplementation.cs b/Source/Plugin.BLE/Android/BleImplementation.cs index 38e8d297..635c3e8d 100644 --- a/Source/Plugin.BLE/Android/BleImplementation.cs +++ b/Source/Plugin.BLE/Android/BleImplementation.cs @@ -85,18 +85,12 @@ protected override IAdapter CreateNativeAdapter() public override Task TrySetStateAsync(bool on) { - if (on) - { - var enable = new Intent(BluetoothAdapter.ActionRequestEnable); - enable.SetFlags(ActivityFlags.NewTask); - Application.Context.StartActivity(enable); - return Task.FromResult(true); - } - else - { - Abstractions.Trace.Message("WARNING TrySetStateAsync(false) is not implemented for Android"); - return Task.FromResult(false); - } + const string ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE"; + + var intent = new Intent(on ? BluetoothAdapter.ActionRequestEnable : ACTION_REQUEST_DISABLE); + intent.SetFlags(ActivityFlags.NewTask); + Application.Context.StartActivity(intent); + return Task.FromResult(true); } } } \ No newline at end of file From 5b44ca681862cf690abcb1cf9261bdb6c74a6e84 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Mon, 4 Mar 2024 16:55:12 +0100 Subject: [PATCH 163/193] IBluetoothLE: update documentation of method 'TrySetStateAsync' --- Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs b/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs index 0bd46a14..ce229337 100644 --- a/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs +++ b/Source/Plugin.BLE/Shared/Contracts/IBluetoothLE.cs @@ -31,8 +31,8 @@ public interface IBluetoothLE bool IsOn { get; } /// - /// Try set the state of the Bluetooth on/off - /// 2024-01-14: Only supported in Windows + /// Try to set the state of the Bluetooth (on/off). + /// Supported on: Android & Windows. /// /// /// true if the the method executed with success otherwice false From 2f19eaec943e3878b39e8e85513f3e5ab63b63e3 Mon Sep 17 00:00:00 2001 From: Janus Weil Date: Sat, 16 Mar 2024 16:47:30 +0100 Subject: [PATCH 164/193] BLE.Client.Maui: remove MainPage * this is example code from a template that is not being used anyhwere --- .../BLE.Client/BLE.Client.Maui/MainPage.xaml | 47 ------------------- .../BLE.Client.Maui/MainPage.xaml.cs | 37 --------------- 2 files changed, 84 deletions(-) delete mode 100644 Source/BLE.Client/BLE.Client.Maui/MainPage.xaml delete mode 100644 Source/BLE.Client/BLE.Client.Maui/MainPage.xaml.cs diff --git a/Source/BLE.Client/BLE.Client.Maui/MainPage.xaml b/Source/BLE.Client/BLE.Client.Maui/MainPage.xaml deleted file mode 100644 index 294dbcdb..00000000 --- a/Source/BLE.Client/BLE.Client.Maui/MainPage.xaml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - -