Test rejected call signalling and deferred hangup recovery
This commit is contained in:
1 parent
38e2cc4336
commit
7e328b3908
3 files changed
+76
-1
No files matched your search
@@ -231,6 +231,12 @@ static int check_dm_submissions(void) {
|
||||
struct UTUN_INSTANCE* saved = instance; instance = NULL;
|
||||
rc = utun_bridge_dm_send(conv, text, sizeof(text) - 1, generation, 17); instance = saved;
|
||||
REQUIRE(rc == -1);
|
||||
REQUIRE(utun_bridge_call_start(NULL, node, 1) == -1 && utun_bridge_call_start("7", 0, 1) == -1);
|
||||
REQUIRE(utun_bridge_call_accept(0) == -1 && utun_bridge_call_decline(0) == -1 && utun_bridge_call_hangup(0) == -1);
|
||||
instance = NULL;
|
||||
REQUIRE(utun_bridge_call_start("7", node, 1) == -1 && utun_bridge_call_accept(1) == -1);
|
||||
REQUIRE(utun_bridge_call_decline(1) == -1 && utun_bridge_call_hangup(1) == -1);
|
||||
instance = saved;
|
||||
uasync_poll(async, 0); REQUIRE(!results[15] && !results[16] && !results[17]);
|
||||
printf("[PASS] Android PM: UTF-8, commit ACK, stale generation, SQL rollback/retry and rejected requests\n");
|
||||
return 0;
|
||||
|
||||
@@ -320,7 +320,7 @@ object CallController : TelecomCallManager.Listener {
|
||||
|
||||
/** Ошибка постановки не удерживает FGS/audio, но сохраняет команду и запрещает новый media до queue ACK. */
|
||||
private fun queueEnd(id: Long, decline: Boolean) {
|
||||
if (sendEnd(id, decline)) return
|
||||
if (sendEnd(id, decline)) { pendingEnds.value = pendingEnds.value - id; return }
|
||||
pendingEnds.value = pendingEnds.value + (id to decline)
|
||||
if (endRetry?.isActive == true) return
|
||||
endRetry = scope.launch {
|
||||
|
||||
+69
@@ -41,6 +41,7 @@ class CallServiceLifecycleTest {
|
||||
assertSame(main, Looper.myLooper()); Dispatchers.setMain(dispatcher)
|
||||
context = Context().also { ChatApplication.instance = it }; AudioManager.shared = AudioManager()
|
||||
NativeLib.ready = true; NativeLib.failActivityRequest = false; NativeLib.acceptActivityRequest = true
|
||||
NativeLib.rejectedCallCommands = emptySet()
|
||||
AppLanguage.failText = false; AppLanguage.language = "en"; NotificationCompat.failBuild = false
|
||||
Handler.rejectImmediate = false; Handler.rejectDelayed = false; PendingIntent.reset()
|
||||
MediaAudioController.beforeIdle = {}
|
||||
@@ -55,6 +56,7 @@ class CallServiceLifecycleTest {
|
||||
gates.forEach { it.countDown() }; MediaAudioController.beforeIdle = {}
|
||||
TelecomCallManager.pending = false
|
||||
NativeLib.failActivityRequest = false
|
||||
NativeLib.rejectedCallCommands = emptySet()
|
||||
Handler.rejectImmediate = false; Handler.rejectDelayed = false
|
||||
AppLanguage.failText = false; NotificationCompat.failBuild = false; CallVideoEngine.failCreate = false
|
||||
power.failures = emptySet(); power.locks.forEach { it.failures = emptySet() }
|
||||
@@ -109,6 +111,73 @@ class CallServiceLifecycleTest {
|
||||
return service
|
||||
}
|
||||
|
||||
@Test fun rejectedOutgoingSignallingEndsAndReleasesReservedService() {
|
||||
NativeLib.rejectedCallCommands = setOf("start")
|
||||
val service = create(); val id = request(); start(service, id)
|
||||
await("rejected call must end") { state?.phase == CallPhase.ENDED }
|
||||
assertFalse(power.locks.single().held); assertFalse(service.audio.active)
|
||||
assertFalse(CallController.blocksMedia()); diagnostic("start queue rejected")
|
||||
}
|
||||
|
||||
@Test fun rejectedAcceptDeclinesTheIncomingCallAndReleasesService() {
|
||||
val id = 17L
|
||||
CallController.onEvent(34, ByteBuffer.allocate(18).order(ByteOrder.LITTLE_ENDIAN)
|
||||
.put(1.toByte()).put('7'.code.toByte()).putLong(99).putLong(id).array())
|
||||
assertEquals(CallPhase.INCOMING, state?.phase)
|
||||
NativeLib.rejectedCallCommands = setOf("accept")
|
||||
CallController.accept(); val service = create(); start(service, id)
|
||||
await("rejected answer must end") { state?.phase == CallPhase.ENDED }
|
||||
assertTrue(NativeLib.callCommands.contains("decline" to id)); assertFalse(power.locks.single().held)
|
||||
diagnostic("accept queue rejected")
|
||||
}
|
||||
|
||||
@Test fun rejectedContinueKeepsCallerChoiceAvailableForRetry() {
|
||||
val service = create(); val id = request(); start(service, id)
|
||||
await("initial signaling queued") { NativeLib.callCommands.contains("start" to id) }
|
||||
CallController.onEvent(66, ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(id).array())
|
||||
assertEquals(CallPhase.WAIT_DECISION, state?.phase)
|
||||
NativeLib.rejectedCallCommands = setOf("continue"); CallController.continueCall(id)
|
||||
assertEquals(CallPhase.WAIT_DECISION, state?.phase); diagnostic("continue queue rejected")
|
||||
NativeLib.rejectedCallCommands = emptySet(); CallController.continueCall(id)
|
||||
assertEquals(CallPhase.OUTGOING, state?.phase)
|
||||
}
|
||||
|
||||
@Test fun rejectedHangupReleasesAudioButWaitsForQueueAckBeforeRestoringRadio() {
|
||||
RadioAudioEngine.instance().activeGroup.value = 7
|
||||
active(); val id = requireNotNull(state).callId
|
||||
NativeLib.rejectedCallCommands = setOf("hangup"); CallController.end(); dispatcher.scheduler.runCurrent()
|
||||
assertEquals(CallPhase.ENDED, state?.phase); assertFalse(power.locks.single().held)
|
||||
assertTrue(CallController.blocksMedia()); assertTrue(RadioAudioEngine.instance().restored.isEmpty())
|
||||
val starts = context.serviceStarts.size
|
||||
CallController.start("7", 101, "Other", mediaConfirmed = true)
|
||||
assertEquals(starts, context.serviceStarts.size); assertEquals(id, state?.callId)
|
||||
dispatcher.scheduler.advanceTimeBy(250); dispatcher.scheduler.runCurrent()
|
||||
assertTrue(NativeLib.callCommands.count { it == "hangup" to id } >= 2)
|
||||
NativeLib.rejectedCallCommands = emptySet()
|
||||
dispatcher.scheduler.advanceTimeBy(500); dispatcher.scheduler.runCurrent()
|
||||
await("radio restored after end queue ACK") { RadioAudioEngine.instance().restored == listOf(7L) }
|
||||
assertFalse(CallController.blocksMedia()); diagnostic("hangup queue rejected")
|
||||
}
|
||||
|
||||
@Test fun nativeEndEventCompletesPendingHangupWithoutAnotherCommand() {
|
||||
active(); val id = requireNotNull(state).callId
|
||||
NativeLib.rejectedCallCommands = setOf("hangup"); CallController.end()
|
||||
assertTrue(CallController.blocksMedia())
|
||||
CallController.onEvent(38, ByteBuffer.allocate(9).order(ByteOrder.LITTLE_ENDIAN).putLong(id).put(0.toByte()).array())
|
||||
dispatcher.scheduler.runCurrent(); assertFalse(CallController.blocksMedia())
|
||||
val attempts = NativeLib.callCommands.size
|
||||
dispatcher.scheduler.advanceTimeBy(250); dispatcher.scheduler.runCurrent()
|
||||
assertEquals(attempts, NativeLib.callCommands.size)
|
||||
}
|
||||
|
||||
@Test fun resetCancelsPendingSignallingRetriesBeforeCoreStop() {
|
||||
active(); NativeLib.rejectedCallCommands = setOf("hangup"); CallController.end()
|
||||
assertTrue(CallController.blocksMedia()); CallController.reset(); dispatcher.scheduler.runCurrent()
|
||||
assertFalse(CallController.blocksMedia()); val attempts = NativeLib.callCommands.size
|
||||
dispatcher.scheduler.advanceTimeBy(30_000); dispatcher.scheduler.runCurrent()
|
||||
assertEquals(attempts, NativeLib.callCommands.size)
|
||||
}
|
||||
|
||||
@Test fun normalStartupOwnsOneLockReceiverAndForegroundBeforeAudio() {
|
||||
val service = create(); val id = request(); start(service, id)
|
||||
assertTrue(power.locks.single().held); assertFalse(power.locks.single().counted)
|
||||
|
||||
Reference in new issue
Block a user