mirror of
https://github.com/inthehand/32feet.git
synced 2026-10-24 01:48:29 +08:00
Windows disconnect support for BLE
Dispose native BLE objects when the user explicitly calls RemoteGattServer.Disconnect Windows disconnect support for BLE Dispose native BLE objects when the user explicitly calls RemoteGattServer.Disconnect Windows (BLE) Move LastKnownAddress to BluetoothDevice.windows.cs Windows (BLE) Move LastKnownAddress field to BluetoothDevice.windows.cs
This commit is contained in:
@@ -179,6 +179,11 @@ namespace InTheHand.Bluetooth
|
||||
_gatt.Disconnect();
|
||||
}
|
||||
|
||||
void PlatformCleanup()
|
||||
{
|
||||
// Android has no explicit cleanup 🤪
|
||||
}
|
||||
|
||||
async Task<GattService> PlatformGetPrimaryService(BluetoothUuid service)
|
||||
{
|
||||
await WaitForServiceDiscovery();
|
||||
|
||||
@@ -102,6 +102,10 @@ namespace InTheHand.Bluetooth
|
||||
{
|
||||
}
|
||||
|
||||
void PlatformCleanup()
|
||||
{
|
||||
}
|
||||
|
||||
Task<GattService> PlatformGetPrimaryService(BluetoothUuid service)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
|
||||
@@ -29,6 +29,10 @@ namespace InTheHand.Bluetooth
|
||||
{
|
||||
}
|
||||
|
||||
void PlatformCleanup()
|
||||
{
|
||||
}
|
||||
|
||||
Task<GattService> PlatformGetPrimaryService(BluetoothUuid service)
|
||||
{
|
||||
return Task.FromResult((GattService)null);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Devices.Bluetooth;
|
||||
|
||||
@@ -14,15 +16,101 @@ namespace InTheHand.Bluetooth
|
||||
partial class BluetoothDevice
|
||||
{
|
||||
internal BluetoothLEDevice NativeDevice;
|
||||
internal readonly ConcurrentDictionary<int,IDisposable> NativeDisposeList = new ConcurrentDictionary<int, IDisposable>();
|
||||
private string _cachedId;
|
||||
private string _cachedName;
|
||||
internal ulong LastKnownAddress;
|
||||
|
||||
internal BluetoothDevice(BluetoothLEDevice device)
|
||||
{
|
||||
NativeDevice = device;
|
||||
AddDisposableObject(this, device);
|
||||
|
||||
|
||||
if (device == null) return;
|
||||
|
||||
LastKnownAddress = device.BluetoothAddress;
|
||||
|
||||
// this will cache the Id and Name for use in cases where 'NativeDevice' has been disposed.
|
||||
GetId();
|
||||
GetName();
|
||||
}
|
||||
|
||||
~BluetoothDevice()
|
||||
{
|
||||
NativeDevice.Dispose();
|
||||
DisposeAllNativeObjects();
|
||||
}
|
||||
|
||||
/// <summary>Adds a native (IDisposable) object to the dispose list</summary>
|
||||
/// <param name="container">This is generally the managed object that contains the native object but it can be anything that can serve as a unique key.</param>
|
||||
/// <param name="disposableObject">The native object that we will disposed when the user requests a disconnect</param>
|
||||
internal void AddDisposableObject(object container, IDisposable disposableObject)
|
||||
{
|
||||
if (NativeDisposeList.TryGetValue(container.GetHashCode(), out IDisposable existingValue))
|
||||
{
|
||||
NativeDisposeList.TryUpdate(container.GetHashCode(), disposableObject, existingValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeDisposeList.TryAdd(container.GetHashCode(), disposableObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Called in RemoteServer.PlatformCleanup to dispose all of the native object that have been collected.</summary>
|
||||
internal void DisposeAllNativeObjects()
|
||||
{
|
||||
Dictionary<int, IDisposable> itemsDisposed = new Dictionary<int, IDisposable>();
|
||||
foreach (var kv in NativeDisposeList)
|
||||
{
|
||||
kv.Value?.Dispose();
|
||||
itemsDisposed.Add(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
foreach (var kv in itemsDisposed)
|
||||
{
|
||||
NativeDisposeList.TryUpdate(kv.Key, null, kv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to Recreate 'NativeDevice' if it is in a disposed state.
|
||||
/// </summary>
|
||||
/// <param name="deviceAddress">The bluetooth device address</param>
|
||||
/// <returns>True if 'NativeDevice' was recreated</returns>
|
||||
internal async Task<bool> CreateNativeInstance()
|
||||
{
|
||||
if (LastKnownAddress == 0) return false;
|
||||
|
||||
if (NativeDisposeList.TryGetValue(GetHashCode(), out IDisposable existingItem))
|
||||
{
|
||||
if (existingItem == null)
|
||||
{
|
||||
// The native object was disposed as the result of a call to RemoteGattServer.Disconnect.
|
||||
// we need to create another one.
|
||||
BluetoothLEDevice nativeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(LastKnownAddress);
|
||||
if (nativeDevice != null)
|
||||
{
|
||||
AddDisposableObject(this, nativeDevice);
|
||||
NativeDevice = nativeDevice;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Checks if the native object for this container has been disposed.</summary>
|
||||
/// <param name="container">This is generally the managed object that contains the native object but it can be anything that can serve as a unique key.</param>
|
||||
/// <returns>True if the container exists and it's native object has been disposed.</returns>
|
||||
internal bool IsDisposedItem(object container)
|
||||
{
|
||||
if (NativeDisposeList.TryGetValue(container.GetHashCode(), out IDisposable existingItem))
|
||||
{
|
||||
return existingItem == null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static implicit operator BluetoothLEDevice(BluetoothDevice device)
|
||||
@@ -63,24 +151,34 @@ namespace InTheHand.Bluetooth
|
||||
return device;
|
||||
}
|
||||
|
||||
string GetId()
|
||||
internal string GetId()
|
||||
{
|
||||
return NativeDevice.BluetoothAddress.ToString("X6");
|
||||
if (IsDisposedItem(this)) return _cachedId;
|
||||
_cachedId = NativeDevice.BluetoothAddress.ToString("X6");
|
||||
return _cachedId;
|
||||
}
|
||||
|
||||
string GetName()
|
||||
internal string GetName()
|
||||
{
|
||||
if(NativeDevice.Name.StartsWith("Bluetooth "))
|
||||
if (IsDisposedItem(this)) return _cachedName;
|
||||
if (NativeDevice.Name.StartsWith("Bluetooth "))
|
||||
{
|
||||
return NativeDevice.DeviceInformation.Name;
|
||||
_cachedName = NativeDevice.DeviceInformation.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedName = NativeDevice.Name;
|
||||
}
|
||||
|
||||
return NativeDevice.Name;
|
||||
return _cachedName;
|
||||
}
|
||||
|
||||
private RemoteGattServer _remoteGattServer;
|
||||
RemoteGattServer GetGatt()
|
||||
{
|
||||
return new RemoteGattServer(this);
|
||||
if (_remoteGattServer == null)
|
||||
_remoteGattServer = new RemoteGattServer(this);
|
||||
return _remoteGattServer;
|
||||
}
|
||||
|
||||
/*bool GetWatchingAdvertisements()
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace InTheHand.Bluetooth
|
||||
{
|
||||
_service = service;
|
||||
_isPrimary = isPrimary;
|
||||
device.AddDisposableObject(this,service);
|
||||
}
|
||||
|
||||
public static implicit operator WBluetooth.GattDeviceService(GattService service)
|
||||
|
||||
@@ -27,20 +27,35 @@ namespace InTheHand.Bluetooth
|
||||
|
||||
bool GetConnected()
|
||||
{
|
||||
if (Device.IsDisposedItem(Device)) return false;
|
||||
return Device.NativeDevice.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected;
|
||||
}
|
||||
|
||||
async Task PlatformConnect()
|
||||
{
|
||||
// Ensure that our native objects have not been disposed.
|
||||
// If they have, re-create the native device object.
|
||||
if (await Device.CreateNativeInstance()) PlatformInit();
|
||||
|
||||
var status = await Device.NativeDevice.RequestAccessAsync();
|
||||
if(status == Windows.Devices.Enumeration.DeviceAccessStatus.Allowed)
|
||||
{
|
||||
Device.LastKnownAddress = Device.NativeDevice.BluetoothAddress;
|
||||
var session = await Windows.Devices.Bluetooth.GenericAttributeProfile.GattSession.FromDeviceIdAsync(Device.NativeDevice.BluetoothDeviceId);
|
||||
if(session != null)
|
||||
{
|
||||
// Even though this is a local variable, we still want to add it to our dispose list so
|
||||
// we don't have to rely on the GC to clean it up.
|
||||
Device.AddDisposableObject(this, session);
|
||||
session.MaintainConnection = true;
|
||||
}
|
||||
|
||||
// need to request something to force a connection
|
||||
await Device.NativeDevice.GetGattServicesAsync(cacheMode: Windows.Devices.Bluetooth.BluetoothCacheMode.Uncached);
|
||||
var services = await Device.NativeDevice.GetGattServicesAsync(cacheMode: Windows.Devices.Bluetooth.BluetoothCacheMode.Uncached);
|
||||
foreach (var service in services.Services)
|
||||
{
|
||||
service.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -53,8 +68,25 @@ namespace InTheHand.Bluetooth
|
||||
// Windows has no explicit disconnect 🤪
|
||||
}
|
||||
|
||||
void PlatformCleanup()
|
||||
{
|
||||
// The user has explicitly called the Disconnect method so unhook ConnectionStatusChanged
|
||||
// and dispose all of the native windows bluetooth objects. This will release the device
|
||||
// so that it can be used by another application or re-connected by the current
|
||||
// application.
|
||||
if (Device.NativeDisposeList.TryGetValue(Device.GetHashCode(), out IDisposable existingDevice))
|
||||
{
|
||||
if (existingDevice != null)
|
||||
{
|
||||
Device.NativeDevice.ConnectionStatusChanged -= NativeDevice_ConnectionStatusChanged;
|
||||
Device.DisposeAllNativeObjects();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task<GattService> PlatformGetPrimaryService(BluetoothUuid service)
|
||||
{
|
||||
if (await Device.CreateNativeInstance()) PlatformInit();
|
||||
var result = await Device.NativeDevice.GetGattServicesForUuidAsync(service, Windows.Devices.Bluetooth.BluetoothCacheMode.Uncached);
|
||||
|
||||
if (result == null || result.Services.Count == 0)
|
||||
@@ -65,6 +97,7 @@ namespace InTheHand.Bluetooth
|
||||
|
||||
async Task<List<GattService>> PlatformGetPrimaryServices(BluetoothUuid? service)
|
||||
{
|
||||
if (await Device.CreateNativeInstance()) PlatformInit();
|
||||
var services = new List<GattService>();
|
||||
var nativeDevice = Device.NativeDevice;
|
||||
Windows.Devices.Bluetooth.BluetoothCacheMode cacheMode = nativeDevice.DeviceInformation.Pairing.IsPaired ? Windows.Devices.Bluetooth.BluetoothCacheMode.Cached : Windows.Devices.Bluetooth.BluetoothCacheMode.Uncached;
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace InTheHand.Bluetooth
|
||||
{
|
||||
PlatformDisconnect();
|
||||
Device.OnGattServerDisconnected();
|
||||
PlatformCleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user