send and receive session terminates
This commit is contained in:
parent
00f273b0c0
commit
859bc0bef3
|
@ -164,6 +164,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
||||||
break;
|
break;
|
||||||
case DECLINED_OR_BUSY:
|
case DECLINED_OR_BUSY:
|
||||||
binding.status.setText(R.string.rtp_state_declined_or_busy);
|
binding.status.setText(R.string.rtp_state_declined_or_busy);
|
||||||
|
break;
|
||||||
case CONNECTIVITY_ERROR:
|
case CONNECTIVITY_ERROR:
|
||||||
binding.status.setText(R.string.rtp_state_connectivity_error);
|
binding.status.setText(R.string.rtp_state_connectivity_error);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -90,6 +90,7 @@ public abstract class AbstractJingleConnection {
|
||||||
REJECTED,
|
REJECTED,
|
||||||
RETRACTED,
|
RETRACTED,
|
||||||
SESSION_INITIALIZED, //equal to 'PENDING'
|
SESSION_INITIALIZED, //equal to 'PENDING'
|
||||||
|
SESSION_INITIALIZED_PRE_APPROVED,
|
||||||
SESSION_ACCEPTED, //equal to 'ACTIVE'
|
SESSION_ACCEPTED, //equal to 'ACTIVE'
|
||||||
TERMINATED_SUCCESS, //equal to 'ENDED' (after successful call) ui will just close
|
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_DECLINED_OR_BUSY, //equal to 'ENDED' (after other party declined the call)
|
||||||
|
|
|
@ -33,8 +33,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
final ImmutableMap.Builder<State, Collection<State>> transitionBuilder = new ImmutableMap.Builder<>();
|
final ImmutableMap.Builder<State, Collection<State>> transitionBuilder = new ImmutableMap.Builder<>();
|
||||||
transitionBuilder.put(State.NULL, ImmutableList.of(State.PROPOSED, State.SESSION_INITIALIZED));
|
transitionBuilder.put(State.NULL, ImmutableList.of(State.PROPOSED, State.SESSION_INITIALIZED));
|
||||||
transitionBuilder.put(State.PROPOSED, ImmutableList.of(State.ACCEPTED, State.PROCEED, State.REJECTED, State.RETRACTED));
|
transitionBuilder.put(State.PROPOSED, ImmutableList.of(State.ACCEPTED, State.PROCEED, State.REJECTED, State.RETRACTED));
|
||||||
transitionBuilder.put(State.PROCEED, ImmutableList.of(State.SESSION_INITIALIZED));
|
transitionBuilder.put(State.PROCEED, ImmutableList.of(State.SESSION_INITIALIZED_PRE_APPROVED));
|
||||||
transitionBuilder.put(State.SESSION_INITIALIZED, ImmutableList.of(State.SESSION_ACCEPTED));
|
transitionBuilder.put(State.SESSION_INITIALIZED, ImmutableList.of(State.SESSION_ACCEPTED, State.TERMINATED_CANCEL_OR_TIMEOUT));
|
||||||
|
transitionBuilder.put(State.SESSION_INITIALIZED_PRE_APPROVED, ImmutableList.of(State.SESSION_ACCEPTED, State.TERMINATED_CANCEL_OR_TIMEOUT));
|
||||||
|
transitionBuilder.put(State.SESSION_ACCEPTED, ImmutableList.of(State.TERMINATED_SUCCESS, State.TERMINATED_CONNECTIVITY_ERROR));
|
||||||
VALID_TRANSITIONS = transitionBuilder.build();
|
VALID_TRANSITIONS = transitionBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,27 +75,33 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
|
|
||||||
private void receiveSessionTerminate(final JinglePacket jinglePacket) {
|
private void receiveSessionTerminate(final JinglePacket jinglePacket) {
|
||||||
final Reason reason = jinglePacket.getReason();
|
final Reason reason = jinglePacket.getReason();
|
||||||
switch (reason) {
|
final State previous = this.state;
|
||||||
case SUCCESS:
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received session terminate reason=" + reason + " while in state " + previous);
|
||||||
transitionOrThrow(State.TERMINATED_SUCCESS);
|
webRTCWrapper.close();
|
||||||
break;
|
transitionOrThrow(reasonToState(reason));
|
||||||
case DECLINE:
|
if (previous == State.PROPOSED || previous == State.SESSION_INITIALIZED) {
|
||||||
case BUSY:
|
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
|
||||||
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);
|
jingleConnectionManager.finishConnection(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static State reasonToState(Reason reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case SUCCESS:
|
||||||
|
return State.TERMINATED_SUCCESS;
|
||||||
|
case DECLINE:
|
||||||
|
case BUSY:
|
||||||
|
return State.TERMINATED_DECLINED_OR_BUSY;
|
||||||
|
case CANCEL:
|
||||||
|
case TIMEOUT:
|
||||||
|
return State.TERMINATED_CANCEL_OR_TIMEOUT;
|
||||||
|
default:
|
||||||
|
return State.TERMINATED_CONNECTIVITY_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void receiveTransportInfo(final JinglePacket jinglePacket) {
|
private void receiveTransportInfo(final JinglePacket jinglePacket) {
|
||||||
if (isInState(State.SESSION_INITIALIZED, State.SESSION_ACCEPTED)) {
|
if (isInState(State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
|
||||||
final RtpContentMap contentMap;
|
final RtpContentMap contentMap;
|
||||||
try {
|
try {
|
||||||
contentMap = RtpContentMap.of(jinglePacket);
|
contentMap = RtpContentMap.of(jinglePacket);
|
||||||
|
@ -142,10 +150,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
return;
|
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 oldState = this.state;
|
final State target;
|
||||||
if (transition(State.SESSION_INITIALIZED)) {
|
if (this.state == State.PROCEED) {
|
||||||
|
target = State.SESSION_INITIALIZED_PRE_APPROVED;
|
||||||
|
} else {
|
||||||
|
target = State.SESSION_INITIALIZED;
|
||||||
|
}
|
||||||
|
if (transition(target)) {
|
||||||
this.initiatorRtpContentMap = contentMap;
|
this.initiatorRtpContentMap = contentMap;
|
||||||
if (oldState == State.PROCEED) {
|
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
|
||||||
Log.d(Config.LOGTAG, "automatically accepting");
|
Log.d(Config.LOGTAG, "automatically accepting");
|
||||||
sendSessionAccept();
|
sendSessionAccept();
|
||||||
} else {
|
} else {
|
||||||
|
@ -297,7 +310,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
if (from.equals(id.with)) {
|
if (from.equals(id.with)) {
|
||||||
if (isInitiator()) {
|
if (isInitiator()) {
|
||||||
if (transition(State.PROCEED)) {
|
if (transition(State.PROCEED)) {
|
||||||
this.sendSessionInitiate();
|
this.sendSessionInitiate(State.SESSION_INITIALIZED_PRE_APPROVED);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, String.format("%s: ignoring proceed because already in %s", id.account.getJid().asBareJid(), this.state));
|
Log.d(Config.LOGTAG, String.format("%s: ignoring proceed because already in %s", id.account.getJid().asBareJid(), this.state));
|
||||||
}
|
}
|
||||||
|
@ -331,7 +344,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSessionInitiate() {
|
private void sendSessionInitiate(final State targetState) {
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate");
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate");
|
||||||
setupWebRTC();
|
setupWebRTC();
|
||||||
try {
|
try {
|
||||||
|
@ -339,21 +352,31 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
||||||
Log.d(Config.LOGTAG, "description: " + webRTCSessionDescription.description);
|
Log.d(Config.LOGTAG, "description: " + webRTCSessionDescription.description);
|
||||||
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
|
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
|
||||||
sendSessionInitiate(rtpContentMap);
|
sendSessionInitiate(rtpContentMap, targetState);
|
||||||
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
|
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.d(Config.LOGTAG, "unable to sendSessionInitiate", e);
|
Log.d(Config.LOGTAG, "unable to sendSessionInitiate", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSessionInitiate(RtpContentMap rtpContentMap) {
|
private void sendSessionInitiate(RtpContentMap rtpContentMap, final State targetState) {
|
||||||
this.initiatorRtpContentMap = rtpContentMap;
|
this.initiatorRtpContentMap = rtpContentMap;
|
||||||
this.transitionOrThrow(State.SESSION_INITIALIZED);
|
this.transitionOrThrow(targetState);
|
||||||
final JinglePacket sessionInitiate = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId);
|
final JinglePacket sessionInitiate = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId);
|
||||||
Log.d(Config.LOGTAG, sessionInitiate.toString());
|
Log.d(Config.LOGTAG, sessionInitiate.toString());
|
||||||
send(sessionInitiate);
|
send(sessionInitiate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendSessionTerminate(final Reason reason) {
|
||||||
|
final State target = reasonToState(reason);
|
||||||
|
transitionOrThrow(target);
|
||||||
|
final JinglePacket jinglePacket = new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId);
|
||||||
|
jinglePacket.setReason(reason);
|
||||||
|
send(jinglePacket);
|
||||||
|
Log.d(Config.LOGTAG, jinglePacket.toString());
|
||||||
|
jingleConnectionManager.finishConnection(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void sendTransportInfo(final String contentName, IceUdpTransportInfo.Candidate candidate) {
|
private void sendTransportInfo(final String contentName, IceUdpTransportInfo.Candidate candidate) {
|
||||||
final RtpContentMap transportInfo;
|
final RtpContentMap transportInfo;
|
||||||
try {
|
try {
|
||||||
|
@ -377,6 +400,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
public RtpEndUserState getEndUserState() {
|
public RtpEndUserState getEndUserState() {
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case PROPOSED:
|
case PROPOSED:
|
||||||
|
case SESSION_INITIALIZED:
|
||||||
if (isInitiator()) {
|
if (isInitiator()) {
|
||||||
return RtpEndUserState.RINGING;
|
return RtpEndUserState.RINGING;
|
||||||
} else {
|
} else {
|
||||||
|
@ -388,7 +412,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
} else {
|
} else {
|
||||||
return RtpEndUserState.ACCEPTING_CALL;
|
return RtpEndUserState.ACCEPTING_CALL;
|
||||||
}
|
}
|
||||||
case SESSION_INITIALIZED:
|
case SESSION_INITIALIZED_PRE_APPROVED:
|
||||||
return RtpEndUserState.CONNECTING;
|
return RtpEndUserState.CONNECTING;
|
||||||
case SESSION_ACCEPTED:
|
case SESSION_ACCEPTED:
|
||||||
final PeerConnection.PeerConnectionState state = webRTCWrapper.getState();
|
final PeerConnection.PeerConnectionState state = webRTCWrapper.getState();
|
||||||
|
@ -446,20 +470,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endCall() {
|
public void endCall() {
|
||||||
|
if (isInitiator() && isInState(State.SESSION_INITIALIZED)) {
|
||||||
//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();
|
webRTCWrapper.close();
|
||||||
|
sendSessionTerminate(Reason.CANCEL);
|
||||||
|
} else if (isInState(State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
|
||||||
|
webRTCWrapper.close();
|
||||||
|
sendSessionTerminate(Reason.SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
//TODO during earlier stages we want to retract the proposal etc
|
throw new IllegalStateException("called 'endCall' while in state " + this.state);
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": called 'endCall' while in state " + this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,9 +548,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionChange(PeerConnection.PeerConnectionState newState) {
|
public void onConnectionChange(final 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();
|
updateEndUserState();
|
||||||
|
if (newState == PeerConnection.PeerConnectionState.FAILED) { //TODO guard this in isState(initiated,initated_approved,accepted) otherwise it might fire too late
|
||||||
|
sendSessionTerminate(Reason.CONNECTIVITY_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateEndUserState() {
|
private void updateEndUserState() {
|
||||||
|
|
|
@ -207,10 +207,17 @@ public class WebRTCWrapper {
|
||||||
this.peerConnection = peerConnection;
|
this.peerConnection = peerConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void closeOrThrow() {
|
||||||
requirePeerConnection().close();
|
requirePeerConnection().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
final PeerConnection peerConnection = this.peerConnection;
|
||||||
|
if (peerConnection != null) {
|
||||||
|
peerConnection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ListenableFuture<SessionDescription> createOffer() {
|
public ListenableFuture<SessionDescription> createOffer() {
|
||||||
return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
|
return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class JinglePacket extends IqPacket {
|
||||||
|
|
||||||
public void setReason(final Reason reason) {
|
public void setReason(final Reason reason) {
|
||||||
final Element jingle = findChild("jingle", Namespace.JINGLE);
|
final Element jingle = findChild("jingle", Namespace.JINGLE);
|
||||||
jingle.addChild(new Element("reason").addChild(reason.toString()));
|
jingle.addChild("reason").addChild(reason.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
//RECOMMENDED for session-initiate, NOT RECOMMENDED otherwise
|
//RECOMMENDED for session-initiate, NOT RECOMMENDED otherwise
|
||||||
|
|
Loading…
Reference in a new issue