Search in sources :

Example 1 with Propose

use of eu.siacs.conversations.xmpp.jingle.stanzas.Propose in project Conversations by siacs.

the class JingleConnectionManager method deliverMessage.

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) {
        return;
    }
    if ("accept".equals(message.getName())) {
        for (AbstractJingleConnection connection : connections.values()) {
            if (connection instanceof JingleRtpConnection) {
                final JingleRtpConnection rtpConnection = (JingleRtpConnection) connection;
                final AbstractJingleConnection.Id id = connection.getId();
                if (id.account == account && id.sessionId.equals(sessionId)) {
                    rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
                    return;
                }
            }
        }
        return;
    }
    final boolean fromSelf = from.asBareJid().equals(account.getJid().asBareJid());
    final boolean addressedDirectly = to != null && to.equals(account.getJid());
    final AbstractJingleConnection.Id id;
    if (fromSelf) {
        if (to != null && to.isFullJid()) {
            id = AbstractJingleConnection.Id.of(account, to, sessionId);
        } else {
            return;
        }
    } else {
        id = AbstractJingleConnection.Id.of(account, from, sessionId);
    }
    final AbstractJingleConnection existingJingleConnection = connections.get(id);
    if (existingJingleConnection != null) {
        if (existingJingleConnection instanceof JingleRtpConnection) {
            ((JingleRtpConnection) existingJingleConnection).deliveryMessage(from, message, serverMsgId, timestamp);
        } else {
            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 Message previousBusy = c.findRtpSession(sessionId, Message.STATUS_RECEIVED);
            if (previousBusy != null) {
                previousBusy.setBody(new RtpSessionStatus(true, 0).toString());
                if (serverMsgId != null) {
                    previousBusy.setServerMsgId(serverMsgId);
                }
                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");
                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");
        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);
            if (media.contains(Media.UNKNOWN)) {
                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));
            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
                    retractSessionProposal(matchingSessionProposal.get());
                    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);
                }
                return;
            }
            final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
            if (isBusy() || stranger) {
                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);
                    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);
                    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");
                }
            } else {
                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");
        }
    } else if (addressedDirectly && "proceed".equals(message.getName())) {
        synchronized (rtpSessionProposals) {
            final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
            if (proposal != null) {
                rtpSessionProposals.remove(proposal);
                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");
                if (remoteMsgId == null) {
                    return;
                }
                final MessagePacket errorMessage = new MessagePacket();
                errorMessage.setTo(from);
                errorMessage.setId(remoteMsgId);
                errorMessage.setType(MessagePacket.TYPE_ERROR);
                final Element error = errorMessage.addChild("error");
                error.setAttribute("code", "404");
                error.setAttribute("type", "cancel");
                error.addChild("item-not-found", "urn:ietf:params:xml:ns:xmpp-stanzas");
                mXmppConnectionService.sendMessagePacket(account, errorMessage);
            }
        }
    } else if (addressedDirectly && "reject".equals(message.getName())) {
        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);
                toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY, proposal.media);
                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");
            }
        }
    } else {
        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retrieved out of order jingle message" + message);
    }
}
Also used : Conversational(eu.siacs.conversations.entities.Conversational) Content(eu.siacs.conversations.xmpp.jingle.stanzas.Content) RtpDescription(eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription) Config(eu.siacs.conversations.Config) ScheduledFuture(java.util.concurrent.ScheduledFuture) IqPacket(eu.siacs.conversations.xmpp.stanzas.IqPacket) Reason(eu.siacs.conversations.xmpp.jingle.stanzas.Reason) HashMap(java.util.HashMap) Collections2(com.google.common.collect.Collections2) Account(eu.siacs.conversations.entities.Account) Element(eu.siacs.conversations.xml.Element) Conversation(eu.siacs.conversations.entities.Conversation) SecureRandom(java.security.SecureRandom) GenericDescription(eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription) Optional(com.google.common.base.Optional) Map(java.util.Map) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) FileTransferDescription(eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription) Objects(com.google.common.base.Objects) WeakReference(java.lang.ref.WeakReference) Log(android.util.Log) XmppConnection(eu.siacs.conversations.xmpp.XmppConnection) MessagePacket(eu.siacs.conversations.xmpp.stanzas.MessagePacket) ImmutableSet(com.google.common.collect.ImmutableSet) Contact(eu.siacs.conversations.entities.Contact) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Propose(eu.siacs.conversations.xmpp.jingle.stanzas.Propose) Set(java.util.Set) JinglePacket(eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket) ComparisonChain(com.google.common.collect.ComparisonChain) Executors(java.util.concurrent.Executors) TimeUnit(java.util.concurrent.TimeUnit) Transferable(eu.siacs.conversations.entities.Transferable) List(java.util.List) Message(eu.siacs.conversations.entities.Message) RtpSessionStatus(eu.siacs.conversations.entities.RtpSessionStatus) XmppConnectionService(eu.siacs.conversations.services.XmppConnectionService) Namespace(eu.siacs.conversations.xml.Namespace) OnIqPacketReceived(eu.siacs.conversations.xmpp.OnIqPacketReceived) Base64(android.util.Base64) Preconditions(com.google.common.base.Preconditions) CacheBuilder(com.google.common.cache.CacheBuilder) Cache(com.google.common.cache.Cache) AbstractConnectionManager(eu.siacs.conversations.services.AbstractConnectionManager) Collections(java.util.Collections) Jid(eu.siacs.conversations.xmpp.Jid) Message(eu.siacs.conversations.entities.Message) Element(eu.siacs.conversations.xml.Element) GenericDescription(eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription) Conversation(eu.siacs.conversations.entities.Conversation) RtpDescription(eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription) MessagePacket(eu.siacs.conversations.xmpp.stanzas.MessagePacket) Propose(eu.siacs.conversations.xmpp.jingle.stanzas.Propose) RtpSessionStatus(eu.siacs.conversations.entities.RtpSessionStatus)

Aggregations

Base64 (android.util.Base64)1 Log (android.util.Log)1 Objects (com.google.common.base.Objects)1 Optional (com.google.common.base.Optional)1 Preconditions (com.google.common.base.Preconditions)1 Cache (com.google.common.cache.Cache)1 CacheBuilder (com.google.common.cache.CacheBuilder)1 Collections2 (com.google.common.collect.Collections2)1 ComparisonChain (com.google.common.collect.ComparisonChain)1 ImmutableSet (com.google.common.collect.ImmutableSet)1 Config (eu.siacs.conversations.Config)1 Account (eu.siacs.conversations.entities.Account)1 Contact (eu.siacs.conversations.entities.Contact)1 Conversation (eu.siacs.conversations.entities.Conversation)1 Conversational (eu.siacs.conversations.entities.Conversational)1 Message (eu.siacs.conversations.entities.Message)1 RtpSessionStatus (eu.siacs.conversations.entities.RtpSessionStatus)1 Transferable (eu.siacs.conversations.entities.Transferable)1 AbstractConnectionManager (eu.siacs.conversations.services.AbstractConnectionManager)1 XmppConnectionService (eu.siacs.conversations.services.XmppConnectionService)1