From dae7834488d4c1bcd32d97816f7f0a69609455c0 Mon Sep 17 00:00:00 2001 From: "henry.yao" Date: Tue, 2 May 2023 20:01:30 +0800 Subject: [PATCH 1/2] Try to reconnect when network gets available --- .../github/gotify/service/WebSocketService.kt | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt index eb331b4..108d054 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt @@ -5,9 +5,11 @@ import android.app.Notification import android.app.NotificationManager import android.app.PendingIntent import android.app.Service +import android.content.Context import android.content.Intent import android.graphics.Color import android.net.ConnectivityManager +import android.net.Network import android.net.Uri import android.os.Build import android.os.IBinder @@ -43,6 +45,14 @@ internal class WebSocketService : Service() { private lateinit var settings: Settings private var connection: WebSocketConnection? = null + private val networkCallback: ConnectivityManager.NetworkCallback = + object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + super.onAvailable(network) + Log.i("WebSocket: Network available, reconnect if needed.") + connection?.start() + } + } private val appIdToApp = ConcurrentHashMap() private val lastReceivedMessage = AtomicLong(NOT_LOADED) @@ -67,9 +77,12 @@ internal class WebSocketService : Service() { override fun onDestroy() { super.onDestroy() - if (connection != null) { - connection!!.close() + connection?.close() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + (getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager) + .unregisterNetworkCallback(networkCallback) } + Log.w("Destroy ${javaClass.simpleName}") } @@ -110,7 +123,9 @@ internal class WebSocketService : Service() { .onMessage { message -> onMessage(message) } .onReconnected { notifyMissedNotifications() } .start() - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + cm.registerDefaultNetworkCallback(networkCallback) + } fetchApps() } From 9d8ee015195ba72afeba4b0421958819f2922770 Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Tue, 2 May 2023 18:32:38 +0200 Subject: [PATCH 2/2] Fix thread safety syncExec synced on the Listener instance, but it modified properties of the WebSocketConnection without synchronizing on the WebSocketConnection. --- .../gotify/service/WebSocketConnection.kt | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt index b76605e..f0b80ec 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt @@ -116,13 +116,18 @@ internal class WebSocketConnection( @Synchronized fun close() { if (webSocket != null) { + webSocket?.close(1000, "") + closed() Log.i("WebSocket(${ID.get()}): closing existing connection.") - state = State.Disconnected - webSocket!!.close(1000, "") - webSocket = null } } + @Synchronized + private fun closed() { + webSocket = null + state = State.Disconnected + } + @Synchronized fun scheduleReconnect(seconds: Long) { if (state == State.Connecting || state == State.Connected) { @@ -150,7 +155,7 @@ internal class WebSocketConnection( private inner class Listener(private val id: Long) : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { - syncExec { + syncExec(id) { state = State.Connected Log.i("WebSocket($id): opened") onOpen.run() @@ -164,7 +169,7 @@ internal class WebSocketConnection( } override fun onMessage(webSocket: WebSocket, text: String) { - syncExec { + syncExec(id) { Log.i("WebSocket($id): received message $text") val message = Utils.JSON.fromJson(text, Message::class.java) onMessageCallback(message) @@ -173,12 +178,12 @@ internal class WebSocketConnection( } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { - syncExec { + syncExec(id) { if (state == State.Connected) { Log.w("WebSocket($id): closed") onClose.run() } - state = State.Disconnected + closed() } super.onClosed(webSocket, code, reason) } @@ -187,11 +192,10 @@ internal class WebSocketConnection( val code = if (response != null) "StatusCode: ${response.code()}" else "" val message = if (response != null) response.message() else "" Log.e("WebSocket($id): failure $code Message: $message", t) - syncExec { - state = State.Disconnected + syncExec(id) { + closed() if (response != null && response.code() >= 400 && response.code() <= 499) { onBadRequest.execute(message) - close() return@syncExec } @@ -209,13 +213,12 @@ internal class WebSocketConnection( } super.onFailure(webSocket, t, response) } + } - private fun syncExec(runnable: Runnable) { - synchronized(this) { - if (ID.get() == id) { - runnable.run() - } - } + @Synchronized + private fun syncExec(id: Long, runnable: () -> Unit) { + if (ID.get() == id) { + runnable() } }