mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-30 16:48:41 +01:00
Resolve the call log entry for outgoing calls that fail during setup.
This commit is contained in:
committed by
Michelle Tang
parent
0d87ed08c2
commit
5bff46f586
+20
-6
@@ -257,9 +257,7 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
|
||||
protected @NonNull WebRtcServiceState handleLocalHangup(@NonNull WebRtcServiceState currentState) {
|
||||
RemotePeer activePeer = currentState.getCallInfoState().getActivePeer();
|
||||
if (activePeer != null) {
|
||||
webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer,
|
||||
true,
|
||||
currentState.getCallSetupState(activePeer).isAcceptWithVideo() || currentState.getLocalDeviceState().getCameraState().isEnabled());
|
||||
markNotAccepted(currentState, activePeer.getCallId());
|
||||
}
|
||||
|
||||
return activeCallDelegate.handleLocalHangup(currentState);
|
||||
@@ -280,9 +278,7 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
|
||||
callEndReason == CallManager.CallEndReason.TIMEOUT ||
|
||||
callEndReason == CallManager.CallEndReason.REMOTE_GLARE))
|
||||
{
|
||||
webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer,
|
||||
true,
|
||||
currentState.getCallSetupState(activePeer).isAcceptWithVideo() || currentState.getLocalDeviceState().getCameraState().isEnabled());
|
||||
markNotAccepted(currentState, remotePeer.getCallId());
|
||||
}
|
||||
|
||||
return activeCallDelegate.handleEndedRemote(currentState, callEndReason, remotePeer);
|
||||
@@ -290,11 +286,15 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
|
||||
|
||||
@Override
|
||||
protected @NonNull WebRtcServiceState handleEnded(@NonNull WebRtcServiceState currentState, @NonNull CallManager.CallEndReason callEndReason, @NonNull RemotePeer remotePeer) {
|
||||
markNotAccepted(currentState, remotePeer.getCallId());
|
||||
|
||||
return activeCallDelegate.handleEnded(currentState, callEndReason, remotePeer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull WebRtcServiceState handleSetupFailure(@NonNull WebRtcServiceState currentState, @NonNull CallId callId) {
|
||||
markNotAccepted(currentState, callId);
|
||||
|
||||
return activeCallDelegate.handleSetupFailure(currentState, callId);
|
||||
}
|
||||
|
||||
@@ -307,4 +307,18 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
|
||||
protected @NonNull WebRtcServiceState handleSetEnableVideo(@NonNull WebRtcServiceState currentState, boolean enable) {
|
||||
return callSetupDelegate.handleSetEnableVideo(currentState, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies linked devices the active outgoing call ended unaccepted. No-op if {@code callId} isn't the active peer's.
|
||||
*/
|
||||
private void markNotAccepted(@NonNull WebRtcServiceState currentState, @NonNull CallId callId) {
|
||||
RemotePeer activePeer = currentState.getCallInfoState().getActivePeer();
|
||||
if (activePeer == null || !activePeer.getCallId().equals(callId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer,
|
||||
true,
|
||||
currentState.getCallSetupState(activePeer).isEnableVideoOnCreate() || currentState.getLocalDeviceState().getCameraState().isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.service.webrtc
|
||||
|
||||
import android.app.Application
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.ringrtc.CallId
|
||||
import org.signal.ringrtc.CallManager
|
||||
import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink
|
||||
import org.thoughtcrime.securesms.events.WebRtcViewModel
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.ringrtc.CameraState
|
||||
import org.thoughtcrime.securesms.ringrtc.OutgoingVideoSourceRouter
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState
|
||||
import org.thoughtcrime.securesms.testutil.SystemOutLogger
|
||||
|
||||
/**
|
||||
* State transition tests for the outgoing 1:1 call processor, focused on calls that die during setup
|
||||
* and still have an ongoing call log entry to resolve.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class, instrumentedPackages = ["org.signal.ringrtc"])
|
||||
class OutgoingCallActionProcessorTest {
|
||||
|
||||
companion object {
|
||||
private val CALL_ID = CallId(42L)
|
||||
private val OTHER_CALL_ID = CallId(43L)
|
||||
|
||||
@JvmStatic
|
||||
@BeforeClass
|
||||
fun setUpClass() {
|
||||
Log.initialize(SystemOutLogger())
|
||||
}
|
||||
}
|
||||
|
||||
private val callManager: CallManager = mockk(relaxed = true)
|
||||
private val webRtcInteractor: WebRtcInteractor = mockk(relaxed = true)
|
||||
private val router: OutgoingVideoSourceRouter = mockk(relaxed = true)
|
||||
private val localSink: BroadcastVideoSink = mockk()
|
||||
|
||||
private val processor = OutgoingCallActionProcessor(webRtcInteractor)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
every { webRtcInteractor.callManager } returns callManager
|
||||
every { router.cameraState } returns CameraState(CameraState.Direction.FRONT, 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a dialing video call, when it ends with a connection failure, then I expect a not accepted sync event`() {
|
||||
val state = outgoingDialingCall()
|
||||
val activePeer = state.callInfoState.requireActivePeer()
|
||||
|
||||
processor.handleEnded(state, CallManager.CallEndReason.CONNECTION_FAILURE, activePeer)
|
||||
|
||||
verify { webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer, true, true) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a dialing audio call, when it times out, then I expect a not accepted sync event`() {
|
||||
val state = outgoingDialingCall(videoCall = false)
|
||||
val activePeer = state.callInfoState.requireActivePeer()
|
||||
|
||||
processor.handleEnded(state, CallManager.CallEndReason.TIMEOUT, activePeer)
|
||||
|
||||
verify { webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer, true, false) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a dialing video call, when setup fails, then I expect a not accepted sync event`() {
|
||||
val state = outgoingDialingCall()
|
||||
val activePeer = state.callInfoState.requireActivePeer()
|
||||
|
||||
processor.handleSetupFailure(state, CALL_ID)
|
||||
|
||||
verify { webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer, true, true) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a dialing video call, when an unrelated call ends, then I expect no sync event`() {
|
||||
val state = outgoingDialingCall()
|
||||
|
||||
processor.handleEnded(state, CallManager.CallEndReason.CONNECTION_FAILURE, RemotePeer(RecipientId.from(2L), OTHER_CALL_ID))
|
||||
|
||||
verify(exactly = 0) { webRtcInteractor.sendNotAcceptedCallEventSyncMessage(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a dialing video call, when an unrelated call fails setup, then I expect no sync event`() {
|
||||
val state = outgoingDialingCall()
|
||||
|
||||
processor.handleSetupFailure(state, OTHER_CALL_ID)
|
||||
|
||||
verify(exactly = 0) { webRtcInteractor.sendNotAcceptedCallEventSyncMessage(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a video call that has not started its camera yet, when setup fails, then I expect a video sync event`() {
|
||||
val state = outgoingDialingCall(videoCall = true, cameraEnabled = false)
|
||||
val activePeer = state.callInfoState.requireActivePeer()
|
||||
|
||||
processor.handleSetupFailure(state, CALL_ID)
|
||||
|
||||
verify { webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer, true, true) }
|
||||
}
|
||||
|
||||
private fun outgoingDialingCall(videoCall: Boolean = true, cameraEnabled: Boolean = videoCall): WebRtcServiceState {
|
||||
val peer = RemotePeer(RecipientId.from(1L), CALL_ID)
|
||||
peer.dialing()
|
||||
|
||||
return WebRtcServiceState(processor)
|
||||
.builder()
|
||||
.changeCallInfoState()
|
||||
.callState(WebRtcViewModel.State.CALL_OUTGOING)
|
||||
.activePeer(peer)
|
||||
.commit()
|
||||
.changeCallSetupState(CALL_ID)
|
||||
.enableVideoOnCreate(videoCall)
|
||||
.commit()
|
||||
.changeLocalDeviceState()
|
||||
.cameraState(if (cameraEnabled) CameraState(CameraState.Direction.FRONT, 2) else CameraState.UNKNOWN)
|
||||
.commit()
|
||||
.changeVideoState()
|
||||
.router(router)
|
||||
.localSink(localSink)
|
||||
.commit()
|
||||
.build()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user