remember terminal RTP session state

if the activity is not connected during finish it won’t receive the last end user state.

this code remembers it even if the actual session is already gone. so when activity reconnects and
we can’t find the real rtp session we can look up the last state instead.
This commit is contained in:
Daniel Gultsch 2020-06-11 21:17:13 +02:00
parent 7e2d87f39c
commit 552e17e39a
3 changed files with 42 additions and 8 deletions

View file

@ -53,6 +53,7 @@ import eu.siacs.conversations.utils.PermissionUtils;
import eu.siacs.conversations.utils.TimeFrameUtils; import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
@ -427,7 +428,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager() final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
.findJingleRtpConnection(account, with, sessionId); .findJingleRtpConnection(account, with, sessionId);
if (reference == null || reference.get() == null) { if (reference == null || reference.get() == null) {
throw new IllegalStateException("failed to initialize activity with running rtp session. session not found"); final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession = xmppConnectionService
.getJingleConnectionManager().getTerminalSessionState(with, sessionId);
if (terminatedRtpSession == null) {
throw new IllegalStateException("failed to initialize activity with running rtp session. session not found");
}
initializeWithTerminatedSessionState(account, with, terminatedRtpSession);
return true;
} }
this.rtpConnectionReference = reference; this.rtpConnectionReference = reference;
final RtpEndUserState currentState = requireRtpConnection().getEndUserState(); final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
@ -451,6 +458,20 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return false; return false;
} }
private void initializeWithTerminatedSessionState(final Account account, final Jid with, final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession) {
Log.d(Config.LOGTAG,"initializeWithTerminatedSessionState()");
if (terminatedRtpSession.state == RtpEndUserState.ENDED) {
finish();
return;
}
RtpEndUserState state = terminatedRtpSession.state;
resetIntent(account, with, terminatedRtpSession.state, terminatedRtpSession.media);
updateButtonConfiguration(state);
updateStateDisplay(state);
updateProfilePicture(state);
binding.with.setText(account.getRoster().getContact(with).getDisplayName());
}
private void reInitializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) { private void reInitializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
runOnUiThread(() -> initializeActivityWithRunningRtpSession(account, with, sessionId)); runOnUiThread(() -> initializeActivityWithRunningRtpSession(account, with, sessionId));
resetIntent(account, with, sessionId); resetIntent(account, with, sessionId);

View file

@ -11,7 +11,6 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.ComparisonChain; import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.j2objc.annotations.Weak;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.security.SecureRandom; import java.security.SecureRandom;
@ -57,8 +56,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>(); private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>();
private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>(); private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
private final Cache<PersistableSessionId, JingleRtpConnection.State> endedSessions = CacheBuilder.newBuilder() private final Cache<PersistableSessionId, TerminatedRtpSession> terminatedSessions = CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.MINUTES) .expireAfterWrite(24, TimeUnit.HOURS)
.build(); .build();
private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>(); private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
@ -92,7 +91,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if (FileTransferDescription.NAMESPACES.contains(descriptionNamespace)) { if (FileTransferDescription.NAMESPACES.contains(descriptionNamespace)) {
connection = new JingleFileTransferConnection(this, id, from); connection = new JingleFileTransferConnection(this, id, from);
} else if (Namespace.JINGLE_APPS_RTP.equals(descriptionNamespace) && !usesTor(account)) { } else if (Namespace.JINGLE_APPS_RTP.equals(descriptionNamespace) && !usesTor(account)) {
final boolean sessionEnded = this.endedSessions.asMap().containsKey(PersistableSessionId.of(id)); final boolean sessionEnded = this.terminatedSessions.asMap().containsKey(PersistableSessionId.of(id));
final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with); final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
if (isBusy() || sessionEnded || stranger) { if (isBusy() || sessionEnded || stranger) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": rejected session with " + id.with + " because busy. sessionEnded=" + sessionEnded + ", stranger=" + stranger); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": rejected session with " + id.with + " because busy. sessionEnded=" + sessionEnded + ", stranger=" + stranger);
@ -684,8 +683,12 @@ public class JingleConnectionManager extends AbstractConnectionManager {
throw e; throw e;
} }
void endSession(AbstractJingleConnection.Id id, final AbstractJingleConnection.State state) { void setTerminalSessionState(AbstractJingleConnection.Id id, final RtpEndUserState state, final Set<Media> media) {
this.endedSessions.put(PersistableSessionId.of(id), state); this.terminatedSessions.put(PersistableSessionId.of(id), new TerminatedRtpSession(state, media));
}
public TerminatedRtpSession getTerminalSessionState(final Jid with, final String sessionId) {
return this.terminatedSessions.getIfPresent(new PersistableSessionId(with, sessionId));
} }
private static class PersistableSessionId { private static class PersistableSessionId {
@ -716,6 +719,16 @@ public class JingleConnectionManager extends AbstractConnectionManager {
} }
} }
public static class TerminatedRtpSession {
public final RtpEndUserState state;
public final Set<Media> media;
TerminatedRtpSession(RtpEndUserState state, Set<Media> media) {
this.state = state;
this.media = media;
}
}
public enum DeviceDiscoveryState { public enum DeviceDiscoveryState {
SEARCHING, DISCOVERED, FAILED; SEARCHING, DISCOVERED, FAILED;

View file

@ -912,7 +912,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
if (isInState(State.PROCEED)) { if (isInState(State.PROCEED)) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ending call while in state PROCEED just means ending the connection"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ending call while in state PROCEED just means ending the connection");
this.jingleConnectionManager.endSession(id, State.TERMINATED_SUCCESS);
this.webRTCWrapper.close(); this.webRTCWrapper.close();
transitionOrThrow(State.TERMINATED_SUCCESS); //arguably this wasn't success; but not a real failure either transitionOrThrow(State.TERMINATED_SUCCESS); //arguably this wasn't success; but not a real failure either
this.finish(); this.finish();
@ -1189,6 +1188,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (isTerminated()) { if (isTerminated()) {
this.cancelRingingTimeout(); this.cancelRingingTimeout();
this.webRTCWrapper.verifyClosed(); this.webRTCWrapper.verifyClosed();
this.jingleConnectionManager.setTerminalSessionState(id, getEndUserState(), getMedia());
this.jingleConnectionManager.finishConnectionOrThrow(this); this.jingleConnectionManager.finishConnectionOrThrow(this);
} else { } else {
throw new IllegalStateException(String.format("Unable to call finish from %s", this.state)); throw new IllegalStateException(String.format("Unable to call finish from %s", this.state));