From e636a94de09f733d7954d8f0264f2a874c4e156a Mon Sep 17 00:00:00 2001 From: andrew-signal Date: Mon, 23 Feb 2026 16:53:22 -0500 Subject: [PATCH] Fix bug where we constantly cycled network stack when on network with PAC proxy. --- .../securesms/dependencies/AppDependencies.kt | 5 +++-- .../securesms/messages/IncomingMessageObserver.kt | 12 +++++++----- .../securesms/push/SignalServiceNetworkAccess.kt | 11 +++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt index 427d9d2b46..0e9bc13d54 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt @@ -74,6 +74,7 @@ import org.whispersystems.signalservice.api.svr.SvrBApi import org.whispersystems.signalservice.api.username.UsernameApi import org.whispersystems.signalservice.api.websocket.SignalWebSocket import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState +import org.whispersystems.signalservice.internal.configuration.HttpProxy import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration import org.whispersystems.signalservice.internal.push.PushServiceSocket import java.util.function.Supplier @@ -410,9 +411,9 @@ object AppDependencies { networkModule.openConnections() } - fun onSystemHttpProxyChange(host: String?, port: Int?): Boolean { + fun onSystemHttpProxyChange(systemHttpProxy: HttpProxy?): Boolean { val currentSystemProxy = signalServiceNetworkAccess.getConfiguration().systemHttpProxy.orNull() - return if (currentSystemProxy?.host != host || currentSystemProxy?.port != port) { + return if (currentSystemProxy?.host != systemHttpProxy?.host || currentSystemProxy?.port != systemHttpProxy?.port) { resetNetwork() true } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt index 2644ed4e39..9fda11d5c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt @@ -4,7 +4,6 @@ import android.app.Application import android.app.Service import android.content.Context import android.content.Intent -import android.net.ProxyInfo import android.os.IBinder import androidx.annotation.VisibleForTesting import androidx.core.app.NotificationCompat @@ -31,6 +30,7 @@ import org.thoughtcrime.securesms.keyvalue.isDecisionPending import org.thoughtcrime.securesms.messages.MessageDecryptor.FollowUpOperation import org.thoughtcrime.securesms.messages.protocol.BufferedProtocolStore import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess.Companion.toApplicableSystemHttpProxy import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.util.AlarmSleepTimer import org.thoughtcrime.securesms.util.AppForegroundObserver @@ -42,6 +42,7 @@ import org.whispersystems.signalservice.api.util.UptimeSleepTimer import org.whispersystems.signalservice.api.websocket.SignalWebSocket import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException +import org.whispersystems.signalservice.internal.configuration.HttpProxy import org.whispersystems.signalservice.internal.push.Envelope import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.Semaphore @@ -91,7 +92,7 @@ class IncomingMessageObserver( private val lock: ReentrantLock = ReentrantLock() private val connectionNecessarySemaphore = Semaphore(0) - private var previousProxyInfo: ProxyInfo? = null + private var previousSystemHttpProxy: HttpProxy? = null private val networkConnectionListener = NetworkConnectionListener( context = context, onNetworkLost = { isNetworkUnavailable -> @@ -108,13 +109,14 @@ class IncomingMessageObserver( } }, onProxySettingsChanged = { proxyInfo -> - if (proxyInfo != previousProxyInfo) { - val networkReset = AppDependencies.onSystemHttpProxyChange(proxyInfo?.host, proxyInfo?.port) + val systemHttpProxy = proxyInfo.toApplicableSystemHttpProxy() + if (systemHttpProxy?.host != previousSystemHttpProxy?.host || systemHttpProxy?.port != previousSystemHttpProxy?.port) { + val networkReset = AppDependencies.onSystemHttpProxyChange(systemHttpProxy) if (networkReset) { Log.i(TAG, "System proxy configuration changed, network reset.") } } - previousProxyInfo = proxyInfo + previousSystemHttpProxy = systemHttpProxy } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt index 65eaa593be..54781a4443 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.push import android.content.Context import android.net.ConnectivityManager +import android.net.ProxyInfo import android.net.Uri import androidx.core.content.ContextCompat import com.google.i18n.phonenumbers.PhoneNumberUtil @@ -143,9 +144,15 @@ class SignalServiceNetworkAccess(context: Context) { private fun getSystemHttpProxy(context: Context): HttpProxy? { val connectivityManager = ContextCompat.getSystemService(context, ConnectivityManager::class.java) ?: return null - return connectivityManager + val proxyInfo = connectivityManager .activeNetwork ?.let { connectivityManager.getLinkProperties(it)?.httpProxy } + + return proxyInfo.toApplicableSystemHttpProxy() + } + + fun ProxyInfo?.toApplicableSystemHttpProxy(): HttpProxy? { + return this ?.takeIf { !it.exclusionList.contains(BuildConfig.SIGNAL_URL.stripProtocol()) } // NB: Edit carefully, dear reader, as the line below is written from hard won experience. // It turns out, that despite being documented *nowhere*, if a PAC file is set @@ -156,7 +163,7 @@ class SignalServiceNetworkAccess(context: Context) { // So, if we do not explicitly check that a PAC file is not set, the proxy // we pass to libsignal may be syntactically invalid, and the user may be // rendered unable to connect. - ?.takeIf { proxyInfo -> proxyInfo.pacFileUrl.equals(Uri.EMPTY) } + ?.takeIf { it.pacFileUrl == Uri.EMPTY } ?.let { proxy -> HttpProxy(proxy.host, proxy.port) } } }