diff --git a/Laerdal.McuMgr.Bindings.Android.Native/settings.gradle b/Laerdal.McuMgr.Bindings.Android.Native/settings.gradle index 52402747..e7dd3f2d 100644 --- a/Laerdal.McuMgr.Bindings.Android.Native/settings.gradle +++ b/Laerdal.McuMgr.Bindings.Android.Native/settings.gradle @@ -12,6 +12,6 @@ dependencyResolutionManagement { mavenCentral() } } -rootProject.name = "LaerdalMcuMgrWrapperAndSampleApp" +rootProject.name = "LaerdalMcuMgrWrapper" include ':app' include ':mcumgr-laerdal-wrapper' diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.cs index 9ef5745d..6c216d27 100644 --- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.cs +++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.cs @@ -66,6 +66,10 @@ public void FatalErrorOccurredAdvertisement(string resource, string errorMessage public void FileDownloadProgressPercentageAndDataThroughputChangedAdvertisement(int progressPercentage, float averageThroughput) => _downloaderCallbacksProxy.FileDownloadProgressPercentageAndDataThroughputChangedAdvertisement(progressPercentage, averageThroughput); //raises the actual event + + public void Dispose() + { + } } } } \ No newline at end of file diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.cs index f06ad9aa..c3d227a6 100644 --- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.cs +++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.cs @@ -74,6 +74,14 @@ public void FileUploadProgressPercentageAndDataThroughputChangedAdvertisement(in public bool TrySetContext(object context) => throw new NotImplementedException(); public bool TrySetBluetoothDevice(object bluetoothDevice) => throw new NotImplementedException(); public bool TryInvalidateCachedTransport() => throw new NotImplementedException(); + + public void Dispose() + { + } + + public void CleanupResourcesOfLastUpload() + { + } } } } \ No newline at end of file diff --git a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.cs b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.cs index 901029ae..bf282a08 100644 --- a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.cs +++ b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.cs @@ -74,6 +74,14 @@ public void FatalErrorOccurredAdvertisement(EFirmwareInstallationState state, EF public void FirmwareUploadProgressPercentageAndDataThroughputChangedAdvertisement(int progressPercentage, float averageThroughput) => _firmwareInstallerCallbacksProxy.FirmwareUploadProgressPercentageAndDataThroughputChangedAdvertisement(progressPercentage, averageThroughput); //raises the actual event + + public void CleanupResourcesOfLastInstallation() + { + } + + public void Dispose() + { + } } } } \ No newline at end of file diff --git a/Laerdal.McuMgr/Droid/FileDownloader/FileDownloader.cs b/Laerdal.McuMgr/Droid/FileDownloader/FileDownloader.cs index a6a075b4..a69ffc2d 100644 --- a/Laerdal.McuMgr/Droid/FileDownloader/FileDownloader.cs +++ b/Laerdal.McuMgr/Droid/FileDownloader/FileDownloader.cs @@ -57,7 +57,39 @@ internal AndroidFileDownloaderProxy(INativeFileDownloaderCallbacksProxy fileDown _fileDownloaderCallbacksProxy = fileDownloaderCallbacksProxy ?? throw new ArgumentNullException(nameof(fileDownloaderCallbacksProxy)); } + // public new void Dispose() { ... } dont there is no need to override the base implementation + + private bool _alreadyDisposed; + protected override void Dispose(bool disposing) + { + if (_alreadyDisposed) + { + base.Dispose(disposing); //vital + return; + } + + if (disposing) + { + CleanupInfrastructure(); + } + + _alreadyDisposed = true; + + base.Dispose(disposing); + } + private void CleanupInfrastructure() + { + try + { + Disconnect(); + } + catch + { + // ignored + } + } + #region commands public new EFileDownloaderVerdict BeginDownload(string remoteFilePath) @@ -66,7 +98,6 @@ internal AndroidFileDownloaderProxy(INativeFileDownloaderCallbacksProxy fileDown } #endregion commands - #region android callbacks -> csharp event emitters diff --git a/Laerdal.McuMgr/Droid/FileUploader/FileUploader.cs b/Laerdal.McuMgr/Droid/FileUploader/FileUploader.cs index ce6c4723..c1b75c5c 100644 --- a/Laerdal.McuMgr/Droid/FileUploader/FileUploader.cs +++ b/Laerdal.McuMgr/Droid/FileUploader/FileUploader.cs @@ -58,7 +58,45 @@ internal AndroidFileUploaderProxy(INativeFileUploaderCallbacksProxy fileUploader { _fileUploaderCallbacksProxy = fileUploaderCallbacksProxy ?? throw new ArgumentNullException(nameof(fileUploaderCallbacksProxy)); } + + // public new void Dispose() { ... } dont there is no need to override the base implementation + + private bool _alreadyDisposed; + protected override void Dispose(bool disposing) + { + if (_alreadyDisposed) + { + base.Dispose(disposing); //vital + return; + } + if (disposing) + { + CleanupInfrastructure(); + } + + _alreadyDisposed = true; + + base.Dispose(disposing); + } + + private void CleanupInfrastructure() + { + try + { + Disconnect(); + } + catch + { + // ignored + } + } + + public void CleanupResourcesOfLastUpload() + { + //nothing to do in android + } + #region commands public new EFileUploaderVerdict BeginUpload(string remoteFilePath, byte[] data) diff --git a/Laerdal.McuMgr/Droid/FirmwareInstaller/FirmwareInstaller.cs b/Laerdal.McuMgr/Droid/FirmwareInstaller/FirmwareInstaller.cs index a1979a65..64d19e07 100644 --- a/Laerdal.McuMgr/Droid/FirmwareInstaller/FirmwareInstaller.cs +++ b/Laerdal.McuMgr/Droid/FirmwareInstaller/FirmwareInstaller.cs @@ -58,6 +58,46 @@ internal AndroidFirmwareInstallerProxy(INativeFirmwareInstallerCallbacksProxy fi { _firmwareInstallerCallbacksProxy = firmwareInstallerCallbacksProxy ?? throw new ArgumentNullException(nameof(firmwareInstallerCallbacksProxy)); } + + // public new void Dispose() { ... } dont there is no need to override the base implementation + + private bool _alreadyDisposed; + protected override void Dispose(bool disposing) + { + if (_alreadyDisposed) + { + base.Dispose(disposing); //vital + return; + } + + if (disposing) + { + CleanupInfrastructure(); + } + + _alreadyDisposed = true; + + base.Dispose(disposing); + } + + private void CleanupInfrastructure() + { + try + { + Disconnect(); + } + catch + { + // ignored + } + } + + public void CleanupResourcesOfLastInstallation() + { + //nothing to do here for android only ios needs this + } + + #region commands public EFirmwareInstallationVerdict BeginInstallation( byte[] data, @@ -83,6 +123,10 @@ public EFirmwareInstallationVerdict BeginInstallation( return TranslateFirmwareInstallationVerdict(nativeVerdict); } + + #endregion + + #region callbacks -> events public override void FatalErrorOccurredAdvertisement(EAndroidFirmwareInstallationState state, EAndroidFirmwareInstallerFatalErrorType fatalErrorType, string errorMessage) { @@ -164,6 +208,8 @@ public override void FirmwareUploadProgressPercentageAndDataThroughputChangedAdv progressPercentage: progressPercentage ); } + + #endregion static private EAndroidFirmwareInstallationMode TranslateFirmwareInstallationMode(EFirmwareInstallationMode mode) => mode switch { diff --git a/Laerdal.McuMgr/Shared/FileDownloader/Contracts/IFileDownloader.cs b/Laerdal.McuMgr/Shared/FileDownloader/Contracts/IFileDownloader.cs index 979093fb..ad030e3b 100644 --- a/Laerdal.McuMgr/Shared/FileDownloader/Contracts/IFileDownloader.cs +++ b/Laerdal.McuMgr/Shared/FileDownloader/Contracts/IFileDownloader.cs @@ -1,11 +1,17 @@ // ReSharper disable UnusedMember.Global // ReSharper disable EventNeverSubscribedTo.Global +using System; + namespace Laerdal.McuMgr.FileDownloader.Contracts { /// Downloads a file on a specific Nordic-chip-based BLE device /// For the file-downloading process to even commence you need to be authenticated with the AED device that's being targeted. - public interface IFileDownloader : IFileDownloaderCommandable, IFileDownloaderQueryable, IFileDownloaderEventSubscribable + public interface IFileDownloader : + IFileDownloaderCommandable, + IFileDownloaderQueryable, + IFileDownloaderEventSubscribable, + IDisposable { } } diff --git a/Laerdal.McuMgr/Shared/FileDownloader/Contracts/Native/INativeFileDownloaderProxy.cs b/Laerdal.McuMgr/Shared/FileDownloader/Contracts/Native/INativeFileDownloaderProxy.cs index 00f05f0c..6d18d82c 100644 --- a/Laerdal.McuMgr/Shared/FileDownloader/Contracts/Native/INativeFileDownloaderProxy.cs +++ b/Laerdal.McuMgr/Shared/FileDownloader/Contracts/Native/INativeFileDownloaderProxy.cs @@ -1,6 +1,12 @@ -namespace Laerdal.McuMgr.FileDownloader.Contracts.Native +using System; + +namespace Laerdal.McuMgr.FileDownloader.Contracts.Native { - internal interface INativeFileDownloaderProxy : INativeFileDownloaderQueryableProxy, INativeFileDownloaderCommandableProxy, INativeFileDownloaderCallbacksProxy + internal interface INativeFileDownloaderProxy : + INativeFileDownloaderQueryableProxy, + INativeFileDownloaderCommandableProxy, + INativeFileDownloaderCallbacksProxy, + IDisposable { } } \ No newline at end of file diff --git a/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs b/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs index abeb0fe7..aaf1a99d 100644 --- a/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs +++ b/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs @@ -28,6 +28,13 @@ internal FileDownloader(INativeFileDownloaderProxy nativeFileDownloaderProxy) _nativeFileDownloaderProxy = nativeFileDownloaderProxy ?? throw new ArgumentNullException(nameof(nativeFileDownloaderProxy)); _nativeFileDownloaderProxy.FileDownloader = this; //vital } + + public void Dispose() + { + _nativeFileDownloaderProxy?.Dispose(); + + GC.SuppressFinalize(this); + } public string LastFatalErrorMessage => _nativeFileDownloaderProxy?.LastFatalErrorMessage; diff --git a/Laerdal.McuMgr/Shared/FileUploader/Contracts/IFileUploader.cs b/Laerdal.McuMgr/Shared/FileUploader/Contracts/IFileUploader.cs index 3b8718be..f416653e 100644 --- a/Laerdal.McuMgr/Shared/FileUploader/Contracts/IFileUploader.cs +++ b/Laerdal.McuMgr/Shared/FileUploader/Contracts/IFileUploader.cs @@ -1,11 +1,18 @@ // ReSharper disable UnusedMember.Global // ReSharper disable EventNeverSubscribedTo.Global +using System; + namespace Laerdal.McuMgr.FileUploader.Contracts { /// Uploads a file on a specific Nordic-chip-based BLE device /// For the file-uploading process to even commence you need to be authenticated with the AED device that's being targeted. - public interface IFileUploader : IFileUploaderCommandable, IFileUploaderQueryable, IFileUploaderEventSubscribable + public interface IFileUploader : + IFileUploaderCommandable, + IFileUploaderQueryable, + IFileUploaderEventSubscribable, + IFileUploaderCleanupable, + IDisposable { } } diff --git a/Laerdal.McuMgr/Shared/FileUploader/Contracts/IFileUploaderCleanupable.cs b/Laerdal.McuMgr/Shared/FileUploader/Contracts/IFileUploaderCleanupable.cs new file mode 100644 index 00000000..7c6b4927 --- /dev/null +++ b/Laerdal.McuMgr/Shared/FileUploader/Contracts/IFileUploaderCleanupable.cs @@ -0,0 +1,7 @@ +namespace Laerdal.McuMgr.FileUploader.Contracts +{ + public interface IFileUploaderCleanupable + { + void CleanupResourcesOfLastUpload(); + } +} \ No newline at end of file diff --git a/Laerdal.McuMgr/Shared/FileUploader/Contracts/Native/INativeFileUploaderCleanupProxy.cs b/Laerdal.McuMgr/Shared/FileUploader/Contracts/Native/INativeFileUploaderCleanupProxy.cs new file mode 100644 index 00000000..37ef88c0 --- /dev/null +++ b/Laerdal.McuMgr/Shared/FileUploader/Contracts/Native/INativeFileUploaderCleanupProxy.cs @@ -0,0 +1,7 @@ +namespace Laerdal.McuMgr.FileUploader.Contracts.Native +{ + internal interface INativeFileUploaderCleanupProxy + { + void CleanupResourcesOfLastUpload(); + } +} diff --git a/Laerdal.McuMgr/Shared/FileUploader/Contracts/Native/INativeFileUploaderProxy.cs b/Laerdal.McuMgr/Shared/FileUploader/Contracts/Native/INativeFileUploaderProxy.cs index 9d80cc98..fdbafb62 100644 --- a/Laerdal.McuMgr/Shared/FileUploader/Contracts/Native/INativeFileUploaderProxy.cs +++ b/Laerdal.McuMgr/Shared/FileUploader/Contracts/Native/INativeFileUploaderProxy.cs @@ -1,6 +1,13 @@ -namespace Laerdal.McuMgr.FileUploader.Contracts.Native +using System; + +namespace Laerdal.McuMgr.FileUploader.Contracts.Native { - internal interface INativeFileUploaderProxy : INativeFileUploaderQueryableProxy, INativeFileUploaderCommandableProxy, INativeFileUploaderCallbacksProxy + internal interface INativeFileUploaderProxy : + INativeFileUploaderQueryableProxy, + INativeFileUploaderCommandableProxy, + INativeFileUploaderCallbacksProxy, + INativeFileUploaderCleanupProxy, + IDisposable { } } \ No newline at end of file diff --git a/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs b/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs index 9cc8a83c..3ba3fb68 100644 --- a/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs +++ b/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs @@ -31,6 +31,13 @@ internal FileUploader(INativeFileUploaderProxy nativeFileUploaderProxy) _nativeFileUploaderProxy.FileUploader = this; //vital } + public void Dispose() + { + _nativeFileUploaderProxy?.Dispose(); + + GC.SuppressFinalize(this); + } + public bool TrySetContext(object context) => _nativeFileUploaderProxy?.TrySetContext(context) ?? false; public bool TrySetBluetoothDevice(object bluetoothDevice) => _nativeFileUploaderProxy?.TrySetContext(bluetoothDevice) ?? false; public bool TryInvalidateCachedTransport() => _nativeFileUploaderProxy?.TryInvalidateCachedTransport() ?? false; @@ -49,6 +56,7 @@ public EFileUploaderVerdict BeginUpload(string remoteFilePath, byte[] data) public void Cancel() => _nativeFileUploaderProxy?.Cancel(); public void Disconnect() => _nativeFileUploaderProxy?.Disconnect(); + public void CleanupResourcesOfLastUpload() => _nativeFileUploaderProxy?.CleanupResourcesOfLastUpload(); private event EventHandler _cancelled; private event EventHandler _logEmitted; @@ -265,6 +273,8 @@ ex is not ArgumentException //10 wops probably missing native lib symbols! Cancelled -= UploadAsyncOnCancelled; StateChanged -= UploadAsyncOnStateChanged; FatalErrorOccurred -= UploadAsyncOnFatalErrorOccurred; + + CleanupResourcesOfLastUpload(); //vital } void UploadAsyncOnCancelled(object sender, CancelledEventArgs ea) diff --git a/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/IFirmwareInstaller.cs b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/IFirmwareInstaller.cs index 0e4f1936..829c93e3 100644 --- a/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/IFirmwareInstaller.cs +++ b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/IFirmwareInstaller.cs @@ -1,10 +1,16 @@ // ReSharper disable UnusedMember.Global // ReSharper disable EventNeverSubscribedTo.Global +using System; + namespace Laerdal.McuMgr.FirmwareInstaller.Contracts { /// Upgrades the firmware on a specific Nordic-chip-based BLE device - public interface IFirmwareInstaller : IFirmwareInstallerQueryable, IFirmwareInstallerEventSubscribable, IFirmwareInstallerCommandable + public interface IFirmwareInstaller : + IFirmwareInstallerQueryable, + IFirmwareInstallerEventSubscribable, + IFirmwareInstallerCommandable, + IDisposable { } } diff --git a/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Native/INativeFirmwareInstallerCleanupableProxy.cs b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Native/INativeFirmwareInstallerCleanupableProxy.cs new file mode 100644 index 00000000..9a93e1c4 --- /dev/null +++ b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Native/INativeFirmwareInstallerCleanupableProxy.cs @@ -0,0 +1,7 @@ +namespace Laerdal.McuMgr.FirmwareInstaller.Contracts.Native +{ + internal interface INativeFirmwareInstallerCleanupableProxy + { + void CleanupResourcesOfLastInstallation(); + } +} \ No newline at end of file diff --git a/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Native/INativeFirmwareInstallerProxy.cs b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Native/INativeFirmwareInstallerProxy.cs index 80120e2a..1338115a 100644 --- a/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Native/INativeFirmwareInstallerProxy.cs +++ b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Native/INativeFirmwareInstallerProxy.cs @@ -1,6 +1,13 @@ +using System; + namespace Laerdal.McuMgr.FirmwareInstaller.Contracts.Native { - internal interface INativeFirmwareInstallerProxy : INativeFirmwareInstallerCommandableProxy, INativeFirmwareInstallerQueryableProxy, INativeFirmwareInstallerCallbacksProxy + internal interface INativeFirmwareInstallerProxy : + INativeFirmwareInstallerCommandableProxy, + INativeFirmwareInstallerQueryableProxy, + INativeFirmwareInstallerCleanupableProxy, + INativeFirmwareInstallerCallbacksProxy, + IDisposable { string Nickname { get; set; } } diff --git a/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs b/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs index b97d8e06..0ffc949d 100644 --- a/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs +++ b/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs @@ -30,6 +30,13 @@ internal FirmwareInstaller(INativeFirmwareInstallerProxy nativeFirmwareInstaller _nativeFirmwareInstallerProxy.FirmwareInstaller = this; //vital } + public void Dispose() + { + _nativeFirmwareInstallerProxy?.Dispose(); + + GC.SuppressFinalize(this); + } + public EFirmwareInstallationVerdict BeginInstallation( byte[] data, EFirmwareInstallationMode mode = EFirmwareInstallationMode.TestAndConfirm, @@ -61,6 +68,7 @@ public EFirmwareInstallationVerdict BeginInstallation( public void Cancel() => _nativeFirmwareInstallerProxy?.Cancel(); public void Disconnect() => _nativeFirmwareInstallerProxy?.Disconnect(); + public void CleanupResourcesOfLastUpload() => _nativeFirmwareInstallerProxy?.CleanupResourcesOfLastInstallation(); private event EventHandler _cancelled; private event EventHandler _logEmitted; @@ -229,6 +237,8 @@ ex is not ArgumentException //10 wops probably missing native lib symbols! Cancelled -= FirmwareInstallationAsyncOnCancelled; StateChanged -= FirmwareInstallationAsyncOnStateChanged; FatalErrorOccurred -= FirmwareInstallationAsyncOnFatalErrorOccurred; + + CleanupResourcesOfLastUpload(); } return; @@ -324,22 +334,30 @@ void FirmwareInstallationAsyncOnFatalErrorOccurred(object sender, FatalErrorOccu private void OnStateChanged(StateChangedEventArgs ea) { - _stateChanged?.Invoke(this, ea); - - switch (ea) + try { - case { NewState: EFirmwareInstallationState.Idle }: - _fileUploadProgressEventsCount = 0; //its vital to reset the counter here to account for retries - break; + switch (ea) + { + case { NewState: EFirmwareInstallationState.Idle }: + _fileUploadProgressEventsCount = 0; //its vital to reset the counter here to account for retries + break; - case { NewState: EFirmwareInstallationState.Testing } when _fileUploadProgressEventsCount <= 1: //works both on ios and android - OnIdenticalFirmwareCachedOnTargetDeviceDetected(new(ECachedFirmwareType.CachedButInactive)); - break; + case { NewState: EFirmwareInstallationState.Testing } when _fileUploadProgressEventsCount <= 1: //works both on ios and android + OnIdenticalFirmwareCachedOnTargetDeviceDetected(new(ECachedFirmwareType.CachedButInactive)); + break; - case { NewState: EFirmwareInstallationState.Complete } when _fileUploadProgressEventsCount <= 1: //works both on ios and android - OnIdenticalFirmwareCachedOnTargetDeviceDetected(new(ECachedFirmwareType.CachedAndActive)); - break; + case { NewState: EFirmwareInstallationState.Complete } when _fileUploadProgressEventsCount <= 1: //works both on ios and android + OnIdenticalFirmwareCachedOnTargetDeviceDetected(new(ECachedFirmwareType.CachedAndActive)); + break; + } } + finally + { + _stateChanged?.Invoke(this, ea); //00 must be dead last + } + + //00 if we raise the state-changed event before the switch statement then the calling environment will unwire the event handlers of + // the identical-firmware-cached-on-target-device-detected event before it gets fired and the event will be ignored altogether } private int _fileUploadProgressEventsCount; diff --git a/Laerdal.McuMgr/iOS/FileDownloader/FileDownloader.cs b/Laerdal.McuMgr/iOS/FileDownloader/FileDownloader.cs index 3407520a..427eaede 100644 --- a/Laerdal.McuMgr/iOS/FileDownloader/FileDownloader.cs +++ b/Laerdal.McuMgr/iOS/FileDownloader/FileDownloader.cs @@ -33,7 +33,7 @@ static private INativeFileDownloaderProxy ValidateArgumentsAndConstructProxy(CBP //ReSharper disable once InconsistentNaming private sealed class IOSNativeFileDownloaderProxy : IOSListenerForFileDownloader, INativeFileDownloaderProxy { - private readonly IOSFileDownloader _nativeFileDownloader; + private IOSFileDownloader _nativeFileDownloader; private readonly INativeFileDownloaderCallbacksProxy _nativeFileDownloaderCallbacksProxy; internal IOSNativeFileDownloaderProxy(CBPeripheral bluetoothDevice, INativeFileDownloaderCallbacksProxy nativeFileDownloaderCallbacksProxy) @@ -45,6 +45,41 @@ internal IOSNativeFileDownloaderProxy(CBPeripheral bluetoothDevice, INativeFileD _nativeFileDownloaderCallbacksProxy = nativeFileDownloaderCallbacksProxy; //composition-over-inheritance } + // public new void Dispose() { ... } dont there is no need to override the base implementation + + private bool _alreadyDisposed; + protected override void Dispose(bool disposing) + { + if (_alreadyDisposed) + { + base.Dispose(disposing); //vital + return; + } + + if (disposing) + { + CleanupInfrastructure(); + } + + _alreadyDisposed = true; + + base.Dispose(disposing); + } + + private void CleanupInfrastructure() + { + try + { + Disconnect(); + } + catch + { + // ignored + } + + _nativeFileDownloader?.Dispose(); + _nativeFileDownloader = null; + } #region commands diff --git a/Laerdal.McuMgr/iOS/FileUploader/FileUploader.cs b/Laerdal.McuMgr/iOS/FileUploader/FileUploader.cs index a47cde44..1b99a5fe 100644 --- a/Laerdal.McuMgr/iOS/FileUploader/FileUploader.cs +++ b/Laerdal.McuMgr/iOS/FileUploader/FileUploader.cs @@ -33,7 +33,7 @@ static private INativeFileUploaderProxy ValidateArgumentsAndConstructProxy(CBPer //ReSharper disable once InconsistentNaming private sealed class IOSNativeFileUploaderProxy : IOSListenerForFileUploader, INativeFileUploaderProxy { - private readonly IOSFileUploader _nativeFileUploader; + private IOSFileUploader _nativeFileUploader; private readonly INativeFileUploaderCallbacksProxy _nativeFileUploaderCallbacksProxy; internal IOSNativeFileUploaderProxy(CBPeripheral bluetoothDevice, INativeFileUploaderCallbacksProxy nativeFileUploaderCallbacksProxy) @@ -51,15 +51,70 @@ internal IOSNativeFileUploaderProxy(CBPeripheral bluetoothDevice, INativeFileUpl public string LastFatalErrorMessage => _nativeFileUploader?.LastFatalErrorMessage; public void Cancel() => _nativeFileUploader?.Cancel(); - public void Disconnect() => _nativeFileUploader?.Disconnect(); + + // public new void Dispose() { ... } dont there is no need to override the base implementation + + private bool _alreadyDisposed; + protected override void Dispose(bool disposing) + { + if (_alreadyDisposed) + { + base.Dispose(disposing); //vital + return; + } + + if (disposing) + { + CleanupInfrastructure(); + CleanupResourcesOfLastUpload(); // shouldnt be necessary but just in case + } + + _alreadyDisposed = true; + + base.Dispose(disposing); + } + private void CleanupInfrastructure() + { + try + { + Disconnect(); + } + catch + { + // ignored + } + + _nativeFileUploader?.Dispose(); //order + _nativeFileUploader = null; + } + + public void CleanupResourcesOfLastUpload() //00 + { + _nsDataOfFileInCurrentlyActiveUpload?.Dispose(); + _nsDataOfFileInCurrentlyActiveUpload = null; + + //00 the method needs to be public so that it can be called manually when someone uses BeginUpload() instead of UploadAsync()! + } + + private NSData _nsDataOfFileInCurrentlyActiveUpload; public EFileUploaderVerdict BeginUpload(string remoteFilePath, byte[] data) { - var nsData = NSData.FromArray(data); //todo should nsdata be tracked in a private variable and then cleaned up properly? - var verdict = TranslateFileUploaderVerdict(_nativeFileUploader.BeginUpload(remoteFilePath, nsData)); + var nsDataOfFileToUpload = NSData.FromArray(data); - return verdict; + var verdict = TranslateFileUploaderVerdict(_nativeFileUploader.BeginUpload( + data: nsDataOfFileToUpload, + remoteFilePath: remoteFilePath + )); + if (verdict != EFileUploaderVerdict.Success) + { + nsDataOfFileToUpload.Dispose(); + return verdict; + } + + _nsDataOfFileInCurrentlyActiveUpload = nsDataOfFileToUpload; + return EFileUploaderVerdict.Success; } public bool TrySetContext(object context) diff --git a/Laerdal.McuMgr/iOS/FirmwareInstaller/FirmwareInstaller.cs b/Laerdal.McuMgr/iOS/FirmwareInstaller/FirmwareInstaller.cs index 09f29051..e062c398 100644 --- a/Laerdal.McuMgr/iOS/FirmwareInstaller/FirmwareInstaller.cs +++ b/Laerdal.McuMgr/iOS/FirmwareInstaller/FirmwareInstaller.cs @@ -33,7 +33,7 @@ static private INativeFirmwareInstallerProxy ValidateArgumentsAndConstructProxy( // ReSharper disable once InconsistentNaming private sealed class IOSNativeFirmwareInstallerProxy : IOSListenerForFirmwareInstaller, INativeFirmwareInstallerProxy { - private readonly IOSFirmwareInstaller _nativeFirmwareInstaller; + private IOSFirmwareInstaller _nativeFirmwareInstaller; private readonly INativeFirmwareInstallerCallbacksProxy _nativeFirmwareInstallerCallbacksProxy; public string Nickname { get; set; } @@ -47,7 +47,51 @@ internal IOSNativeFirmwareInstallerProxy(CBPeripheral bluetoothDevice, INativeFi _nativeFirmwareInstallerCallbacksProxy = nativeFirmwareInstallerCallbacksProxy; //composition-over-inheritance } + // public new void Dispose() { ... } dont there is no need to override the base implementation + private bool _alreadyDisposed; + private NSData _nsDataForFirmwareOfCurrentlyActiveInstallation; + protected override void Dispose(bool disposing) + { + if (_alreadyDisposed) + { + base.Dispose(disposing); //vital + return; + } + + if (disposing) + { + CleanupInfrastructure(); + CleanupResourcesOfLastInstallation(); // shouldnt be necessary but just in case + } + + _alreadyDisposed = true; + + base.Dispose(disposing); + } + + private void CleanupInfrastructure() + { + try + { + Disconnect(); + } + catch + { + // ignored + } + + _nativeFirmwareInstaller?.Dispose(); + _nativeFirmwareInstaller = null; + } + + public void CleanupResourcesOfLastInstallation() //00 + { + _nsDataForFirmwareOfCurrentlyActiveInstallation?.Dispose(); + _nsDataForFirmwareOfCurrentlyActiveInstallation = null; + + //00 the method needs to be public so that it can be called manually when someone uses BeginUpload() instead of UploadAsync()! + } #region commands @@ -67,22 +111,27 @@ public EFirmwareInstallationVerdict BeginInstallation( int? byteAlignment = null ) { - //todo nsdata might have to be tracked in a private variable and then cleaned up properly we suspect that it could - //todo potentially be getting disposed ahead of time taking down with it the underlying native byte array on the ios side - //todo - //todo https://stackoverflow.com/questions/77461311/should-i-keep-the-c-sharp-reference-to-an-nsdata-object-around-until-i-know-for - var nsData = NSData.FromArray(data); - - var verdict = _nativeFirmwareInstaller.BeginInstallation( + var nsDataOfFirmware = NSData.FromArray(data); + + var verdict = TranslateFirmwareInstallationVerdict(_nativeFirmwareInstaller.BeginInstallation( mode: TranslateFirmwareInstallationMode(mode), - imageData: nsData, + imageData: nsDataOfFirmware, eraseSettings: eraseSettings ?? false, pipelineDepth: pipelineDepth ?? -1, byteAlignment: byteAlignment ?? -1, estimatedSwapTimeInMilliseconds: estimatedSwapTimeInMilliseconds ?? -1 - ); + )); + + if (verdict != EFirmwareInstallationVerdict.Success) + { + nsDataOfFirmware.Dispose(); + } + else + { + _nsDataForFirmwareOfCurrentlyActiveInstallation = nsDataOfFirmware; + } - return TranslateFirmwareInstallationVerdict(verdict); + return verdict; } #endregion commands diff --git a/README.md b/README.md index 24e5537a..a86b432a 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ to the device.** - **At the time of this writing the generated ios-nugets are built based on the iphoneos16.2 sdk** +- **For the time being Nordics' Android/Java libs are compiled in a way that emits Java1.8 bytecode so as to keep the libraries backwards compatible with versions of Android all the way back to 7. Our Java "glue-code" under 'Laerdal.McuMgr.Bindings.Android.Native' is compiled in the same fashion.** ## 🚀 Using the Nugets in your Projects diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a8df4b6c..360c3923 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,7 +12,8 @@ trigger: pool: name: 'Azure Pipelines' - vmImage: 'macOS-13' + vmImage: 'macOS-13' # to future maintainers if you ever want to switch back to azure be warned that you will need to first make sure that macos14 is available on azure if macos14 + # to future maintainers is not available on azure you will run into trouble making the build work for the latest versions of ios sdks (17+) and maccatalyst sdks variables: - group: shared-variables # needed for the github access token which is used to create new releases in github