JingleRtpConnection code clean up
This commit is contained in:
parent
1471969237
commit
80c49955f0
|
@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableMultimap;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
|
@ -26,23 +25,6 @@ import com.google.common.util.concurrent.Futures;
|
|||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.PeerConnection;
|
||||
import org.webrtc.VideoTrack;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import eu.siacs.conversations.BuildConfig;
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||
|
@ -61,7 +43,6 @@ import eu.siacs.conversations.utils.IP;
|
|||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
||||
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
|
||||
|
@ -73,6 +54,23 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
|||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.PeerConnection;
|
||||
import org.webrtc.VideoTrack;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class JingleRtpConnection extends AbstractJingleConnection
|
||||
implements WebRTCWrapper.EventCallback {
|
||||
|
||||
|
@ -195,64 +193,37 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
}
|
||||
|
||||
private static State reasonToState(Reason reason) {
|
||||
switch (reason) {
|
||||
case SUCCESS:
|
||||
return State.TERMINATED_SUCCESS;
|
||||
case DECLINE:
|
||||
case BUSY:
|
||||
return State.TERMINATED_DECLINED_OR_BUSY;
|
||||
case CANCEL:
|
||||
case TIMEOUT:
|
||||
return State.TERMINATED_CANCEL_OR_TIMEOUT;
|
||||
case SECURITY_ERROR:
|
||||
return State.TERMINATED_SECURITY_ERROR;
|
||||
case FAILED_APPLICATION:
|
||||
case UNSUPPORTED_TRANSPORTS:
|
||||
case UNSUPPORTED_APPLICATIONS:
|
||||
return State.TERMINATED_APPLICATION_FAILURE;
|
||||
default:
|
||||
return State.TERMINATED_CONNECTIVITY_ERROR;
|
||||
}
|
||||
return switch (reason) {
|
||||
case SUCCESS -> State.TERMINATED_SUCCESS;
|
||||
case DECLINE, BUSY -> State.TERMINATED_DECLINED_OR_BUSY;
|
||||
case CANCEL, TIMEOUT -> State.TERMINATED_CANCEL_OR_TIMEOUT;
|
||||
case SECURITY_ERROR -> State.TERMINATED_SECURITY_ERROR;
|
||||
case FAILED_APPLICATION, UNSUPPORTED_TRANSPORTS, UNSUPPORTED_APPLICATIONS -> State
|
||||
.TERMINATED_APPLICATION_FAILURE;
|
||||
default -> State.TERMINATED_CONNECTIVITY_ERROR;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
synchronized void deliverPacket(final JinglePacket jinglePacket) {
|
||||
switch (jinglePacket.getAction()) {
|
||||
case SESSION_INITIATE:
|
||||
receiveSessionInitiate(jinglePacket);
|
||||
break;
|
||||
case TRANSPORT_INFO:
|
||||
receiveTransportInfo(jinglePacket);
|
||||
break;
|
||||
case SESSION_ACCEPT:
|
||||
receiveSessionAccept(jinglePacket);
|
||||
break;
|
||||
case SESSION_TERMINATE:
|
||||
receiveSessionTerminate(jinglePacket);
|
||||
break;
|
||||
case CONTENT_ADD:
|
||||
receiveContentAdd(jinglePacket);
|
||||
break;
|
||||
case CONTENT_ACCEPT:
|
||||
receiveContentAccept(jinglePacket);
|
||||
break;
|
||||
case CONTENT_REJECT:
|
||||
receiveContentReject(jinglePacket);
|
||||
break;
|
||||
case CONTENT_REMOVE:
|
||||
receiveContentRemove(jinglePacket);
|
||||
break;
|
||||
case CONTENT_MODIFY:
|
||||
receiveContentModify(jinglePacket);
|
||||
break;
|
||||
default:
|
||||
case SESSION_INITIATE -> receiveSessionInitiate(jinglePacket);
|
||||
case TRANSPORT_INFO -> receiveTransportInfo(jinglePacket);
|
||||
case SESSION_ACCEPT -> receiveSessionAccept(jinglePacket);
|
||||
case SESSION_TERMINATE -> receiveSessionTerminate(jinglePacket);
|
||||
case CONTENT_ADD -> receiveContentAdd(jinglePacket);
|
||||
case CONTENT_ACCEPT -> receiveContentAccept(jinglePacket);
|
||||
case CONTENT_REJECT -> receiveContentReject(jinglePacket);
|
||||
case CONTENT_REMOVE -> receiveContentRemove(jinglePacket);
|
||||
case CONTENT_MODIFY -> receiveContentModify(jinglePacket);
|
||||
default -> {
|
||||
respondOk(jinglePacket);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
String.format(
|
||||
"%s: received unhandled jingle action %s",
|
||||
id.account.getJid().asBareJid(), jinglePacket.getAction()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,15 +325,22 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates =
|
||||
contentMap.contents.entrySet();
|
||||
final RtpContentMap remote = getRemoteContentMap();
|
||||
final Set<String> remoteContentIds = remote == null ? Collections.emptySet() : remote.contents.keySet();
|
||||
final Set<String> remoteContentIds =
|
||||
remote == null ? Collections.emptySet() : remote.contents.keySet();
|
||||
if (Collections.disjoint(remoteContentIds, contentMap.contents.keySet())) {
|
||||
Log.d(Config.LOGTAG,"received transport-info for unknown contents "+contentMap.contents.keySet()+" (known: "+remoteContentIds+")");
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
"received transport-info for unknown contents "
|
||||
+ contentMap.contents.keySet()
|
||||
+ " (known: "
|
||||
+ remoteContentIds
|
||||
+ ")");
|
||||
respondOk(jinglePacket);
|
||||
pendingIceCandidates.addAll(candidates);
|
||||
return;
|
||||
}
|
||||
if (this.state != State.SESSION_ACCEPTED) {
|
||||
Log.d(Config.LOGTAG,"received transport-info prematurely. adding to backlog");
|
||||
Log.d(Config.LOGTAG, "received transport-info prematurely. adding to backlog");
|
||||
respondOk(jinglePacket);
|
||||
pendingIceCandidates.addAll(candidates);
|
||||
return;
|
||||
|
@ -401,8 +379,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final boolean hasFullTransportInfo = modification.hasFullTransportInfo();
|
||||
final ListenableFuture<RtpContentMap> future =
|
||||
receiveRtpContentMap(
|
||||
modification, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
||||
Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
|
||||
modification,
|
||||
this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
||||
Futures.addCallback(
|
||||
future,
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(final RtpContentMap rtpContentMap) {
|
||||
receiveContentAdd(jinglePacket, rtpContentMap);
|
||||
|
@ -418,9 +399,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
+ ": improperly formatted contents in content-add",
|
||||
throwable);
|
||||
webRTCWrapper.close();
|
||||
sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
|
||||
sendSessionTerminate(
|
||||
Reason.ofThrowable(rootCause), rootCause.getMessage());
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
},
|
||||
MoreExecutors.directExecutor());
|
||||
} else {
|
||||
terminateWithOutOfOrder(jinglePacket);
|
||||
}
|
||||
|
@ -508,8 +491,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final boolean hasFullTransportInfo = receivedContentAccept.hasFullTransportInfo();
|
||||
final ListenableFuture<RtpContentMap> future =
|
||||
receiveRtpContentMap(
|
||||
receivedContentAccept, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
||||
Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
|
||||
receivedContentAccept,
|
||||
this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
||||
Futures.addCallback(
|
||||
future,
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(final RtpContentMap result) {
|
||||
receiveContentAccept(result);
|
||||
|
@ -518,9 +504,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
@Override
|
||||
public void onFailure(@NonNull final Throwable throwable) {
|
||||
webRTCWrapper.close();
|
||||
sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
|
||||
sendSessionTerminate(
|
||||
Reason.ofThrowable(throwable), throwable.getMessage());
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
},
|
||||
MoreExecutors.directExecutor());
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, "received content-accept did not match our outgoing content-add");
|
||||
terminateWithOutOfOrder(jinglePacket);
|
||||
|
@ -573,25 +561,34 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final boolean isInitiator = isInitiator();
|
||||
final RtpContentMap currentOutgoing = this.outgoingContentAdd;
|
||||
final RtpContentMap remoteContentMap = this.getRemoteContentMap();
|
||||
final Set<String> currentOutgoingMediaIds = currentOutgoing == null ? Collections.emptySet() : currentOutgoing.contents.keySet();
|
||||
final Set<String> currentOutgoingMediaIds =
|
||||
currentOutgoing == null
|
||||
? Collections.emptySet()
|
||||
: currentOutgoing.contents.keySet();
|
||||
Log.d(Config.LOGTAG, "receiveContentModification(" + modification + ")");
|
||||
if (currentOutgoing != null && currentOutgoingMediaIds.containsAll(modification.keySet())) {
|
||||
respondOk(jinglePacket);
|
||||
final RtpContentMap modifiedContentMap;
|
||||
try {
|
||||
modifiedContentMap = currentOutgoing.modifiedSendersChecked(isInitiator, modification);
|
||||
modifiedContentMap =
|
||||
currentOutgoing.modifiedSendersChecked(isInitiator, modification);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
webRTCWrapper.close();
|
||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||
return;
|
||||
}
|
||||
this.outgoingContentAdd = modifiedContentMap;
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": processed content-modification for pending content-add");
|
||||
} else if (remoteContentMap != null && remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
id.account.getJid().asBareJid()
|
||||
+ ": processed content-modification for pending content-add");
|
||||
} else if (remoteContentMap != null
|
||||
&& remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
|
||||
respondOk(jinglePacket);
|
||||
final RtpContentMap modifiedRemoteContentMap;
|
||||
try {
|
||||
modifiedRemoteContentMap = remoteContentMap.modifiedSendersChecked(isInitiator, modification);
|
||||
modifiedRemoteContentMap =
|
||||
remoteContentMap.modifiedSendersChecked(isInitiator, modification);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
webRTCWrapper.close();
|
||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||
|
@ -601,20 +598,27 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
try {
|
||||
offer = SessionDescription.of(modifiedRemoteContentMap, !isInitiator());
|
||||
} catch (final IllegalArgumentException | NullPointerException e) {
|
||||
Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-modify to SDP", e);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
id.getAccount().getJid().asBareJid()
|
||||
+ ": unable convert offer from content-modify to SDP",
|
||||
e);
|
||||
webRTCWrapper.close();
|
||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||
return;
|
||||
}
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": auto accepting content-modification");
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
id.account.getJid().asBareJid() + ": auto accepting content-modification");
|
||||
this.autoAcceptContentModify(modifiedRemoteContentMap, offer);
|
||||
} else {
|
||||
Log.d(Config.LOGTAG,"received unsupported content modification "+modification);
|
||||
Log.d(Config.LOGTAG, "received unsupported content modification " + modification);
|
||||
respondWithItemNotFound(jinglePacket);
|
||||
}
|
||||
}
|
||||
|
||||
private void autoAcceptContentModify(final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
|
||||
private void autoAcceptContentModify(
|
||||
final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
|
||||
this.setRemoteContentMap(modifiedRemoteContentMap);
|
||||
final org.webrtc.SessionDescription sdp =
|
||||
new org.webrtc.SessionDescription(
|
||||
|
@ -625,8 +629,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final SessionDescription answer = setLocalSessionDescription();
|
||||
final RtpContentMap rtpContentMap = RtpContentMap.of(answer, isInitiator());
|
||||
modifyLocalContentMap(rtpContentMap);
|
||||
// we do not need to send an answer but do we have to resend the candidates currently in SDP?
|
||||
//resendCandidatesFromSdp(answer);
|
||||
// we do not need to send an answer but do we have to resend the candidates currently in
|
||||
// SDP?
|
||||
// resendCandidatesFromSdp(answer);
|
||||
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
|
||||
} catch (final Exception e) {
|
||||
Log.d(Config.LOGTAG, "unable to accept content add", Throwables.getRootCause(e));
|
||||
|
@ -635,17 +640,20 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
}
|
||||
}
|
||||
|
||||
private static ImmutableMultimap<String, IceUdpTransportInfo.Candidate> parseCandidates(final SessionDescription answer) {
|
||||
final ImmutableMultimap.Builder<String, IceUdpTransportInfo.Candidate> candidateBuilder = new ImmutableMultimap.Builder<>();
|
||||
for(final SessionDescription.Media media : answer.media) {
|
||||
private static ImmutableMultimap<String, IceUdpTransportInfo.Candidate> parseCandidates(
|
||||
final SessionDescription answer) {
|
||||
final ImmutableMultimap.Builder<String, IceUdpTransportInfo.Candidate> candidateBuilder =
|
||||
new ImmutableMultimap.Builder<>();
|
||||
for (final SessionDescription.Media media : answer.media) {
|
||||
final String mid = Iterables.getFirst(media.attributes.get("mid"), null);
|
||||
if (Strings.isNullOrEmpty(mid)) {
|
||||
continue;
|
||||
}
|
||||
for(final String sdpCandidate : media.attributes.get("candidate")) {
|
||||
final IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
|
||||
for (final String sdpCandidate : media.attributes.get("candidate")) {
|
||||
final IceUdpTransportInfo.Candidate candidate =
|
||||
IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
|
||||
if (candidate != null) {
|
||||
candidateBuilder.put(mid,candidate);
|
||||
candidateBuilder.put(mid, candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -677,7 +685,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
if (ourSummary.equals(ContentAddition.summary(receivedContentReject))) {
|
||||
this.outgoingContentAdd = null;
|
||||
respondOk(jinglePacket);
|
||||
Log.d(Config.LOGTAG,jinglePacket.toString());
|
||||
Log.d(Config.LOGTAG, jinglePacket.toString());
|
||||
receiveContentReject(ourSummary);
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, "received content-reject did not match our outgoing content-add");
|
||||
|
@ -813,7 +821,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
"Unexpected rollback condition. Senders were not uniformly none");
|
||||
}
|
||||
|
||||
public synchronized void acceptContentAdd(@NonNull final Set<ContentAddition.Summary> contentAddition) {
|
||||
public synchronized void acceptContentAdd(
|
||||
@NonNull final Set<ContentAddition.Summary> contentAddition) {
|
||||
final RtpContentMap incomingContentAdd = this.incomingContentAdd;
|
||||
if (incomingContentAdd == null) {
|
||||
throw new IllegalStateException("No incoming content add");
|
||||
|
@ -822,37 +831,61 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
if (contentAddition.equals(ContentAddition.summary(incomingContentAdd))) {
|
||||
this.incomingContentAdd = null;
|
||||
final Set<Content.Senders> senders = incomingContentAdd.getSenders();
|
||||
Log.d(Config.LOGTAG,"senders of incoming content-add: "+senders);
|
||||
Log.d(Config.LOGTAG, "senders of incoming content-add: " + senders);
|
||||
if (senders.equals(Content.Senders.receiveOnly(isInitiator()))) {
|
||||
Log.d(Config.LOGTAG,"content addition is receive only. we want to upgrade to 'both'");
|
||||
final RtpContentMap modifiedSenders = incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
|
||||
final JinglePacket proposedContentModification = modifiedSenders.toStub().toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
"content addition is receive only. we want to upgrade to 'both'");
|
||||
final RtpContentMap modifiedSenders =
|
||||
incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
|
||||
final JinglePacket proposedContentModification =
|
||||
modifiedSenders
|
||||
.toStub()
|
||||
.toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
|
||||
proposedContentModification.setTo(id.with);
|
||||
xmppConnectionService.sendIqPacket(id.account, proposedContentModification, (account, response) -> {
|
||||
xmppConnectionService.sendIqPacket(
|
||||
id.account,
|
||||
proposedContentModification,
|
||||
(account, response) -> {
|
||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||
Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has accepted our upgrade to senders=both");
|
||||
acceptContentAdd(ContentAddition.summary(modifiedSenders), modifiedSenders);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
id.account.getJid().asBareJid()
|
||||
+ ": remote has accepted our upgrade to senders=both");
|
||||
acceptContentAdd(
|
||||
ContentAddition.summary(modifiedSenders), modifiedSenders);
|
||||
} else {
|
||||
Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has rejected our upgrade to senders=both");
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
id.account.getJid().asBareJid()
|
||||
+ ": remote has rejected our upgrade to senders=both");
|
||||
acceptContentAdd(contentAddition, incomingContentAdd);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Accepted content add does not match pending content-add");
|
||||
throw new IllegalStateException(
|
||||
"Accepted content add does not match pending content-add");
|
||||
}
|
||||
}
|
||||
|
||||
private void acceptContentAdd(@NonNull final Set<ContentAddition.Summary> contentAddition, final RtpContentMap incomingContentAdd) {
|
||||
private void acceptContentAdd(
|
||||
@NonNull final Set<ContentAddition.Summary> contentAddition,
|
||||
final RtpContentMap incomingContentAdd) {
|
||||
final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
|
||||
final RtpContentMap modifiedContentMap = getRemoteContentMap().addContent(incomingContentAdd, setup);
|
||||
final RtpContentMap modifiedContentMap =
|
||||
getRemoteContentMap().addContent(incomingContentAdd, setup);
|
||||
this.setRemoteContentMap(modifiedContentMap);
|
||||
|
||||
final SessionDescription offer;
|
||||
try {
|
||||
offer = SessionDescription.of(modifiedContentMap, !isInitiator());
|
||||
} catch (final IllegalArgumentException | NullPointerException e) {
|
||||
Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-add to SDP", e);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
id.getAccount().getJid().asBareJid()
|
||||
+ ": unable convert offer from content-add to SDP",
|
||||
e);
|
||||
webRTCWrapper.close();
|
||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||
return;
|
||||
|
@ -893,10 +926,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
addIceCandidatesFromBlackLog();
|
||||
|
||||
modifyLocalContentMap(rtpContentMap);
|
||||
final ListenableFuture<RtpContentMap> future = prepareOutgoingContentMap(contentAcceptMap);
|
||||
final ListenableFuture<RtpContentMap> future =
|
||||
prepareOutgoingContentMap(contentAcceptMap);
|
||||
Futures.addCallback(
|
||||
future,
|
||||
new FutureCallback<RtpContentMap>() {
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(final RtpContentMap rtpContentMap) {
|
||||
sendContentAccept(rtpContentMap);
|
||||
|
@ -917,7 +951,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
}
|
||||
|
||||
private void sendContentAccept(final RtpContentMap contentAcceptMap) {
|
||||
final JinglePacket jinglePacket = contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
|
||||
final JinglePacket jinglePacket =
|
||||
contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
|
||||
send(jinglePacket);
|
||||
}
|
||||
|
||||
|
@ -963,7 +998,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
// ICE-restart
|
||||
// and if that's the case we are seeing an answer.
|
||||
// This might be more spec compliant but also more error prone potentially
|
||||
final boolean isSignalStateStable = this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
|
||||
final boolean isSignalStateStable =
|
||||
this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
|
||||
// TODO a stable signal state can be another indicator that we have an offer to restart ICE
|
||||
final boolean isOffer = rtpContentMap.emptyCandidates();
|
||||
final RtpContentMap restartContentMap;
|
||||
|
@ -1027,7 +1063,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final RtpContentMap restartContentMap,
|
||||
final boolean isOffer)
|
||||
throws ExecutionException, InterruptedException {
|
||||
final SessionDescription sessionDescription = SessionDescription.of(restartContentMap, !isInitiator());
|
||||
final SessionDescription sessionDescription =
|
||||
SessionDescription.of(restartContentMap, !isInitiator());
|
||||
final org.webrtc.SessionDescription.Type type =
|
||||
isOffer
|
||||
? org.webrtc.SessionDescription.Type.OFFER
|
||||
|
@ -1127,7 +1164,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
return Futures.immediateFailedFuture(e);
|
||||
}
|
||||
}
|
||||
private ListenableFuture<RtpContentMap> receiveRtpContentMap(final RtpContentMap receivedContentMap, final boolean expectVerification) {
|
||||
|
||||
private ListenableFuture<RtpContentMap> receiveRtpContentMap(
|
||||
final RtpContentMap receivedContentMap, final boolean expectVerification) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
"receiveRtpContentMap("
|
||||
|
@ -1183,7 +1222,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, false);
|
||||
Futures.addCallback(
|
||||
future,
|
||||
new FutureCallback<RtpContentMap>() {
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
|
||||
receiveSessionInitiate(jinglePacket, rtpContentMap);
|
||||
|
@ -1272,7 +1311,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
|
||||
Futures.addCallback(
|
||||
future,
|
||||
new FutureCallback<RtpContentMap>() {
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
|
||||
receiveSessionAccept(jinglePacket, rtpContentMap);
|
||||
|
@ -1438,7 +1477,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
|
||||
}
|
||||
|
||||
private void failureToPerformAction(final JinglePacket.Action action, final Throwable throwable) {
|
||||
private void failureToPerformAction(
|
||||
final JinglePacket.Action action, final Throwable throwable) {
|
||||
if (isTerminated()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1459,7 +1499,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
}
|
||||
|
||||
private void prepareSessionAccept(
|
||||
final org.webrtc.SessionDescription webRTCSessionDescription, final boolean includeCandidates) {
|
||||
final org.webrtc.SessionDescription webRTCSessionDescription,
|
||||
final boolean includeCandidates) {
|
||||
final SessionDescription sessionDescription =
|
||||
SessionDescription.parse(webRTCSessionDescription.description);
|
||||
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription, false);
|
||||
|
@ -1475,7 +1516,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
prepareOutgoingContentMap(respondingRtpContentMap);
|
||||
Futures.addCallback(
|
||||
outgoingContentMapFuture,
|
||||
new FutureCallback<RtpContentMap>() {
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
||||
if (includeCandidates) {
|
||||
|
@ -1548,30 +1589,23 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
+ ": delivered message to JingleRtpConnection "
|
||||
+ message);
|
||||
switch (message.getName()) {
|
||||
case "propose":
|
||||
receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp);
|
||||
break;
|
||||
case "proceed":
|
||||
receiveProceed(from, Proceed.upgrade(message), serverMessageId, timestamp);
|
||||
break;
|
||||
case "retract":
|
||||
receiveRetract(from, serverMessageId, timestamp);
|
||||
break;
|
||||
case "reject":
|
||||
receiveReject(from, serverMessageId, timestamp);
|
||||
break;
|
||||
case "accept":
|
||||
receiveAccept(from, serverMessageId, timestamp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case "propose" -> receivePropose(
|
||||
from, Propose.upgrade(message), serverMessageId, timestamp);
|
||||
case "proceed" -> receiveProceed(
|
||||
from, Proceed.upgrade(message), serverMessageId, timestamp);
|
||||
case "retract" -> receiveRetract(from, serverMessageId, timestamp);
|
||||
case "reject" -> receiveReject(from, serverMessageId, timestamp);
|
||||
case "accept" -> receiveAccept(from, serverMessageId, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
void deliverFailedProceed(final String message) {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
id.account.getJid().asBareJid() + ": receive message error for proceed message ("+Strings.nullToEmpty(message)+")");
|
||||
id.account.getJid().asBareJid()
|
||||
+ ": receive message error for proceed message ("
|
||||
+ Strings.nullToEmpty(message)
|
||||
+ ")");
|
||||
if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) {
|
||||
webRTCWrapper.close();
|
||||
Log.d(
|
||||
|
@ -1609,9 +1643,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
this.message.setTime(timestamp);
|
||||
this.message.setCarbon(true); // indicate that call was accepted on other device
|
||||
this.writeLogMessageSuccess(0);
|
||||
this.xmppConnectionService
|
||||
.getNotificationService()
|
||||
.cancelIncomingCallNotification();
|
||||
this.xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
|
||||
this.finish();
|
||||
}
|
||||
|
||||
|
@ -1749,14 +1781,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
private synchronized void ringingTimeout() {
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": timeout reached for ringing");
|
||||
switch (this.state) {
|
||||
case PROPOSED:
|
||||
case PROPOSED -> {
|
||||
message.markUnread();
|
||||
rejectCallFromProposed();
|
||||
break;
|
||||
case SESSION_INITIALIZED:
|
||||
}
|
||||
case SESSION_INITIALIZED -> {
|
||||
message.markUnread();
|
||||
rejectCallFromSessionInitiate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
xmppConnectionService.getNotificationService().pushMissedCallNow(message);
|
||||
}
|
||||
|
@ -1932,7 +1964,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
}
|
||||
|
||||
private void prepareSessionInitiate(
|
||||
final org.webrtc.SessionDescription webRTCSessionDescription, final boolean includeCandidates, final State targetState) {
|
||||
final org.webrtc.SessionDescription webRTCSessionDescription,
|
||||
final boolean includeCandidates,
|
||||
final State targetState) {
|
||||
final SessionDescription sessionDescription =
|
||||
SessionDescription.parse(webRTCSessionDescription.description);
|
||||
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription, true);
|
||||
|
@ -1947,7 +1981,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
encryptSessionInitiate(rtpContentMap);
|
||||
Futures.addCallback(
|
||||
outgoingContentMapFuture,
|
||||
new FutureCallback<RtpContentMap>() {
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
||||
if (includeCandidates) {
|
||||
|
@ -1956,7 +1990,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
"including "
|
||||
+ candidates.size()
|
||||
+ " candidates in session initiate");
|
||||
sendSessionInitiate(outgoingContentMap.withCandidates(candidates), targetState);
|
||||
sendSessionInitiate(
|
||||
outgoingContentMap.withCandidates(candidates), targetState);
|
||||
webRTCWrapper.resetPendingCandidates();
|
||||
} else {
|
||||
sendSessionInitiate(outgoingContentMap, targetState);
|
||||
|
@ -2170,60 +2205,63 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
|
||||
public RtpEndUserState getEndUserState() {
|
||||
switch (this.state) {
|
||||
case NULL:
|
||||
case PROPOSED:
|
||||
case SESSION_INITIALIZED:
|
||||
case NULL, PROPOSED, SESSION_INITIALIZED -> {
|
||||
if (isInitiator()) {
|
||||
return RtpEndUserState.RINGING;
|
||||
} else {
|
||||
return RtpEndUserState.INCOMING_CALL;
|
||||
}
|
||||
case PROCEED:
|
||||
}
|
||||
case PROCEED -> {
|
||||
if (isInitiator()) {
|
||||
return RtpEndUserState.RINGING;
|
||||
} else {
|
||||
return RtpEndUserState.ACCEPTING_CALL;
|
||||
}
|
||||
case SESSION_INITIALIZED_PRE_APPROVED:
|
||||
}
|
||||
case SESSION_INITIALIZED_PRE_APPROVED -> {
|
||||
if (isInitiator()) {
|
||||
return RtpEndUserState.RINGING;
|
||||
} else {
|
||||
return RtpEndUserState.CONNECTING;
|
||||
}
|
||||
case SESSION_ACCEPTED:
|
||||
}
|
||||
case SESSION_ACCEPTED -> {
|
||||
final ContentAddition ca = getPendingContentAddition();
|
||||
if (ca != null && ca.direction == ContentAddition.Direction.INCOMING) {
|
||||
return RtpEndUserState.INCOMING_CONTENT_ADD;
|
||||
}
|
||||
return getPeerConnectionStateAsEndUserState();
|
||||
case REJECTED:
|
||||
case REJECTED_RACED:
|
||||
case TERMINATED_DECLINED_OR_BUSY:
|
||||
}
|
||||
case REJECTED, REJECTED_RACED, TERMINATED_DECLINED_OR_BUSY -> {
|
||||
if (isInitiator()) {
|
||||
return RtpEndUserState.DECLINED_OR_BUSY;
|
||||
} else {
|
||||
return RtpEndUserState.ENDED;
|
||||
}
|
||||
case TERMINATED_SUCCESS:
|
||||
case ACCEPTED:
|
||||
case RETRACTED:
|
||||
case TERMINATED_CANCEL_OR_TIMEOUT:
|
||||
}
|
||||
case TERMINATED_SUCCESS, ACCEPTED, RETRACTED, TERMINATED_CANCEL_OR_TIMEOUT -> {
|
||||
return RtpEndUserState.ENDED;
|
||||
case RETRACTED_RACED:
|
||||
}
|
||||
case RETRACTED_RACED -> {
|
||||
if (isInitiator()) {
|
||||
return RtpEndUserState.ENDED;
|
||||
} else {
|
||||
return RtpEndUserState.RETRACTED;
|
||||
}
|
||||
case TERMINATED_CONNECTIVITY_ERROR:
|
||||
}
|
||||
case TERMINATED_CONNECTIVITY_ERROR -> {
|
||||
return zeroDuration()
|
||||
? RtpEndUserState.CONNECTIVITY_ERROR
|
||||
: RtpEndUserState.CONNECTIVITY_LOST_ERROR;
|
||||
case TERMINATED_APPLICATION_FAILURE:
|
||||
}
|
||||
case TERMINATED_APPLICATION_FAILURE -> {
|
||||
return RtpEndUserState.APPLICATION_ERROR;
|
||||
case TERMINATED_SECURITY_ERROR:
|
||||
}
|
||||
case TERMINATED_SECURITY_ERROR -> {
|
||||
return RtpEndUserState.SECURITY_ERROR;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
String.format("%s has no equivalent EndUserState", this.state));
|
||||
}
|
||||
|
@ -2237,19 +2275,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
// be in SESSION_ACCEPTED even though the peerConnection has been torn down
|
||||
return RtpEndUserState.ENDING_CALL;
|
||||
}
|
||||
switch (state) {
|
||||
case CONNECTED:
|
||||
return RtpEndUserState.CONNECTED;
|
||||
case NEW:
|
||||
case CONNECTING:
|
||||
return RtpEndUserState.CONNECTING;
|
||||
case CLOSED:
|
||||
return RtpEndUserState.ENDING_CALL;
|
||||
default:
|
||||
return zeroDuration()
|
||||
return switch (state) {
|
||||
case CONNECTED -> RtpEndUserState.CONNECTED;
|
||||
case NEW, CONNECTING -> RtpEndUserState.CONNECTING;
|
||||
case CLOSED -> RtpEndUserState.ENDING_CALL;
|
||||
default -> zeroDuration()
|
||||
? RtpEndUserState.CONNECTIVITY_ERROR
|
||||
: RtpEndUserState.RECONNECTING;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public ContentAddition getPendingContentAddition() {
|
||||
|
@ -2284,9 +2317,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
} else if (initiatorContentMap != null) {
|
||||
return initiatorContentMap.getMedia();
|
||||
} else if (isTerminated()) {
|
||||
return Collections.emptySet(); //we might fail before we ever got a chance to set media
|
||||
return Collections.emptySet(); // we might fail before we ever got a chance to set media
|
||||
} else {
|
||||
return Preconditions.checkNotNull(this.proposedMedia, "RTP connection has not been initialized properly");
|
||||
return Preconditions.checkNotNull(
|
||||
this.proposedMedia, "RTP connection has not been initialized properly");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2306,35 +2340,29 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
throw new IllegalStateException(String.format("%s has already been proposed", media));
|
||||
}
|
||||
// TODO add state protection - can only add while ACCEPTED or so
|
||||
Log.d(Config.LOGTAG,"adding media: "+media);
|
||||
Log.d(Config.LOGTAG, "adding media: " + media);
|
||||
return webRTCWrapper.addTrack(media);
|
||||
}
|
||||
|
||||
public synchronized void acceptCall() {
|
||||
switch (this.state) {
|
||||
case PROPOSED:
|
||||
case PROPOSED -> {
|
||||
cancelRingingTimeout();
|
||||
acceptCallFromProposed();
|
||||
break;
|
||||
case SESSION_INITIALIZED:
|
||||
}
|
||||
case SESSION_INITIALIZED -> {
|
||||
cancelRingingTimeout();
|
||||
acceptCallFromSessionInitialized();
|
||||
break;
|
||||
case ACCEPTED:
|
||||
Log.w(
|
||||
}
|
||||
case ACCEPTED -> Log.w(
|
||||
Config.LOGTAG,
|
||||
id.account.getJid().asBareJid()
|
||||
+ ": the call has already been accepted with another client. UI was just lagging behind");
|
||||
break;
|
||||
case PROCEED:
|
||||
case SESSION_ACCEPTED:
|
||||
Log.w(
|
||||
case PROCEED, SESSION_ACCEPTED -> Log.w(
|
||||
Config.LOGTAG,
|
||||
id.account.getJid().asBareJid()
|
||||
+ ": the call has already been accepted. user probably double tapped the UI");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Can not accept call from " + this.state);
|
||||
default -> throw new IllegalStateException("Can not accept call from " + this.state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2356,14 +2384,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
return;
|
||||
}
|
||||
switch (this.state) {
|
||||
case PROPOSED:
|
||||
rejectCallFromProposed();
|
||||
break;
|
||||
case SESSION_INITIALIZED:
|
||||
rejectCallFromSessionInitiate();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Can not reject call from " + this.state);
|
||||
case PROPOSED -> rejectCallFromProposed();
|
||||
case SESSION_INITIALIZED -> rejectCallFromSessionInitiate();
|
||||
default -> throw new IllegalStateException("Can not reject call from " + this.state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2428,9 +2451,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
finish();
|
||||
}
|
||||
|
||||
private void setupWebRTC(final Set<Media> media, final List<PeerConnection.IceServer> iceServers, final boolean trickle) throws WebRTCWrapper.InitializationException {
|
||||
private void setupWebRTC(
|
||||
final Set<Media> media,
|
||||
final List<PeerConnection.IceServer> iceServers,
|
||||
final boolean trickle)
|
||||
throws WebRTCWrapper.InitializationException {
|
||||
this.jingleConnectionManager.ensureConnectionIsRegistered(this);
|
||||
this.webRTCWrapper.setup(this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
|
||||
this.webRTCWrapper.setup(
|
||||
this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
|
||||
this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle);
|
||||
}
|
||||
|
||||
|
@ -2606,7 +2634,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final Throwable cause = Throwables.getRootCause(e);
|
||||
webRTCWrapper.close();
|
||||
if (isTerminated()) {
|
||||
Log.d(Config.LOGTAG, "failed to renegotiate. session was already terminated", cause);
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
"failed to renegotiate. session was already terminated",
|
||||
cause);
|
||||
return;
|
||||
}
|
||||
Log.d(Config.LOGTAG, "failed to renegotiate. sending session-terminate", cause);
|
||||
|
@ -2691,7 +2722,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
prepareOutgoingContentMap(contentAdd);
|
||||
Futures.addCallback(
|
||||
outgoingContentMapFuture,
|
||||
new FutureCallback<RtpContentMap>() {
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
||||
sendContentAdd(outgoingContentMap);
|
||||
|
@ -2889,9 +2920,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (Arrays.asList("stun", "stuns", "turn", "turns")
|
||||
.contains(type)
|
||||
&& Arrays.asList("udp", "tcp").contains(transport)) {
|
||||
|
@ -2906,10 +2934,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
|
||||
// STUN URLs do not support a query section since M110
|
||||
final String uri;
|
||||
if (Arrays.asList("stun","stuns").contains(type)) {
|
||||
uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host),port);
|
||||
if (Arrays.asList("stun", "stuns").contains(type)) {
|
||||
uri =
|
||||
String.format(
|
||||
"%s:%s:%s",
|
||||
type, IP.wrapIPv6(host), port);
|
||||
} else {
|
||||
uri = String.format(
|
||||
uri =
|
||||
String.format(
|
||||
"%s:%s:%s?transport=%s",
|
||||
type,
|
||||
IP.wrapIPv6(host),
|
||||
|
|
Loading…
Reference in a new issue