Detailed changes
@@ -96,7 +96,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
);
private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT = Arrays.asList(
RtpEndUserState.CONNECTING,
- RtpEndUserState.CONNECTED
+ RtpEndUserState.CONNECTED,
+ RtpEndUserState.RECONNECTING
+ );
+ private static final List<RtpEndUserState> STATES_CONSIDERED_CONNECTED = Arrays.asList(
+ RtpEndUserState.CONNECTED,
+ RtpEndUserState.RECONNECTING
);
private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
private static final int REQUEST_ACCEPT_CALL = 0x1111;
@@ -502,7 +507,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private boolean isConnected() {
final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
- return connection != null && connection.getEndUserState() == RtpEndUserState.CONNECTED;
+ return connection != null && STATES_CONSIDERED_CONNECTED.contains(connection.getEndUserState());
}
private boolean switchToPictureInPicture() {
@@ -661,6 +666,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
case CONNECTED:
setTitle(R.string.rtp_state_connected);
break;
+ case RECONNECTING:
+ setTitle(R.string.rtp_state_reconnecting);
+ break;
case ACCEPTING_CALL:
setTitle(R.string.rtp_state_accepting_call);
break;
@@ -803,7 +811,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@SuppressLint("RestrictedApi")
private void updateInCallButtonConfiguration(final RtpEndUserState state, final Set<Media> media) {
- if (state == RtpEndUserState.CONNECTED && !isPictureInPicture()) {
+ if (STATES_CONSIDERED_CONNECTED.contains(state) && !isPictureInPicture()) {
Preconditions.checkArgument(media.size() > 0, "Media must not be empty");
if (media.contains(Media.VIDEO)) {
final JingleRtpConnection rtpConnection = requireRtpConnection();
@@ -998,7 +1006,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
RendererCommon.ScalingType.SCALE_ASPECT_FILL,
RendererCommon.ScalingType.SCALE_ASPECT_FIT
);
- if (state == RtpEndUserState.CONNECTED) {
+ if (STATES_CONSIDERED_CONNECTED.contains(state)) {
binding.appBarLayout.setVisibility(View.GONE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.remoteVideoWrapper.setVisibility(View.VISIBLE);
@@ -1035,24 +1035,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return RtpEndUserState.CONNECTING;
}
case SESSION_ACCEPTED:
- //TODO refactor this out into separate method (that uses switch for better readability)
- final PeerConnection.PeerConnectionState state;
- try {
- state = webRTCWrapper.getState();
- } catch (final WebRTCWrapper.PeerConnectionNotInitialized e) {
- //We usually close the WebRTCWrapper *before* transitioning so we might still
- //be in SESSION_ACCEPTED even though the peerConnection has been torn down
- return RtpEndUserState.ENDING_CALL;
- }
- if (state == PeerConnection.PeerConnectionState.CONNECTED) {
- return RtpEndUserState.CONNECTED;
- } else if (state == PeerConnection.PeerConnectionState.NEW || state == PeerConnection.PeerConnectionState.CONNECTING) {
- return RtpEndUserState.CONNECTING;
- } else if (state == PeerConnection.PeerConnectionState.CLOSED) {
- return RtpEndUserState.ENDING_CALL;
- } else {
- return rtpConnectionStarted == 0 ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
- }
+ return getPeerConnectionStateAsEndUserState();
case REJECTED:
case REJECTED_RACED:
case TERMINATED_DECLINED_OR_BUSY:
@@ -1082,6 +1065,29 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
throw new IllegalStateException(String.format("%s has no equivalent EndUserState", this.state));
}
+
+ private RtpEndUserState getPeerConnectionStateAsEndUserState() {
+ final PeerConnection.PeerConnectionState state;
+ try {
+ state = webRTCWrapper.getState();
+ } catch (final WebRTCWrapper.PeerConnectionNotInitialized e) {
+ //We usually close the WebRTCWrapper *before* transitioning so we might still
+ //be in SESSION_ACCEPTED even though the peerConnection has been torn down
+ return RtpEndUserState.ENDING_CALL;
+ }
+ switch (state) {
+ case CONNECTED:
+ return RtpEndUserState.CONNECTED;
+ case NEW:
+ case CONNECTING:
+ return RtpEndUserState.CONNECTING;
+ case CLOSED:
+ return RtpEndUserState.ENDING_CALL;
+ default:
+ return rtpConnectionStarted == 0 ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.RECONNECTING;
+ }
+ }
+
public Set<Media> getMedia() {
final State current = getState();
if (current == State.NULL) {
@@ -1331,29 +1337,31 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
@Override
public void onConnectionChange(final PeerConnection.PeerConnectionState oldState, final PeerConnection.PeerConnectionState newState) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed: "+oldState+"->" + newState);
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed: " + oldState + "->" + newState);
+ final boolean neverConnected = this.rtpConnectionStarted == 0;
if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
this.rtpConnectionStarted = SystemClock.elapsedRealtime();
}
if (newState == PeerConnection.PeerConnectionState.CLOSED && this.rtpConnectionEnded == 0) {
this.rtpConnectionEnded = SystemClock.elapsedRealtime();
}
- //TODO 'failed' means we need to restart ICE
- //
- //TODO 'disconnected' can probably be ignored as "This is a less stringent test than failed
- // and may trigger intermittently and resolve just as spontaneously on less reliable networks,
- // or during temporary disconnections. When the problem resolves, the connection may return
- // to the connected state."
- // Obviously the UI needs to reflect this new state with a 'reconnecting' display or something
- if (Arrays.asList(PeerConnection.PeerConnectionState.FAILED, PeerConnection.PeerConnectionState.DISCONNECTED).contains(newState)) {
+
+ if (neverConnected && Arrays.asList(PeerConnection.PeerConnectionState.FAILED, PeerConnection.PeerConnectionState.DISCONNECTED).contains(newState)) {
if (isTerminated()) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": not sending session-terminate after connectivity error because session is already in state " + this.state);
return;
}
new Thread(this::closeWebRTCSessionAfterFailedConnection).start();
- } else {
- updateEndUserState();
+ } else if (newState == PeerConnection.PeerConnectionState.FAILED) {
+ Log.d(Config.LOGTAG, "attempting to restart ICE");
+ webRTCWrapper.restartIce();
}
+ updateEndUserState();
+ }
+
+ @Override
+ public void onRenegotiationNeeded() {
+ Log.d(Config.LOGTAG, "onRenegotiationNeeded()");
}
private void closeWebRTCSessionAfterFailedConnection() {
@@ -4,6 +4,7 @@ public enum RtpEndUserState {
INCOMING_CALL, //received a 'propose' message
CONNECTING, //session-initiate or session-accepted but no webrtc peer connection yet
CONNECTED, //session-accepted and webrtc peer connection is connected
+ RECONNECTING, //session-accepted and webrtc peer connection was connected once but is currently disconnected or failed
FINDING_DEVICE, //'propose' has been sent out; no 184 ack yet
RINGING, //'propose' has been sent out and it has been 184 acked
ACCEPTING_CALL, //'proceed' message has been sent; but no session-initiate has been received
@@ -100,13 +100,14 @@ public class WebRTCWrapper {
@Override
public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
- eventCallback.onConnectionChange(currentState, newState);
+ final PeerConnection.PeerConnectionState oldState = currentState;
currentState = newState;
+ eventCallback.onConnectionChange(oldState, newState);
}
@Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
-
+ Log.d(EXTENDED_LOGGING_TAG, "onIceConnectionChange(" + iceConnectionState + ")");
}
@Override
@@ -152,7 +153,10 @@ public class WebRTCWrapper {
@Override
public void onRenegotiationNeeded() {
- Log.d(EXTENDED_LOGGING_TAG,"onRenegotiationNeeded - current state: "+currentState);
+ Log.d(EXTENDED_LOGGING_TAG, "onRenegotiationNeeded()");
+ if (currentState != null && currentState != PeerConnection.PeerConnectionState.NEW) {
+ eventCallback.onRenegotiationNeeded();
+ }
}
@Override
@@ -293,6 +297,10 @@ public class WebRTCWrapper {
this.peerConnection = peerConnection;
}
+ void restartIce() {
+ requirePeerConnection().restartIce();
+ }
+
synchronized void close() {
final PeerConnection peerConnection = this.peerConnection;
final CapturerChoice capturerChoice = this.capturerChoice;
@@ -538,6 +546,8 @@ public class WebRTCWrapper {
void onConnectionChange(PeerConnection.PeerConnectionState oldState, PeerConnection.PeerConnectionState newState);
void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices);
+
+ void onRenegotiationNeeded();
}
private static abstract class SetSdpObserver implements SdpObserver {
@@ -904,6 +904,7 @@
<string name="rtp_state_incoming_video_call">Incoming video call</string>
<string name="rtp_state_connecting">Connecting</string>
<string name="rtp_state_connected">Connected</string>
+ <string name="rtp_state_reconnecting">Reconnecting</string>
<string name="rtp_state_accepting_call">Accepting call</string>
<string name="rtp_state_ending_call">Ending call</string>
<string name="answer_call">Answer</string>