@@ -52,14 +52,16 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public class JingleConnectionManager extends AbstractConnectionManager {
- static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
+ static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE =
+ Executors.newSingleThreadScheduledExecutor();
final ToneManager toneManager;
- private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>();
- private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
+ private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals =
+ new HashMap<>();
+ private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection>
+ connections = new ConcurrentHashMap<>();
- private final Cache<PersistableSessionId, TerminatedRtpSession> terminatedSessions = CacheBuilder.newBuilder()
- .expireAfterWrite(24, TimeUnit.HOURS)
- .build();
+ private final Cache<PersistableSessionId, TerminatedRtpSession> terminatedSessions =
+ CacheBuilder.newBuilder().expireAfterWrite(24, TimeUnit.HOURS).build();
private final HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
@@ -87,17 +89,31 @@ public class JingleConnectionManager extends AbstractConnectionManager {
} else if (packet.getAction() == JinglePacket.Action.SESSION_INITIATE) {
final Jid from = packet.getFrom();
final Content content = packet.getJingleContent();
- final String descriptionNamespace = content == null ? null : content.getDescriptionNamespace();
+ final String descriptionNamespace =
+ content == null ? null : content.getDescriptionNamespace();
final AbstractJingleConnection connection;
if (FileTransferDescription.NAMESPACES.contains(descriptionNamespace)) {
connection = new JingleFileTransferConnection(this, id, from);
- } else if (Namespace.JINGLE_APPS_RTP.equals(descriptionNamespace) && isUsingClearNet(account)) {
- final boolean sessionEnded = this.terminatedSessions.asMap().containsKey(PersistableSessionId.of(id));
- final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
+ } else if (Namespace.JINGLE_APPS_RTP.equals(descriptionNamespace)
+ && isUsingClearNet(account)) {
+ final boolean sessionEnded =
+ this.terminatedSessions.asMap().containsKey(PersistableSessionId.of(id));
+ final boolean stranger =
+ isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
if (isBusy() || sessionEnded || stranger) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": rejected session with " + id.with + " because busy. sessionEnded=" + sessionEnded + ", stranger=" + stranger);
- mXmppConnectionService.sendIqPacket(account, packet.generateResponse(IqPacket.TYPE.RESULT), null);
- final JinglePacket sessionTermination = new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": rejected session with "
+ + id.with
+ + " because busy. sessionEnded="
+ + sessionEnded
+ + ", stranger="
+ + stranger);
+ mXmppConnectionService.sendIqPacket(
+ account, packet.generateResponse(IqPacket.TYPE.RESULT), null);
+ final JinglePacket sessionTermination =
+ new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId);
sessionTermination.setTo(id.with);
sessionTermination.setReason(Reason.BUSY, null);
mXmppConnectionService.sendIqPacket(account, sessionTermination, null);
@@ -105,7 +121,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
connection = new JingleRtpConnection(this, id, from);
} else {
- respondWithJingleError(account, packet, "unsupported-info", "feature-not-implemented", "cancel");
+ respondWithJingleError(
+ account, packet, "unsupported-info", "feature-not-implemented", "cancel");
return;
}
connections.put(id, connection);
@@ -136,7 +153,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
synchronized (this.rtpSessionProposals) {
return this.rtpSessionProposals.containsValue(DeviceDiscoveryState.DISCOVERED)
|| this.rtpSessionProposals.containsValue(DeviceDiscoveryState.SEARCHING)
- || this.rtpSessionProposals.containsValue(DeviceDiscoveryState.SEARCHING_ACKNOWLEDGED);
+ || this.rtpSessionProposals.containsValue(
+ DeviceDiscoveryState.SEARCHING_ACKNOWLEDGED);
}
}
@@ -152,14 +170,17 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
- private Optional<RtpSessionProposal> findMatchingSessionProposal(final Account account, final Jid with, final Set<Media> media) {
+ private Optional<RtpSessionProposal> findMatchingSessionProposal(
+ final Account account, final Jid with, final Set<Media> media) {
synchronized (this.rtpSessionProposals) {
- for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) {
+ for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry :
+ this.rtpSessionProposals.entrySet()) {
final RtpSessionProposal proposal = entry.getKey();
final DeviceDiscoveryState state = entry.getValue();
- final boolean openProposal = state == DeviceDiscoveryState.DISCOVERED
- || state == DeviceDiscoveryState.SEARCHING
- || state == DeviceDiscoveryState.SEARCHING_ACKNOWLEDGED;
+ final boolean openProposal =
+ state == DeviceDiscoveryState.DISCOVERED
+ || state == DeviceDiscoveryState.SEARCHING
+ || state == DeviceDiscoveryState.SEARCHING_ACKNOWLEDGED;
if (openProposal
&& proposal.account == account
&& proposal.with.equals(with.asBareJid())
@@ -171,7 +192,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
return Optional.absent();
}
- private boolean hasMatchingRtpSession(final Account account, final Jid with, final Set<Media> media) {
+ private boolean hasMatchingRtpSession(
+ final Account account, final Jid with, final Set<Media> media) {
for (AbstractJingleConnection connection : this.connections.values()) {
if (connection instanceof JingleRtpConnection) {
final JingleRtpConnection rtpConnection = (JingleRtpConnection) connection;
@@ -189,7 +211,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
private boolean isWithStrangerAndStrangerNotificationsAreOff(final Account account, Jid with) {
- final boolean notifyForStrangers = mXmppConnectionService.getNotificationService().notificationsFromStrangers();
+ final boolean notifyForStrangers =
+ mXmppConnectionService.getNotificationService().notificationsFromStrangers();
if (notifyForStrangers) {
return false;
}
@@ -197,11 +220,17 @@ public class JingleConnectionManager extends AbstractConnectionManager {
return !contact.showInContactList();
}
- ScheduledFuture<?> schedule(final Runnable runnable, final long delay, final TimeUnit timeUnit) {
+ ScheduledFuture<?> schedule(
+ final Runnable runnable, final long delay, final TimeUnit timeUnit) {
return SCHEDULED_EXECUTOR_SERVICE.schedule(runnable, delay, timeUnit);
}
- void respondWithJingleError(final Account account, final IqPacket original, String jingleCondition, String condition, String conditionType) {
+ void respondWithJingleError(
+ final Account account,
+ final IqPacket original,
+ String jingleCondition,
+ String condition,
+ String conditionType) {
final IqPacket response = original.generateResponse(IqPacket.TYPE.ERROR);
final Element error = response.addChild("error");
error.setAttribute("type", conditionType);
@@ -210,7 +239,14 @@ public class JingleConnectionManager extends AbstractConnectionManager {
account.getXmppConnection().sendIqPacket(response, null);
}
- public void deliverMessage(final Account account, final Jid to, final Jid from, final Element message, String remoteMsgId, String serverMsgId, long timestamp) {
+ public void deliverMessage(
+ final Account account,
+ final Jid to,
+ final Jid from,
+ final Element message,
+ String remoteMsgId,
+ String serverMsgId,
+ long timestamp) {
Preconditions.checkArgument(Namespace.JINGLE_MESSAGE.equals(message.getNamespace()));
final String sessionId = message.getAttribute("id");
if (sessionId == null) {
@@ -244,16 +280,24 @@ public class JingleConnectionManager extends AbstractConnectionManager {
final AbstractJingleConnection existingJingleConnection = connections.get(id);
if (existingJingleConnection != null) {
if (existingJingleConnection instanceof JingleRtpConnection) {
- ((JingleRtpConnection) existingJingleConnection).deliveryMessage(from, message, serverMsgId, timestamp);
+ ((JingleRtpConnection) existingJingleConnection)
+ .deliveryMessage(from, message, serverMsgId, timestamp);
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + existingJingleConnection.getClass().getName() + " does not support jingle messages");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": "
+ + existingJingleConnection.getClass().getName()
+ + " does not support jingle messages");
}
return;
}
if (fromSelf) {
if ("proceed".equals(message.getName())) {
- final Conversation c = mXmppConnectionService.findOrCreateConversation(account, id.with, false, false);
+ final Conversation c =
+ mXmppConnectionService.findOrCreateConversation(
+ account, id.with, false, false);
final Message previousBusy = c.findRtpSession(sessionId, Message.STATUS_RECEIVED);
if (previousBusy != null) {
previousBusy.setBody(new RtpSessionStatus(true, 0).toString());
@@ -262,84 +306,138 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
previousBusy.setTime(timestamp);
mXmppConnectionService.updateMessage(previousBusy, true);
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": updated previous busy because call got picked up by another device");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": updated previous busy because call got picked up by another device");
return;
}
}
- //TODO handle reject for cases where we donβt have carbon copies (normally reject is to be sent to own bare jid as well)
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ignore jingle message from self");
+ // TODO handle reject for cases where we donβt have carbon copies (normally reject is to
+ // be sent to own bare jid as well)
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid() + ": ignore jingle message from self");
return;
}
if ("propose".equals(message.getName())) {
final Propose propose = Propose.upgrade(message);
final List<GenericDescription> descriptions = propose.getDescriptions();
- final Collection<RtpDescription> rtpDescriptions = Collections2.transform(
- Collections2.filter(descriptions, d -> d instanceof RtpDescription),
- input -> (RtpDescription) input
- );
- if (rtpDescriptions.size() > 0 && rtpDescriptions.size() == descriptions.size() && isUsingClearNet(account)) {
- final Collection<Media> media = Collections2.transform(rtpDescriptions, RtpDescription::getMedia);
+ final Collection<RtpDescription> rtpDescriptions =
+ Collections2.transform(
+ Collections2.filter(descriptions, d -> d instanceof RtpDescription),
+ input -> (RtpDescription) input);
+ if (rtpDescriptions.size() > 0
+ && rtpDescriptions.size() == descriptions.size()
+ && isUsingClearNet(account)) {
+ final Collection<Media> media =
+ Collections2.transform(rtpDescriptions, RtpDescription::getMedia);
if (media.contains(Media.UNKNOWN)) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": encountered unknown media in session proposal. " + propose);
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": encountered unknown media in session proposal. "
+ + propose);
return;
}
- final Optional<RtpSessionProposal> matchingSessionProposal = findMatchingSessionProposal(account, id.with, ImmutableSet.copyOf(media));
+ final Optional<RtpSessionProposal> matchingSessionProposal =
+ findMatchingSessionProposal(account, id.with, ImmutableSet.copyOf(media));
if (matchingSessionProposal.isPresent()) {
final String ourSessionId = matchingSessionProposal.get().sessionId;
final String theirSessionId = id.sessionId;
if (ComparisonChain.start()
- .compare(ourSessionId, theirSessionId)
- .compare(account.getJid().toEscapedString(), id.with.toEscapedString())
- .result() > 0) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": our session lost tie break. automatically accepting their session. winning Session=" + theirSessionId);
- //TODO a retract for this reason should probably include some indication of tie break
+ .compare(ourSessionId, theirSessionId)
+ .compare(
+ account.getJid().toEscapedString(),
+ id.with.toEscapedString())
+ .result()
+ > 0) {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": our session lost tie break. automatically accepting their session. winning Session="
+ + theirSessionId);
+ // TODO a retract for this reason should probably include some indication of
+ // tie break
retractSessionProposal(matchingSessionProposal.get());
- final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, from);
+ final JingleRtpConnection rtpConnection =
+ new JingleRtpConnection(this, id, from);
this.connections.put(id, rtpConnection);
rtpConnection.setProposedMedia(ImmutableSet.copyOf(media));
rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": our session won tie break. waiting for other party to accept. winningSession=" + ourSessionId);
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": our session won tie break. waiting for other party to accept. winningSession="
+ + ourSessionId);
}
return;
}
- final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
+ final boolean stranger =
+ isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
if (isBusy() || stranger) {
- writeLogMissedIncoming(account, id.with.asBareJid(), id.sessionId, serverMsgId, timestamp);
+ writeLogMissedIncoming(
+ account, id.with.asBareJid(), id.sessionId, serverMsgId, timestamp);
if (stranger) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring call proposal from stranger " + id.with);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": ignoring call proposal from stranger "
+ + id.with);
return;
}
final int activeDevices = account.activeDevicesWithRtpCapability();
Log.d(Config.LOGTAG, "active devices with rtp capability: " + activeDevices);
if (activeDevices == 0) {
- final MessagePacket reject = mXmppConnectionService.getMessageGenerator().sessionReject(from, sessionId);
+ final MessagePacket reject =
+ mXmppConnectionService
+ .getMessageGenerator()
+ .sessionReject(from, sessionId);
mXmppConnectionService.sendMessagePacket(account, reject);
} else {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring proposal because busy on this device but there are other devices");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": ignoring proposal because busy on this device but there are other devices");
}
} else {
- final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, from);
+ final JingleRtpConnection rtpConnection =
+ new JingleRtpConnection(this, id, from);
this.connections.put(id, rtpConnection);
rtpConnection.setProposedMedia(ImmutableSet.copyOf(media));
rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
}
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to react to proposed session with " + rtpDescriptions.size() + " rtp descriptions of " + descriptions.size() + " total descriptions");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": unable to react to proposed session with "
+ + rtpDescriptions.size()
+ + " rtp descriptions of "
+ + descriptions.size()
+ + " total descriptions");
}
} else if (addressedDirectly && "proceed".equals(message.getName())) {
synchronized (rtpSessionProposals) {
- final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
+ final RtpSessionProposal proposal =
+ getRtpSessionProposal(account, from.asBareJid(), sessionId);
if (proposal != null) {
rtpSessionProposals.remove(proposal);
- final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, account.getJid());
+ final JingleRtpConnection rtpConnection =
+ new JingleRtpConnection(this, id, account.getJid());
rtpConnection.setProposedMedia(proposal.media);
this.connections.put(id, rtpConnection);
rtpConnection.transitionOrThrow(AbstractJingleConnection.State.PROPOSED);
rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver proceed");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": no rtp session proposal found for "
+ + from
+ + " to deliver proceed");
if (remoteMsgId == null) {
return;
}
@@ -355,63 +453,77 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
} else if (addressedDirectly && "reject".equals(message.getName())) {
- final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
+ final RtpSessionProposal proposal =
+ getRtpSessionProposal(account, from.asBareJid(), sessionId);
synchronized (rtpSessionProposals) {
if (proposal != null && rtpSessionProposals.remove(proposal) != null) {
- writeLogMissedOutgoing(account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
+ writeLogMissedOutgoing(
+ account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY, proposal.media);
- mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, proposal.with, proposal.sessionId, RtpEndUserState.DECLINED_OR_BUSY);
+ mXmppConnectionService.notifyJingleRtpConnectionUpdate(
+ account,
+ proposal.with,
+ proposal.sessionId,
+ RtpEndUserState.DECLINED_OR_BUSY);
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver reject");
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": no rtp session proposal found for "
+ + from
+ + " to deliver reject");
}
}
} else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retrieved out of order jingle message" + message);
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": retrieved out of order jingle message"
+ + message);
}
-
}
- private RtpSessionProposal getRtpSessionProposal(final Account account, Jid from, String sessionId) {
+ private RtpSessionProposal getRtpSessionProposal(
+ final Account account, Jid from, String sessionId) {
for (RtpSessionProposal rtpSessionProposal : rtpSessionProposals.keySet()) {
- if (rtpSessionProposal.sessionId.equals(sessionId) && rtpSessionProposal.with.equals(from) && rtpSessionProposal.account.getJid().equals(account.getJid())) {
+ if (rtpSessionProposal.sessionId.equals(sessionId)
+ && rtpSessionProposal.with.equals(from)
+ && rtpSessionProposal.account.getJid().equals(account.getJid())) {
return rtpSessionProposal;
}
}
return null;
}
- private void writeLogMissedOutgoing(final Account account, Jid with, final String sessionId, String serverMsgId, long timestamp) {
- final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
- account,
- with.asBareJid(),
- false,
- false
- );
- final Message message = new Message(
- conversation,
- Message.STATUS_SEND,
- Message.TYPE_RTP_SESSION,
- sessionId
- );
+ private void writeLogMissedOutgoing(
+ final Account account,
+ Jid with,
+ final String sessionId,
+ String serverMsgId,
+ long timestamp) {
+ final Conversation conversation =
+ mXmppConnectionService.findOrCreateConversation(
+ account, with.asBareJid(), false, false);
+ final Message message =
+ new Message(conversation, Message.STATUS_SEND, Message.TYPE_RTP_SESSION, sessionId);
message.setBody(new RtpSessionStatus(false, 0).toString());
message.setServerMsgId(serverMsgId);
message.setTime(timestamp);
writeMessage(message);
}
- private void writeLogMissedIncoming(final Account account, Jid with, final String sessionId, String serverMsgId, long timestamp) {
- final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
- account,
- with.asBareJid(),
- false,
- false
- );
- final Message message = new Message(
- conversation,
- Message.STATUS_RECEIVED,
- Message.TYPE_RTP_SESSION,
- sessionId
- );
+ private void writeLogMissedIncoming(
+ final Account account,
+ Jid with,
+ final String sessionId,
+ String serverMsgId,
+ long timestamp) {
+ final Conversation conversation =
+ mXmppConnectionService.findOrCreateConversation(
+ account, with.asBareJid(), false, false);
+ final Message message =
+ new Message(
+ conversation, Message.STATUS_RECEIVED, Message.TYPE_RTP_SESSION, sessionId);
message.setBody(new RtpSessionStatus(false, 0).toString());
message.setServerMsgId(serverMsgId);
message.setTime(timestamp);
@@ -430,34 +542,41 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
public void startJingleFileTransfer(final Message message) {
- Preconditions.checkArgument(message.isFileOrImage(), "Message is not of type file or image");
+ Preconditions.checkArgument(
+ message.isFileOrImage(), "Message is not of type file or image");
final Transferable old = message.getTransferable();
if (old != null) {
old.cancel();
}
final Account account = message.getConversation().getAccount();
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(message);
- final JingleFileTransferConnection connection = new JingleFileTransferConnection(this, id, account.getJid());
+ final JingleFileTransferConnection connection =
+ new JingleFileTransferConnection(this, id, account.getJid());
mXmppConnectionService.markMessage(message, Message.STATUS_WAITING);
this.connections.put(id, connection);
connection.init(message);
}
public Optional<OngoingRtpSession> getOngoingRtpConnection(final Contact contact) {
- for (final Map.Entry<AbstractJingleConnection.Id, AbstractJingleConnection> entry : this.connections.entrySet()) {
+ for (final Map.Entry<AbstractJingleConnection.Id, AbstractJingleConnection> entry :
+ this.connections.entrySet()) {
if (entry.getValue() instanceof JingleRtpConnection) {
final AbstractJingleConnection.Id id = entry.getKey();
- if (id.account == contact.getAccount() && id.with.asBareJid().equals(contact.getJid().asBareJid())) {
+ if (id.account == contact.getAccount()
+ && id.with.asBareJid().equals(contact.getJid().asBareJid())) {
return Optional.of(id);
}
}
}
synchronized (this.rtpSessionProposals) {
- for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) {
+ for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry :
+ this.rtpSessionProposals.entrySet()) {
RtpSessionProposal proposal = entry.getKey();
- if (proposal.account == contact.getAccount() && contact.getJid().asBareJid().equals(proposal.with)) {
+ if (proposal.account == contact.getAccount()
+ && contact.getJid().asBareJid().equals(proposal.with)) {
final DeviceDiscoveryState preexistingState = entry.getValue();
- if (preexistingState != null && preexistingState != DeviceDiscoveryState.FAILED) {
+ if (preexistingState != null
+ && preexistingState != DeviceDiscoveryState.FAILED) {
return Optional.of(proposal);
}
}
@@ -473,7 +592,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
void finishConnectionOrThrow(final AbstractJingleConnection connection) {
final AbstractJingleConnection.Id id = connection.getId();
if (this.connections.remove(id) == null) {
- throw new IllegalStateException(String.format("Unable to finish connection with id=%s", id.toString()));
+ throw new IllegalStateException(
+ String.format("Unable to finish connection with id=%s", id.toString()));
}
}
@@ -492,49 +612,70 @@ public class JingleConnectionManager extends AbstractConnectionManager {
return firedUpdates;
}
- void getPrimaryCandidate(final Account account, final boolean initiator, final OnPrimaryCandidateFound listener) {
+ void getPrimaryCandidate(
+ final Account account,
+ final boolean initiator,
+ final OnPrimaryCandidateFound listener) {
if (Config.DISABLE_PROXY_LOOKUP) {
listener.onPrimaryCandidateFound(false, null);
return;
}
if (!this.primaryCandidates.containsKey(account.getJid().asBareJid())) {
- final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Namespace.BYTE_STREAMS);
+ final Jid proxy =
+ account.getXmppConnection().findDiscoItemByFeature(Namespace.BYTE_STREAMS);
if (proxy != null) {
IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
iq.setTo(proxy);
iq.query(Namespace.BYTE_STREAMS);
- account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- final Element streamhost = packet.query().findChild("streamhost", Namespace.BYTE_STREAMS);
- final String host = streamhost == null ? null : streamhost.getAttribute("host");
- final String port = streamhost == null ? null : streamhost.getAttribute("port");
- if (host != null && port != null) {
- try {
- JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
- candidate.setHost(host);
- candidate.setPort(Integer.parseInt(port));
- candidate.setType(JingleCandidate.TYPE_PROXY);
- candidate.setJid(proxy);
- candidate.setPriority(655360 + (initiator ? 30 : 0));
- primaryCandidates.put(account.getJid().asBareJid(), candidate);
- listener.onPrimaryCandidateFound(true, candidate);
- } catch (final NumberFormatException e) {
- listener.onPrimaryCandidateFound(false, null);
- }
- } else {
- listener.onPrimaryCandidateFound(false, null);
- }
- }
- });
+ account.getXmppConnection()
+ .sendIqPacket(
+ iq,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(
+ Account account, IqPacket packet) {
+ final Element streamhost =
+ packet.query()
+ .findChild(
+ "streamhost",
+ Namespace.BYTE_STREAMS);
+ final String host =
+ streamhost == null
+ ? null
+ : streamhost.getAttribute("host");
+ final String port =
+ streamhost == null
+ ? null
+ : streamhost.getAttribute("port");
+ if (host != null && port != null) {
+ try {
+ JingleCandidate candidate =
+ new JingleCandidate(nextRandomId(), true);
+ candidate.setHost(host);
+ candidate.setPort(Integer.parseInt(port));
+ candidate.setType(JingleCandidate.TYPE_PROXY);
+ candidate.setJid(proxy);
+ candidate.setPriority(
+ 655360 + (initiator ? 30 : 0));
+ primaryCandidates.put(
+ account.getJid().asBareJid(), candidate);
+ listener.onPrimaryCandidateFound(true, candidate);
+ } catch (final NumberFormatException e) {
+ listener.onPrimaryCandidateFound(false, null);
+ }
+ } else {
+ listener.onPrimaryCandidateFound(false, null);
+ }
+ }
+ });
} else {
listener.onPrimaryCandidateFound(false, null);
}
} else {
- listener.onPrimaryCandidateFound(true,
- this.primaryCandidates.get(account.getJid().asBareJid()));
+ listener.onPrimaryCandidateFound(
+ true, this.primaryCandidates.get(account.getJid().asBareJid()));
}
}
@@ -556,64 +697,77 @@ public class JingleConnectionManager extends AbstractConnectionManager {
private void retractSessionProposal(RtpSessionProposal rtpSessionProposal) {
final Account account = rtpSessionProposal.account;
toneManager.transition(RtpEndUserState.ENDED, rtpSessionProposal.media);
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retracting rtp session proposal with " + rtpSessionProposal.with);
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": retracting rtp session proposal with "
+ + rtpSessionProposal.with);
this.rtpSessionProposals.remove(rtpSessionProposal);
- final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(rtpSessionProposal);
- writeLogMissedOutgoing(account, rtpSessionProposal.with, rtpSessionProposal.sessionId, null, System.currentTimeMillis());
+ final MessagePacket messagePacket =
+ mXmppConnectionService.getMessageGenerator().sessionRetract(rtpSessionProposal);
+ writeLogMissedOutgoing(
+ account,
+ rtpSessionProposal.with,
+ rtpSessionProposal.sessionId,
+ null,
+ System.currentTimeMillis());
mXmppConnectionService.sendMessagePacket(account, messagePacket);
}
- public String initializeRtpSession(final Account account, final Jid with, final Set<Media> media) {
+ public String initializeRtpSession(
+ final Account account, final Jid with, final Set<Media> media) {
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with);
- final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, account.getJid());
+ final JingleRtpConnection rtpConnection =
+ new JingleRtpConnection(this, id, account.getJid());
rtpConnection.setProposedMedia(media);
this.connections.put(id, rtpConnection);
rtpConnection.sendSessionInitiate();
return id.sessionId;
}
- public void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) {
+ public void proposeJingleRtpSession(
+ final Account account, final Jid with, final Set<Media> media) {
synchronized (this.rtpSessionProposals) {
- for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) {
+ for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry :
+ this.rtpSessionProposals.entrySet()) {
RtpSessionProposal proposal = entry.getKey();
if (proposal.account == account && with.asBareJid().equals(proposal.with)) {
final DeviceDiscoveryState preexistingState = entry.getValue();
- if (preexistingState != null && preexistingState != DeviceDiscoveryState.FAILED) {
+ if (preexistingState != null
+ && preexistingState != DeviceDiscoveryState.FAILED) {
final RtpEndUserState endUserState = preexistingState.toEndUserState();
toneManager.transition(endUserState, media);
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
- account,
- with,
- proposal.sessionId,
- endUserState
- );
+ account, with, proposal.sessionId, endUserState);
return;
}
}
}
if (isBusy()) {
if (hasMatchingRtpSession(account, with, media)) {
- Log.d(Config.LOGTAG, "ignoring request to propose jingle session because the other party already created one for us");
+ Log.d(
+ Config.LOGTAG,
+ "ignoring request to propose jingle session because the other party already created one for us");
return;
}
- throw new IllegalStateException("There is already a running RTP session. This should have been caught by the UI");
+ throw new IllegalStateException(
+ "There is already a running RTP session. This should have been caught by the UI");
}
- final RtpSessionProposal proposal = RtpSessionProposal.of(account, with.asBareJid(), media);
+ final RtpSessionProposal proposal =
+ RtpSessionProposal.of(account, with.asBareJid(), media);
this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING);
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
- account,
- proposal.with,
- proposal.sessionId,
- RtpEndUserState.FINDING_DEVICE
- );
- final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionProposal(proposal);
+ account, proposal.with, proposal.sessionId, RtpEndUserState.FINDING_DEVICE);
+ final MessagePacket messagePacket =
+ mXmppConnectionService.getMessageGenerator().sessionProposal(proposal);
mXmppConnectionService.sendMessagePacket(account, messagePacket);
}
}
public boolean hasMatchingProposal(final Account account, final Jid with) {
synchronized (this.rtpSessionProposals) {
- for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) {
+ for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry :
+ this.rtpSessionProposals.entrySet()) {
final RtpSessionProposal proposal = entry.getKey();
if (proposal.account == account && with.asBareJid().equals(proposal.with)) {
return true;
@@ -642,10 +796,12 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if (sid != null) {
for (final AbstractJingleConnection connection : this.connections.values()) {
if (connection instanceof JingleFileTransferConnection) {
- final JingleFileTransferConnection fileTransfer = (JingleFileTransferConnection) connection;
+ final JingleFileTransferConnection fileTransfer =
+ (JingleFileTransferConnection) connection;
final JingleTransport transport = fileTransfer.getTransport();
if (transport instanceof JingleInBandTransport) {
- final JingleInBandTransport inBandTransport = (JingleInBandTransport) transport;
+ final JingleInBandTransport inBandTransport =
+ (JingleInBandTransport) transport;
if (inBandTransport.matches(account, sid)) {
inBandTransport.deliverPayload(packet, payload);
}
@@ -655,7 +811,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
Log.d(Config.LOGTAG, "unable to deliver ibb packet: " + packet.toString());
- account.getXmppConnection().sendIqPacket(packet.generateResponse(IqPacket.TYPE.ERROR), null);
+ account.getXmppConnection()
+ .sendIqPacket(packet.generateResponse(IqPacket.TYPE.ERROR), null);
}
public void notifyRebound(final Account account) {
@@ -668,8 +825,10 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
- public WeakReference<JingleRtpConnection> findJingleRtpConnection(Account account, Jid with, String sessionId) {
- final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with, sessionId);
+ public WeakReference<JingleRtpConnection> findJingleRtpConnection(
+ Account account, Jid with, String sessionId) {
+ final AbstractJingleConnection.Id id =
+ AbstractJingleConnection.Id.of(account, with, sessionId);
final AbstractJingleConnection connection = connections.get(id);
if (connection instanceof JingleRtpConnection) {
return new WeakReference<>((JingleRtpConnection) connection);
@@ -679,34 +838,53 @@ public class JingleConnectionManager extends AbstractConnectionManager {
private void resendSessionProposals(final Account account) {
synchronized (this.rtpSessionProposals) {
- for (final Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) {
+ for (final Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry :
+ this.rtpSessionProposals.entrySet()) {
final RtpSessionProposal proposal = entry.getKey();
- if (entry.getValue() == DeviceDiscoveryState.SEARCHING && proposal.account == account) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resending session proposal to " + proposal.with);
- final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionProposal(proposal);
+ if (entry.getValue() == DeviceDiscoveryState.SEARCHING
+ && proposal.account == account) {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": resending session proposal to "
+ + proposal.with);
+ final MessagePacket messagePacket =
+ mXmppConnectionService.getMessageGenerator().sessionProposal(proposal);
mXmppConnectionService.sendMessagePacket(account, messagePacket);
}
}
}
}
- public void updateProposedSessionDiscovered(Account account, Jid from, String sessionId, final DeviceDiscoveryState target) {
+ public void updateProposedSessionDiscovered(
+ Account account, Jid from, String sessionId, final DeviceDiscoveryState target) {
synchronized (this.rtpSessionProposals) {
- final RtpSessionProposal sessionProposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
- final DeviceDiscoveryState currentState = sessionProposal == null ? null : rtpSessionProposals.get(sessionProposal);
+ final RtpSessionProposal sessionProposal =
+ getRtpSessionProposal(account, from.asBareJid(), sessionId);
+ final DeviceDiscoveryState currentState =
+ sessionProposal == null ? null : rtpSessionProposals.get(sessionProposal);
if (currentState == null) {
Log.d(Config.LOGTAG, "unable to find session proposal for session id " + sessionId);
return;
}
if (currentState == DeviceDiscoveryState.DISCOVERED) {
- Log.d(Config.LOGTAG, "session proposal already at discovered. not going to fall back");
+ Log.d(
+ Config.LOGTAG,
+ "session proposal already at discovered. not going to fall back");
return;
}
this.rtpSessionProposals.put(sessionProposal, target);
final RtpEndUserState endUserState = target.toEndUserState();
toneManager.transition(endUserState, sessionProposal.media);
- mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, sessionProposal.with, sessionProposal.sessionId, endUserState);
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": flagging session " + sessionId + " as " + target);
+ mXmppConnectionService.notifyJingleRtpConnectionUpdate(
+ account, sessionProposal.with, sessionProposal.sessionId, endUserState);
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": flagging session "
+ + sessionId
+ + " as "
+ + target);
}
}
@@ -731,7 +909,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
public void failProceed(Account account, final Jid with, String sessionId) {
- final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with, sessionId);
+ final AbstractJingleConnection.Id id =
+ AbstractJingleConnection.Id.of(account, with, sessionId);
final AbstractJingleConnection existingJingleConnection = connections.get(id);
if (existingJingleConnection instanceof JingleRtpConnection) {
((JingleRtpConnection) existingJingleConnection).deliverFailedProceed();
@@ -742,13 +921,17 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if (connections.containsValue(connection)) {
return;
}
- final IllegalStateException e = new IllegalStateException("JingleConnection has not been registered with connection manager");
+ final IllegalStateException e =
+ new IllegalStateException(
+ "JingleConnection has not been registered with connection manager");
Log.e(Config.LOGTAG, "ensureConnectionIsRegistered() failed. Going to throw", e);
throw e;
}
- void setTerminalSessionState(AbstractJingleConnection.Id id, final RtpEndUserState state, final Set<Media> media) {
- this.terminatedSessions.put(PersistableSessionId.of(id), new TerminatedRtpSession(state, media));
+ void setTerminalSessionState(
+ AbstractJingleConnection.Id id, final RtpEndUserState state, final Set<Media> media) {
+ this.terminatedSessions.put(
+ PersistableSessionId.of(id), new TerminatedRtpSession(state, media));
}
public TerminatedRtpSession getTerminalSessionState(final Jid with, final String sessionId) {
@@ -773,8 +956,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PersistableSessionId that = (PersistableSessionId) o;
- return Objects.equal(with, that.with) &&
- Objects.equal(sessionId, that.sessionId);
+ return Objects.equal(with, that.with) && Objects.equal(sessionId, that.sessionId);
}
@Override
@@ -794,7 +976,10 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
public enum DeviceDiscoveryState {
- SEARCHING, SEARCHING_ACKNOWLEDGED, DISCOVERED, FAILED;
+ SEARCHING,
+ SEARCHING_ACKNOWLEDGED,
+ DISCOVERED,
+ FAILED;
public RtpEndUserState toEndUserState() {
switch (this) {
@@ -835,9 +1020,9 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RtpSessionProposal proposal = (RtpSessionProposal) o;
- return Objects.equal(account.getJid(), proposal.account.getJid()) &&
- Objects.equal(with, proposal.with) &&
- Objects.equal(sessionId, proposal.sessionId);
+ return Objects.equal(account.getJid(), proposal.account.getJid())
+ && Objects.equal(with, proposal.with)
+ && Objects.equal(sessionId, proposal.sessionId);
}
@Override
@@ -33,6 +33,8 @@ import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -61,91 +63,103 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
-public class JingleRtpConnection extends AbstractJingleConnection implements WebRTCWrapper.EventCallback {
+public class JingleRtpConnection extends AbstractJingleConnection
+ implements WebRTCWrapper.EventCallback {
- public static final List<State> STATES_SHOWING_ONGOING_CALL = Arrays.asList(
- State.PROCEED,
- State.SESSION_INITIALIZED_PRE_APPROVED,
- State.SESSION_ACCEPTED
- );
+ public static final List<State> STATES_SHOWING_ONGOING_CALL =
+ Arrays.asList(
+ State.PROCEED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED);
private static final long BUSY_TIME_OUT = 30;
- private static final List<State> TERMINATED = Arrays.asList(
- State.ACCEPTED,
- State.REJECTED,
- State.REJECTED_RACED,
- State.RETRACTED,
- State.RETRACTED_RACED,
- State.TERMINATED_SUCCESS,
- State.TERMINATED_DECLINED_OR_BUSY,
- State.TERMINATED_CONNECTIVITY_ERROR,
- State.TERMINATED_CANCEL_OR_TIMEOUT,
- State.TERMINATED_APPLICATION_FAILURE,
- State.TERMINATED_SECURITY_ERROR
- );
+ private static final List<State> TERMINATED =
+ Arrays.asList(
+ State.ACCEPTED,
+ State.REJECTED,
+ State.REJECTED_RACED,
+ State.RETRACTED,
+ State.RETRACTED_RACED,
+ State.TERMINATED_SUCCESS,
+ State.TERMINATED_DECLINED_OR_BUSY,
+ State.TERMINATED_CONNECTIVITY_ERROR,
+ State.TERMINATED_CANCEL_OR_TIMEOUT,
+ State.TERMINATED_APPLICATION_FAILURE,
+ State.TERMINATED_SECURITY_ERROR);
private static final Map<State, Collection<State>> VALID_TRANSITIONS;
static {
- final ImmutableMap.Builder<State, Collection<State>> transitionBuilder = new ImmutableMap.Builder<>();
- transitionBuilder.put(State.NULL, ImmutableList.of(
+ final ImmutableMap.Builder<State, Collection<State>> transitionBuilder =
+ new ImmutableMap.Builder<>();
+ transitionBuilder.put(
+ State.NULL,
+ ImmutableList.of(
+ State.PROPOSED,
+ State.SESSION_INITIALIZED,
+ State.TERMINATED_APPLICATION_FAILURE,
+ State.TERMINATED_SECURITY_ERROR));
+ transitionBuilder.put(
State.PROPOSED,
- State.SESSION_INITIALIZED,
- State.TERMINATED_APPLICATION_FAILURE,
- State.TERMINATED_SECURITY_ERROR
- ));
- transitionBuilder.put(State.PROPOSED, ImmutableList.of(
- State.ACCEPTED,
+ ImmutableList.of(
+ State.ACCEPTED,
+ State.PROCEED,
+ State.REJECTED,
+ State.RETRACTED,
+ State.TERMINATED_APPLICATION_FAILURE,
+ State.TERMINATED_SECURITY_ERROR,
+ State.TERMINATED_CONNECTIVITY_ERROR // only used when the xmpp connection
+ // rebinds
+ ));
+ transitionBuilder.put(
State.PROCEED,
- State.REJECTED,
- State.RETRACTED,
- State.TERMINATED_APPLICATION_FAILURE,
- State.TERMINATED_SECURITY_ERROR,
- State.TERMINATED_CONNECTIVITY_ERROR //only used when the xmpp connection rebinds
- ));
- transitionBuilder.put(State.PROCEED, ImmutableList.of(
- State.REJECTED_RACED,
- State.RETRACTED_RACED,
+ ImmutableList.of(
+ State.REJECTED_RACED,
+ State.RETRACTED_RACED,
+ State.SESSION_INITIALIZED_PRE_APPROVED,
+ State.TERMINATED_SUCCESS,
+ State.TERMINATED_APPLICATION_FAILURE,
+ State.TERMINATED_SECURITY_ERROR,
+ State.TERMINATED_CONNECTIVITY_ERROR // at this state used for error
+ // bounces of the proceed message
+ ));
+ transitionBuilder.put(
+ State.SESSION_INITIALIZED,
+ ImmutableList.of(
+ State.SESSION_ACCEPTED,
+ State.TERMINATED_SUCCESS,
+ State.TERMINATED_DECLINED_OR_BUSY,
+ State.TERMINATED_CONNECTIVITY_ERROR, // at this state used for IQ errors
+ // and IQ timeouts
+ State.TERMINATED_CANCEL_OR_TIMEOUT,
+ State.TERMINATED_APPLICATION_FAILURE,
+ State.TERMINATED_SECURITY_ERROR));
+ transitionBuilder.put(
State.SESSION_INITIALIZED_PRE_APPROVED,
- State.TERMINATED_SUCCESS,
- State.TERMINATED_APPLICATION_FAILURE,
- State.TERMINATED_SECURITY_ERROR,
- State.TERMINATED_CONNECTIVITY_ERROR //at this state used for error bounces of the proceed message
- ));
- transitionBuilder.put(State.SESSION_INITIALIZED, ImmutableList.of(
+ ImmutableList.of(
+ State.SESSION_ACCEPTED,
+ State.TERMINATED_SUCCESS,
+ State.TERMINATED_DECLINED_OR_BUSY,
+ State.TERMINATED_CONNECTIVITY_ERROR, // at this state used for IQ errors
+ // and IQ timeouts
+ State.TERMINATED_CANCEL_OR_TIMEOUT,
+ State.TERMINATED_APPLICATION_FAILURE,
+ State.TERMINATED_SECURITY_ERROR));
+ transitionBuilder.put(
State.SESSION_ACCEPTED,
- State.TERMINATED_SUCCESS,
- State.TERMINATED_DECLINED_OR_BUSY,
- State.TERMINATED_CONNECTIVITY_ERROR, //at this state used for IQ errors and IQ timeouts
- State.TERMINATED_CANCEL_OR_TIMEOUT,
- State.TERMINATED_APPLICATION_FAILURE,
- State.TERMINATED_SECURITY_ERROR
- ));
- transitionBuilder.put(State.SESSION_INITIALIZED_PRE_APPROVED, ImmutableList.of(
- State.SESSION_ACCEPTED,
- State.TERMINATED_SUCCESS,
- State.TERMINATED_DECLINED_OR_BUSY,
- State.TERMINATED_CONNECTIVITY_ERROR, //at this state used for IQ errors and IQ timeouts
- State.TERMINATED_CANCEL_OR_TIMEOUT,
- State.TERMINATED_APPLICATION_FAILURE,
- State.TERMINATED_SECURITY_ERROR
- ));
- transitionBuilder.put(State.SESSION_ACCEPTED, ImmutableList.of(
- State.TERMINATED_SUCCESS,
- State.TERMINATED_DECLINED_OR_BUSY,
- State.TERMINATED_CONNECTIVITY_ERROR,
- State.TERMINATED_CANCEL_OR_TIMEOUT,
- State.TERMINATED_APPLICATION_FAILURE,
- State.TERMINATED_SECURITY_ERROR
- ));
+ ImmutableList.of(
+ State.TERMINATED_SUCCESS,
+ State.TERMINATED_DECLINED_OR_BUSY,
+ State.TERMINATED_CONNECTIVITY_ERROR,
+ State.TERMINATED_CANCEL_OR_TIMEOUT,
+ State.TERMINATED_APPLICATION_FAILURE,
+ State.TERMINATED_SECURITY_ERROR));
VALID_TRANSITIONS = transitionBuilder.build();
}
private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
- private final Queue<Map.Entry<String, RtpContentMap.DescriptionTransport>> pendingIceCandidates = new LinkedList<>();
+ private final Queue<Map.Entry<String, RtpContentMap.DescriptionTransport>>
+ pendingIceCandidates = new LinkedList<>();
private final OmemoVerification omemoVerification = new OmemoVerification();
private final Message message;
private State state = State.NULL;
- private StateTransitionException stateTransitionException;
private Set<Media> proposedMedia;
private RtpContentMap initiatorRtpContentMap;
private RtpContentMap responderRtpContentMap;
@@ -156,18 +170,16 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
super(jingleConnectionManager, id, initiator);
- final Conversation conversation = jingleConnectionManager.getXmppConnectionService().findOrCreateConversation(
- id.account,
- id.with.asBareJid(),
- false,
- false
- );
- this.message = new Message(
- conversation,
- isInitiator() ? Message.STATUS_SEND : Message.STATUS_RECEIVED,
- Message.TYPE_RTP_SESSION,
- id.sessionId
- );
+ final Conversation conversation =
+ jingleConnectionManager
+ .getXmppConnectionService()
+ .findOrCreateConversation(id.account, id.with.asBareJid(), false, false);
+ this.message =
+ new Message(
+ conversation,
+ isInitiator() ? Message.STATUS_SEND : Message.STATUS_RECEIVED,
+ Message.TYPE_RTP_SESSION,
+ id.sessionId);
}
private static State reasonToState(Reason reason) {
@@ -208,7 +220,11 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
break;
default:
respondOk(jinglePacket);
- Log.d(Config.LOGTAG, String.format("%s: received unhandled jingle action %s", id.account.getJid().asBareJid(), jinglePacket.getAction()));
+ Log.d(
+ Config.LOGTAG,
+ String.format(
+ "%s: received unhandled jingle action %s",
+ id.account.getJid().asBareJid(), jinglePacket.getAction()));
break;
}
}
@@ -222,8 +238,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (!isInitiator() && isInState(State.PROPOSED, State.SESSION_INITIALIZED)) {
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
}
- if (isInState(State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
- //we might have already changed resources (full jid) at this point; so this might not even reach the other party
+ if (isInState(
+ State.SESSION_INITIALIZED,
+ State.SESSION_INITIALIZED_PRE_APPROVED,
+ State.SESSION_ACCEPTED)) {
+ // we might have already changed resources (full jid) at this point; so this might not
+ // even reach the other party
sendSessionTerminate(Reason.CONNECTIVITY_ERROR);
} else {
transitionOrThrow(State.TERMINATED_CONNECTIVITY_ERROR);
@@ -235,9 +255,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
respondOk(jinglePacket);
final JinglePacket.ReasonWrapper wrapper = jinglePacket.getReason();
final State previous = this.state;
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received session terminate reason=" + wrapper.reason + "(" + Strings.nullToEmpty(wrapper.text) + ") while in state " + previous);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": received session terminate reason="
+ + wrapper.reason
+ + "("
+ + Strings.nullToEmpty(wrapper.text)
+ + ") while in state "
+ + previous);
if (TERMINATED.contains(previous)) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring session terminate because already in " + previous);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": ignoring session terminate because already in "
+ + previous);
return;
}
webRTCWrapper.close();
@@ -251,13 +283,23 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
private void receiveTransportInfo(final JinglePacket jinglePacket) {
- //Due to the asynchronicity of processing session-init we might move from NULL|PROCEED to INITIALIZED only after transport-info has been received
- if (isInState(State.NULL, State.PROCEED, State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
+ // Due to the asynchronicity of processing session-init we might move from NULL|PROCEED to
+ // INITIALIZED only after transport-info has been received
+ if (isInState(
+ State.NULL,
+ State.PROCEED,
+ State.SESSION_INITIALIZED,
+ State.SESSION_INITIALIZED_PRE_APPROVED,
+ State.SESSION_ACCEPTED)) {
final RtpContentMap contentMap;
try {
contentMap = RtpContentMap.of(jinglePacket);
} catch (final IllegalArgumentException | NullPointerException e) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents; ignoring", e);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": improperly formatted contents; ignoring",
+ e);
respondOk(jinglePacket);
return;
}
@@ -265,18 +307,27 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} else {
if (isTerminated()) {
respondOk(jinglePacket);
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring out-of-order transport info; we where already terminated");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": ignoring out-of-order transport info; we where already terminated");
} else {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received transport info while in state=" + this.state);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": received transport info while in state="
+ + this.state);
terminateWithOutOfOrder(jinglePacket);
}
}
}
- private void receiveTransportInfo(final JinglePacket jinglePacket, final RtpContentMap contentMap) {
- final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates = contentMap.contents.entrySet();
+ private void receiveTransportInfo(
+ final JinglePacket jinglePacket, final RtpContentMap contentMap) {
+ final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates =
+ contentMap.contents.entrySet();
if (this.state == State.SESSION_ACCEPTED) {
- //zero candidates + modified credentials are an ICE restart offer
+ // zero candidates + modified credentials are an ICE restart offer
if (checkForIceRestart(jinglePacket, contentMap)) {
return;
}
@@ -284,7 +335,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
try {
processCandidates(candidates);
} catch (final WebRTCWrapper.PeerConnectionNotInitialized e) {
- Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnection was not initialized when processing transport info. this usually indicates a race condition that can be ignored");
+ Log.w(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": PeerConnection was not initialized when processing transport info. this usually indicates a race condition that can be ignored");
}
} else {
respondOk(jinglePacket);
@@ -292,7 +346,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
}
- private boolean checkForIceRestart(final JinglePacket jinglePacket, final RtpContentMap rtpContentMap) {
+ private boolean checkForIceRestart(
+ final JinglePacket jinglePacket, final RtpContentMap rtpContentMap) {
final RtpContentMap existing = getRemoteContentMap();
final IceUdpTransportInfo.Credentials existingCredentials;
final IceUdpTransportInfo.Credentials newCredentials;
@@ -306,7 +361,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (existingCredentials.equals(newCredentials)) {
return false;
}
- //TODO an alternative approach is to check if we already got an iq result to our ICE-restart
+ // TODO an alternative approach is to check if we already got an iq result to our
+ // 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 isOffer = rtpContentMap.emptyCandidates();
@@ -314,10 +370,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
try {
if (isOffer) {
Log.d(Config.LOGTAG, "received offer to restart ICE " + newCredentials);
- restartContentMap = existing.modifiedCredentials(newCredentials, IceUdpTransportInfo.Setup.ACTPASS);
+ restartContentMap =
+ existing.modifiedCredentials(
+ newCredentials, IceUdpTransportInfo.Setup.ACTPASS);
} else {
final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
- Log.d(Config.LOGTAG, "received confirmation of ICE restart" + newCredentials + " peer_setup=" + setup);
+ Log.d(
+ Config.LOGTAG,
+ "received confirmation of ICE restart"
+ + newCredentials
+ + " peer_setup="
+ + setup);
// DTLS setup attribute needs to be rewritten to reflect current peer state
// https://groups.google.com/g/discuss-webrtc/c/DfpIMwvUfeM
restartContentMap = existing.modifiedCredentials(newCredentials, setup);
@@ -333,7 +396,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
respondOk(jinglePacket);
final Throwable rootCause = Throwables.getRootCause(exception);
if (rootCause instanceof WebRTCWrapper.PeerConnectionNotInitialized) {
- //If this happens a termination is already in progress
+ // If this happens a termination is already in progress
Log.d(Config.LOGTAG, "ignoring PeerConnectionNotInitialized on ICE restart");
return true;
}
@@ -359,13 +422,22 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
this.peerDtlsSetup = setup;
}
- private boolean applyIceRestart(final JinglePacket jinglePacket, final RtpContentMap restartContentMap, final boolean isOffer) throws ExecutionException, InterruptedException {
+ private boolean applyIceRestart(
+ final JinglePacket jinglePacket,
+ final RtpContentMap restartContentMap,
+ final boolean isOffer)
+ throws ExecutionException, InterruptedException {
final SessionDescription sessionDescription = SessionDescription.of(restartContentMap);
- final org.webrtc.SessionDescription.Type type = isOffer ? org.webrtc.SessionDescription.Type.OFFER : org.webrtc.SessionDescription.Type.ANSWER;
- org.webrtc.SessionDescription sdp = new org.webrtc.SessionDescription(type, sessionDescription.toString());
+ final org.webrtc.SessionDescription.Type type =
+ isOffer
+ ? org.webrtc.SessionDescription.Type.OFFER
+ : org.webrtc.SessionDescription.Type.ANSWER;
+ org.webrtc.SessionDescription sdp =
+ new org.webrtc.SessionDescription(type, sessionDescription.toString());
if (isOffer && webRTCWrapper.getSignalingState() != PeerConnection.SignalingState.STABLE) {
if (isInitiator()) {
- //We ignore the offer and respond with tie-break. This will clause the responder not to apply the content map
+ // We ignore the offer and respond with tie-break. This will clause the responder
+ // not to apply the content map
return false;
}
}
@@ -375,7 +447,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
webRTCWrapper.setIsReadyToReceiveIceCandidates(false);
final SessionDescription localSessionDescription = setLocalSessionDescription();
setLocalContentMap(RtpContentMap.of(localSessionDescription));
- //We need to respond OK before sending any candidates
+ // We need to respond OK before sending any candidates
respondOk(jinglePacket);
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
} else {
@@ -384,32 +456,40 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return true;
}
- private void processCandidates(final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
+ private void processCandidates(
+ final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : contents) {
processCandidate(content);
}
}
- private void processCandidate(final Map.Entry<String, RtpContentMap.DescriptionTransport> content) {
+ private void processCandidate(
+ final Map.Entry<String, RtpContentMap.DescriptionTransport> content) {
final RtpContentMap rtpContentMap = getRemoteContentMap();
final List<String> indices = toIdentificationTags(rtpContentMap);
- final String sdpMid = content.getKey(); //aka content name
+ final String sdpMid = content.getKey(); // aka content name
final IceUdpTransportInfo transport = content.getValue().transport;
final IceUdpTransportInfo.Credentials credentials = transport.getCredentials();
- //TODO check that credentials remained the same
+ // TODO check that credentials remained the same
for (final IceUdpTransportInfo.Candidate candidate : transport.getCandidates()) {
final String sdp;
try {
sdp = candidate.toSdpAttribute(credentials.ufrag);
} catch (final IllegalArgumentException e) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring invalid ICE candidate " + e.getMessage());
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": ignoring invalid ICE candidate "
+ + e.getMessage());
continue;
}
final int mLineIndex = indices.indexOf(sdpMid);
if (mLineIndex < 0) {
- Log.w(Config.LOGTAG, "mLineIndex not found for " + sdpMid + ". available indices " + indices);
+ Log.w(
+ Config.LOGTAG,
+ "mLineIndex not found for " + sdpMid + ". available indices " + indices);
}
final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp);
Log.d(Config.LOGTAG, "received candidate: " + iceCandidate);
@@ -423,14 +503,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private List<String> toIdentificationTags(final RtpContentMap rtpContentMap) {
final Group originalGroup = rtpContentMap.group;
- final List<String> identificationTags = originalGroup == null ? rtpContentMap.getNames() : originalGroup.getIdentificationTags();
+ final List<String> identificationTags =
+ originalGroup == null
+ ? rtpContentMap.getNames()
+ : originalGroup.getIdentificationTags();
if (identificationTags.size() == 0) {
- Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
+ Log.w(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
}
return identificationTags;
}
- private ListenableFuture<RtpContentMap> receiveRtpContentMap(final JinglePacket jinglePacket, final boolean expectVerification) {
+ private ListenableFuture<RtpContentMap> receiveRtpContentMap(
+ final JinglePacket jinglePacket, final boolean expectVerification) {
final RtpContentMap receivedContentMap;
try {
receivedContentMap = RtpContentMap.of(jinglePacket);
@@ -438,17 +525,26 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return Futures.immediateFailedFuture(e);
}
if (receivedContentMap instanceof OmemoVerifiedRtpContentMap) {
- final ListenableFuture<AxolotlService.OmemoVerifiedPayload<RtpContentMap>> future = id.account.getAxolotlService().decrypt((OmemoVerifiedRtpContentMap) receivedContentMap, id.with);
- return Futures.transform(future, omemoVerifiedPayload -> {
- //TODO test if an exception here triggers a correct abort
- omemoVerification.setOrEnsureEqual(omemoVerifiedPayload);
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received verifiable DTLS fingerprint via " + omemoVerification);
- return omemoVerifiedPayload.getPayload();
- }, MoreExecutors.directExecutor());
+ final ListenableFuture<AxolotlService.OmemoVerifiedPayload<RtpContentMap>> future =
+ id.account
+ .getAxolotlService()
+ .decrypt((OmemoVerifiedRtpContentMap) receivedContentMap, id.with);
+ return Futures.transform(
+ future,
+ omemoVerifiedPayload -> {
+ // TODO test if an exception here triggers a correct abort
+ omemoVerification.setOrEnsureEqual(omemoVerifiedPayload);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": received verifiable DTLS fingerprint via "
+ + omemoVerification);
+ return omemoVerifiedPayload.getPayload();
+ },
+ MoreExecutors.directExecutor());
} else if (Config.REQUIRE_RTP_VERIFICATION || expectVerification) {
return Futures.immediateFailedFuture(
- new SecurityException("DTLS fingerprint was unexpectedly not verifiable")
- );
+ new SecurityException("DTLS fingerprint was unexpectedly not verifiable"));
} else {
return Futures.immediateFuture(receivedContentMap);
}
@@ -456,13 +552,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void receiveSessionInitiate(final JinglePacket jinglePacket) {
if (isInitiator()) {
- Log.d(Config.LOGTAG, String.format("%s: received session-initiate even though we were initiating", id.account.getJid().asBareJid()));
+ Log.d(
+ Config.LOGTAG,
+ String.format(
+ "%s: received session-initiate even though we were initiating",
+ id.account.getJid().asBareJid()));
if (isTerminated()) {
- Log.d(Config.LOGTAG, String.format(
- "%s: got a reason to terminate with out-of-order. but already in state %s",
- id.account.getJid().asBareJid(),
- getState()
- ));
+ Log.d(
+ Config.LOGTAG,
+ String.format(
+ "%s: got a reason to terminate with out-of-order. but already in state %s",
+ id.account.getJid().asBareJid(), getState()));
respondWithOutOfOrder(jinglePacket);
} else {
terminateWithOutOfOrder(jinglePacket);
@@ -470,43 +570,51 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return;
}
final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, false);
- Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
- @Override
- public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
- receiveSessionInitiate(jinglePacket, rtpContentMap);
- }
+ Futures.addCallback(
+ future,
+ new FutureCallback<RtpContentMap>() {
+ @Override
+ public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
+ receiveSessionInitiate(jinglePacket, rtpContentMap);
+ }
- @Override
- public void onFailure(@NonNull final Throwable throwable) {
- respondOk(jinglePacket);
- sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
- }
- }, MoreExecutors.directExecutor());
+ @Override
+ public void onFailure(@NonNull final Throwable throwable) {
+ respondOk(jinglePacket);
+ sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
+ }
+ },
+ MoreExecutors.directExecutor());
}
- private void receiveSessionInitiate(final JinglePacket jinglePacket, final RtpContentMap contentMap) {
+ private void receiveSessionInitiate(
+ final JinglePacket jinglePacket, final RtpContentMap contentMap) {
try {
contentMap.requireContentDescriptions();
contentMap.requireDTLSFingerprint(true);
} catch (final RuntimeException e) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents", Throwables.getRootCause(e));
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid() + ": improperly formatted contents",
+ Throwables.getRootCause(e));
respondOk(jinglePacket);
sendSessionTerminate(Reason.of(e), e.getMessage());
return;
}
- Log.d(Config.LOGTAG, "processing session-init with " + contentMap.contents.size() + " contents");
+ Log.d(
+ Config.LOGTAG,
+ "processing session-init with " + contentMap.contents.size() + " contents");
final State target;
if (this.state == State.PROCEED) {
Preconditions.checkState(
proposedMedia != null && proposedMedia.size() > 0,
- "proposed media must be set when processing pre-approved session-initiate"
- );
+ "proposed media must be set when processing pre-approved session-initiate");
if (!this.proposedMedia.equals(contentMap.getMedia())) {
- sendSessionTerminate(Reason.SECURITY_ERROR, String.format(
- "Your session proposal (Jingle Message Initiation) included media %s but your session-initiate was %s",
- this.proposedMedia,
- contentMap.getMedia()
- ));
+ sendSessionTerminate(
+ Reason.SECURITY_ERROR,
+ String.format(
+ "Your session proposal (Jingle Message Initiation) included media %s but your session-initiate was %s",
+ this.proposedMedia, contentMap.getMedia()));
return;
}
target = State.SESSION_INITIALIZED_PRE_APPROVED;
@@ -517,67 +625,100 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
respondOk(jinglePacket);
pendingIceCandidates.addAll(contentMap.contents.entrySet());
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": automatically accepting session-initiate");
sendSessionAccept();
} else {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received not pre-approved session-initiate. start ringing");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": received not pre-approved session-initiate. start ringing");
startRinging();
}
} else {
- Log.d(Config.LOGTAG, String.format("%s: received session-initiate while in state %s", id.account.getJid().asBareJid(), state));
+ Log.d(
+ Config.LOGTAG,
+ String.format(
+ "%s: received session-initiate while in state %s",
+ id.account.getJid().asBareJid(), state));
terminateWithOutOfOrder(jinglePacket);
}
}
private void receiveSessionAccept(final JinglePacket jinglePacket) {
if (!isInitiator()) {
- Log.d(Config.LOGTAG, String.format("%s: received session-accept even though we were responding", id.account.getJid().asBareJid()));
+ Log.d(
+ Config.LOGTAG,
+ String.format(
+ "%s: received session-accept even though we were responding",
+ id.account.getJid().asBareJid()));
terminateWithOutOfOrder(jinglePacket);
return;
}
- final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
- Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
- @Override
- public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
- receiveSessionAccept(jinglePacket, rtpContentMap);
- }
+ final ListenableFuture<RtpContentMap> future =
+ receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
+ Futures.addCallback(
+ future,
+ new FutureCallback<RtpContentMap>() {
+ @Override
+ public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
+ receiveSessionAccept(jinglePacket, rtpContentMap);
+ }
- @Override
- public void onFailure(@NonNull final Throwable throwable) {
- respondOk(jinglePacket);
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents in session-accept", throwable);
- webRTCWrapper.close();
- sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
- }
- }, MoreExecutors.directExecutor());
+ @Override
+ public void onFailure(@NonNull final Throwable throwable) {
+ respondOk(jinglePacket);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": improperly formatted contents in session-accept",
+ throwable);
+ webRTCWrapper.close();
+ sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
+ }
+ },
+ MoreExecutors.directExecutor());
}
- private void receiveSessionAccept(final JinglePacket jinglePacket, final RtpContentMap contentMap) {
+ private void receiveSessionAccept(
+ final JinglePacket jinglePacket, final RtpContentMap contentMap) {
try {
contentMap.requireContentDescriptions();
contentMap.requireDTLSFingerprint();
} catch (final RuntimeException e) {
respondOk(jinglePacket);
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents in session-accept", e);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": improperly formatted contents in session-accept",
+ e);
webRTCWrapper.close();
sendSessionTerminate(Reason.of(e), e.getMessage());
return;
}
final Set<Media> initiatorMedia = this.initiatorRtpContentMap.getMedia();
if (!initiatorMedia.equals(contentMap.getMedia())) {
- sendSessionTerminate(Reason.SECURITY_ERROR, String.format(
- "Your session-included included media %s but our session-initiate was %s",
- this.proposedMedia,
- contentMap.getMedia()
- ));
+ sendSessionTerminate(
+ Reason.SECURITY_ERROR,
+ String.format(
+ "Your session-included included media %s but our session-initiate was %s",
+ this.proposedMedia, contentMap.getMedia()));
return;
}
- Log.d(Config.LOGTAG, "processing session-accept with " + contentMap.contents.size() + " contents");
+ Log.d(
+ Config.LOGTAG,
+ "processing session-accept with " + contentMap.contents.size() + " contents");
if (transition(State.SESSION_ACCEPTED)) {
respondOk(jinglePacket);
receiveSessionAccept(contentMap);
} else {
- Log.d(Config.LOGTAG, String.format("%s: received session-accept while in state %s", id.account.getJid().asBareJid(), state));
+ Log.d(
+ Config.LOGTAG,
+ String.format(
+ "%s: received session-accept while in state %s",
+ id.account.getJid().asBareJid(), state));
respondOk(jinglePacket);
}
}
@@ -589,21 +730,29 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
try {
sessionDescription = SessionDescription.of(contentMap);
} catch (final IllegalArgumentException | NullPointerException e) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable convert offer from session-accept to SDP", e);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": unable convert offer from session-accept to SDP",
+ e);
webRTCWrapper.close();
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
return;
}
- final org.webrtc.SessionDescription answer = new org.webrtc.SessionDescription(
- org.webrtc.SessionDescription.Type.ANSWER,
- sessionDescription.toString()
- );
+ final org.webrtc.SessionDescription answer =
+ new org.webrtc.SessionDescription(
+ org.webrtc.SessionDescription.Type.ANSWER, sessionDescription.toString());
try {
this.webRTCWrapper.setRemoteDescription(answer).get();
} catch (final Exception e) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to set remote description after receiving session-accept", Throwables.getRootCause(e));
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": unable to set remote description after receiving session-accept",
+ Throwables.getRootCause(e));
webRTCWrapper.close();
- sendSessionTerminate(Reason.FAILED_APPLICATION, Throwables.getRootCause(e).getMessage());
+ sendSessionTerminate(
+ Reason.FAILED_APPLICATION, Throwables.getRootCause(e).getMessage());
return;
}
processCandidates(contentMap.contents.entrySet());
@@ -618,7 +767,11 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
try {
offer = SessionDescription.of(rtpContentMap);
} catch (final IllegalArgumentException | NullPointerException e) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable convert offer from session-initiate to SDP", e);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": unable convert offer from session-initiate to SDP",
+ e);
webRTCWrapper.close();
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
return;
@@ -630,9 +783,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
discoverIceServers(iceServers -> sendSessionAccept(media, offer, iceServers));
}
- private synchronized void sendSessionAccept(final Set<Media> media, final SessionDescription offer, final List<PeerConnection.IceServer> iceServers) {
+ private synchronized void sendSessionAccept(
+ final Set<Media> media,
+ final SessionDescription offer,
+ final List<PeerConnection.IceServer> iceServers) {
if (isTerminated()) {
- Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": ICE servers got discovered when session was already terminated. nothing to do.");
+ Log.w(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": ICE servers got discovered when session was already terminated. nothing to do.");
return;
}
try {
@@ -643,14 +802,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
sendSessionTerminate(Reason.FAILED_APPLICATION);
return;
}
- final org.webrtc.SessionDescription sdp = new org.webrtc.SessionDescription(
- org.webrtc.SessionDescription.Type.OFFER,
- offer.toString()
- );
+ final org.webrtc.SessionDescription sdp =
+ new org.webrtc.SessionDescription(
+ org.webrtc.SessionDescription.Type.OFFER, offer.toString());
try {
this.webRTCWrapper.setRemoteDescription(sdp).get();
addIceCandidatesFromBlackLog();
- org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.setLocalDescription().get();
+ org.webrtc.SessionDescription webRTCSessionDescription =
+ this.webRTCWrapper.setLocalDescription().get();
prepareSessionAccept(webRTCSessionDescription);
} catch (final Exception e) {
failureToAcceptSession(e);
@@ -671,18 +830,24 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
Map.Entry<String, RtpContentMap.DescriptionTransport> foo;
while ((foo = this.pendingIceCandidates.poll()) != null) {
processCandidate(foo);
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": added candidate from back log");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid() + ": added candidate from back log");
}
}
- private void prepareSessionAccept(final org.webrtc.SessionDescription webRTCSessionDescription) {
- final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
+ private void prepareSessionAccept(
+ final org.webrtc.SessionDescription webRTCSessionDescription) {
+ final SessionDescription sessionDescription =
+ SessionDescription.parse(webRTCSessionDescription.description);
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
this.responderRtpContentMap = respondingRtpContentMap;
storePeerDtlsSetup(respondingRtpContentMap.getDtlsSetup().flip());
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
- final ListenableFuture<RtpContentMap> outgoingContentMapFuture = prepareOutgoingContentMap(respondingRtpContentMap);
- Futures.addCallback(outgoingContentMapFuture,
+ final ListenableFuture<RtpContentMap> outgoingContentMapFuture =
+ prepareOutgoingContentMap(respondingRtpContentMap);
+ Futures.addCallback(
+ outgoingContentMapFuture,
new FutureCallback<RtpContentMap>() {
@Override
public void onSuccess(final RtpContentMap outgoingContentMap) {
@@ -694,35 +859,56 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
failureToAcceptSession(throwable);
}
},
- MoreExecutors.directExecutor()
- );
+ MoreExecutors.directExecutor());
}
private void sendSessionAccept(final RtpContentMap rtpContentMap) {
if (isTerminated()) {
- Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": preparing session accept was too slow. already terminated. nothing to do.");
+ Log.w(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": preparing session accept was too slow. already terminated. nothing to do.");
return;
}
transitionOrThrow(State.SESSION_ACCEPTED);
- final JinglePacket sessionAccept = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
+ final JinglePacket sessionAccept =
+ rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
send(sessionAccept);
}
- private ListenableFuture<RtpContentMap> prepareOutgoingContentMap(final RtpContentMap rtpContentMap) {
+ private ListenableFuture<RtpContentMap> prepareOutgoingContentMap(
+ final RtpContentMap rtpContentMap) {
if (this.omemoVerification.hasDeviceId()) {
- ListenableFuture<AxolotlService.OmemoVerifiedPayload<OmemoVerifiedRtpContentMap>> verifiedPayloadFuture = id.account.getAxolotlService()
- .encrypt(rtpContentMap, id.with, omemoVerification.getDeviceId());
- return Futures.transform(verifiedPayloadFuture, verifiedPayload -> {
- omemoVerification.setOrEnsureEqual(verifiedPayload);
- return verifiedPayload.getPayload();
- }, MoreExecutors.directExecutor());
+ ListenableFuture<AxolotlService.OmemoVerifiedPayload<OmemoVerifiedRtpContentMap>>
+ verifiedPayloadFuture =
+ id.account
+ .getAxolotlService()
+ .encrypt(
+ rtpContentMap,
+ id.with,
+ omemoVerification.getDeviceId());
+ return Futures.transform(
+ verifiedPayloadFuture,
+ verifiedPayload -> {
+ omemoVerification.setOrEnsureEqual(verifiedPayload);
+ return verifiedPayload.getPayload();
+ },
+ MoreExecutors.directExecutor());
} else {
return Futures.immediateFuture(rtpContentMap);
}
}
- synchronized void deliveryMessage(final Jid from, final Element message, final String serverMessageId, final long timestamp) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": delivered message to JingleRtpConnection " + message);
+ synchronized void deliveryMessage(
+ final Jid from,
+ final Element message,
+ final String serverMessageId,
+ final long timestamp) {
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": delivered message to JingleRtpConnection "
+ + message);
switch (message.getName()) {
case "propose":
receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp);
@@ -745,47 +931,73 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
void deliverFailedProceed() {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": receive message error for proceed message");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid() + ": receive message error for proceed message");
if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) {
webRTCWrapper.close();
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": transitioned into connectivity error");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid() + ": transitioned into connectivity error");
this.finish();
}
}
private void receiveAccept(final Jid from, final String serverMsgId, final long timestamp) {
- final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
+ final boolean originatedFromMyself =
+ from.asBareJid().equals(id.account.getJid().asBareJid());
if (originatedFromMyself) {
if (transition(State.ACCEPTED)) {
if (serverMsgId != null) {
this.message.setServerMsgId(serverMsgId);
}
this.message.setTime(timestamp);
- this.message.setCarbon(true); //indicate that call was accepted on other device
+ 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();
} else {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to transition to accept because already in state=" + this.state);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": unable to transition to accept because already in state="
+ + this.state);
}
} else {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring 'accept' from " + from);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid() + ": ignoring 'accept' from " + from);
}
}
private void receiveReject(final Jid from, final String serverMsgId, final long timestamp) {
- final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
- //reject from another one of my clients
+ final boolean originatedFromMyself =
+ from.asBareJid().equals(id.account.getJid().asBareJid());
+ // reject from another one of my clients
if (originatedFromMyself) {
receiveRejectFromMyself(serverMsgId, timestamp);
} else if (isInitiator()) {
if (from.equals(id.with)) {
receiveRejectFromResponder();
} else {
- Log.d(Config.LOGTAG, id.account.getJid() + ": ignoring reject from " + from + " for session with " + id.with);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid()
+ + ": ignoring reject from "
+ + from
+ + " for session with "
+ + id.with);
}
} else {
- Log.d(Config.LOGTAG, id.account.getJid() + ": ignoring reject from " + from + " for session with " + id.with);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid()
+ + ": ignoring reject from "
+ + from
+ + " for session with "
+ + id.with);
}
}