Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ internal void Disconnect()
if (_bluetoothGatt != null)
{
_bluetoothGatt.Disconnect();

// Android BLE best practice: Refresh the service cache before closing
// This prevents stale service information from being cached between reconnections
// The refresh() method is a hidden API, so we use reflection to call it
try
{
var refreshMethod = _bluetoothGatt.Class?.GetMethod("refresh");
refreshMethod?.Invoke(_bluetoothGatt);
}
catch
{
// Silently ignore if refresh fails (not critical, but helpful)
}

_bluetoothGatt.Close();
_bluetoothGatt.Dispose();
_bluetoothGatt = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BrickController2.DeviceManagement.IO;
using FluentAssertions;
using System;
using System.Collections.Generic;
using Xunit;

Expand Down Expand Up @@ -42,6 +43,22 @@ public void Initialize_NoCommit_ChangeIsReportedFiveTimesOnly()
lastValues.ToArray().Should().AllBeEquivalentTo(0);
}

[Fact]
public void Clear_AnyChange_ReturnsFalseAndAllDefaultValues()
{
// Arrange
var group = new OutputValuesGroup<Half>(5);
group.SetOutput(3, Half.Pi);
// Act
group.Clear();

// Assert
var result = group.TryGetValues(out var values);
result.Should().BeFalse();
values.Length.Should().Be(5);
values.ToArray().Should().AllBeEquivalentTo(Half.Zero);
}

[Theory]
[InlineData(2)]
[InlineData(7)]
Expand Down Expand Up @@ -115,7 +132,7 @@ public void Commit_ExistingChange_NoChangeIsReportedThen()
changedValues.Should().BeEquivalentTo([new KeyValuePair<int, short>(0, 7)]);

// Act
group.Commmit();
group.Commit();

// Assert
group.TryGetChanges(out var values).Should().BeFalse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ protected virtual void OnCharacteristicChanged(Guid characteristicGuid, byte[] d
{
}

protected abstract void OnDeviceDisconnecting();

protected virtual Task<bool> AfterConnectSetupAsync(bool requestDeviceInformation, CancellationToken token)
{
return Task.FromResult(true);
Expand All @@ -116,9 +118,13 @@ private async Task DisconnectInternalAsync()
{
await StopOutputTaskAsync();
DeviceState = DeviceState.Disconnecting;
// notify disconnection
OnDeviceDisconnecting();
// execute native device disconnection + cleanup
await _bleDevice.DisconnectAsync();
_bleDevice = null;
}
_onDeviceDisconnected = null;

DeviceState = DeviceState.Disconnected;
}
Expand All @@ -129,8 +135,10 @@ private void OnDeviceDisconnected(IBluetoothLEDevice bluetoothLEDevice)
{
using (await _asyncLock.LockAsync())
{
var disconnectedCallback = _onDeviceDisconnected;
await DisconnectInternalAsync();
_onDeviceDisconnected?.Invoke(this);
// notify
disconnectedCallback?.Invoke(this);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ protected override async Task<bool> ValidateServicesAsync(IEnumerable<IGattServi
return _characteristic is not null && _firmwareRevisionCharacteristic is not null && _modelNumberCharacteristic is not null;
}

protected override void OnDeviceDisconnecting()
{
// Clear cached characteristic references to prevent using stale native Android objects on reconnection
_characteristic = null;
_modelNumberCharacteristic = null;
_firmwareRevisionCharacteristic = null;
}

protected override void OnCharacteristicChanged(Guid characteristicGuid, byte[] data)
{
if (characteristicGuid != _characteristic!.Uuid || data.Length < 4 || data[0] != 0x00)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ protected override Task<bool> ValidateServicesAsync(IEnumerable<IGattService>? s

return Task.FromResult(_characteristic != null && _firmwareRevisionCharacteristic != null && _modelNumberCharacteristic != null);
}
protected override void OnDeviceDisconnecting()
{
// Clear cached characteristic references to prevent using stale native Android objects on reconnection
_characteristic = null;
_modelNumberCharacteristic = null;
_firmwareRevisionCharacteristic = null;
}

protected override void OnCharacteristicChanged(Guid characteristicGuid, byte[] data)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ protected override Task<bool> ValidateServicesAsync(IEnumerable<IGattService>? s
return Task.FromResult(_characteristic is not null);
}


protected override void OnDeviceDisconnecting()
{
// Clear cached characteristic reference to prevent using stale native Android objects on reconnection
_characteristic = null;
}

protected override async Task ProcessOutputsAsync(CancellationToken token)
{
lock (_outputLock)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ protected override void OnCharacteristicChanged(Guid characteristicGuid, byte[]
BatteryVoltage = bateryVoltage;
}
}
protected override void OnDeviceDisconnecting()
{
_writeCharacteristic = null;
_notifyCharacteristic = null;
_hardwareRevisionCharacteristic = null;
_firmwareRevisionCharacteristic = null;
}

protected override async Task<bool> AfterConnectSetupAsync(bool requestDeviceInformation, CancellationToken token)
{
Expand Down
Loading
Loading