From a90bd89c9ab79eb7bc36e7783a39b93f4b142471 Mon Sep 17 00:00:00 2001 From: jubb Date: Thu, 3 Mar 2022 17:03:32 +1100 Subject: [PATCH] refactor: adding reconnecting logic and visuals --- .../securesms/calls/WebRtcCallActivity.kt | 3 ++ .../securesms/service/WebRtcCallService.kt | 37 +++++++++++++------ .../securesms/webrtc/CallManager.kt | 16 ++++---- .../video/RemoteRotationVideoProxySink.kt | 2 - app/src/main/res/layout/activity_webrtc.xml | 10 +++++ app/src/main/res/values/strings.xml | 1 + 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt index fbdfb605d..a3b9944a7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt @@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_CONNECTED import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_INCOMING import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_OUTGOING import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_PRE_INIT +import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RECONNECTING import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RINGING import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.EARPIECE import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SPEAKER_PHONE @@ -223,6 +224,8 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { state !in listOf(CALL_CONNECTED, CALL_RINGING, CALL_PRE_INIT) || wantsToAnswer incomingControlGroup.isVisible = state in listOf(CALL_RINGING, CALL_PRE_INIT) && !wantsToAnswer + reconnectingText.isVisible = state == CALL_RECONNECTING + endCallButton.isVisible = endCallButton.isVisible || state == CALL_RECONNECTING } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt index a87c08b3e..8ada380e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt @@ -51,6 +51,7 @@ import org.webrtc.SessionDescription import java.util.UUID import java.util.concurrent.ExecutionException import java.util.concurrent.Executors +import java.util.concurrent.ScheduledFuture import java.util.concurrent.TimeUnit import javax.inject.Inject import org.thoughtcrime.securesms.webrtc.data.State as CallState @@ -101,7 +102,8 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { const val INVALID_NOTIFICATION_ID = -1 private const val TIMEOUT_SECONDS = 90L - private const val MAX_TIMEOUTS = 3 + private const val RECONNECT_SECONDS = 5L + private const val MAX_RECONNECTS = 5 fun cameraEnabled(context: Context, enabled: Boolean) = Intent(context, WebRtcCallService::class.java) .setAction(ACTION_SET_MUTE_VIDEO) @@ -188,6 +190,9 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { private var wantsToAnswer = false private var currentTimeouts = 0 private var isNetworkAvailable = true + private var scheduledTimeout: ScheduledFuture<*>? = null + private var scheduledReconnectTimeout: ScheduledFuture<*>? = null + private var scheduledReconnect: ScheduledFuture<*>? = null private val lockManager by lazy { LockManager(this) } private val serviceExecutor = Executors.newSingleThreadExecutor() @@ -407,7 +412,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { callManager.startOutgoingRinger(OutgoingRinger.Type.RINGING) setCallInProgressNotification(TYPE_OUTGOING_RINGING, callManager.recipient) callManager.insertCallMessage(recipient.address.serialize(), CallMessageType.CALL_OUTGOING) - timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) + scheduledTimeout = timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) callManager.setAudioEnabled(true) val expectedState = callManager.currentConnectionState @@ -463,7 +468,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { callManager.silenceIncomingRinger() callManager.postViewModelState(CallViewModel.State.CALL_INCOMING) - timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) + scheduledTimeout = timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) callManager.initializeAudioForCall() callManager.initializeVideo(this) @@ -598,8 +603,13 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { val callId = callManager.callId ?: return val numTimeouts = ++currentTimeouts - if (callId == getCallId(intent) && isNetworkAvailable && numTimeouts <= 5) { + if (callId == getCallId(intent) && isNetworkAvailable && numTimeouts <= MAX_RECONNECTS) { + Log.d("Loki", "Trying to re-connect") callManager.networkReestablished() + scheduledTimeout = timeoutExecutor.schedule(TimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) + } else { + Log.d("Loki", "Network isn't available, timeouts == $numTimeouts out of $MAX_RECONNECTS") + scheduledReconnect = timeoutExecutor.schedule(CheckReconnectedRunnable(callId, this), RECONNECT_SECONDS, TimeUnit.SECONDS) } } @@ -783,17 +793,22 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { if (newState == CONNECTED) { + scheduledTimeout?.cancel(false) + scheduledReconnect?.cancel(false) + scheduledReconnectTimeout?.cancel(false) + scheduledTimeout = null + scheduledReconnect = null + scheduledReconnectTimeout = null val intent = Intent(this, WebRtcCallService::class.java) .setAction(ACTION_ICE_CONNECTED) startService(intent) - } else if (newState == FAILED) { - val intent = hangupIntent(this) - startService(intent) - } else if (newState == DISCONNECTED) { + } else if (newState in arrayOf(FAILED, DISCONNECTED) && scheduledReconnectTimeout == null) { callManager.callId?.let { callId -> - callManager.postViewModelState(CallViewModel.State.CALL_RECONNECTING) - timeoutExecutor.schedule(CheckReconnectedRunnable(callId, this), 5, TimeUnit.SECONDS) - timeoutExecutor.schedule(ReconnectTimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) + callManager.postConnectionEvent(Event.IceDisconnect) { + callManager.postViewModelState(CallViewModel.State.CALL_RECONNECTING) + scheduledReconnect = timeoutExecutor.schedule(CheckReconnectedRunnable(callId, this), RECONNECT_SECONDS, TimeUnit.SECONDS) + scheduledReconnectTimeout = timeoutExecutor.schedule(ReconnectTimeoutRunnable(callId, this), TIMEOUT_SECONDS, TimeUnit.SECONDS) + } } ?: run { val intent = hangupIntent(this) startService(intent) diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt index 5b27cd79a..ac74d58e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt @@ -675,17 +675,15 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va val callId = callId ?: return val recipient = recipient ?: return - if (isReestablishing) return - isReestablishing = true - Log.d("Loki", "start re-establish") + postConnectionEvent(Event.NetworkReconnect) { + Log.d("Loki", "start re-establish") - val offer = connection.createOffer(MediaConstraints().apply { - mandatory.add(MediaConstraints.KeyValuePair("IceRestart", "true")) - }) - connection.setLocalDescription(offer) + val offer = connection.createOffer(MediaConstraints().apply { + mandatory.add(MediaConstraints.KeyValuePair("IceRestart", "true")) + }) + connection.setLocalDescription(offer) - MessageSender.sendNonDurably(CallMessage.offer(offer.description, callId), recipient.address).success { - isReestablishing = false + MessageSender.sendNonDurably(CallMessage.offer(offer.description, callId), recipient.address) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RemoteRotationVideoProxySink.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RemoteRotationVideoProxySink.kt index f5835087e..2b0caef89 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RemoteRotationVideoProxySink.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/video/RemoteRotationVideoProxySink.kt @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.webrtc.video -import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.webrtc.data.quadrantRotation import org.webrtc.VideoFrame import org.webrtc.VideoSink @@ -19,7 +18,6 @@ class RemoteRotationVideoProxySink: VideoSink { val modifiedRotation = thisFrame.rotation - quadrantRotation val newFrame = VideoFrame(thisFrame.buffer, modifiedRotation, thisFrame.timestampNs) - Log.d("Loki", "sending frame: w=${newFrame.buffer.width}, h=${newFrame.buffer.height}, rw=${newFrame.rotatedWidth}, rh=${newFrame.rotatedHeight}, rot=${newFrame.rotation}") thisSink.onFrame(newFrame) } diff --git a/app/src/main/res/layout/activity_webrtc.xml b/app/src/main/res/layout/activity_webrtc.xml index 61d6f9d7b..99a59b79a 100644 --- a/app/src/main/res/layout/activity_webrtc.xml +++ b/app/src/main/res/layout/activity_webrtc.xml @@ -46,6 +46,16 @@ app:layout_constraintTop_toBottomOf="@id/remote_recipient" tools:visibility="visible" /> + + Call Missed You missed a call because you need to enable the \'Voice and video calls\' permission in the Privacy Settings. Session Call + Reconnecting…