diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index dac289003..9ab4073b2 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -289,7 +289,8 @@
-
+
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index d12de90b7..777953735 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -105,7 +105,7 @@ public final class Config {
public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb
public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true;
- public static final boolean DISABLE_HTTP_UPLOAD = true;
+ public static final boolean DISABLE_HTTP_UPLOAD = false;
public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts
public static final boolean BACKGROUND_STANZA_LOGGING = false; //log all stanzas that were received while the app is in background
public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index 8a521ee9d..9a51393a9 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -364,8 +364,8 @@ public class NotificationService {
fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString());
fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString());
fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
- fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ //fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ //fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return PendingIntent.getActivity(mXmppConnectionService, requestCode, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 1ac96354e..22df52035 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -643,6 +643,7 @@ public class XmppConnectionService extends Service {
case ACTION_DISMISS_CALL:
final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
Log.d(Config.LOGTAG,"received intent to dismiss call with session id "+sessionId);
+ mJingleConnectionManager.rejectRtpSession(sessionId);
break;
case ACTION_DISMISS_ERROR_NOTIFICATIONS:
dismissErrorNotifications();
diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
index 2464f81b7..5778a3de9 100644
--- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
@@ -70,6 +70,15 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
+ @Override
+ public void onNewIntent(final Intent intent) {
+ super.onNewIntent(intent);
+ if (ACTION_ACCEPT.equals(intent.getAction())) {
+ Log.d(Config.LOGTAG,"accepting through onNewIntent()");
+ requireRtpConnection().acceptCall();
+ }
+ }
+
@Override
void onBackendConnected() {
final Intent intent = getIntent();
@@ -150,6 +159,10 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public void onJingleRtpConnectionUpdate(Account account, Jid with, RtpEndUserState state) {
final AbstractJingleConnection.Id id = requireRtpConnection().getId();
if (account == id.account && id.with.equals(with)) {
+ if (state == RtpEndUserState.ENDED) {
+ finish();
+ return;
+ }
runOnUiThread(() -> {
updateStateDisplay(state);
updateButtonConfiguration(state);
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java
index 603371394..5b60479b8 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java
@@ -87,8 +87,13 @@ public abstract class AbstractJingleConnection {
PROPOSED,
ACCEPTED,
PROCEED,
+ REJECTED,
+ RETRACTED,
SESSION_INITIALIZED, //equal to 'PENDING'
SESSION_ACCEPTED, //equal to 'ACTIVE'
- TERMINATED //equal to 'ENDED'
+ TERMINATED_SUCCESS, //equal to 'ENDED' (after successful call) ui will just close
+ TERMINATED_DECLINED_OR_BUSY, //equal to 'ENDED' (after other party declined the call)
+ TERMINATED_CONNECTIVITY_ERROR, //equal to 'ENDED' (but after network failures; ui will display retry button)
+ TERMINATED_CANCEL_OR_TIMEOUT //more or less the same as retracted; caller pressed end call before session was accepted
}
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 982dc00b4..404cb0cc4 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -271,6 +271,16 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
+ public void rejectRtpSession(final String sessionId) {
+ for(final AbstractJingleConnection connection : this.connections.values()) {
+ if (connection.getId().sessionId.equals(sessionId)) {
+ if (connection instanceof JingleRtpConnection) {
+ ((JingleRtpConnection) connection).rejectCall();
+ }
+ }
+ }
+ }
+
public static class RtpSessionProposal {
private final Account account;
public final Jid with;
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java
index 68964d131..e309e4289 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java
@@ -156,7 +156,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
@Override
public void onFileTransferAborted() {
- JingleFileTransferConnection.this.sendSessionTerminate("connectivity-error");
+ JingleFileTransferConnection.this.sendSessionTerminate(Reason.CONNECTIVITY_ERROR);
JingleFileTransferConnection.this.fail();
}
};
@@ -245,23 +245,20 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
if (action == JinglePacket.Action.SESSION_INITIATE) {
init(packet);
} else if (action == JinglePacket.Action.SESSION_TERMINATE) {
- Reason reason = packet.getReason();
- if (reason != null) {
- if (reason.hasChild("cancel")) {
+ final Reason reason = packet.getReason();
+ switch (reason) {
+ case CANCEL:
this.cancelled = true;
this.fail();
- } else if (reason.hasChild("success")) {
+ break;
+ case SUCCESS:
this.receiveSuccess();
- } else {
- final List children = reason.getChildren();
- if (children.size() == 1) {
- this.fail(children.get(0).getName());
- } else {
- this.fail();
- }
- }
- } else {
- this.fail();
+ break;
+ default:
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received session-terminate with reason " + reason);
+ this.fail();
+ break;
+
}
} else if (action == JinglePacket.Action.SESSION_ACCEPT) {
receiveAccept(packet);
@@ -756,7 +753,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
connection.setActivated(true);
} else {
Log.d(Config.LOGTAG, "activated connection not found");
- sendSessionTerminate("failed-transport");
+ sendSessionTerminate(Reason.FAILED_TRANSPORT);
this.fail();
}
}
@@ -894,7 +891,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
}
private void sendSuccess() {
- sendSessionTerminate("success");
+ sendSessionTerminate(Reason.SUCCESS);
this.disconnectSocks5Connections();
this.mJingleStatus = JINGLE_STATUS_FINISHED;
this.message.setStatus(Message.STATUS_RECEIVED);
@@ -1014,10 +1011,10 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
@Override
public void cancel() {
this.cancelled = true;
- abort("cancel");
+ abort(Reason.CANCEL);
}
- void abort(final String reason) {
+ void abort(final Reason reason) {
this.disconnectSocks5Connections();
if (this.transport instanceof JingleInBandTransport) {
this.transport.disconnect();
@@ -1065,11 +1062,9 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
this.jingleConnectionManager.finishConnection(this);
}
- private void sendSessionTerminate(String reason) {
+ private void sendSessionTerminate(Reason reason) {
final JinglePacket packet = bootstrapPacket(JinglePacket.Action.SESSION_TERMINATE);
- final Reason r = new Reason();
- r.addChild(reason);
- packet.setReason(r);
+ packet.setReason(reason);
this.sendJinglePacket(packet);
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
index 56991635f..9b73b131d 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
@@ -23,6 +23,7 @@ import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
+import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
@@ -33,7 +34,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
static {
final ImmutableMap.Builder> transitionBuilder = new ImmutableMap.Builder<>();
transitionBuilder.put(State.NULL, ImmutableList.of(State.PROPOSED, State.SESSION_INITIALIZED));
- transitionBuilder.put(State.PROPOSED, ImmutableList.of(State.ACCEPTED, State.PROCEED));
+ transitionBuilder.put(State.PROPOSED, ImmutableList.of(State.ACCEPTED, State.PROCEED, State.REJECTED));
transitionBuilder.put(State.PROCEED, ImmutableList.of(State.SESSION_INITIALIZED));
transitionBuilder.put(State.SESSION_INITIALIZED, ImmutableList.of(State.SESSION_ACCEPTED));
VALID_TRANSITIONS = transitionBuilder.build();
@@ -63,12 +64,36 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
case SESSION_ACCEPT:
receiveSessionAccept(jinglePacket);
break;
+ case SESSION_TERMINATE:
+ receiveSessionTerminate(jinglePacket);
+ break;
default:
Log.d(Config.LOGTAG, String.format("%s: received unhandled jingle action %s", id.account.getJid().asBareJid(), jinglePacket.getAction()));
break;
}
}
+ private void receiveSessionTerminate(final JinglePacket jinglePacket) {
+ final Reason reason = jinglePacket.getReason();
+ switch (reason) {
+ case SUCCESS:
+ transitionOrThrow(State.TERMINATED_SUCCESS);
+ break;
+ case DECLINE:
+ case BUSY:
+ transitionOrThrow(State.TERMINATED_DECLINED_OR_BUSY);
+ break;
+ case CANCEL:
+ case TIMEOUT:
+ transitionOrThrow(State.TERMINATED_CANCEL_OR_TIMEOUT);
+ break;
+ default:
+ transitionOrThrow(State.TERMINATED_CONNECTIVITY_ERROR);
+ break;
+ }
+ jingleConnectionManager.finishConnection(this);
+ }
+
private void receiveTransportInfo(final JinglePacket jinglePacket) {
if (isInState(State.SESSION_INITIALIZED, State.SESSION_ACCEPTED)) {
final RtpContentMap contentMap;
@@ -315,10 +340,24 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} else if (state == PeerConnection.PeerConnectionState.CLOSED) {
return RtpEndUserState.ENDING_CALL;
} else {
- return RtpEndUserState.FAILED;
+ return RtpEndUserState.ENDING_CALL;
}
+ case REJECTED:
+ case TERMINATED_DECLINED_OR_BUSY:
+ if (isInitiator()) {
+ return RtpEndUserState.DECLINED_OR_BUSY;
+ } else {
+ return RtpEndUserState.ENDED;
+ }
+ case TERMINATED_SUCCESS:
+ case ACCEPTED:
+ case RETRACTED:
+ case TERMINATED_CANCEL_OR_TIMEOUT:
+ return RtpEndUserState.ENDED;
+ case TERMINATED_CONNECTIVITY_ERROR:
+ return RtpEndUserState.CONNECTIVITY_ERROR;
}
- return RtpEndUserState.FAILED;
+ throw new IllegalStateException(String.format("%s has no equivalent EndUserState", this.state));
}
@@ -331,19 +370,34 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
acceptCallFromSessionInitialized();
break;
default:
- throw new IllegalStateException("Can not pick up call from " + this.state);
+ throw new IllegalStateException("Can not accept call from " + this.state);
}
}
public void rejectCall() {
- Log.d(Config.LOGTAG, "todo rejecting call");
- xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
+ switch (this.state) {
+ case PROPOSED:
+ rejectCallFromProposed();
+ break;
+ default:
+ throw new IllegalStateException("Can not reject call from " + this.state);
+ }
}
public void endCall() {
+
+ //TODO from `propose` we call `retract`
+
if (isInState(State.SESSION_INITIALIZED, State.SESSION_ACCEPTED)) {
+ //TODO during session_initialized we might not have a peer connection yet (if the session was initialized directly)
+
+ //TODO from session_initialized we call `cancel`
+
+ //TODO from session_accepted we call `success`
+
webRTCWrapper.close();
} else {
+ //TODO during earlier stages we want to retract the proposal etc
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": called 'endCall' while in state " + this.state);
}
}
@@ -356,10 +410,23 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void acceptCallFromProposed() {
transitionOrThrow(State.PROCEED);
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
+ //Note that Movim needs 'accept', correct is 'proceed' https://github.com/movim/movim/issues/916
+ this.sendJingleMessage("proceed");
+
+ //TODO send `accept` to self
+ }
+
+ private void rejectCallFromProposed() {
+ transitionOrThrow(State.REJECTED);
+ xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
+ this.sendJingleMessage("reject");
+ jingleConnectionManager.finishConnection(this);
+ }
+
+ private void sendJingleMessage(final String action) {
final MessagePacket messagePacket = new MessagePacket();
messagePacket.setTo(id.with);
- //Note that Movim needs 'accept', correct is 'proceed' https://github.com/movim/movim/issues/916
- messagePacket.addChild("proceed", Namespace.JINGLE_MESSAGE).setAttribute("id", id.sessionId);
+ messagePacket.addChild(action, Namespace.JINGLE_MESSAGE).setAttribute("id", id.sessionId);
Log.d(Config.LOGTAG, messagePacket.toString());
xmppConnectionService.sendMessagePacket(id.account, messagePacket);
}
@@ -400,7 +467,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
@Override
public void onConnectionChange(PeerConnection.PeerConnectionState newState) {
- Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": PeerConnectionState changed to "+newState);
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed to " + newState);
updateEndUserState();
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java
index 30055a3d6..e8d8bd5d2 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java
@@ -9,5 +9,7 @@ public enum RtpEndUserState {
ACCEPTED_ON_OTHER_DEVICE, //received 'accept' from one of our own devices
ACCEPTING_CALL, //'proceed' message has been sent; but no session-initiate has been received
ENDING_CALL, //libwebrt says 'closed' but session-terminate hasnt gone through
- FAILED //something went wrong. TODO needs more concrete error states
+ ENDED, //close UI
+ DECLINED_OR_BUSY, //other party declined; no retry button
+ CONNECTIVITY_ERROR //network error; retry button
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java
index 3b4ef47d1..25ca3d5fd 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java
@@ -70,12 +70,20 @@ public class JinglePacket extends IqPacket {
public Reason getReason() {
final Element reason = getJingleChild("reason");
- return reason == null ? null : Reason.upgrade(reason);
+ if (reason == null) {
+ return Reason.UNKNOWN;
+ }
+ for(Element child : reason.getChildren()) {
+ if (!"text".equals(child.getName())) {
+ return Reason.of(child.getName());
+ }
+ }
+ return Reason.UNKNOWN;
}
public void setReason(final Reason reason) {
final Element jingle = findChild("jingle", Namespace.JINGLE);
- jingle.addChild(reason);
+ jingle.addChild(new Element("reason").addChild(reason.toString()));
}
//RECOMMENDED for session-initiate, NOT RECOMMENDED otherwise
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java
index 1eae65c5b..070f77226 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java
@@ -1,20 +1,23 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
-import com.google.common.base.Preconditions;
+import android.support.annotation.NonNull;
-import eu.siacs.conversations.xml.Element;
+import com.google.common.base.CaseFormat;
-public class Reason extends Element {
+public enum Reason {
+ SUCCESS, DECLINE, BUSY, CANCEL, CONNECTIVITY_ERROR, FAILED_TRANSPORT, TIMEOUT, UNKNOWN;
- public Reason() {
- super("reason");
+ public static Reason of(final String value) {
+ try {
+ return Reason.valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, value));
+ } catch (Exception e) {
+ return UNKNOWN;
+ }
}
- public static Reason upgrade(final Element element) {
- Preconditions.checkArgument("reason".equals(element.getName()));
- final Reason reason = new Reason();
- reason.setAttributes(element.getAttributes());
- reason.setChildren(element.getChildren());
- return reason;
+ @Override
+ @NonNull
+ public String toString() {
+ return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, super.toString());
}
-}
+}
\ No newline at end of file