Prepared Sync logic to be separated from the rest of the logic.

This commit is contained in:
Koen J 2025-05-16 12:11:41 +02:00
commit 120ded5274
3 changed files with 77 additions and 51 deletions

View file

@ -67,6 +67,7 @@ class StateSync {
mdnsBroadcast = Settings.instance.synchronization.broadcast,
mdnsConnectDiscovered = Settings.instance.synchronization.connectDiscovered,
bindListener = Settings.instance.synchronization.localConnections,
connectLastKnown = Settings.instance.synchronization.connectLast,
relayHandshakeAllowed = Settings.instance.synchronization.connectThroughRelay,
relayPairAllowed = Settings.instance.synchronization.pairThroughRelay,
relayEnabled = Settings.instance.synchronization.discoverThroughRelay,
@ -74,21 +75,21 @@ class StateSync {
relayConnectRelayed = Settings.instance.synchronization.connectThroughRelay
)
).apply {
syncService?.onAuthorized = { sess, isNewlyAuthorized, isNewSession ->
onAuthorized = { sess, isNewlyAuthorized, isNewSession ->
if (isNewSession) {
deviceUpdatedOrAdded.emit(sess.remotePublicKey, sess)
StateApp.instance.scope.launch { checkForSync(sess) }
StateApp.instance.scope.launch(Dispatchers.IO) { checkForSync(sess) }
}
}
syncService?.onUnauthorized = { sess ->
onUnauthorized = { sess ->
StateApp.instance.scope.launch(Dispatchers.Main) {
UIDialogs.showConfirmationDialog(
context,
"Device Unauthorized: ${sess.displayName}",
action = {
Logger.i(TAG, "${sess.remotePublicKey} unauthorized received")
syncService?.removeAuthorizedDevice(sess.remotePublicKey)
removeAuthorizedDevice(sess.remotePublicKey)
deviceRemoved.emit(sess.remotePublicKey)
},
cancelAction = {}
@ -96,9 +97,9 @@ class StateSync {
}
}
syncService?.onConnectedChanged = { sess, _ -> deviceUpdatedOrAdded.emit(sess.remotePublicKey, sess) }
syncService?.onClose = { sess -> deviceRemoved.emit(sess.remotePublicKey) }
syncService?.onData = { it, opcode, subOpcode, data ->
onConnectedChanged = { sess, _ -> deviceUpdatedOrAdded.emit(sess.remotePublicKey, sess) }
onClose = { sess -> deviceRemoved.emit(sess.remotePublicKey) }
onData = { it, opcode, subOpcode, data ->
val dataCopy = ByteArray(data.remaining())
data.get(dataCopy)
@ -111,7 +112,7 @@ class StateSync {
}
}
}
syncService?.authorizePrompt = { remotePublicKey, callback ->
authorizePrompt = { remotePublicKey, callback ->
val scope = StateApp.instance.scopeOrNull
val activity = SyncShowPairingCodeActivity.activity

View file

@ -66,6 +66,7 @@ class SyncService(
private var _serverSocket: ServerSocket? = null
private var _thread: Thread? = null
private var _connectThread: Thread? = null
private var _mdnsThread: Thread? = null
@Volatile private var _started = false
private val _sessions: MutableMap<String, SyncSession> = mutableMapOf()
private val _lastConnectTimesMdns: MutableMap<String, Long> = mutableMapOf()
@ -82,6 +83,7 @@ class SyncService(
private val _remotePendingStatusUpdate = mutableMapOf<String, (complete: Boolean?, message: String) -> Unit>()
private var _nsdManager: NsdManager? = null
private var _scope: CoroutineScope? = null
private val _mdnsCache = mutableMapOf<String, SyncDeviceInfo>()
private var _discoveryListener: NsdManager.DiscoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(regType: String) {
Log.d(TAG, "Service discovery started for $regType")
@ -93,7 +95,11 @@ class SyncService(
override fun onServiceLost(service: NsdServiceInfo) {
Log.e(TAG, "service lost: $service")
// TODO: Handle service lost, e.g., remove device
val urlSafePkey = service.attributes["pk"]?.decodeToString() ?: return
val pkey = Base64.getDecoder().decode(urlSafePkey.replace('-', '+').replace('_', '/')).toBase64()
synchronized(_mdnsCache) {
_mdnsCache.remove(pkey)
}
}
override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
@ -120,30 +126,11 @@ class SyncService(
}
val urlSafePkey = attributes.get("pk")?.decodeToString() ?: return
val pkey = Base64.getEncoder().encodeToString(Base64.getDecoder().decode(urlSafePkey.replace('-', '+').replace('_', '/')))
val pkey = Base64.getDecoder().decode(urlSafePkey.replace('-', '+').replace('_', '/')).toBase64()
val syncDeviceInfo = SyncDeviceInfo(pkey, adrs.map { it.hostAddress }.toTypedArray(), port, null)
val authorized = isAuthorized(pkey)
if (authorized && !isConnected(pkey)) {
val now = System.currentTimeMillis()
val lastConnectTime = synchronized(_lastConnectTimesMdns) {
_lastConnectTimesMdns[pkey] ?: 0
}
//Connect once every 30 seconds, max
if (now - lastConnectTime > 30000) {
synchronized(_lastConnectTimesMdns) {
_lastConnectTimesMdns[pkey] = now
}
Logger.i(TAG, "Found device authorized device '${name}' with pkey=$pkey, attempting to connect")
try {
connect(syncDeviceInfo)
} catch (e: Throwable) {
Logger.i(TAG, "Failed to connect to $pkey", e)
}
}
synchronized(_mdnsCache) {
_mdnsCache[pkey] = syncDeviceInfo
}
}
@ -167,7 +154,11 @@ class SyncService(
override fun onServiceLost() {
Log.v(TAG, "onServiceLost: $service")
// TODO: Handle service lost
val urlSafePkey = service.attributes["pk"]?.decodeToString() ?: return
val pkey = Base64.getDecoder().decode(urlSafePkey.replace('-', '+').replace('_', '/')).toBase64()
synchronized(_mdnsCache) {
_mdnsCache.remove(pkey)
}
}
override fun onServiceInfoCallbackRegistrationFailed(errorCode: Int) {
@ -260,9 +251,7 @@ class SyncService(
_nsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
if (settings.mdnsConnectDiscovered) {
_nsdManager?.apply {
discoverServices(serviceName, NsdManager.PROTOCOL_DNS_SD, _discoveryListener)
}
startMdnsRetryLoop()
}
if (settings.mdnsBroadcast) {
@ -319,6 +308,42 @@ class SyncService(
}.apply { start() }
}
private fun startMdnsRetryLoop() {
_nsdManager?.apply {
discoverServices(serviceName, NsdManager.PROTOCOL_DNS_SD, _discoveryListener)
}
_mdnsThread = Thread {
while (_started) {
try {
val now = System.currentTimeMillis()
synchronized(_mdnsCache) {
for ((pkey, info) in _mdnsCache) {
if (!database.isAuthorized(pkey) || isConnected(pkey)) continue
val last = synchronized(_lastConnectTimesMdns) {
_lastConnectTimesMdns[pkey] ?: 0L
}
if (now - last > 30_000L) {
_lastConnectTimesMdns[pkey] = now
try {
Logger.i(TAG, "MDNS-retry: connecting to $pkey")
connect(info)
} catch (ex: Throwable) {
Logger.w(TAG, "MDNS retry failed for $pkey", ex)
}
}
}
}
} catch (ex: Throwable) {
Logger.e(TAG, "Error in MDNS retry loop", ex)
}
Thread.sleep(5000)
}
}.apply { start() }
}
private fun startConnectLastLoop() {
_connectThread = Thread {
Log.i(TAG, "Running auto reconnector")

View file

@ -370,22 +370,22 @@
<string name="networking">Networking</string>
<string name="synchronization">Synchronization</string>
<string name="enabled_description">Enable feature</string>
<string name="broadcast">Broadcast</string>
<string name="broadcast_description">Allow device to broadcast presence</string>
<string name="connect_discovered">Connect discovered</string>
<string name="connect_discovered_description">Allow device to search for and initiate connection with known paired devices</string>
<string name="connect_last">Try connect last</string>
<string name="connect_last_description">Allow device to automatically connect to last known</string>
<string name="discover_through_relay">Discover through relay</string>
<string name="discover_through_relay_description">Allow paired devices to be discovered and connected to through the relay</string>
<string name="pair_through_relay">Pair through relay</string>
<string name="pair_through_relay_description">Allow devices to be paired through the relay</string>
<string name="connect_through_relay">Connection through relay</string>
<string name="connect_through_relay_description">Allow devices to be connected to through the relay</string>
<string name="connect_local_direct_through_relay">Connect direct through relay</string>
<string name="connect_local_direct_through_relay_description">Allow devices to be directly locally connected to through information discovered from the relay</string>
<string name="local_connections">Local connections</string>
<string name="local_connections_description">Allow device to be directly locally connected</string>
<string name="broadcast">mDNS Broadcast</string>
<string name="broadcast_description">Allow device to broadcast presence using mDNS</string>
<string name="connect_discovered">mDNS Connect</string>
<string name="connect_discovered_description">Allow device to search for and initiate connection with known paired devices using mDNS</string>
<string name="connect_last">Connect Last Known</string>
<string name="connect_last_description">Allow device to automatically connect to last known endpoints</string>
<string name="discover_through_relay">Relay Enable</string>
<string name="discover_through_relay_description">Allow device to use a relay for discovery/relaying connection</string>
<string name="pair_through_relay">Relay Pairing</string>
<string name="pair_through_relay_description">Allow device to be paired through the relay</string>
<string name="connect_through_relay">Relay Connect Relayed</string>
<string name="connect_through_relay_description">Allow device to be connected to using a relayed connection</string>
<string name="connect_local_direct_through_relay">Relay Connect Direct</string>
<string name="connect_local_direct_through_relay_description">Allow device to be directly connected to using relay published information</string>
<string name="local_connections">Bind Listener</string>
<string name="local_connections_description">Allow device to be directly connected to</string>
<string name="gesture_controls">Gesture controls</string>
<string name="volume_slider">Volume slider</string>
<string name="volume_slider_descr">Enable slide gesture to change volume</string>