Improve calling reliability with relay server response cache.

Co-authored-by: Cody Henthorne <cody@signal.org>
This commit is contained in:
Jim Gustafson
2025-03-18 03:50:54 -07:00
committed by Cody Henthorne
parent 92a28f7103
commit 076b47e695
3 changed files with 79 additions and 11 deletions

View File

@@ -1033,17 +1033,26 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
public void retrieveTurnServers(@NonNull RemotePeer remotePeer) {
networkExecutor.execute(() -> {
try {
List<TurnServerInfo> turnServerInfos = NetworkResultUtil.toBasicLegacy(SignalNetwork.calling().getTurnServerInfo());
List<PeerConnection.IceServer> iceServers = mapToIceServers(turnServerInfos);
process((s, p) -> {
RemotePeer activePeer = s.getCallInfoState().getActivePeer();
if (activePeer != null && activePeer.getCallId().equals(remotePeer.getCallId())) {
return p.handleTurnServerUpdate(s, iceServers, TextSecurePreferences.isTurnOnly(context));
}
List<PeerConnection.IceServer> cachedServers = TurnServerCache.getCachedServers();
if (cachedServers != null) {
processTurnServers(remotePeer, cachedServers);
return;
}
Log.w(TAG, "Ignoring received turn servers for incorrect call id. requesting_call_id: " + remotePeer.getCallId() + " current_call_id: " + (activePeer != null ? activePeer.getCallId() : "null"));
return s;
});
List<TurnServerInfo> turnServerInfos = NetworkResultUtil.toBasicLegacy(SignalNetwork.calling().getTurnServerInfo());
// Find *any* provided ttl values as long as they are valid.
long minTtl = turnServerInfos.stream()
.map(TurnServerInfo::getTtl)
.filter(ttl -> ttl != null && ttl > 0)
.min(Long::compare)
.orElse(0L);
List<PeerConnection.IceServer> iceServers = mapToIceServers(turnServerInfos);
TurnServerCache.updateCache(iceServers, minTtl);
processTurnServers(remotePeer, iceServers);
} catch (IOException e) {
Log.w(TAG, "Unable to retrieve turn servers: ", e);
process((s, p) -> p.handleSetupFailure(s, remotePeer.getCallId()));
@@ -1051,6 +1060,18 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
});
}
private void processTurnServers(RemotePeer remotePeer, List<PeerConnection.IceServer> servers) {
process((s, p) -> {
RemotePeer activePeer = s.getCallInfoState().getActivePeer();
if (activePeer != null && activePeer.getCallId().equals(remotePeer.getCallId())) {
return p.handleTurnServerUpdate(s, servers, TextSecurePreferences.isTurnOnly(context));
}
Log.w(TAG, "Ignoring received turn servers for incorrect call id. requesting_call_id: " + remotePeer.getCallId() + " current_call_id: " + (activePeer != null ? activePeer.getCallId() : "null"));
return s;
});
}
private static List<PeerConnection.IceServer> mapToIceServers(@NonNull List<TurnServerInfo> turnServerInfos) {
List<PeerConnection.IceServer> iceServers = new ArrayList<>();

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.service.webrtc
import android.os.SystemClock
import org.webrtc.PeerConnection
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
/**
* Provide an in-memory cache of TURN servers used for calling as the endpoint
* is rate limited and the data should be valid for the duration of a app run.
*/
object TurnServerCache {
private var iceServers: List<PeerConnection.IceServer>? = null
private var lastUpdated: Duration = Duration.ZERO
private var cacheTtl: Duration = Duration.ZERO
@JvmStatic
fun getCachedServers(): List<PeerConnection.IceServer>? {
val now = SystemClock.elapsedRealtime().milliseconds
return if (iceServers != null && now > lastUpdated && now < (lastUpdated + cacheTtl)) {
iceServers
} else {
null
}
}
@JvmStatic
fun updateCache(newServers: List<PeerConnection.IceServer>, ttl: Long) {
lastUpdated = SystemClock.elapsedRealtime().milliseconds
cacheTtl = ttl.seconds
iceServers = newServers
}
}

View File

@@ -1,6 +1,5 @@
package org.whispersystems.signalservice.api.messages.calls;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
@@ -22,6 +21,9 @@ public class TurnServerInfo {
@JsonProperty
private List<String> urlsWithIps;
@JsonProperty
private Long ttl;
public String getUsername() {
return username;
}
@@ -42,4 +44,8 @@ public class TurnServerInfo {
public List<String> getUrlsWithIps() {
return urlsWithIps;
}
public Long getTtl() {
return ttl;
};
}