From fe6bedf1ab7d9c06fbe6f0ab4cb6841ac47b29f2 Mon Sep 17 00:00:00 2001
From: Yalu Zhang <yalu.zhang@iopsys.eu>
Date: Tue, 10 Dec 2024 15:10:16 +0000
Subject: [PATCH] Fix a bug that call clearing tones are not started when two
 other call parties hangup

Call scenario
- DUT calls A and the call is answered
- B calls DUT
- Press R to accept B
- A hangs up and then B hangs up There are no call clearing tones after a brief beep.

Cause
handle_dialtone_timeout() calls chan_voicemngr_get_active_subchannel() to get the sub-channel
and the sub[0] is returned and its state is ONHOOK. So the call clearing procedure is not
started by handle_dialtone_timeout().

Solution
Let chan_voicemngr_get_active_subchannel() select the sub-channel with state CALLENDED over
ONHOOK.

Also remove the unused call state ANSWER.
---
 src/channels/chan_voicemngr.c | 20 +++++++++++++-------
 src/channels/chan_voicemngr.h |  1 -
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/src/channels/chan_voicemngr.c b/src/channels/chan_voicemngr.c
index 25024c1..0ce404c 100644
--- a/src/channels/chan_voicemngr.c
+++ b/src/channels/chan_voicemngr.c
@@ -1341,7 +1341,6 @@ static int chan_voicemngr_devicestate(const char *device_number)
 		case DIALING: return AST_DEVICE_INUSE;
 		case CALLING: return AST_DEVICE_INUSE;
 		case INCALL: return AST_DEVICE_INUSE;
-		case ANSWER: return AST_DEVICE_INUSE;
 		case CALLENDED: return AST_DEVICE_NOT_INUSE;
 		case RINGING: return AST_DEVICE_RINGINUSE;
 		case CALLWAITING: return AST_DEVICE_INUSE;
@@ -1893,7 +1892,6 @@ static char *state2str(enum chan_voicemngr_channel_state state)
 	case DIALING:		return "DIALING";
 	case CALLING:		return "CALLING";
 	case INCALL:		return "INCALL";
-	case ANSWER:		return "ANSWER";
 	case CALLENDED:		return "CALLENDED";
 	case RINGING:		return "RINGING";
 	case CALLWAITING:	return "CALLWAITING";
@@ -2100,17 +2098,25 @@ struct chan_voicemngr_subchannel* chan_voicemngr_get_active_subchannel(const str
 			case TRANSFERING:
 			case RINGBACK:
 				sub = p->sub[i];
-				return sub;
+				return sub; // Select this sub-channel and return right away without checking the next one
+
 			case CALLWAITING:
 			case ONHOLD:
 				break;
+
 			case ONHOOK:
-			case ANSWER:
-			case CALLENDED:
+				// This is the least active state and will be selected as the last choice
 				if (!sub) {
 					sub = p->sub[i];
 				}
 				break;
+
+			case CALLENDED:
+				/* Select this sub-channel and override the sub-channel with ONHOOK state because
+				 * CALLENDED is more active than ONHOOK */
+				sub = p->sub[i];
+				break;
+
 			default:
 				ast_log(LOG_WARNING, "Unhandled channel state %s\n", state2str(p->sub[i]->channel_state));
 				break;
@@ -2499,7 +2505,6 @@ static int handle_dialtone_timeout(const void *data)
 	pvt_lock(p, "dialtone timeout");
 	strncpy(p->extensionCallStatus, "Disconnected", CALL_STATUS_MAX_LEN);
 	p->dialtone_timeout_timer_id = -1;
-
 	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
 
 	ast_debug(9, "Dialtone timeout, sub->channel_state: %s\n", state2str(sub->channel_state));
@@ -3223,7 +3228,8 @@ static void audio_packet_handler(pe_packet_t *p)
 	pvt = chan_voicemngr_get_pvt_from_lineid(iflist, ap->line);
 	sub = chan_voicemngr_get_active_subchannel(pvt);
 	if (!pvt || !sub) {
-		ast_log(LOG_ERROR, "Failed to find subchannel for %s/%d/%d\n", frame.src, ap->line, ap->connection_id);
+		ast_log(LOG_DEBUG, "Failed to find sub-channel for %s/%d/%d. Maybe the call has been ended\n",
+				frame.src, ap->line, ap->connection_id);
 		endpt_connection(ap->line, ap->connection_id, "destroy"); // Request line close
 		return;
 	}
diff --git a/src/channels/chan_voicemngr.h b/src/channels/chan_voicemngr.h
index db486cd..9e25ce8 100644
--- a/src/channels/chan_voicemngr.h
+++ b/src/channels/chan_voicemngr.h
@@ -45,7 +45,6 @@ enum chan_voicemngr_channel_state {
 	DIALING,
 	CALLING,
 	INCALL,
-	ANSWER,
 	CALLENDED,
 	RINGING,
 	CALLWAITING,
-- 
GitLab