Improve MCU re-connection magic

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2019-05-27 18:32:27 +02:00
parent ff7d5c8bab
commit 92e56cdef2
4 changed files with 76 additions and 54 deletions

View File

@ -199,7 +199,7 @@ public class CallController extends BaseController {
private SpotlightView spotlightView; private SpotlightView spotlightView;
private ExternalSignalingServer externalSignalingServer; private ExternalSignalingServer externalSignalingServer;
private MagicWebSocketInstance webSocketClient; private MagicWebSocketInstance webSocketClient ;
private WebSocketConnectionHelper webSocketConnectionHelper; private WebSocketConnectionHelper webSocketConnectionHelper;
private boolean hasMCU; private boolean hasMCU;
private boolean hasExternalSignalingServer; private boolean hasExternalSignalingServer;
@ -1166,11 +1166,7 @@ public class CallController extends BaseController {
conversationUser, externalSignalingServer.getExternalSignalingTicket(), conversationUser, externalSignalingServer.getExternalSignalingTicket(),
TextUtils.isEmpty(credentials)); TextUtils.isEmpty(credentials));
if (webSocketClient.isConnected()) { joinRoomAndCall();
joinRoomAndCall();
} else {
webSocketClient.restartWebSocket();
}
} }
@Subscribe(threadMode = ThreadMode.BACKGROUND) @Subscribe(threadMode = ThreadMode.BACKGROUND)

View File

@ -38,6 +38,7 @@ import org.greenrobot.eventbus.EventBus;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -56,20 +57,23 @@ public class MagicWebSocketInstance extends WebSocketListener {
private String webSocketTicket; private String webSocketTicket;
private String resumeId; private String resumeId;
private String sessionId; private String sessionId;
private String ncBackendSession;
private boolean hasMCU; private boolean hasMCU;
private boolean connected; private boolean connected;
private WebSocketConnectionHelper webSocketConnectionHelper; private WebSocketConnectionHelper webSocketConnectionHelper;
private WebSocket webSocket; private WebSocket internalWebSocket;
private MagicMap magicMap; private MagicMap magicMap;
private String connectionUrl; private String connectionUrl;
private String currentRoomToken; private String currentRoomToken;
private boolean isPermanentlyClosed = false;
private int restartCount = 0; private int restartCount = 0;
private boolean reconnecting = false;
private HashMap<String, String> displayNameHashMap; private HashMap<String, String> displayNameHashMap;
private HashMap<String, String> userIdSesssionHashMap; private HashMap<String, String> userIdSesssionHashMap;
private List<String> messagesQueue = new ArrayList<>();
MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl, String webSocketTicket) { MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl, String webSocketTicket) {
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
@ -81,53 +85,74 @@ public class MagicWebSocketInstance extends WebSocketListener {
this.userIdSesssionHashMap = new HashMap<>(); this.userIdSesssionHashMap = new HashMap<>();
magicMap = new MagicMap(); magicMap = new MagicMap();
connected = false;
restartWebSocket(); restartWebSocket();
} }
@Override private void sendHello() {
public void onOpen(WebSocket webSocket, Response response) {
try { try {
if (TextUtils.isEmpty(resumeId)) { if (TextUtils.isEmpty(resumeId)) {
webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModel(conversationUser, webSocketTicket))); internalWebSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModel(conversationUser, webSocketTicket)));
} else { } else {
webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModelForResume(resumeId))); internalWebSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModelForResume(resumeId)));
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Failed to serialize hello model"); Log.e(TAG, "Failed to serialize hello model");
} }
} }
public void restartWebSocket() { @Override
public void onOpen(WebSocket webSocket, Response response) {
sendHello();
}
private void restartWebSocket() {
reconnecting = true;
if (internalWebSocket != null) {
internalWebSocket.close(1000, null);
internalWebSocket.cancel();
messagesQueue = new ArrayList<>();
currentRoomToken = "";
}
Request request = new Request.Builder().url(connectionUrl).build(); Request request = new Request.Builder().url(connectionUrl).build();
this.webSocket = okHttpClient.newWebSocket(request, this); internalWebSocket = okHttpClient.newWebSocket(request, this);
restartCount++;
} }
@Override @Override
public void onMessage(WebSocket webSocket, String text) { public void onMessage(WebSocket webSocket, String text) {
Log.d(TAG, "Receiving : " + text); Log.d(TAG, "Receiving : " + text);
try { try {
BaseWebSocketMessage baseWebSocketMessage = LoganSquare.parse(text, BaseWebSocketMessage.class); BaseWebSocketMessage baseWebSocketMessage = LoganSquare.parse(text, BaseWebSocketMessage.class);
String messageType = baseWebSocketMessage.getType(); String messageType = baseWebSocketMessage.getType();
switch (messageType) { switch (messageType) {
case "hello": case "hello":
connected = true;
reconnecting = false;
restartCount = 0; restartCount = 0;
HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage = LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class); HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage = LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class);
resumeId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId(); resumeId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId();
sessionId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId(); sessionId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId();
hasMCU = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().serverHasMCUSupport(); hasMCU = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().serverHasMCUSupport();
connected = true;
for (int i = 0; i < messagesQueue.size(); i++) {
webSocket.send(messagesQueue.get(i));
}
messagesQueue = new ArrayList<>();
eventBus.post(new WebSocketCommunicationEvent("hello", null)); eventBus.post(new WebSocketCommunicationEvent("hello", null));
break; break;
case "error": case "error":
ErrorOverallWebSocketMessage errorOverallWebSocketMessage = LoganSquare.parse(text, ErrorOverallWebSocketMessage.class); ErrorOverallWebSocketMessage errorOverallWebSocketMessage = LoganSquare.parse(text, ErrorOverallWebSocketMessage.class);
if (("no_such_session").equals(errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode().equals("no_such_session"))) { if (("no_such_session").equals(errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) {
resumeId = ""; resumeId = "";
restartWebSocket();
} } else if (("hello_expected") .equals(errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) {
if (!isPermanentlyClosed) {
restartWebSocket(); restartWebSocket();
} }
break; break;
case "room": case "room":
JoinedRoomOverallWebSocketMessage joinedRoomOverallWebSocketMessage = LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage.class); JoinedRoomOverallWebSocketMessage joinedRoomOverallWebSocketMessage = LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage.class);
@ -201,7 +226,7 @@ public class MagicWebSocketInstance extends WebSocketListener {
break; break;
case "bye": case "bye":
connected = false; connected = false;
isPermanentlyClosed = true; resumeId = "";
default: default:
break; break;
} }
@ -218,18 +243,12 @@ public class MagicWebSocketInstance extends WebSocketListener {
@Override @Override
public void onClosing(WebSocket webSocket, int code, String reason) { public void onClosing(WebSocket webSocket, int code, String reason) {
Log.d(TAG, "Closing : " + code + " / " + reason); Log.d(TAG, "Closing : " + code + " / " + reason);
connected = false;
} }
@Override @Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) { public void onFailure(WebSocket webSocket, Throwable t, Response response) {
Log.d(TAG, "Error : " + t.getMessage()); Log.d(TAG, "Error : " + t.getMessage());
connected = false; restartWebSocket();
if (restartCount < 4) {
restartWebSocket();
} else {
isPermanentlyClosed = true;
}
} }
public String getSessionId() { public String getSessionId() {
@ -241,28 +260,36 @@ public class MagicWebSocketInstance extends WebSocketListener {
} }
public void joinRoomWithRoomTokenAndSession(String roomToken, String normalBackendSession) { public void joinRoomWithRoomTokenAndSession(String roomToken, String normalBackendSession) {
if (!roomToken.equals(currentRoomToken)) { if (!roomToken.equals(currentRoomToken) || !normalBackendSession.equals(ncBackendSession)) {
if (isConnected()) { ncBackendSession = normalBackendSession;
try { try {
webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession))); String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession));
} catch (IOException e) { if (!connected || reconnecting) {
Log.e(TAG, "Failed to serialize room overall websocket message"); messagesQueue.add(message);
} else {
internalWebSocket.send(message);
} }
} catch (IOException e) {
e.printStackTrace();
} }
} else { } else if (!roomToken.equals("")) {
HashMap<String, String> joinRoomHashMap = new HashMap<>(); HashMap<String, String> joinRoomHashMap = new HashMap<>();
joinRoomHashMap.put("roomToken", currentRoomToken); joinRoomHashMap.put("roomToken", currentRoomToken);
eventBus.post(new WebSocketCommunicationEvent("roomJoined", joinRoomHashMap)); eventBus.post(new WebSocketCommunicationEvent("roomJoined", joinRoomHashMap));
} }
} }
public void sendCallMessage(NCMessageWrapper ncMessageWrapper) { public void sendCallMessage(NCMessageWrapper ncMessageWrapper) {
if (isConnected()) { try {
try { String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledCallMessageModel(ncMessageWrapper));
webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledCallMessageModel(ncMessageWrapper))); if (!connected || reconnecting) {
} catch (IOException e) { messagesQueue.add(message);
Log.e(TAG, "Failed to serialize signaling message"); } else {
internalWebSocket.send(message);
} }
} catch (IOException e) {
Log.e(TAG, "Failed to serialize signaling message");
} }
} }
@ -273,36 +300,35 @@ public class MagicWebSocketInstance extends WebSocketListener {
} }
public void requestOfferForSessionIdWithType(String sessionIdParam, String roomType) { public void requestOfferForSessionIdWithType(String sessionIdParam, String roomType) {
if (isConnected()) { try {
try { String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledRequestOfferModel(sessionIdParam, roomType));
webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledRequestOfferModel(sessionIdParam, roomType))); if (!connected || reconnecting) {
} catch (IOException e) { messagesQueue.add(message);
Log.e(TAG, "Failed to offer request"); } else {
internalWebSocket.send(message);
} }
} catch (IOException e) {
Log.e(TAG, "Failed to offer request");
} }
} }
void sendBye() { void sendBye() {
if (isConnected()) { if (connected) {
try { try {
ByeWebSocketMessage byeWebSocketMessage = new ByeWebSocketMessage(); ByeWebSocketMessage byeWebSocketMessage = new ByeWebSocketMessage();
byeWebSocketMessage.setType("bye"); byeWebSocketMessage.setType("bye");
byeWebSocketMessage.setBye(new HashMap<>()); byeWebSocketMessage.setBye(new HashMap<>());
webSocket.send(LoganSquare.serialize(byeWebSocketMessage)); internalWebSocket.send(LoganSquare.serialize(byeWebSocketMessage));
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Failed to serialize bye message"); Log.e(TAG, "Failed to serialize bye message");
} }
} }
} }
public boolean isConnected() { boolean isConnected() {
return connected; return connected;
} }
boolean isPermanentlyClosed() {
return isPermanentlyClosed;
}
public String getDisplayNameForSession(String session) { public String getDisplayNameForSession(String session) {
if (displayNameHashMap.containsKey(session)) { if (displayNameHashMap.containsKey(session)) {
return displayNameHashMap.get(session); return displayNameHashMap.get(session);

View File

@ -57,7 +57,7 @@ public class WebSocketConnectionHelper {
MagicWebSocketInstance magicWebSocketInstance; MagicWebSocketInstance magicWebSocketInstance;
if (userId != -1 && magicWebSocketInstanceMap.containsKey(userEntity.getId()) && (magicWebSocketInstance = magicWebSocketInstanceMap.get(userEntity.getId())) != null && !magicWebSocketInstance.isPermanentlyClosed()) { if (userId != -1 && magicWebSocketInstanceMap.containsKey(userEntity.getId()) && (magicWebSocketInstance = magicWebSocketInstanceMap.get(userEntity.getId())) != null) {
return magicWebSocketInstance; return magicWebSocketInstance;
} else { } else {
if (userId == -1) { if (userId == -1) {

View File

@ -13,7 +13,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.0' classpath 'com.android.tools.build:gradle:3.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong