From c878848537232cd943be33ffeea818896286ed00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuli=20M=C3=A4=C3=A4tt=C3=A4?= Date: Fri, 11 Aug 2023 13:56:49 +0300 Subject: [PATCH] Polar Ble SDK v.5.2.0 **iOS SDK updates:** PolarBleApiObserver.deviceDisconnected() is called with a pairingError in the case of 'Peer removed pairing information'. --- .../gatt/client/psftp/BlePsFtpClient.java | 42 +++-- .../proto/communications_pftp_request.proto | 14 ++ .../polar/sdk/api/PolarBleApiDefaultImpl.kt | 2 +- .../gatt/client/psftp/BlePsFtpClientTest.kt | 3 +- sources/iOS/ios-communications/Podfile | 1 + .../sdk/api/PolarBleApiDefaultImpl.swift | 2 +- .../sdk/api/PolarBleApiObservers.swift | 3 +- .../sdk/impl/PolarBleApiImpl.swift | 4 +- .../sdk/impl/protobuf/pftp_request.pb.swift | 2 +- .../model/gatt/client/pmd/BlePmdClient.swift | 5 +- .../gatt/client/psftp/BlePsFtpClient.swift | 50 ++++-- .../communications_pftp_request.pb.swift | 158 ++++++++++++++++++ .../project.pbxproj | 18 +- 13 files changed, 265 insertions(+), 39 deletions(-) create mode 100644 sources/Android/android-communications/library/src/main/proto/communications_pftp_request.proto create mode 100644 sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/protobuf/communications_pftp_request.pb.swift diff --git a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClient.java b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClient.java index 01d2b85a..eb29d212 100644 --- a/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClient.java +++ b/sources/Android/android-communications/library/src/main/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClient.java @@ -31,6 +31,7 @@ import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.core.SingleOnSubscribe; import io.reactivex.rxjava3.schedulers.Schedulers; +import com.polar.androidcommunications.api.ble.model.proto.CommunicationsPftpRequest; /** * Polar simple file transfer client declaration. @@ -50,6 +51,9 @@ public class BlePsFtpClient extends BleGattBase { private final AtomicInteger notificationPacketsWritten = new AtomicInteger(0); private final AtomicInteger packetsCount = new AtomicInteger(5); // default every 5th packet is written with response private static final int PROTOCOL_TIMEOUT_SECONDS = 90; + private static final int PROTOCOL_TIMEOUT_EXTENDED_SECONDS = 900; + + private final List extendedWriteTimeoutFilePaths = Collections.singletonList("/SYNCPART.TGZ"); /** * true = uses attribute operation WRITE @@ -232,10 +236,10 @@ public Single request(final byte[] header, Scheduler sche ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { txInterface.transmitMessages(BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, requestData, false); - waitPacketsWritten(packetsWritten, mtuWaiting, requestData.size()); + waitPacketsWritten(packetsWritten, mtuWaiting, requestData.size(), PROTOCOL_TIMEOUT_SECONDS); requestData.clear(); // start waiting for packets - readResponse(outputStream); + readResponse(outputStream, PROTOCOL_TIMEOUT_SECONDS); emitter.onSuccess(outputStream); } catch (InterruptedException ex) { BleLogger.e(TAG, "Request interrupted. Exception: " + ex.getMessage()); @@ -259,14 +263,14 @@ public Single request(final byte[] header, Scheduler sche .subscribeOn(scheduler); } - private void waitPacketsWritten(final AtomicInteger written, AtomicBoolean waiting, int count) throws InterruptedException, BleDisconnected, BlePsFtpUtils.PftpOperationTimeout { + private void waitPacketsWritten(final AtomicInteger written, AtomicBoolean waiting, int count, long timeoutSeconds) throws InterruptedException, BleDisconnected, BlePsFtpUtils.PftpOperationTimeout { try { waiting.set(true); while (written.get() < count) { synchronized (written) { if (written.get() != count) { int was = written.get(); - written.wait(PROTOCOL_TIMEOUT_SECONDS * 1000); + written.wait(timeoutSeconds * 1000); if (was == written.get()) { if (!txInterface.isConnected()) { throw new BleDisconnected("Connection lost during waiting packets to be written"); @@ -318,6 +322,7 @@ public Flowable write(final byte[] header, final ByteArrayInputStream data int next = 0; long totalPayload = totalStream.available(); BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); + final long timeoutSeconds = getWriteTimeoutForFilePath(CommunicationsPftpRequest.PbPFtpOperation.parseFrom(header).getPath()); do { byte[] airPacket; int counter = 0; @@ -332,7 +337,7 @@ public Flowable write(final byte[] header, final ByteArrayInputStream data if (useAttributeLevelResponse.get()) { counter = 1; packetsWrittenWithResponse.set(0); - waitPacketsWritten(packetsWrittenWithResponse, mtuWaiting, 1); + waitPacketsWritten(packetsWrittenWithResponse, mtuWaiting, 1, timeoutSeconds); packetsWritten.set(0); counter = 0; } else { @@ -375,7 +380,7 @@ public Flowable write(final byte[] header, final ByteArrayInputStream data currentOperationWrite.set(false); ByteArrayOutputStream response = new ByteArrayOutputStream(); try { - readResponse(response); + readResponse(response, timeoutSeconds); } catch (InterruptedException ex) { // catch interrupted as it cannot be rethrown onwards BleLogger.e(TAG, "write interrupted while reading response"); @@ -401,6 +406,15 @@ public Flowable write(final byte[] header, final ByteArrayInputStream data .serialize(); } + private long getWriteTimeoutForFilePath(final String filePath) { + for (final String path : extendedWriteTimeoutFilePaths) { + if (filePath.startsWith(path)) { + return PROTOCOL_TIMEOUT_EXTENDED_SECONDS; + } + } + return PROTOCOL_TIMEOUT_SECONDS; + } + public Single query(final int id, final byte[] parameters) { return query(id, parameters, Schedulers.newThread()); } @@ -411,10 +425,10 @@ private void handleMtuInterrupted(boolean dataAvailable, int lastRequest) { byte[] cancelPacket = new byte[]{0x00, 0x00, 0x00}; try { if (mtuWaiting.get()) { - waitPacketsWritten(packetsWritten, mtuWaiting, lastRequest); + waitPacketsWritten(packetsWritten, mtuWaiting, lastRequest, PROTOCOL_TIMEOUT_SECONDS); } txInterface.transmitMessages(BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, Collections.singletonList(cancelPacket), useAttributeLevelResponse.get()); - waitPacketsWritten(packetsWritten, mtuWaiting, 1); + waitPacketsWritten(packetsWritten, mtuWaiting, 1, PROTOCOL_TIMEOUT_SECONDS); BleLogger.d(TAG, "MTU interrupted. Stream cancel has been successfully send"); } catch (Throwable throwable) { BleLogger.e(TAG, "Exception while trying to cancel streaming"); @@ -446,9 +460,9 @@ public Single query(final int id, final byte[] parameters ByteArrayOutputStream response = new ByteArrayOutputStream(); try { txInterface.transmitMessages(BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, requs, false); - waitPacketsWritten(packetsWritten, mtuWaiting, requs.size()); + waitPacketsWritten(packetsWritten, mtuWaiting, requs.size(), PROTOCOL_TIMEOUT_SECONDS); requs.clear(); - readResponse(response); + readResponse(response, PROTOCOL_TIMEOUT_SECONDS); emitter.onSuccess(response); } catch (InterruptedException ex) { // Note RX throws InterruptedException when the stream is not completed, and it is unsubscribed @@ -500,7 +514,7 @@ public Completable sendNotification(final int id, final byte[] parameters, Sched BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); List requs = BlePsFtpUtils.buildRfc76MessageFrameAll(totalStream, mtuSize.get(), sequenceNumber); txInterface.transmitMessages(BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_H2D_CHARACTERISTIC, requs, false); - waitPacketsWritten(notificationPacketsWritten, notificationWaiting, requs.size()); + waitPacketsWritten(notificationPacketsWritten, notificationWaiting, requs.size(), PROTOCOL_TIMEOUT_SECONDS); emitter.onComplete(); } else { BleLogger.e(TAG, "Send notification id: " + id + " failed. PS-FTP notification not enabled"); @@ -616,7 +630,7 @@ public Completable waitPsFtpClientReady(final boolean checkConnection, Scheduler } @VisibleForTesting - void readResponse(ByteArrayOutputStream outputStream) throws Exception { + void readResponse(ByteArrayOutputStream outputStream, final long timeoutSeconds) throws Exception { long status = 0; int next = 0; BlePsFtpUtils.Rfc76SequenceNumber sequenceNumber = new BlePsFtpUtils.Rfc76SequenceNumber(); @@ -626,7 +640,7 @@ void readResponse(ByteArrayOutputStream outputStream) throws Exception { if (txInterface.isConnected()) { synchronized (mtuInputQueue) { if (mtuInputQueue.isEmpty()) { - mtuInputQueue.wait(PROTOCOL_TIMEOUT_SECONDS * 1000L); + mtuInputQueue.wait(timeoutSeconds * 1000L); } } } else { @@ -640,7 +654,7 @@ void readResponse(ByteArrayOutputStream outputStream) throws Exception { if (response.status == BlePsFtpUtils.RFC76_STATUS_MORE) { byte[] cancelPacket = new byte[]{0x00, 0x00, 0x00}; txInterface.transmitMessages(BlePsFtpUtils.RFC77_PFTP_SERVICE, BlePsFtpUtils.RFC77_PFTP_MTU_CHARACTERISTIC, Collections.singletonList(cancelPacket), true); - waitPacketsWritten(packetsWritten, mtuWaiting, 1); + waitPacketsWritten(packetsWritten, mtuWaiting, 1, timeoutSeconds); BleLogger.d(TAG, "Sequence number mismatch. Stream cancel has been successfully send"); } throw new BlePsFtpUtils.PftpResponseError("Air packet lost!", 303); diff --git a/sources/Android/android-communications/library/src/main/proto/communications_pftp_request.proto b/sources/Android/android-communications/library/src/main/proto/communications_pftp_request.proto new file mode 100644 index 00000000..b43c5995 --- /dev/null +++ b/sources/Android/android-communications/library/src/main/proto/communications_pftp_request.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +package com.polar.androidcommunications.api.ble.model.proto; + +message PbPFtpOperation { + enum Command { + GET = 0; + PUT = 1; + MERGE = 2; + REMOVE = 3; + } + required Command command = 1; + required string path = 2; +} diff --git a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApiDefaultImpl.kt b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApiDefaultImpl.kt index 4ebcd7f1..8f6e0db9 100644 --- a/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApiDefaultImpl.kt +++ b/sources/Android/android-communications/library/src/sdk/java/com/polar/sdk/api/PolarBleApiDefaultImpl.kt @@ -25,6 +25,6 @@ object PolarBleApiDefaultImpl { */ @JvmStatic fun versionInfo(): String { - return "5.1.0" + return "5.2.0" } } \ No newline at end of file diff --git a/sources/Android/android-communications/library/src/test/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClientTest.kt b/sources/Android/android-communications/library/src/test/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClientTest.kt index 6baab6cf..9646b783 100644 --- a/sources/Android/android-communications/library/src/test/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClientTest.kt +++ b/sources/Android/android-communications/library/src/test/java/com/polar/androidcommunications/api/ble/model/gatt/client/psftp/BlePsFtpClientTest.kt @@ -98,6 +98,7 @@ internal class BlePsFtpClientTest { val frame11 = byteArrayOf(0xB7.toByte(), 0x82.toByte(), 0x08.toByte(), 0x0A.toByte(), 0x06.toByte(), 0x08.toByte(), 0x03.toByte(), 0x10.toByte(), 0x00.toByte(), 0x18.toByte(), 0x02.toByte(), 0x8A.toByte(), 0x01.toByte(), 0x0D.toByte(), 0x0A.toByte(), 0x09.toByte(), 0x5A.toByte(), 0x48.toByte(), 0x5F.toByte(), 0x4A.toByte()) val frame12 = byteArrayOf(0xC3.toByte(), 0x5A.toByte(), 0x48.toByte(), 0x5F.toByte(), 0x4A.toByte(), 0x41.toByte(), 0x10.toByte(), 0x09.toByte()) val output = ByteArrayOutputStream() + val timeoutSeconds = 90L // Act blePsFtpClient.processServiceData(RFC77_PFTP_MTU_CHARACTERISTIC, frame0, 0, true) @@ -113,7 +114,7 @@ internal class BlePsFtpClientTest { blePsFtpClient.processServiceData(RFC77_PFTP_MTU_CHARACTERISTIC, frame10, 0, true) blePsFtpClient.processServiceData(RFC77_PFTP_MTU_CHARACTERISTIC, frame11, 0, true) blePsFtpClient.processServiceData(RFC77_PFTP_MTU_CHARACTERISTIC, frame12, 0, true) - blePsFtpClient.readResponse(output) + blePsFtpClient.readResponse(output, timeoutSeconds) // Assert val expectedArray = (frame0.drop(1) + diff --git a/sources/iOS/ios-communications/Podfile b/sources/iOS/ios-communications/Podfile index 4ab9dd7b..7486a944 100644 --- a/sources/iOS/ios-communications/Podfile +++ b/sources/iOS/ios-communications/Podfile @@ -12,6 +12,7 @@ def swift_protobuf_pod end target 'iOSCommunications' do + swift_protobuf_pod rx_swift_pod end diff --git a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiDefaultImpl.swift b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiDefaultImpl.swift index 44045c7a..9105248d 100644 --- a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiDefaultImpl.swift +++ b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiDefaultImpl.swift @@ -18,6 +18,6 @@ public class PolarBleApiDefaultImpl { /// /// - Returns: version in format major.minor.patch public static func versionInfo() -> String { - return "5.1.0" + return "5.2.0" } } diff --git a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiObservers.swift b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiObservers.swift index d791c71a..7e4a7ade 100644 --- a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiObservers.swift +++ b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/api/PolarBleApiObservers.swift @@ -20,7 +20,8 @@ public protocol PolarBleApiObserver: AnyObject { /// If PolarBleApi#disconnectFromPolarDevice is not called, a new connection attempt is dispatched automatically. /// /// - Parameter identifier: Polar device info - func deviceDisconnected(_ identifier: PolarDeviceInfo) + /// - Parameter pairingError: If true, it indicates that the disconnection was caused by a pairing error. In this case, try removing the pairing from the system settings. + func deviceDisconnected(_ identifier: PolarDeviceInfo, pairingError: Bool) } /// Bluetooth state observer. diff --git a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/PolarBleApiImpl.swift b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/PolarBleApiImpl.swift index 5f43226c..0a84bbad 100644 --- a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/PolarBleApiImpl.swift +++ b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/PolarBleApiImpl.swift @@ -137,7 +137,9 @@ import UIKit case .sessionOpenPark where session.previousState == .sessionOpen: fallthrough case .sessionClosed where session.previousState == .sessionOpen: fallthrough case .sessionClosed where session.previousState == .sessionClosing: - self.observer?.deviceDisconnected(info) + self.observer?.deviceDisconnected(info, pairingError: false) + case .sessionOpenPark where session.previousState == .sessionOpening: + self.observer?.deviceDisconnected(info, pairingError: true) case .sessionOpening: self.observer?.deviceConnecting(info) case .sessionClosed: fallthrough diff --git a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/protobuf/pftp_request.pb.swift b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/protobuf/pftp_request.pb.swift index 0b198d5d..dc0f7b60 100644 --- a/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/protobuf/pftp_request.pb.swift +++ b/sources/iOS/ios-communications/Sources/PolarBleSdk/sdk/impl/protobuf/pftp_request.pb.swift @@ -187,7 +187,7 @@ public struct Protocol_PbPFtpOperation { #if swift(>=4.2) -extension Protocol_PbPFtpOperation.Command: CaseIterable { +extension Communications_PbPFtpOperation.Command { // Support synthesized by the compiler. } diff --git a/sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/gatt/client/pmd/BlePmdClient.swift b/sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/gatt/client/pmd/BlePmdClient.swift index b8251d23..b13e902d 100644 --- a/sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/gatt/client/pmd/BlePmdClient.swift +++ b/sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/gatt/client/pmd/BlePmdClient.swift @@ -704,8 +704,9 @@ public class BlePmdClient: BleGattClientBase { var more = resp.more while (more) { let parameters = try self.pmdCpResponseQueue.poll(60) - more = parameters[0] != 0 - resp.parameters.append(parameters.subdata(in: 1..() let packetsWritten=AtomicInteger(initialValue:0) @@ -112,10 +115,10 @@ public class BlePsFtpClient: BleGattClientBase { } } - func waitPacketsWritten(_ written: AtomicInteger, canceled: BlockOperation , count: Int ) throws { + func waitPacketsWritten(_ written: AtomicInteger, canceled: BlockOperation, count: Int, timeout: TimeInterval) throws { while written.get() < count { let was = written.get() - try written.checkAndWait(count, secs: PROTOCOL_TIMEOUT, canceled: canceled, canceledError: BlePsFtpException.operationCanceled, timeoutCall: { + try written.checkAndWait(count, secs: timeout, canceled: canceled, canceledError: BlePsFtpException.operationCanceled, timeoutCall: { BleLogger.trace("PS-FTP OPERATION TIMEOUT") }) if (!(gattServiceTransmitter?.isConnected() ?? false) || was == written.get()) { @@ -126,7 +129,7 @@ public class BlePsFtpClient: BleGattClientBase { written.set(0) } - func readResponse(_ outputStream: NSMutableData, inputQueue: AtomicList<[Data: Int]>, canceled: BlockOperation) throws -> Int { + func readResponse(_ outputStream: NSMutableData, inputQueue: AtomicList<[Data: Int]>, canceled: BlockOperation, timeout: TimeInterval) throws -> Int { var frameStatus=0 var next=0 let sequenceNumber = BlePsFtpUtility.BlePsFtpRfc76SequenceNumber() @@ -210,12 +213,12 @@ public class BlePsFtpClient: BleGattClientBase { self.notificationPacketsWritten.set(0) } - fileprivate func transmitMtuPacket(_ packet: Data, canceled: BlockOperation, response: Bool) throws { + fileprivate func transmitMtuPacket(_ packet: Data, canceled: BlockOperation, response: Bool, timeout: TimeInterval) throws { if !canceled.isCancelled { if let transport = self.gattServiceTransmitter { try transport.transmitMessage(self, serviceUuid: BlePsFtpClient.PSFTP_SERVICE, characteristicUuid: BlePsFtpClient.PSFTP_MTU_CHARACTERISTIC, packet: packet, withResponse: response) BleLogger.trace_hex("MTU send ", data: packet) - try self.waitPacketsWritten(self.packetsWritten, canceled: canceled, count: 1) + try self.waitPacketsWritten(self.packetsWritten, canceled: canceled, count: 1, timeout: timeout) return } throw BleGattException.gattTransportNotAvailable @@ -227,7 +230,7 @@ public class BlePsFtpClient: BleGattClientBase { if let transport = self.gattServiceTransmitter { try transport.transmitMessage(self, serviceUuid: BlePsFtpClient.PSFTP_SERVICE, characteristicUuid: BlePsFtpClient.PSFTP_H2D_NOTIFICATION_CHARACTERISTIC, packet: packet, withResponse: response) BleLogger.trace_hex("H2D send ", data: packet) - try self.waitPacketsWritten(self.notificationPacketsWritten, canceled: BlockOperation(), count: 1) + try self.waitPacketsWritten(self.notificationPacketsWritten, canceled: BlockOperation(), count: 1, timeout: PROTOCOL_TIMEOUT) return } throw BleGattException.gattTransportNotAvailable @@ -271,11 +274,11 @@ public class BlePsFtpClient: BleGattClientBase { // send request let requs = BlePsFtpUtility.buildRfc76MessageFrameAll(totalStream, mtuSize: self.mtuSize, sequenceNumber: sequenceNumber) for packet in requs { - try self.transmitMtuPacket(packet, canceled: block ?? BlockOperation(), response: true) + try self.transmitMtuPacket(packet, canceled: block ?? BlockOperation(), response: true, timeout: PROTOCOL_TIMEOUT) anySend = true } let outputStream=NSMutableData() - let error = try self.readResponse(outputStream, inputQueue: self.mtuInputQueue, canceled: block ?? BlockOperation()) + let error = try self.readResponse(outputStream, inputQueue: self.mtuInputQueue, canceled: block ?? BlockOperation(), timeout: PROTOCOL_TIMEOUT) switch ( error ){ case 0: observer(.success(outputStream)) @@ -331,8 +334,14 @@ public class BlePsFtpClient: BleGattClientBase { return Observable.create{ observer in // TODO make improvement, if ex. rx retry is used this create could be called n times, // now the InputStream on n=1... is already consumed + var timeout = self.PROTOCOL_TIMEOUT + do { + timeout = try self.writeTimeout(for: Communications_PbPFtpOperation(serializedData: Data(referencing: header)).path) + } catch { + BleLogger.error("Failed to parse PbPFtpOperation: \(error)") + } let block = BlockOperation() - + block.addExecutionBlock { [unowned self, weak block] in BleLogger.trace("PS-FTP new write operation") self.gattServiceTransmitter?.attributeOperationStarted() @@ -369,9 +378,9 @@ public class BlePsFtpClient: BleGattClientBase { if next == 0 { // first write cannot be canceled - try self.transmitMtuPacket(packet, canceled: BlockOperation(), response: response) + try self.transmitMtuPacket(packet, canceled: BlockOperation(), response: response, timeout: timeout) } else { - try self.transmitMtuPacket(packet, canceled: block ?? BlockOperation(), response: response) + try self.transmitMtuPacket(packet, canceled: block ?? BlockOperation(), response: response, timeout: timeout) } next = 1 @@ -423,7 +432,7 @@ public class BlePsFtpClient: BleGattClientBase { } while more let output = NSMutableData() do { - let error = try self.readResponse(output, inputQueue: self.mtuInputQueue, canceled: block ?? BlockOperation()) + let error = try self.readResponse(output, inputQueue: self.mtuInputQueue, canceled: block ?? BlockOperation(), timeout: timeout) switch ( error ) { case 0: @@ -451,11 +460,20 @@ public class BlePsFtpClient: BleGattClientBase { } } + private func writeTimeout(for filePath: String) -> TimeInterval { + for path in extendedWriteTimeoutFilePaths { + if filePath.hasPrefix(path) { + return PROTOCOL_TIMEOUT_EXTENDED + } + } + return PROTOCOL_TIMEOUT + } + fileprivate func sendMtuCancelPacket() throws { let cancelPacket = [UInt8](repeating: 0, count: 3) packetsWritten.set(0) BleLogger.trace("PS-FTP mtu send cancel packet") - try self.transmitMtuPacket(Data(cancelPacket), canceled: BlockOperation(), response: true) + try self.transmitMtuPacket(Data(cancelPacket), canceled: BlockOperation(), response: true, timeout: PROTOCOL_TIMEOUT) } /// Sends a single query to device, can be called many at once, but will internally make operations atomic @@ -485,10 +503,10 @@ public class BlePsFtpClient: BleGattClientBase { let sequenceNumber=BlePsFtpUtility.BlePsFtpRfc76SequenceNumber() let requs = BlePsFtpUtility.buildRfc76MessageFrameAll(totalStream, mtuSize: self.mtuSize, sequenceNumber: sequenceNumber) for packet in requs { - try self.transmitMtuPacket(packet, canceled: BlockOperation.init(), response: true) + try self.transmitMtuPacket(packet, canceled: BlockOperation.init(), response: true, timeout: PROTOCOL_TIMEOUT) } let outputStream=NSMutableData() - let error = try self.readResponse(outputStream, inputQueue: self.mtuInputQueue, canceled: block ?? BlockOperation()) + let error = try self.readResponse(outputStream, inputQueue: self.mtuInputQueue, canceled: block ?? BlockOperation(), timeout: PROTOCOL_TIMEOUT) switch ( error ){ case 0: observer(.success(outputStream)) diff --git a/sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/protobuf/communications_pftp_request.pb.swift b/sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/protobuf/communications_pftp_request.pb.swift new file mode 100644 index 00000000..7f80d84a --- /dev/null +++ b/sources/iOS/ios-communications/Sources/iOSCommunications/ble/api/model/protobuf/communications_pftp_request.pb.swift @@ -0,0 +1,158 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: pftp_request.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +struct Communications_PbPFtpOperation { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var command: Communications_PbPFtpOperation.Command { + get {return _command ?? .get} + set {_command = newValue} + } + /// Returns true if `command` has been explicitly set. + var hasCommand: Bool {return self._command != nil} + /// Clears the value of `command`. Subsequent reads from it will return its default value. + mutating func clearCommand() {self._command = nil} + + var path: String { + get {return _path ?? String()} + set {_path = newValue} + } + /// Returns true if `path` has been explicitly set. + var hasPath: Bool {return self._path != nil} + /// Clears the value of `path`. Subsequent reads from it will return its default value. + mutating func clearPath() {self._path = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + enum Command: SwiftProtobuf.Enum { + typealias RawValue = Int + case get // = 0 + case put // = 1 + case merge // = 2 + case remove // = 3 + + init() { + self = .get + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .get + case 1: self = .put + case 2: self = .merge + case 3: self = .remove + default: return nil + } + } + + var rawValue: Int { + switch self { + case .get: return 0 + case .put: return 1 + case .merge: return 2 + case .remove: return 3 + } + } + + } + + init() {} + + fileprivate var _command: Communications_PbPFtpOperation.Command? = nil + fileprivate var _path: String? = nil +} + +#if swift(>=4.2) + +extension Communications_PbPFtpOperation.Command: CaseIterable { + // Support synthesized by the compiler. +} + +#endif // swift(>=4.2) + +#if swift(>=5.5) && canImport(_Concurrency) +extension Communications_PbPFtpOperation: @unchecked Sendable {} +extension Communications_PbPFtpOperation.Command: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "protocol" + +extension Communications_PbPFtpOperation: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".PbPFtpOperation" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "command"), + 2: .same(proto: "path"), + ] + + public var isInitialized: Bool { + if self._command == nil {return false} + if self._path == nil {return false} + return true + } + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self._command) }() + case 2: try { try decoder.decodeSingularStringField(value: &self._path) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._command { + try visitor.visitSingularEnumField(value: v, fieldNumber: 1) + } }() + try { if let v = self._path { + try visitor.visitSingularStringField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Communications_PbPFtpOperation, rhs: Communications_PbPFtpOperation) -> Bool { + if lhs._command != rhs._command {return false} + if lhs._path != rhs._path {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Communications_PbPFtpOperation.Command: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "GET"), + 1: .same(proto: "PUT"), + 2: .same(proto: "MERGE"), + 3: .same(proto: "REMOVE"), + ] +} diff --git a/sources/iOS/ios-communications/iOSCommunications.xcodeproj/project.pbxproj b/sources/iOS/ios-communications/iOSCommunications.xcodeproj/project.pbxproj index e9515508..5f8054ed 100644 --- a/sources/iOS/ios-communications/iOSCommunications.xcodeproj/project.pbxproj +++ b/sources/iOS/ios-communications/iOSCommunications.xcodeproj/project.pbxproj @@ -100,7 +100,10 @@ 6CEA10DF2175AA5B00E16FBF /* BlePsFtpUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8BCD381CE5BCEA00A4C6A8 /* BlePsFtpUtility.swift */; }; 6CEA10E02175AA5B00E16FBF /* BlePsFtpClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8BCD391CE5BCEA00A4C6A8 /* BlePsFtpClient.swift */; }; 9B9FF1B90FEF213BF8A98EA7 /* Pods_iOSCommunicationsTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3B778C8226252A704091D8 /* Pods_iOSCommunicationsTests.framework */; }; - A527E7C029ED53730059C22E /* PolarDiskSpaceDataTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C9E71329DD86A9003AF815 /* PolarDiskSpaceDataTest.swift */; }; + A527E7C029ED53730059C22E /* (null) in Sources */ = {isa = PBXBuildFile; }; + A52DB1DB2A1F59770073E795 /* communications_pftp_request.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52DB1DA2A1F59770073E795 /* communications_pftp_request.pb.swift */; }; + A52DB1DC2A1F59770073E795 /* communications_pftp_request.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52DB1DA2A1F59770073E795 /* communications_pftp_request.pb.swift */; }; + A52DB1DD2A1F59770073E795 /* communications_pftp_request.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52DB1DA2A1F59770073E795 /* communications_pftp_request.pb.swift */; }; A5346A3725F21858006752CA /* MockGattServiceTransmitterImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5346A3625F21858006752CA /* MockGattServiceTransmitterImpl.swift */; }; A5346A4125F21866006752CA /* BlePsFtpClientTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5346A4025F21866006752CA /* BlePsFtpClientTest.swift */; }; A5346A4B25F21877006752CA /* BlePsFtpUtilityTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5346A4A25F21877006752CA /* BlePsFtpUtilityTest.swift */; }; @@ -336,6 +339,7 @@ 6CD12AC9201F126500F3A417 /* BleGattException.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BleGattException.swift; path = ble/api/model/gatt/exceptions/BleGattException.swift; sourceTree = ""; }; 8C2C93D3BADEED88F88E65BD /* Pods-PolarBleSdkWatchOs.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PolarBleSdkWatchOs.debug.xcconfig"; path = "Target Support Files/Pods-PolarBleSdkWatchOs/Pods-PolarBleSdkWatchOs.debug.xcconfig"; sourceTree = ""; }; 9252CA4DD4A74A0CD806CB5A /* Pods-PolarBleSdkTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PolarBleSdkTests.release.xcconfig"; path = "Target Support Files/Pods-PolarBleSdkTests/Pods-PolarBleSdkTests.release.xcconfig"; sourceTree = ""; }; + A52DB1DA2A1F59770073E795 /* communications_pftp_request.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = communications_pftp_request.pb.swift; path = ble/api/model/protobuf/communications_pftp_request.pb.swift; sourceTree = ""; }; A5346A3625F21858006752CA /* MockGattServiceTransmitterImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockGattServiceTransmitterImpl.swift; sourceTree = ""; }; A5346A4025F21866006752CA /* BlePsFtpClientTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlePsFtpClientTest.swift; sourceTree = ""; }; A5346A4A25F21877006752CA /* BlePsFtpUtilityTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlePsFtpUtilityTest.swift; sourceTree = ""; }; @@ -594,6 +598,7 @@ 6C5A44911CB2912D0006220D /* model */ = { isa = PBXGroup; children = ( + A52DB1D92A1F594E0073E795 /* protobuf */, A5E5BDB5295AC26500188D55 /* offlinerecording */, 6C5A44BA1CB37F060006220D /* BleDeviceSession.swift */, 6C8BCD411CE5D60100A4C6A8 /* polar */, @@ -696,6 +701,14 @@ name = exceptions; sourceTree = ""; }; + A52DB1D92A1F594E0073E795 /* protobuf */ = { + isa = PBXGroup; + children = ( + A52DB1DA2A1F59770073E795 /* communications_pftp_request.pb.swift */, + ); + name = protobuf; + sourceTree = ""; + }; A5743681291A7D7C00E901A4 /* utils */ = { isa = PBXGroup; children = ( @@ -1306,6 +1319,7 @@ A57D050E29225779002824D9 /* PmdSetting.swift in Sources */, 6C67C7FF1E169CCE00411498 /* BleBasClient.swift in Sources */, 6CCFE4001E5D76C100FAA131 /* BlePsdClient.swift in Sources */, + A52DB1DB2A1F59770073E795 /* communications_pftp_request.pb.swift in Sources */, A5A88BFF299A64C8007B9C28 /* PmdOfflineTrigger.swift in Sources */, A57D053529309329002824D9 /* MagData.swift in Sources */, 6C5A44A41CB291790006220D /* AtomicList.swift in Sources */, @@ -1335,6 +1349,7 @@ A57D05232924C800002824D9 /* PmdTimeStampUtils.swift in Sources */, 6C2409422216A6E3001C1A84 /* CBScanningProtocol.swift in Sources */, A5A127E2273689E0007874B8 /* PolarAdvDataUtility.swift in Sources */, + A52DB1DD2A1F59770073E795 /* communications_pftp_request.pb.swift in Sources */, A57D052B292F60E3002824D9 /* AccData.swift in Sources */, A5385AC129CDBBDF00A6DB6A /* PmdErrors.swift in Sources */, A57D050C292254E2002824D9 /* BlePmdClient.swift in Sources */, @@ -1422,6 +1437,7 @@ A57D05222924C800002824D9 /* PmdTimeStampUtils.swift in Sources */, 6CEA10E02175AA5B00E16FBF /* BlePsFtpClient.swift in Sources */, 6C981041215B7136002820A2 /* AtomicType.swift in Sources */, + A52DB1DC2A1F59770073E795 /* communications_pftp_request.pb.swift in Sources */, A57D052A292F60E3002824D9 /* AccData.swift in Sources */, A5385AC029CDBBDF00A6DB6A /* PmdErrors.swift in Sources */, A57D050B292254E2002824D9 /* BlePmdClient.swift in Sources */,