do not automacially hang up failed webrtc sessions

This commit is contained in:
Daniel Gultsch 2021-11-11 14:40:15 +01:00
parent 4ec0996dff
commit 61851e5f84
5 changed files with 64 additions and 36 deletions

View file

@ -96,7 +96,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
); );
private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT = Arrays.asList( private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT = Arrays.asList(
RtpEndUserState.CONNECTING, 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 String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
private static final int REQUEST_ACCEPT_CALL = 0x1111; private static final int REQUEST_ACCEPT_CALL = 0x1111;
@ -502,7 +507,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private boolean isConnected() { private boolean isConnected() {
final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null; 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() { private boolean switchToPictureInPicture() {
@ -661,6 +666,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
case CONNECTED: case CONNECTED:
setTitle(R.string.rtp_state_connected); setTitle(R.string.rtp_state_connected);
break; break;
case RECONNECTING:
setTitle(R.string.rtp_state_reconnecting);
break;
case ACCEPTING_CALL: case ACCEPTING_CALL:
setTitle(R.string.rtp_state_accepting_call); setTitle(R.string.rtp_state_accepting_call);
break; break;
@ -803,7 +811,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
private void updateInCallButtonConfiguration(final RtpEndUserState state, final Set<Media> media) { 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"); Preconditions.checkArgument(media.size() > 0, "Media must not be empty");
if (media.contains(Media.VIDEO)) { if (media.contains(Media.VIDEO)) {
final JingleRtpConnection rtpConnection = requireRtpConnection(); final JingleRtpConnection rtpConnection = requireRtpConnection();
@ -998,7 +1006,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
RendererCommon.ScalingType.SCALE_ASPECT_FILL, RendererCommon.ScalingType.SCALE_ASPECT_FILL,
RendererCommon.ScalingType.SCALE_ASPECT_FIT RendererCommon.ScalingType.SCALE_ASPECT_FIT
); );
if (state == RtpEndUserState.CONNECTED) { if (STATES_CONSIDERED_CONNECTED.contains(state)) {
binding.appBarLayout.setVisibility(View.GONE); binding.appBarLayout.setVisibility(View.GONE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.remoteVideoWrapper.setVisibility(View.VISIBLE); binding.remoteVideoWrapper.setVisibility(View.VISIBLE);

View file

@ -1035,24 +1035,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return RtpEndUserState.CONNECTING; return RtpEndUserState.CONNECTING;
} }
case SESSION_ACCEPTED: case SESSION_ACCEPTED:
//TODO refactor this out into separate method (that uses switch for better readability) return 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;
}
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;
}
case REJECTED: case REJECTED:
case REJECTED_RACED: case REJECTED_RACED:
case TERMINATED_DECLINED_OR_BUSY: 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)); 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() { public Set<Media> getMedia() {
final State current = getState(); final State current = getState();
if (current == State.NULL) { if (current == State.NULL) {
@ -1331,29 +1337,31 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
@Override @Override
public void onConnectionChange(final PeerConnection.PeerConnectionState oldState, final PeerConnection.PeerConnectionState newState) { 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) { if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
this.rtpConnectionStarted = SystemClock.elapsedRealtime(); this.rtpConnectionStarted = SystemClock.elapsedRealtime();
} }
if (newState == PeerConnection.PeerConnectionState.CLOSED && this.rtpConnectionEnded == 0) { if (newState == PeerConnection.PeerConnectionState.CLOSED && this.rtpConnectionEnded == 0) {
this.rtpConnectionEnded = SystemClock.elapsedRealtime(); this.rtpConnectionEnded = SystemClock.elapsedRealtime();
} }
//TODO 'failed' means we need to restart ICE
// if (neverConnected && Arrays.asList(PeerConnection.PeerConnectionState.FAILED, PeerConnection.PeerConnectionState.DISCONNECTED).contains(newState)) {
//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 (isTerminated()) { 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); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": not sending session-terminate after connectivity error because session is already in state " + this.state);
return; return;
} }
new Thread(this::closeWebRTCSessionAfterFailedConnection).start(); new Thread(this::closeWebRTCSessionAfterFailedConnection).start();
} else { } else if (newState == PeerConnection.PeerConnectionState.FAILED) {
updateEndUserState(); Log.d(Config.LOGTAG, "attempting to restart ICE");
webRTCWrapper.restartIce();
} }
updateEndUserState();
}
@Override
public void onRenegotiationNeeded() {
Log.d(Config.LOGTAG, "onRenegotiationNeeded()");
} }
private void closeWebRTCSessionAfterFailedConnection() { private void closeWebRTCSessionAfterFailedConnection() {

View file

@ -4,6 +4,7 @@ public enum RtpEndUserState {
INCOMING_CALL, //received a 'propose' message INCOMING_CALL, //received a 'propose' message
CONNECTING, //session-initiate or session-accepted but no webrtc peer connection yet CONNECTING, //session-initiate or session-accepted but no webrtc peer connection yet
CONNECTED, //session-accepted and webrtc peer connection is connected 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 FINDING_DEVICE, //'propose' has been sent out; no 184 ack yet
RINGING, //'propose' has been sent out and it has been 184 acked 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 ACCEPTING_CALL, //'proceed' message has been sent; but no session-initiate has been received

View file

@ -100,13 +100,14 @@ public class WebRTCWrapper {
@Override @Override
public void onConnectionChange(final PeerConnection.PeerConnectionState newState) { public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
eventCallback.onConnectionChange(currentState, newState); final PeerConnection.PeerConnectionState oldState = currentState;
currentState = newState; currentState = newState;
eventCallback.onConnectionChange(oldState, newState);
} }
@Override @Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) { public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
Log.d(EXTENDED_LOGGING_TAG, "onIceConnectionChange(" + iceConnectionState + ")");
} }
@Override @Override
@ -152,7 +153,10 @@ public class WebRTCWrapper {
@Override @Override
public void onRenegotiationNeeded() { 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 @Override
@ -293,6 +297,10 @@ public class WebRTCWrapper {
this.peerConnection = peerConnection; this.peerConnection = peerConnection;
} }
void restartIce() {
requirePeerConnection().restartIce();
}
synchronized void close() { synchronized void close() {
final PeerConnection peerConnection = this.peerConnection; final PeerConnection peerConnection = this.peerConnection;
final CapturerChoice capturerChoice = this.capturerChoice; final CapturerChoice capturerChoice = this.capturerChoice;
@ -538,6 +546,8 @@ public class WebRTCWrapper {
void onConnectionChange(PeerConnection.PeerConnectionState oldState, PeerConnection.PeerConnectionState newState); void onConnectionChange(PeerConnection.PeerConnectionState oldState, PeerConnection.PeerConnectionState newState);
void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices); void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices);
void onRenegotiationNeeded();
} }
private static abstract class SetSdpObserver implements SdpObserver { private static abstract class SetSdpObserver implements SdpObserver {

View file

@ -904,6 +904,7 @@
<string name="rtp_state_incoming_video_call">Incoming video call</string> <string name="rtp_state_incoming_video_call">Incoming video call</string>
<string name="rtp_state_connecting">Connecting</string> <string name="rtp_state_connecting">Connecting</string>
<string name="rtp_state_connected">Connected</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_accepting_call">Accepting call</string>
<string name="rtp_state_ending_call">Ending call</string> <string name="rtp_state_ending_call">Ending call</string>
<string name="answer_call">Answer</string> <string name="answer_call">Answer</string>