Improve auth WebSocket lifecycle.

This commit is contained in:
Cody Henthorne
2025-03-18 13:38:21 -04:00
committed by Alex Hart
parent 6bbd899507
commit 323697dfc9
16 changed files with 300 additions and 205 deletions

View File

@@ -67,6 +67,7 @@ import org.thoughtcrime.securesms.service.webrtc.SignalCallManager;
import org.thoughtcrime.securesms.shakereport.ShakeToReport;
import org.thoughtcrime.securesms.stories.Stories;
import org.thoughtcrime.securesms.util.AlarmSleepTimer;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.ByteUnit;
import org.thoughtcrime.securesms.util.EarlyMessageCache;
import org.thoughtcrime.securesms.util.Environment;
@@ -102,15 +103,14 @@ import org.whispersystems.signalservice.api.username.UsernameApi;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.websocket.HealthMonitor;
import org.whispersystems.signalservice.api.websocket.SignalWebSocket;
import org.whispersystems.signalservice.api.websocket.WebSocketFactory;
import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.websocket.LibSignalChatConnection;
import org.whispersystems.signalservice.internal.websocket.LibSignalNetworkExtensions;
import org.whispersystems.signalservice.internal.websocket.OkHttpWebSocketConnection;
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -303,10 +303,37 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider {
@Override
public @NonNull SignalWebSocket.AuthenticatedWebSocket provideAuthWebSocket(@NonNull Supplier<SignalServiceConfiguration> signalServiceConfigurationSupplier, @NonNull Supplier<Network> libSignalNetworkSupplier) {
SleepTimer sleepTimer = !SignalStore.account().isFcmEnabled() || SignalStore.internal().isWebsocketModeForced() ? new AlarmSleepTimer(context) : new UptimeSleepTimer();
SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(sleepTimer);
WebSocketFactory webSocketFactory = provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor, libSignalNetworkSupplier);
SignalWebSocket.AuthenticatedWebSocket webSocket = new SignalWebSocket.AuthenticatedWebSocket(webSocketFactory::createWebSocket);
SleepTimer sleepTimer = !SignalStore.account().isFcmEnabled() || SignalStore.internal().isWebsocketModeForced() ? new AlarmSleepTimer(context) : new UptimeSleepTimer();
SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(sleepTimer);
WebSocketFactory authFactory = () -> {
DynamicCredentialsProvider credentialsProvider = new DynamicCredentialsProvider();
if (credentialsProvider.isInvalid()) {
throw new WebSocketUnavailableException("Invalid auth credentials");
}
if (RemoteConfig.libSignalWebSocketEnabled()) {
Network network = libSignalNetworkSupplier.get();
return new LibSignalChatConnection("libsignal-auth",
network,
credentialsProvider,
Stories.isFeatureEnabled(),
healthMonitor);
} else {
return new OkHttpWebSocketConnection("auth",
signalServiceConfigurationSupplier.get(),
Optional.of(credentialsProvider),
BuildConfig.SIGNAL_AGENT,
healthMonitor,
Stories.isFeatureEnabled());
}
};
SignalWebSocket.AuthenticatedWebSocket webSocket = new SignalWebSocket.AuthenticatedWebSocket(authFactory, sleepTimer, TimeUnit.SECONDS.toMillis(10));
if (AppForegroundObserver.isForegrounded()) {
webSocket.registerKeepAliveToken(SignalWebSocket.FOREGROUND_KEEPALIVE);
}
healthMonitor.monitor(webSocket);
@@ -315,13 +342,33 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider {
@Override
public @NonNull SignalWebSocket.UnauthenticatedWebSocket provideUnauthWebSocket(@NonNull Supplier<SignalServiceConfiguration> signalServiceConfigurationSupplier, @NonNull Supplier<Network> libSignalNetworkSupplier) {
SleepTimer sleepTimer = !SignalStore.account().isFcmEnabled() || SignalStore.internal().isWebsocketModeForced() ? new AlarmSleepTimer(context) : new UptimeSleepTimer();
SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(sleepTimer);
WebSocketFactory webSocketFactory = provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor, libSignalNetworkSupplier);
SignalWebSocket.UnauthenticatedWebSocket webSocket = new SignalWebSocket.UnauthenticatedWebSocket(webSocketFactory::createUnidentifiedWebSocket);
SleepTimer sleepTimer = !SignalStore.account().isFcmEnabled() || SignalStore.internal().isWebsocketModeForced() ? new AlarmSleepTimer(context) : new UptimeSleepTimer();
SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(sleepTimer);
WebSocketFactory unauthFactory = () -> {
if (RemoteConfig.libSignalWebSocketEnabled()) {
Network network = libSignalNetworkSupplier.get();
return new LibSignalChatConnection("libsignal-unauth",
network,
null,
Stories.isFeatureEnabled(),
healthMonitor);
} else {
return new OkHttpWebSocketConnection("unauth",
signalServiceConfigurationSupplier.get(),
Optional.empty(),
BuildConfig.SIGNAL_AGENT,
healthMonitor,
Stories.isFeatureEnabled());
}
};
SignalWebSocket.UnauthenticatedWebSocket webSocket = new SignalWebSocket.UnauthenticatedWebSocket(unauthFactory, sleepTimer, TimeUnit.SECONDS.toMillis(10));
if (AppForegroundObserver.isForegrounded()) {
webSocket.registerKeepAliveToken(SignalWebSocket.FOREGROUND_KEEPALIVE);
}
healthMonitor.monitor(webSocket);
return webSocket;
}
@@ -413,51 +460,6 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider {
return provideClientZkOperations(signalServiceConfiguration).getReceiptOperations();
}
@NonNull WebSocketFactory provideWebSocketFactory(@NonNull Supplier<SignalServiceConfiguration> signalServiceConfigurationSupplier,
@NonNull HealthMonitor healthMonitor,
@NonNull Supplier<Network> libSignalNetworkSupplier)
{
return new WebSocketFactory() {
@Override
public WebSocketConnection createWebSocket() {
if (RemoteConfig.libSignalWebSocketEnabled()) {
Network network = libSignalNetworkSupplier.get();
return new LibSignalChatConnection("libsignal-auth",
network,
new DynamicCredentialsProvider(),
Stories.isFeatureEnabled(),
healthMonitor);
} else {
return new OkHttpWebSocketConnection("normal",
signalServiceConfigurationSupplier.get(),
Optional.of(new DynamicCredentialsProvider()),
BuildConfig.SIGNAL_AGENT,
healthMonitor,
Stories.isFeatureEnabled());
}
}
@Override
public WebSocketConnection createUnidentifiedWebSocket() {
if (RemoteConfig.libSignalWebSocketEnabled()) {
Network network = libSignalNetworkSupplier.get();
return new LibSignalChatConnection("libsignal-unauth",
network,
null,
Stories.isFeatureEnabled(),
healthMonitor);
} else {
return new OkHttpWebSocketConnection("unidentified",
signalServiceConfigurationSupplier.get(),
Optional.empty(),
BuildConfig.SIGNAL_AGENT,
healthMonitor,
Stories.isFeatureEnabled());
}
}
};
}
@Override
public @NonNull BillingApi provideBillingApi() {
return BillingFactory.create(GooglePlayBillingDependencies.INSTANCE, RemoteConfig.messageBackups() && Environment.Backups.supportsGooglePlayBilling());

View File

@@ -11,6 +11,7 @@ import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.subjects.Subject
import okhttp3.ConnectionSpec
import okhttp3.OkHttpClient
import org.signal.core.util.logging.Log
import org.signal.core.util.resettableLazy
import org.signal.libsignal.net.Network
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations
@@ -46,6 +47,7 @@ import org.whispersystems.signalservice.api.username.UsernameApi
import org.whispersystems.signalservice.api.util.Tls12SocketFactory
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.push.PushServiceSocket
import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager
import org.whispersystems.signalservice.internal.util.Util
@@ -64,6 +66,10 @@ class NetworkDependenciesModule(
private val webSocketStateSubject: Subject<WebSocketConnectionState>
) {
companion object {
private val TAG = "NetworkDependencies"
}
private val disposables: CompositeDisposable = CompositeDisposable()
val signalServiceNetworkAccess: SignalServiceNetworkAccess by lazy {
@@ -215,6 +221,7 @@ class NetworkDependenciesModule(
}
fun closeConnections() {
Log.i(TAG, "Closing connections.")
incomingMessageObserver.terminateAsync()
if (_signalServiceMessageSender.isInitialized()) {
signalServiceMessageSender.cancelInFlightRequests()
@@ -224,8 +231,19 @@ class NetworkDependenciesModule(
}
fun openConnections() {
try {
authWebSocket.connect()
} catch (e: WebSocketUnavailableException) {
Log.w(TAG, "Not allowed to start auth websocket", e)
}
try {
unauthWebSocket.connect()
} catch (e: WebSocketUnavailableException) {
Log.w(TAG, "Not allowed to start unauth websocket", e)
}
incomingMessageObserver
unauthWebSocket.connect()
}
fun resetProtocolStores() {