Skip to content
77 changes: 47 additions & 30 deletions src/Linux.Bluetooth/Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,7 @@ public class Adapter : IAdapter1, IDisposable
private IObjectManager ObjectManager =>
_objectManager ?? throw new InvalidOperationException("Adapter object manager has not been initialized.");

~Adapter()
{
Dispose();
}

internal static async Task<Adapter> CreateAsync(IAdapter1 proxy)
public static async Task<Adapter> CreateAsync(IAdapter1 proxy)
{
var adapter = new Adapter
{
Expand Down Expand Up @@ -76,8 +71,6 @@ public void Dispose()

_connTrackedDevices.Clear();
}

GC.SuppressFinalize(this);
}

public event DeviceChangeEventHandlerAsync DeviceFound
Expand Down Expand Up @@ -267,24 +260,40 @@ public Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler)

private async void FireEventForExistingDevicesAsync()
{
var devices = await this.GetDevicesAsync();
foreach (var device in devices)
// async void: exceptions must not escape.
try
{
_deviceFound?.Invoke(this, new DeviceFoundEventArgs(device, isStateChange: false));
var devices = await this.GetDevicesAsync();
foreach (var device in devices)
{
_deviceFound?.Invoke(this, new DeviceFoundEventArgs(device, isStateChange: false));
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"Existing-devices replay threw: {ex.Message}");
}
}

private async void OnDeviceAddedAsync((ObjectPath objectPath, IDictionary<string, IDictionary<string, object>> interfaces) args)
{
if (BlueZManager.IsMatch(BluezConstants.DeviceInterface, args.objectPath, args.interfaces, this))
// async void: exceptions must not escape.
try
{
var device = Connection.System.CreateProxy<IDevice1>(BluezConstants.DbusService, args.objectPath);
if (BlueZManager.IsMatch(BluezConstants.DeviceInterface, args.objectPath, args.interfaces, this))
{
var device = Connection.System.CreateProxy<IDevice1>(BluezConstants.DbusService, args.objectPath);

var dev = await Device.CreateAsync(device);
_deviceFound?.Invoke(this, new DeviceFoundEventArgs(dev));
var dev = await Device.CreateAsync(device);
_deviceFound?.Invoke(this, new DeviceFoundEventArgs(dev));

// Relay this device's connection state if anyone is listening to the adapter-level events.
TrackDeviceForConnection(args.objectPath);
// Relay this device's connection state if anyone is listening to the adapter-level events.
TrackDeviceForConnection(args.objectPath);
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"InterfacesAdded handler threw: {ex.Message}");
}
}

Expand Down Expand Up @@ -412,23 +421,31 @@ private async void FireEventIfPropertyAlreadyTrueAsync(AdapterEventHandlerAsync

private void OnPropertyChanges(PropertyChanges changes)
{
foreach (var pair in changes.Changed)
// Runs on the DBus receive loop: consumer throws must not escape.
try
{
switch (pair.Key)
foreach (var pair in changes.Changed)
{
case "Powered":
if (true.Equals(pair.Value))
{
_poweredOn?.Invoke(this, new BlueZEventArgs());
}
else
{
PoweredOff?.Invoke(this, new BlueZEventArgs());
}

break;
switch (pair.Key)
{
case "Powered":
if (true.Equals(pair.Value))
{
_poweredOn?.Invoke(this, new BlueZEventArgs());
}
else
{
PoweredOff?.Invoke(this, new BlueZEventArgs());
}

break;
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"Adapter property handler threw: {ex.Message}");
}
}
}
}
6 changes: 0 additions & 6 deletions src/Linux.Bluetooth/Agent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ public class Agent : IAgent1, IDisposable
private event AgentDisplayPasskeyEventHandlerAsync? _passkeyDisplayed;
private event AgentOperationCancelledEventHandlerAsync? _operationCancelled;

~Agent()
{
Dispose();
}

private Agent(Connection connection, string capability = DefaultCapability, ObjectPath? objectPath = null)
{
_connection = connection;
Expand Down Expand Up @@ -99,7 +94,6 @@ internal static async Task<Agent> CreateAsync(Connection connection, string capa
public void Dispose()
{
UnregisterObject();
GC.SuppressFinalize(this);
}

/// <summary>
Expand Down
6 changes: 0 additions & 6 deletions src/Linux.Bluetooth/AgentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ public class AgentManager : IAgentManager1, IDisposable
{
private readonly IAgentManager1 _proxy;

~AgentManager()
{
Dispose();
}

private AgentManager(IAgentManager1 proxy)
{
_proxy = proxy;
Expand Down Expand Up @@ -47,7 +42,6 @@ internal static Task<AgentManager> CreateAsync(Connection connection)
/// </summary>
public void Dispose()
{
GC.SuppressFinalize(this);
}

/// <summary>
Expand Down
8 changes: 8 additions & 0 deletions src/Linux.Bluetooth/BlueZManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ public static async Task<IReadOnlyList<Adapter>> GetAdaptersAsync()
return await Task.WhenAll(adapters.Select(Adapter.CreateAsync));
}

/// <summary>Get the adapter proxies without creating signal watchers.</summary>
/// <remarks>Select a proxy, then call <see cref="Adapter.CreateAsync"/> on it.</remarks>
/// <returns>The adapter proxies.</returns>
public static Task<IReadOnlyList<IAdapter1>> GetAdapterProxiesAsync()
{
return GetProxiesAsync<IAdapter1>(BluezConstants.AdapterInterface, rootObject: null);
}

// Normalize a 16, 32 or 128 bit UUID.
public static string NormalizeUUID(string uuid)
{
Expand Down
7 changes: 0 additions & 7 deletions src/Linux.Bluetooth/Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ public class Device : IDevice1, IDisposable

private IDevice1 Proxy => _proxy ?? throw new InvalidOperationException("Device has not been initialized.");

~Device()
{
Dispose();
}

internal static async Task<Device> CreateAsync(IDevice1 proxy)
{
var device = new Device
Expand All @@ -48,8 +43,6 @@ public void Dispose()
{
_propertyWatcher?.Dispose();
_propertyWatcher = null;

GC.SuppressFinalize(this);
}

public event DeviceEventHandlerAsync Connected
Expand Down
19 changes: 13 additions & 6 deletions src/Linux.Bluetooth/GattCharacteristic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,23 @@ private async void Subscribe()

private void OnPropertyChanges(PropertyChanges changes)
{
// Console.WriteLine("OnPropertyChanges called.");
foreach (var pair in changes.Changed)
// Runs on the DBus receive loop: consumer throws must not escape.
try
{
switch (pair.Key)
foreach (var pair in changes.Changed)
{
case "Value":
_onValue?.Invoke(this, new GattCharacteristicValueEventArgs((byte[])pair.Value));
break;
switch (pair.Key)
{
case "Value":
_onValue?.Invoke(this, new GattCharacteristicValueEventArgs((byte[])pair.Value));
break;
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"Characteristic value handler threw: {ex.Message}");
}
}
}
}
28 changes: 19 additions & 9 deletions src/Linux.Bluetooth/GattServer/GattServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ public GattServer(Adapter adapter)
_gattManager = Connection.CreateProxy<IGattManager1>(BluezConstants.DbusService, adapter.ObjectPath);
}

~GattServer()
{
Dispose();
}

public void Dispose()
{
Task.Run(async () =>
Expand All @@ -41,9 +36,8 @@ public void Dispose()
await UnregisterGattApplication();
}).Wait();

Console.Error.WriteLine("Disposed Gatt server.");
Debug.WriteLine("Disposed Gatt server.");
Connection.Dispose();
GC.SuppressFinalize(this);
}

public async Task InitializeAsync()
Expand Down Expand Up @@ -108,7 +102,15 @@ public async Task UnregisterGattApplication()
if (_gattApplication is not null)
{
// unregister the application before unregister objects
await _gattManager.UnregisterApplicationAsync(_gattApplication.ObjectPath);
try
{
await _gattManager.UnregisterApplicationAsync(_gattApplication.ObjectPath);
}
catch (DBusException ex) when (ex.ErrorName == "org.bluez.Error.DoesNotExist")
{
// BlueZ already dropped it (e.g. bluetoothd restarted): that is the wanted end state.
}


foreach (GattService service in _gattApplication.Services)
{
Expand Down Expand Up @@ -149,7 +151,15 @@ public async Task UnregisterAdvertisement()
{
if (_advertisement is not null)
{
await _advManager.UnregisterAdvertisementAsync(_advertisement.ObjectPath);
try
{
await _advManager.UnregisterAdvertisementAsync(_advertisement.ObjectPath);
}
catch (DBusException ex) when (ex.ErrorName == "org.bluez.Error.DoesNotExist")
{
// BlueZ already dropped it (e.g. bluetoothd restarted): that is the wanted end state.
}

Connection.UnregisterObject(_advertisement);
Debug.WriteLine($"Advertisement {_advertisement.ObjectPath} unregistered");
_advertisement = null;
Expand Down