@offline-protocol/mesh-sdk 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +1 -1
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +1 -1
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.s +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/inline-functions.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.s +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.s +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.s +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$AdvertisementCacheEntry.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$MeshObservation.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$PendingFragment.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$fragmentPollingRunnable$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$gattClientCallback$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$gattServerCallback$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$resolveTargetAddress$$inlined$sortedBy$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$routingCleanupRunnable$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$scanWatchdogRunnable$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$startAdvertising$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$startConnectionMonitor$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager$startScanning$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/BleManager.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/InternetManager$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/InternetManager$messagePollingRunnable$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/InternetManager$pingRunnable$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/InternetManager$webSocketListener$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/InternetManager.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/LogThrottler.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolModule$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolModule$Constants.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolModule$ParsedConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolModule$create$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolModule$create$3$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolModule$create$5$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolModule.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/OfflineProtocolPackage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException$AlreadyRunning.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException$InvalidState.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException$NotAvailable.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException$NotRunning.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException$PermissionDenied.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException$PlatformError.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException$StartFailed.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportException.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportManager.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportManagerListener.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/TransportState.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/WifiDirectManager$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/WifiDirectManager$messagePollingRunnable$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/WifiDirectManager$p2pReceiver$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/WifiDirectManager$startPeerDiscovery$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/WifiDirectManager$stopPeerDiscovery$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/WifiDirectManager.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/ble/MeshConnectionRegistry.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshAdvertisementData$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshAdvertisementData.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$ConnectionIntent.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$MeshConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$MeshDecision.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$MeshRole.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$PeerMetrics.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$PeerState.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$RebalanceDirective.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$RemoteCandidate.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController$ScoreWeights.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/offlineprotocol/mesh/MeshController.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/AckConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/AckConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/BleFragment$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/BleFragment.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/DedupConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/DedupConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/DedupStats$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/DedupStats.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/Disposable$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/Disposable.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/DorsConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/DorsConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/EventCallback$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/EventCallback.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverter$DefaultImpls.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverter.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterBoolean.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterCallbackInterface.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterFloat.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalShort.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalString.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalTypeBleFragment.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalTypeFileProgress.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalTypeInternetMessage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalTypeRouteEntry.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalTypeTransportMetrics.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalTypeWifiDirectMessage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalUByte.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterOptionalULong.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterRustBuffer.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterSequenceString.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterSequenceTypeMessageStats.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterSequenceTypeNetworkLink.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterSequenceTypeNetworkNode.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterSequenceTypeRouteEntry.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterSequenceUByte.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterShort.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterString.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeAckConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeBleFragment.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeDedupConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeDedupStats.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeDorsConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeEventCallback.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeFileProgress.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeGradientRoutingConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeInternetMessage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeMessagePriority.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeMessageStats.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeNetworkLink.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeNetworkNode.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeNetworkTopology.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeOfflineProtocol.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypePathConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypePeerDevice.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeProtocolConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeProtocolConfigExtended.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeProtocolError.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeProtocolState.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeRelayConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeRelayPriority.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeReliabilityConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeRetryConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeRouteEntry.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeRoutingStats.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeTransportConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeTransportMetrics.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeTransportType.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterTypeWifiDirectMessage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterUByte.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterUInt.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterULong.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FfiConverterUShort.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FileProgress$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/FileProgress.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ForeignBytes$ByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ForeignBytes.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/GradientRoutingConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/GradientRoutingConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/IntegrityCheckingUniffiLib.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/InternalException.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/InternetMessage$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/InternetMessage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/JavaLangRefCleanable.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/JavaLangRefCleaner.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/MessagePriority$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/MessagePriority.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/MessageStats$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/MessageStats.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/NetworkLink$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/NetworkLink.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/NetworkNode$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/NetworkNode.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/NetworkTopology$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/NetworkTopology.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/NoHandle.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/OfflineProtocol$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/OfflineProtocol$UniffiCleanAction.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/OfflineProtocol.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/OfflineProtocolInterface$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/OfflineProtocolInterface.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/Offline_protocolKt.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/PathConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/PathConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/PeerDevice$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/PeerDevice.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolConfigExtended$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolConfigExtended.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException$AlreadyStarted.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException$ErrorHandler.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException$InvalidConfiguration.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException$InvalidState.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException$NotStarted.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException$Other.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException$SendFailed.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolException.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolState$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ProtocolState.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RelayConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RelayConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RelayPriority$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RelayPriority.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ReliabilityConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/ReliabilityConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RetryConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RetryConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RouteEntry$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RouteEntry.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RoutingStats$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RoutingStats.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RustBuffer$ByReference.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RustBuffer$ByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RustBuffer$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/RustBuffer.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/TransportConfig$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/TransportConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/TransportMetrics$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/TransportMetrics.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/TransportType$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/TransportType.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiCallbackInterfaceClone.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiCallbackInterfaceEventCallbackMethod0.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiCallbackInterfaceFree.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiCleaner$Cleanable.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiCleaner$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiCleaner.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteF32.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteF64.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteI16.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteI32.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteI64.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteI8.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteRustBuffer.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteU16.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteU32.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteU64.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteU8.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureCompleteVoid.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureDroppedCallback.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureDroppedCallbackStruct$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureDroppedCallbackStruct.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultF32$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultF32.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultF64$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultF64.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI16$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI16.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI32$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI32.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI64$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI64.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI8$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultI8.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultRustBuffer$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultRustBuffer.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU16$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU16.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU32$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU32.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU64$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU64.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU8$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultU8.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultVoid$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiForeignFutureResultVoid.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiHandleMap.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiJnaCleanable.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiJnaCleaner.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiLib.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiNullRustCallStatusErrorHandler.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiRustCallStatus$ByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiRustCallStatus$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiRustCallStatus.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiRustCallStatusErrorHandler.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiRustFutureContinuationCallback.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiVTableCallbackInterfaceEventCallback$UniffiByValue.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiVTableCallbackInterfaceEventCallback.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/UniffiWithHandle.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/WifiDirectMessage$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/WifiDirectMessage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/uniffiCallbackInterfaceEventCallback$onEvent.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/uniffiCallbackInterfaceEventCallback$uniffiClone.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/uniffiCallbackInterfaceEventCallback$uniffiFree.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/uniffi/offline_protocol/uniffiCallbackInterfaceEventCallback.class +0 -0
- package/android/src/main/java/com/offlineprotocol/BleManager.kt +551 -33
- package/ios/BleManager.swift +323 -17
- package/package.json +1 -1
- /package/android/build/tmp/kotlin-classes/debug/META-INF/{offlineprotocol_react-native_debug.kotlin_module → offline-protocol_mesh-sdk_debug.kotlin_module} +0 -0
package/ios/BleManager.swift
CHANGED
|
@@ -152,10 +152,16 @@ public class BleManager: NSObject, TransportManager {
|
|
|
152
152
|
private var lastPeerCountUpdate: Date?
|
|
153
153
|
private let SCAN_HEARTBEAT_INTERVAL: TimeInterval = 10.0
|
|
154
154
|
private let SCAN_RESTART_INTERVAL: TimeInterval = 30.0
|
|
155
|
+
/// Force a complete BLE stack refresh periodically even when things seem healthy
|
|
156
|
+
private let FORCED_BLE_REFRESH_INTERVAL: TimeInterval = 120.0
|
|
157
|
+
private var lastForcedBleRefresh: Date?
|
|
155
158
|
private var connectionMonitor: DispatchSourceTimer?
|
|
156
159
|
private var connectionAttemptTimestamps: [UUID: Date] = [:]
|
|
160
|
+
private var connectionRetryCount: [UUID: Int] = [:]
|
|
157
161
|
private let CONNECTION_MONITOR_INTERVAL: TimeInterval = 5.0
|
|
158
162
|
private let MIN_RECONNECT_INTERVAL: TimeInterval = 5.0
|
|
163
|
+
private let MAX_RECONNECT_INTERVAL: TimeInterval = 60.0
|
|
164
|
+
private let MAX_CONNECTION_RETRIES = 5
|
|
159
165
|
private var scanRestartCount: Int = 0
|
|
160
166
|
private var lastCentralReset: Date?
|
|
161
167
|
private let MAX_CONSECUTIVE_SCAN_RESTARTS = 3
|
|
@@ -163,7 +169,18 @@ public class BleManager: NSObject, TransportManager {
|
|
|
163
169
|
private let MINIMUM_RSSI_TO_CONNECT: Int16 = -90
|
|
164
170
|
/// Rate limiting for unknown connectable devices that need GATT verification
|
|
165
171
|
private var unknownDeviceAttempts: [UUID: Date] = [:]
|
|
166
|
-
private let UNKNOWN_DEVICE_RATE_LIMIT: TimeInterval =
|
|
172
|
+
private let UNKNOWN_DEVICE_RATE_LIMIT: TimeInterval = 5.0 // More aggressive for cross-platform discovery
|
|
173
|
+
private let UNKNOWN_DEVICE_MIN_RSSI: Int16 = -80 // More lenient for cross-platform discovery
|
|
174
|
+
private let MAX_UNKNOWN_DEVICE_ATTEMPTS_PER_MINUTE = 10
|
|
175
|
+
/// Proactive scan refresh interval even when discoveries are occurring
|
|
176
|
+
private let PROACTIVE_SCAN_REFRESH_INTERVAL: TimeInterval = 60.0
|
|
177
|
+
private var lastProactiveScanRefresh: Date?
|
|
178
|
+
/// Tracks recently seen advertisement hashes to avoid duplicate processing
|
|
179
|
+
private var recentAdvertisementHashes: [UUID: (hash: Int, timestamp: Date)] = [:]
|
|
180
|
+
/// Initial aggressive discovery phase duration - more frequent scanning initially
|
|
181
|
+
private let AGGRESSIVE_DISCOVERY_PHASE: TimeInterval = 30.0
|
|
182
|
+
/// Tracks when aggressive discovery phase started
|
|
183
|
+
private var aggressiveDiscoveryStarted: Date?
|
|
167
184
|
|
|
168
185
|
// MARK: - Thread helpers
|
|
169
186
|
@inline(__always)
|
|
@@ -391,10 +408,14 @@ public class BleManager: NSObject, TransportManager {
|
|
|
391
408
|
pendingOutboundFragments.removeAll()
|
|
392
409
|
lastSeenMeshAdvertisements.removeAll()
|
|
393
410
|
unknownDeviceAttempts.removeAll()
|
|
411
|
+
recentAdvertisementHashes.removeAll()
|
|
394
412
|
pendingAdvertiseRestart?.cancel()
|
|
395
413
|
pendingAdvertiseRestart = nil
|
|
396
414
|
lastAdvertiseRestartAt = nil
|
|
397
415
|
transportStartAt = nil
|
|
416
|
+
lastProactiveScanRefresh = nil
|
|
417
|
+
lastForcedBleRefresh = nil
|
|
418
|
+
aggressiveDiscoveryStarted = nil
|
|
398
419
|
subscribedCentrals.removeAll()
|
|
399
420
|
|
|
400
421
|
// Clean up managers
|
|
@@ -490,8 +511,18 @@ public class BleManager: NSObject, TransportManager {
|
|
|
490
511
|
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]
|
|
491
512
|
)
|
|
492
513
|
isScanning = true
|
|
493
|
-
|
|
514
|
+
let now = Date()
|
|
515
|
+
scanStartDate = now
|
|
494
516
|
lastDiscoveryDate = scanStartDate
|
|
517
|
+
lastProactiveScanRefresh = now
|
|
518
|
+
// Start aggressive discovery phase for initial faster connection
|
|
519
|
+
if aggressiveDiscoveryStarted == nil {
|
|
520
|
+
aggressiveDiscoveryStarted = now
|
|
521
|
+
print("[BleManager] Starting aggressive discovery phase (\(AGGRESSIVE_DISCOVERY_PHASE)s)")
|
|
522
|
+
emitDiagnostic("info", "Starting aggressive discovery phase", context: [
|
|
523
|
+
"duration": AGGRESSIVE_DISCOVERY_PHASE
|
|
524
|
+
])
|
|
525
|
+
}
|
|
495
526
|
startScanMonitor()
|
|
496
527
|
if logThrottler.shouldLog(key: "scan_started") {
|
|
497
528
|
let context: [String: Any] = [
|
|
@@ -520,6 +551,7 @@ public class BleManager: NSObject, TransportManager {
|
|
|
520
551
|
scanStartDate = nil
|
|
521
552
|
lastDiscoveryDate = nil
|
|
522
553
|
connectionAttemptTimestamps.removeAll()
|
|
554
|
+
connectionRetryCount.removeAll()
|
|
523
555
|
if logThrottler.shouldLog(key: "scan_stopped") {
|
|
524
556
|
print("[BleManager] Stopped scanning (reason: \(reason))")
|
|
525
557
|
}
|
|
@@ -536,12 +568,61 @@ public class BleManager: NSObject, TransportManager {
|
|
|
536
568
|
let now = Date()
|
|
537
569
|
let lastActivity = self.lastDiscoveryDate ?? self.scanStartDate ?? now
|
|
538
570
|
let idleDuration = now.timeIntervalSince(lastActivity)
|
|
571
|
+
|
|
572
|
+
// Check for inactivity-based restart
|
|
539
573
|
if idleDuration >= self.SCAN_RESTART_INTERVAL {
|
|
540
574
|
if self.logThrottler.shouldLog(key: "scan_watchdog", interval: self.SCAN_RESTART_INTERVAL) {
|
|
541
575
|
print("[BleManager] Restarting scan after \(Int(idleDuration))s of inactivity")
|
|
542
576
|
self.emitDiagnostic("warning", "Restarting BLE scan due to inactivity", context: ["idle_seconds": Int(idleDuration)])
|
|
543
577
|
}
|
|
544
578
|
self.restartScanningDueToInactivity()
|
|
579
|
+
return
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Proactive scan refresh even when discoveries are occurring
|
|
583
|
+
// This ensures we don't miss devices due to BLE stack issues
|
|
584
|
+
let lastRefresh = self.lastProactiveScanRefresh ?? self.scanStartDate ?? now
|
|
585
|
+
if now.timeIntervalSince(lastRefresh) >= self.PROACTIVE_SCAN_REFRESH_INTERVAL {
|
|
586
|
+
if self.logThrottler.shouldLog(key: "proactive_scan_refresh", interval: self.PROACTIVE_SCAN_REFRESH_INTERVAL) {
|
|
587
|
+
print("[BleManager] Proactively refreshing BLE scan")
|
|
588
|
+
self.emitDiagnostic("info", "Proactive scan refresh")
|
|
589
|
+
}
|
|
590
|
+
self.lastProactiveScanRefresh = now
|
|
591
|
+
self.restartScanningDueToInactivity()
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Forced complete BLE refresh - more aggressive than proactive refresh
|
|
595
|
+
// This helps recover from edge cases where the BLE stack becomes stuck
|
|
596
|
+
let lastForced = self.lastForcedBleRefresh ?? self.transportStartAt ?? now
|
|
597
|
+
if now.timeIntervalSince(lastForced) >= self.FORCED_BLE_REFRESH_INTERVAL {
|
|
598
|
+
self.lastForcedBleRefresh = now
|
|
599
|
+
if self.logThrottler.shouldLog(key: "forced_ble_refresh", interval: self.FORCED_BLE_REFRESH_INTERVAL) {
|
|
600
|
+
print("[BleManager] Performing forced BLE refresh for reliability")
|
|
601
|
+
self.emitDiagnostic("info", "Forced BLE refresh for reliability", context: [
|
|
602
|
+
"connectedPeers": self.connections.connectedPeripheralCount(),
|
|
603
|
+
"discoveredPeers": self.discoveredPeripherals.count
|
|
604
|
+
])
|
|
605
|
+
}
|
|
606
|
+
// Stop and restart both scanning and advertising
|
|
607
|
+
self.stopScanning(reason: "forced_refresh")
|
|
608
|
+
self.refreshAdvertising(reason: "forced_refresh")
|
|
609
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
|
|
610
|
+
self?.startScanning(reason: "forced_refresh")
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// After aggressive phase ends, do a targeted scan with service UUID filter
|
|
615
|
+
// This can help discover Android devices that might have been missed
|
|
616
|
+
if let started = self.aggressiveDiscoveryStarted,
|
|
617
|
+
now.timeIntervalSince(started) >= self.AGGRESSIVE_DISCOVERY_PHASE,
|
|
618
|
+
now.timeIntervalSince(started) < self.AGGRESSIVE_DISCOVERY_PHASE + self.SCAN_HEARTBEAT_INTERVAL {
|
|
619
|
+
print("[BleManager] Aggressive phase ended, performing targeted service scan")
|
|
620
|
+
self.emitDiagnostic("info", "Aggressive phase ended, performing targeted service scan", context: [
|
|
621
|
+
"discoveredPeers": self.discoveredPeripherals.count,
|
|
622
|
+
"connectedPeers": self.connections.connectedPeripheralCount()
|
|
623
|
+
])
|
|
624
|
+
// Brief targeted scan with service UUID
|
|
625
|
+
self.performTargetedServiceScan()
|
|
545
626
|
}
|
|
546
627
|
}
|
|
547
628
|
timer.resume()
|
|
@@ -566,6 +647,33 @@ public class BleManager: NSObject, TransportManager {
|
|
|
566
647
|
}
|
|
567
648
|
}
|
|
568
649
|
|
|
650
|
+
/// Performs a brief targeted scan with service UUID filter.
|
|
651
|
+
/// This helps discover Android devices that might not advertise our service UUID
|
|
652
|
+
/// in the main advertisement packet but include it in scan response.
|
|
653
|
+
private func performTargetedServiceScan() {
|
|
654
|
+
guard let central = centralManager, central.state == .poweredOn else { return }
|
|
655
|
+
guard isScanning else { return }
|
|
656
|
+
|
|
657
|
+
// Brief stop and restart with service filter
|
|
658
|
+
central.stopScan()
|
|
659
|
+
|
|
660
|
+
// Scan with service UUID filter for 5 seconds
|
|
661
|
+
central.scanForPeripherals(
|
|
662
|
+
withServices: [SERVICE_UUID],
|
|
663
|
+
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
// After 5 seconds, go back to filterless scanning
|
|
667
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { [weak self] in
|
|
668
|
+
guard let self = self, self.isScanning else { return }
|
|
669
|
+
self.centralManager?.stopScan()
|
|
670
|
+
self.centralManager?.scanForPeripherals(
|
|
671
|
+
withServices: nil,
|
|
672
|
+
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]
|
|
673
|
+
)
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
569
677
|
private func evaluateCentralHealthAfterRestart() {
|
|
570
678
|
guard scanRestartCount >= MAX_CONSECUTIVE_SCAN_RESTARTS else { return }
|
|
571
679
|
let now = Date()
|
|
@@ -625,13 +733,22 @@ public class BleManager: NSObject, TransportManager {
|
|
|
625
733
|
private func attemptConnection(to peripheral: CBPeripheral, reason: String, rssi: Int16? = nil, desiredRole: MeshController.MeshRole? = nil) {
|
|
626
734
|
DispatchQueue.main.async { [weak self] in
|
|
627
735
|
guard let self = self else { return }
|
|
736
|
+
|
|
737
|
+
// Atomic check-and-connect: all checks must pass before proceeding
|
|
738
|
+
// This prevents race conditions when multiple discovery callbacks try to connect
|
|
739
|
+
|
|
740
|
+
// 1. Already connected check
|
|
628
741
|
if self.connections.connectedPeripheral(for: peripheral.identifier) != nil {
|
|
629
742
|
return
|
|
630
743
|
}
|
|
744
|
+
|
|
745
|
+
// 2. Recent attempt cooldown check
|
|
631
746
|
let now = Date()
|
|
632
747
|
if let lastAttempt = self.connectionAttemptTimestamps[peripheral.identifier], now.timeIntervalSince(lastAttempt) < self.MIN_RECONNECT_INTERVAL {
|
|
633
748
|
return
|
|
634
749
|
}
|
|
750
|
+
|
|
751
|
+
// 3. RSSI threshold check
|
|
635
752
|
if let effectiveRSSI = rssi ?? self.peripheralRSSI[peripheral.identifier], effectiveRSSI < self.MINIMUM_RSSI_TO_CONNECT {
|
|
636
753
|
if self.logThrottler.shouldLog(key: "rssi_skip_\(peripheral.identifier.uuidString)", interval: 10) {
|
|
637
754
|
self.emitDiagnostic("debug", "Skipping BLE connect due to weak RSSI", context: [
|
|
@@ -642,12 +759,24 @@ public class BleManager: NSObject, TransportManager {
|
|
|
642
759
|
}
|
|
643
760
|
return
|
|
644
761
|
}
|
|
762
|
+
|
|
763
|
+
// 4. Connection capacity check (atomic with the connect call)
|
|
645
764
|
if self.currentConnectionCount() >= self.MAX_CONNECTIONS_PER_DEVICE {
|
|
646
765
|
if self.logThrottler.shouldLog(key: "mesh_conn_cap_ios", interval: 10) {
|
|
647
766
|
print("[BleManager] Connection cap reached, not connecting to \(peripheral.identifier)")
|
|
648
767
|
}
|
|
649
768
|
return
|
|
650
769
|
}
|
|
770
|
+
|
|
771
|
+
// 5. Double-check peripheral state before connecting
|
|
772
|
+
guard peripheral.state != .connecting else {
|
|
773
|
+
if self.logThrottler.shouldLog(key: "already_connecting_\(peripheral.identifier.uuidString)", interval: 5) {
|
|
774
|
+
print("[BleManager] Already connecting to \(peripheral.identifier)")
|
|
775
|
+
}
|
|
776
|
+
return
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// All checks passed - proceed with connection
|
|
651
780
|
self.connectionAttemptTimestamps[peripheral.identifier] = now
|
|
652
781
|
if let desiredRole = desiredRole {
|
|
653
782
|
self.connections.setPendingRole(desiredRole, for: peripheral.identifier)
|
|
@@ -655,6 +784,7 @@ public class BleManager: NSObject, TransportManager {
|
|
|
655
784
|
self.connections.setPendingRole(.member, for: peripheral.identifier)
|
|
656
785
|
}
|
|
657
786
|
peripheral.delegate = self
|
|
787
|
+
|
|
658
788
|
if peripheral.state == .connected {
|
|
659
789
|
self.connections.registerPeripheral(peripheral)
|
|
660
790
|
if let service = peripheral.services?.first(where: { $0.uuid == self.SERVICE_UUID }) {
|
|
@@ -664,6 +794,7 @@ public class BleManager: NSObject, TransportManager {
|
|
|
664
794
|
}
|
|
665
795
|
return
|
|
666
796
|
}
|
|
797
|
+
|
|
667
798
|
self.centralManager?.connect(peripheral, options: nil)
|
|
668
799
|
if self.logThrottler.shouldLog(key: "connect_attempt_\(peripheral.identifier.uuidString)", interval: 10) {
|
|
669
800
|
print("[BleManager] Attempting connection to \(peripheral.identifier) (reason: \(reason))")
|
|
@@ -1034,6 +1165,7 @@ public class BleManager: NSObject, TransportManager {
|
|
|
1034
1165
|
pendingFragments.removeValue(forKey: identifier)
|
|
1035
1166
|
pendingOutboundFragments.removeValue(forKey: deviceId)
|
|
1036
1167
|
connectionAttemptTimestamps.removeValue(forKey: identifier)
|
|
1168
|
+
connectionRetryCount.removeValue(forKey: identifier)
|
|
1037
1169
|
meshController.registerDisconnection(peerId: deviceId)
|
|
1038
1170
|
refreshSelfMetrics()
|
|
1039
1171
|
|
|
@@ -1259,6 +1391,13 @@ public class BleManager: NSObject, TransportManager {
|
|
|
1259
1391
|
/// Checks if we should skip this peripheral based on RSSI filtering.
|
|
1260
1392
|
/// Returns true if the signal is too weak and we're in a dense environment.
|
|
1261
1393
|
private func shouldFilterByRssi(_ rssi: Int16) -> Bool {
|
|
1394
|
+
// During aggressive discovery phase, don't apply density-based filtering
|
|
1395
|
+
if let started = aggressiveDiscoveryStarted,
|
|
1396
|
+
Date().timeIntervalSince(started) < AGGRESSIVE_DISCOVERY_PHASE {
|
|
1397
|
+
// Only filter out extremely weak signals during aggressive phase
|
|
1398
|
+
return rssi < MINIMUM_RSSI_TO_CONNECT
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1262
1401
|
// In dense networks, apply stricter RSSI filtering
|
|
1263
1402
|
let threshold: Int16
|
|
1264
1403
|
if estimatedVisiblePeerCount > ADAPTIVE_HIGH_DENSITY_THRESHOLD {
|
|
@@ -1277,19 +1416,34 @@ public class BleManager: NSObject, TransportManager {
|
|
|
1277
1416
|
/// Checks if we should throttle connection attempts based on rate limits.
|
|
1278
1417
|
/// Returns true if we should skip this connection attempt.
|
|
1279
1418
|
private func shouldThrottleConnection(to peripheral: UUID, now: Date) -> Bool {
|
|
1419
|
+
// During aggressive discovery phase, use much shorter cooldowns
|
|
1420
|
+
let isAggressivePhase = aggressiveDiscoveryStarted.map { now.timeIntervalSince($0) < AGGRESSIVE_DISCOVERY_PHASE } ?? false
|
|
1421
|
+
|
|
1280
1422
|
// Prune old entries
|
|
1281
1423
|
let oneMinuteAgo = now.addingTimeInterval(-60.0)
|
|
1282
1424
|
globalConnectionAttempts = globalConnectionAttempts.filter { $0 > oneMinuteAgo }
|
|
1425
|
+
|
|
1426
|
+
let effectiveCooldown: TimeInterval = isAggressivePhase ? 5.0 : ADAPTIVE_COOLDOWN_PER_PERIPHERAL
|
|
1283
1427
|
peripheralConnectionAttempts = peripheralConnectionAttempts.filter {
|
|
1284
|
-
now.timeIntervalSince($0.value) <
|
|
1428
|
+
now.timeIntervalSince($0.value) < effectiveCooldown
|
|
1285
1429
|
}
|
|
1286
1430
|
|
|
1287
1431
|
// Check per-peripheral cooldown
|
|
1288
1432
|
if let lastAttempt = peripheralConnectionAttempts[peripheral],
|
|
1289
|
-
now.timeIntervalSince(lastAttempt) <
|
|
1433
|
+
now.timeIntervalSince(lastAttempt) < effectiveCooldown {
|
|
1290
1434
|
return true
|
|
1291
1435
|
}
|
|
1292
1436
|
|
|
1437
|
+
// During aggressive phase, allow more connection attempts
|
|
1438
|
+
if isAggressivePhase {
|
|
1439
|
+
// Allow up to 3x the normal rate during aggressive phase
|
|
1440
|
+
let maxAttempts = ADAPTIVE_MAX_CONNECTIONS_PER_MINUTE * 3
|
|
1441
|
+
if globalConnectionAttempts.count >= maxAttempts {
|
|
1442
|
+
return true
|
|
1443
|
+
}
|
|
1444
|
+
return false
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1293
1447
|
// In dense networks, apply global rate limiting
|
|
1294
1448
|
if estimatedVisiblePeerCount > ADAPTIVE_LOW_DENSITY_THRESHOLD {
|
|
1295
1449
|
let maxAttempts = ADAPTIVE_MAX_CONNECTIONS_PER_MINUTE
|
|
@@ -1362,6 +1516,21 @@ public class BleManager: NSObject, TransportManager {
|
|
|
1362
1516
|
}
|
|
1363
1517
|
}
|
|
1364
1518
|
|
|
1519
|
+
// 2b. Check overflow service UUIDs - Android devices sometimes advertise in overflow area
|
|
1520
|
+
// when the main advertisement packet is full
|
|
1521
|
+
if let overflowUUIDs = advertisementData[CBAdvertisementDataOverflowServiceUUIDsKey] as? [CBUUID] {
|
|
1522
|
+
if overflowUUIDs.contains(SERVICE_UUID) {
|
|
1523
|
+
return true
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// 2c. Check solicited service UUIDs - some Android devices use this
|
|
1528
|
+
if let solicitedUUIDs = advertisementData[CBAdvertisementDataSolicitedServiceUUIDsKey] as? [CBUUID] {
|
|
1529
|
+
if solicitedUUIDs.contains(SERVICE_UUID) {
|
|
1530
|
+
return true
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1365
1534
|
// 3. Check if this is a previously discovered mesh device
|
|
1366
1535
|
if lastSeenMeshAdvertisements[peripheral.identifier] != nil {
|
|
1367
1536
|
return true
|
|
@@ -1381,6 +1550,8 @@ public class BleManager: NSObject, TransportManager {
|
|
|
1381
1550
|
|
|
1382
1551
|
// 6. For unknown connectable devices, allow with rate limiting
|
|
1383
1552
|
// These will be verified via GATT service discovery after connection
|
|
1553
|
+
// This is CRITICAL for iOS ↔ Android cross-platform discovery since
|
|
1554
|
+
// each platform may not recognize the other's service UUID format in advertisements
|
|
1384
1555
|
let isConnectable: Bool
|
|
1385
1556
|
if #available(iOS 13.0, *) {
|
|
1386
1557
|
isConnectable = (advertisementData[CBAdvertisementDataIsConnectable] as? NSNumber)?.boolValue ?? false
|
|
@@ -1395,14 +1566,33 @@ public class BleManager: NSObject, TransportManager {
|
|
|
1395
1566
|
return false
|
|
1396
1567
|
}
|
|
1397
1568
|
|
|
1398
|
-
//
|
|
1399
|
-
|
|
1569
|
+
// Check global rate limit for unknown device attempts
|
|
1570
|
+
let oneMinuteAgo = now.addingTimeInterval(-60.0)
|
|
1571
|
+
let recentUnknownAttempts = unknownDeviceAttempts.values.filter { $0 > oneMinuteAgo }.count
|
|
1572
|
+
if recentUnknownAttempts >= MAX_UNKNOWN_DEVICE_ATTEMPTS_PER_MINUTE {
|
|
1573
|
+
return false
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// Use a tiered approach: stronger signals get priority
|
|
1577
|
+
let shouldAttempt: Bool
|
|
1578
|
+
if rssi >= -70 {
|
|
1579
|
+
// Strong signal - always try
|
|
1580
|
+
shouldAttempt = true
|
|
1581
|
+
} else if rssi >= UNKNOWN_DEVICE_MIN_RSSI && recentUnknownAttempts < MAX_UNKNOWN_DEVICE_ATTEMPTS_PER_MINUTE / 2 {
|
|
1582
|
+
// Moderate signal and we have budget
|
|
1583
|
+
shouldAttempt = true
|
|
1584
|
+
} else {
|
|
1585
|
+
shouldAttempt = false
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
if shouldAttempt {
|
|
1400
1589
|
unknownDeviceAttempts[peripheral.identifier] = now
|
|
1401
1590
|
if logThrottler.shouldLog(key: "unknown_connectable_\(peripheral.identifier.uuidString)", interval: 30) {
|
|
1402
1591
|
print("[BleManager] Allowing unknown connectable device for GATT verification: \(peripheral.identifier) RSSI=\(rssi)")
|
|
1403
1592
|
emitDiagnostic("debug", "Allowing unknown device for GATT verification", context: [
|
|
1404
1593
|
"identifier": peripheral.identifier.uuidString,
|
|
1405
|
-
"rssi": rssi
|
|
1594
|
+
"rssi": rssi,
|
|
1595
|
+
"recentAttempts": recentUnknownAttempts
|
|
1406
1596
|
])
|
|
1407
1597
|
}
|
|
1408
1598
|
return true
|
|
@@ -1419,6 +1609,32 @@ public class BleManager: NSObject, TransportManager {
|
|
|
1419
1609
|
}
|
|
1420
1610
|
return nil
|
|
1421
1611
|
}
|
|
1612
|
+
|
|
1613
|
+
/// Computes a hash of the advertisement data for duplicate detection.
|
|
1614
|
+
/// Uses peripheral ID, RSSI bucket, and key advertisement data.
|
|
1615
|
+
private func computeAdvertisementHash(peripheral: CBPeripheral, advertisementData: [String: Any], rssi: Int16) -> Int {
|
|
1616
|
+
var hasher = Hasher()
|
|
1617
|
+
hasher.combine(peripheral.identifier)
|
|
1618
|
+
// Use RSSI buckets of 5 dBm to avoid hash changes from minor signal fluctuations
|
|
1619
|
+
hasher.combine(rssi / 5)
|
|
1620
|
+
|
|
1621
|
+
// Include service UUIDs if present
|
|
1622
|
+
if let serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] {
|
|
1623
|
+
for uuid in serviceUUIDs {
|
|
1624
|
+
hasher.combine(uuid.uuidString)
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
// Include service data if present
|
|
1629
|
+
if let serviceData = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] {
|
|
1630
|
+
for (uuid, data) in serviceData {
|
|
1631
|
+
hasher.combine(uuid.uuidString)
|
|
1632
|
+
hasher.combine(data)
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
return hasher.finalize()
|
|
1637
|
+
}
|
|
1422
1638
|
|
|
1423
1639
|
private func maybeHandleRebalance(reason: String) {
|
|
1424
1640
|
pruneMeshObservations()
|
|
@@ -1592,6 +1808,23 @@ extension BleManager: CBCentralManagerDelegate {
|
|
|
1592
1808
|
// Adaptive scanning: track discoveries for density estimation
|
|
1593
1809
|
recordDiscoveryForDensity(now: now)
|
|
1594
1810
|
|
|
1811
|
+
// Duplicate advertisement detection - avoid processing identical advertisements
|
|
1812
|
+
// This improves performance in dense networks where the same device may be seen many times
|
|
1813
|
+
let advertHash = computeAdvertisementHash(peripheral: peripheral, advertisementData: advertisementData, rssi: rssiValue)
|
|
1814
|
+
if let cached = recentAdvertisementHashes[peripheral.identifier] {
|
|
1815
|
+
// If we've seen this exact advertisement recently, skip processing
|
|
1816
|
+
if cached.hash == advertHash && now.timeIntervalSince(cached.timestamp) < 1.0 {
|
|
1817
|
+
return
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
recentAdvertisementHashes[peripheral.identifier] = (hash: advertHash, timestamp: now)
|
|
1821
|
+
|
|
1822
|
+
// Prune old advertisement cache entries periodically
|
|
1823
|
+
if recentAdvertisementHashes.count > 100 {
|
|
1824
|
+
let cutoff = now.addingTimeInterval(-30.0)
|
|
1825
|
+
recentAdvertisementHashes = recentAdvertisementHashes.filter { $0.value.timestamp > cutoff }
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1595
1828
|
// Smart filtering for iOS ↔ Android interoperability
|
|
1596
1829
|
// Since we scan without a service UUID filter (for Android compatibility),
|
|
1597
1830
|
// we need to filter discovered peripherals here instead.
|
|
@@ -1669,11 +1902,25 @@ extension BleManager: CBCentralManagerDelegate {
|
|
|
1669
1902
|
}
|
|
1670
1903
|
|
|
1671
1904
|
// Adaptive scanning: rate limit connection attempts
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1905
|
+
// Skip throttling for first-time discoveries with strong signals for faster connection
|
|
1906
|
+
let notSeenBefore = lastSeenMeshAdvertisements[peripheral.identifier] == nil
|
|
1907
|
+
let notConnected = connections.connectedPeripheral(for: peripheral.identifier) == nil
|
|
1908
|
+
let isFirstDiscovery = notSeenBefore && notConnected
|
|
1909
|
+
let hasStrongSignal = rssiValue >= -70
|
|
1910
|
+
|
|
1911
|
+
if !isFirstDiscovery || !hasStrongSignal {
|
|
1912
|
+
if shouldThrottleConnection(to: peripheral.identifier, now: now) {
|
|
1913
|
+
if logThrottler.shouldLog(key: "adaptive_throttle_\(peripheral.identifier.uuidString)", interval: 30) {
|
|
1914
|
+
print("[BleManager] Adaptive: throttling connection to \(peripheral.identifier)")
|
|
1915
|
+
}
|
|
1916
|
+
return
|
|
1675
1917
|
}
|
|
1676
|
-
|
|
1918
|
+
} else if isFirstDiscovery && hasStrongSignal {
|
|
1919
|
+
print("[BleManager] Fast-tracking first discovery with strong signal: \(peripheral.identifier) RSSI=\(rssiValue)")
|
|
1920
|
+
emitDiagnostic("info", "Fast-tracking first discovery", context: [
|
|
1921
|
+
"identifier": peripheral.identifier.uuidString,
|
|
1922
|
+
"rssi": rssiValue
|
|
1923
|
+
])
|
|
1677
1924
|
}
|
|
1678
1925
|
|
|
1679
1926
|
let desiredRole: MeshController.MeshRole = (decision.intent == .interCluster) ? .bridge : .member
|
|
@@ -1710,6 +1957,7 @@ extension BleManager: CBCentralManagerDelegate {
|
|
|
1710
1957
|
|
|
1711
1958
|
connections.registerPeripheral(peripheral)
|
|
1712
1959
|
connectionAttemptTimestamps.removeValue(forKey: peripheral.identifier)
|
|
1960
|
+
connectionRetryCount.removeValue(forKey: peripheral.identifier) // Reset retry count on successful connection
|
|
1713
1961
|
|
|
1714
1962
|
// Discover services
|
|
1715
1963
|
peripheral.discoverServices([SERVICE_UUID])
|
|
@@ -1717,14 +1965,36 @@ extension BleManager: CBCentralManagerDelegate {
|
|
|
1717
1965
|
|
|
1718
1966
|
public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
|
|
1719
1967
|
print("[BleManager] Failed to connect to peripheral: \(error?.localizedDescription ?? "unknown")")
|
|
1968
|
+
|
|
1969
|
+
// Increment retry count and calculate backoff
|
|
1970
|
+
let retryCount = (connectionRetryCount[peripheral.identifier] ?? 0) + 1
|
|
1971
|
+
connectionRetryCount[peripheral.identifier] = retryCount
|
|
1972
|
+
|
|
1720
1973
|
emitDiagnostic("error", "Failed to connect to BLE peripheral", context: [
|
|
1721
1974
|
"identifier": peripheral.identifier.uuidString,
|
|
1722
|
-
"error": error?.localizedDescription ?? "unknown"
|
|
1975
|
+
"error": error?.localizedDescription ?? "unknown",
|
|
1976
|
+
"retryCount": retryCount
|
|
1723
1977
|
])
|
|
1724
1978
|
connectionAttemptTimestamps.removeValue(forKey: peripheral.identifier)
|
|
1725
1979
|
_ = connections.consumePendingRole(for: peripheral.identifier)
|
|
1726
|
-
|
|
1727
|
-
|
|
1980
|
+
|
|
1981
|
+
// Give up after max retries
|
|
1982
|
+
guard retryCount <= MAX_CONNECTION_RETRIES else {
|
|
1983
|
+
print("[BleManager] Max retries (\(MAX_CONNECTION_RETRIES)) exceeded for \(peripheral.identifier), giving up")
|
|
1984
|
+
emitDiagnostic("warning", "Max connection retries exceeded", context: [
|
|
1985
|
+
"identifier": peripheral.identifier.uuidString,
|
|
1986
|
+
"retryCount": retryCount
|
|
1987
|
+
])
|
|
1988
|
+
connectionRetryCount.removeValue(forKey: peripheral.identifier)
|
|
1989
|
+
return
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
// Exponential backoff: 5s, 10s, 20s, 40s, 60s (capped)
|
|
1993
|
+
let backoffInterval = min(MAX_RECONNECT_INTERVAL, MIN_RECONNECT_INTERVAL * pow(2.0, Double(retryCount - 1)))
|
|
1994
|
+
|
|
1995
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + backoffInterval) { [weak self] in
|
|
1996
|
+
guard let self = self, self.state == .running else { return }
|
|
1997
|
+
self.attemptConnection(to: peripheral, reason: "retry_fail")
|
|
1728
1998
|
}
|
|
1729
1999
|
}
|
|
1730
2000
|
|
|
@@ -1775,8 +2045,34 @@ extension BleManager: CBCentralManagerDelegate {
|
|
|
1775
2045
|
}
|
|
1776
2046
|
}
|
|
1777
2047
|
|
|
1778
|
-
// Attempt reconnection
|
|
1779
|
-
|
|
2048
|
+
// Attempt reconnection with exponential backoff
|
|
2049
|
+
let retryCount = (connectionRetryCount[peripheral.identifier] ?? 0) + 1
|
|
2050
|
+
connectionRetryCount[peripheral.identifier] = retryCount
|
|
2051
|
+
|
|
2052
|
+
// Give up after max retries
|
|
2053
|
+
guard retryCount <= MAX_CONNECTION_RETRIES else {
|
|
2054
|
+
print("[BleManager] Max retries (\(MAX_CONNECTION_RETRIES)) exceeded for \(peripheral.identifier) on disconnect, giving up")
|
|
2055
|
+
connectionRetryCount.removeValue(forKey: peripheral.identifier)
|
|
2056
|
+
// Notify peer lost since we're giving up
|
|
2057
|
+
if let deviceId = connections.peripheralDeviceId(for: peripheral.identifier) {
|
|
2058
|
+
protocolInstance.removeNeighborRoutes(neighborId: deviceId)
|
|
2059
|
+
try? self.protocolInstance.blePeerLost(peerId: deviceId)
|
|
2060
|
+
meshController.registerDisconnection(peerId: deviceId)
|
|
2061
|
+
refreshSelfMetrics()
|
|
2062
|
+
connections.removeConnectionRole(for: deviceId)
|
|
2063
|
+
connections.removePeripheralDeviceId(for: peripheral.identifier)
|
|
2064
|
+
connections.removeCentralDeviceId(for: peripheral.identifier)
|
|
2065
|
+
DispatchQueue.main.async {
|
|
2066
|
+
self.refreshAdvertising(reason: "disconnect_max_retries")
|
|
2067
|
+
}
|
|
2068
|
+
maybeHandleRebalance(reason: "disconnect_max_retries")
|
|
2069
|
+
}
|
|
2070
|
+
return
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
let backoffInterval = min(MAX_RECONNECT_INTERVAL, MIN_RECONNECT_INTERVAL * pow(2.0, Double(retryCount - 1)))
|
|
2074
|
+
|
|
2075
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + backoffInterval) { [weak self] in
|
|
1780
2076
|
guard let self = self else { return }
|
|
1781
2077
|
if self.state == .running && self.discoveredPeripherals[peripheral.identifier] != nil {
|
|
1782
2078
|
self.attemptConnection(to: peripheral, reason: "retry_disconnect")
|
|
@@ -2118,6 +2414,14 @@ extension BleManager: CBPeripheralManagerDelegate {
|
|
|
2118
2414
|
])
|
|
2119
2415
|
return
|
|
2120
2416
|
}
|
|
2417
|
+
|
|
2418
|
+
// Track this central as subscribed for connection count
|
|
2419
|
+
subscribedCentrals.insert(central.identifier)
|
|
2420
|
+
print("[BleManager] Central subscribed: \(central.identifier)")
|
|
2421
|
+
emitDiagnostic("info", "Central subscribed to characteristic", context: [
|
|
2422
|
+
"central": central.identifier.uuidString,
|
|
2423
|
+
"totalSubscribed": subscribedCentrals.count
|
|
2424
|
+
])
|
|
2121
2425
|
|
|
2122
2426
|
if connections.centralDeviceId(for: central.identifier) == nil && connections.peripheralDeviceId(for: central.identifier) == nil {
|
|
2123
2427
|
ensureDeviceId(for: central.identifier)
|
|
@@ -2131,9 +2435,11 @@ extension BleManager: CBPeripheralManagerDelegate {
|
|
|
2131
2435
|
|
|
2132
2436
|
public func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
|
|
2133
2437
|
print("[BleManager] Central unsubscribed from characteristic: \(characteristic.uuid)")
|
|
2438
|
+
subscribedCentrals.remove(central.identifier)
|
|
2134
2439
|
emitDiagnostic("info", "Central unsubscribed", context: [
|
|
2135
2440
|
"central": central.identifier.uuidString,
|
|
2136
|
-
"characteristic": characteristic.uuid.uuidString
|
|
2441
|
+
"characteristic": characteristic.uuid.uuidString,
|
|
2442
|
+
"remainingSubscribed": subscribedCentrals.count
|
|
2137
2443
|
])
|
|
2138
2444
|
connections.removeCentralDeviceId(for: central.identifier)
|
|
2139
2445
|
}
|
package/package.json
CHANGED