From 4d170db5e0c37fea19a0cd53b2c25f96afbce7ff Mon Sep 17 00:00:00 2001 From: Koen J Date: Thu, 5 Jun 2025 10:31:13 +0200 Subject: [PATCH] Improvements to connection publishing for sync. --- .../java/com/futo/platformplayer/Utility.kt | 27 +++++++++++++++++++ .../sync/internal/SyncService.kt | 20 +++++++++----- .../sync/internal/SyncSocketSession.kt | 22 +++++---------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/Utility.kt b/app/src/main/java/com/futo/platformplayer/Utility.kt index bfa7925b..e2868ed6 100644 --- a/app/src/main/java/com/futo/platformplayer/Utility.kt +++ b/app/src/main/java/com/futo/platformplayer/Utility.kt @@ -339,6 +339,33 @@ fun ByteArray.fromGzip(): ByteArray { return outputStream.toByteArray() } +fun findCandidateAddresses(): List { + val candidates = NetworkInterface.getNetworkInterfaces() + .toList() + .asSequence() + .filter(::isUsableInterface) + .flatMap { nif -> + nif.interfaceAddresses + .asSequence() + .mapNotNull { ia -> + ia.address.takeIf(::isUsableAddress)?.let { addr -> + nif to ia + } + } + } + .toList() + + return candidates + .sortedWith( + compareBy>( + { addressScore(it.second.address) }, + { interfaceScore(it.first) }, + { -it.second.networkPrefixLength.toInt() }, + { -it.first.mtu } + ) + ).map { it.second.address } +} + fun findPreferredAddress(): InetAddress? { val candidates = NetworkInterface.getNetworkInterfaces() .toList() diff --git a/app/src/main/java/com/futo/platformplayer/sync/internal/SyncService.kt b/app/src/main/java/com/futo/platformplayer/sync/internal/SyncService.kt index eb77c02d..9281f144 100644 --- a/app/src/main/java/com/futo/platformplayer/sync/internal/SyncService.kt +++ b/app/src/main/java/com/futo/platformplayer/sync/internal/SyncService.kt @@ -328,7 +328,7 @@ class SyncService( val now = System.currentTimeMillis() synchronized(_mdnsCache) { for ((pkey, info) in _mdnsCache) { - if (!database.isAuthorized(pkey) || isConnected(pkey)) continue + if (!database.isAuthorized(pkey) || getLinkType(pkey) == LinkType.Direct) continue val last = synchronized(_lastConnectTimesMdns) { _lastConnectTimesMdns[pkey] ?: 0L @@ -360,8 +360,8 @@ class SyncService( while (_started) { val authorizedDevices = database.getAllAuthorizedDevices() ?: arrayOf() val addressesToConnect = authorizedDevices.mapNotNull { - val connected = isConnected(it) - if (connected) { + val connectedDirectly = getLinkType(it) == LinkType.Direct + if (connectedDirectly) { return@mapNotNull null } @@ -468,8 +468,13 @@ class SyncService( while (_started && !socketClosed) { val unconnectedAuthorizedDevices = database.getAllAuthorizedDevices() - ?.filter { !isConnected(it) }?.toTypedArray() - ?: arrayOf() + ?.filter { + if (Settings.instance.synchronization.connectLocalDirectThroughRelay) { + getLinkType(it) != LinkType.Direct + } else { + !isConnected(it) + } + }?.toTypedArray() ?: arrayOf() relaySession.publishConnectionInformation( unconnectedAuthorizedDevices, settings.listenerPort, @@ -497,7 +502,7 @@ class SyncService( val potentialLocalAddresses = connectionInfo.ipv4Addresses .filter { it != connectionInfo.remoteIp } - if (connectionInfo.allowLocalDirect && Settings.instance.synchronization.connectLocalDirectThroughRelay) { + if (getLinkType(targetKey) != LinkType.Direct && connectionInfo.allowLocalDirect && Settings.instance.synchronization.connectLocalDirectThroughRelay) { Thread { try { Log.v( @@ -529,7 +534,7 @@ class SyncService( // TODO: Implement hole punching if needed } - if (connectionInfo.allowRemoteRelayed && Settings.instance.synchronization.connectThroughRelay) { + if (getLinkType(targetKey) == LinkType.None && connectionInfo.allowRemoteRelayed && Settings.instance.synchronization.connectThroughRelay) { try { Logger.v( TAG, @@ -741,6 +746,7 @@ class SyncService( ) } + fun getLinkType(publicKey: String): LinkType = synchronized(_sessions) { _sessions[publicKey]?.linkType ?: LinkType.None } fun isConnected(publicKey: String): Boolean = synchronized(_sessions) { _sessions[publicKey]?.connected ?: false } fun isAuthorized(publicKey: String): Boolean = database.isAuthorized(publicKey) fun getSession(publicKey: String): SyncSession? = synchronized(_sessions) { _sessions[publicKey] } diff --git a/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSocketSession.kt b/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSocketSession.kt index e3b7fb30..8e75e5de 100644 --- a/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSocketSession.kt +++ b/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSocketSession.kt @@ -2,6 +2,7 @@ package com.futo.platformplayer.sync.internal import android.os.Build import com.futo.platformplayer.ensureNotMainThread +import com.futo.platformplayer.findCandidateAddresses import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.noise.protocol.CipherStatePair import com.futo.platformplayer.noise.protocol.DHState @@ -1078,20 +1079,9 @@ class SyncSocketSession { ) { if (authorizedKeys.size > 255) throw IllegalArgumentException("Number of authorized keys exceeds 255") - val ipv4Addresses = mutableListOf() - val ipv6Addresses = mutableListOf() - for (nic in NetworkInterface.getNetworkInterfaces()) { - if (nic.isUp) { - for (addr in nic.inetAddresses) { - if (!addr.isLoopbackAddress) { - when (addr) { - is Inet4Address -> ipv4Addresses.add(addr.hostAddress) - is Inet6Address -> ipv6Addresses.add(addr.hostAddress) - } - } - } - } - } + val candidateAddresses = findCandidateAddresses() + val ipv4Addresses = candidateAddresses.filterIsInstance() + val ipv6Addresses = candidateAddresses.filterIsInstance() val deviceName = getDeviceName() val nameBytes = getLimitedUtf8Bytes(deviceName, 255) @@ -1103,12 +1093,12 @@ class SyncSocketSession { data.put(nameBytes) data.put(ipv4Addresses.size.toByte()) for (addr in ipv4Addresses) { - val addrBytes = InetAddress.getByName(addr).address + val addrBytes = addr.address data.put(addrBytes) } data.put(ipv6Addresses.size.toByte()) for (addr in ipv6Addresses) { - val addrBytes = InetAddress.getByName(addr).address + val addrBytes = addr.address data.put(addrBytes) } data.put(if (allowLocalDirect) 1 else 0)