Skip to content

Commit

Permalink
Fix incorrect meetingFailed event when meeting is ended
Browse files Browse the repository at this point in the history
  • Loading branch information
dylonChime committed Feb 12, 2025
1 parent 77142c1 commit 427b402
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Unreleased

### Fixed
* When meeting has ended normally, but not initiated by the client, send a meetingEnded event rather than a meetingFailed event.

### Changed
* Upgrade gradle version and dokka

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,19 @@ class DefaultAudioClientObserver(
}
}
SessionStateControllerAction.FinishDisconnecting -> {
if (currentAudioState == SessionStateControllerAction.Reconnecting) {
notifyAudioClientObserver { observer -> observer.onAudioSessionCancelledReconnect() }
when (currentAudioState) {
SessionStateControllerAction.Connecting -> {
if (newAudioStatus == MeetingSessionStatusCode.AudioServerHungup) {
handleAudioSessionEndedByServer(newAudioStatus)
}
}
SessionStateControllerAction.Reconnecting -> {
notifyAudioClientObserver { observer -> observer.onAudioSessionCancelledReconnect() }
if (newAudioStatus == MeetingSessionStatusCode.AudioServerHungup) {
handleAudioSessionEndedByServer(newAudioStatus)
}
}
else -> Unit
}
}
SessionStateControllerAction.Fail -> {
Expand Down Expand Up @@ -520,10 +531,10 @@ class DefaultAudioClientObserver(
AudioClient.AUDIO_CLIENT_STATE_CONNECTED -> SessionStateControllerAction.FinishConnecting
AudioClient.AUDIO_CLIENT_STATE_RECONNECTING -> SessionStateControllerAction.Reconnecting
AudioClient.AUDIO_CLIENT_STATE_DISCONNECTING -> SessionStateControllerAction.Disconnecting
AudioClient.AUDIO_CLIENT_STATE_DISCONNECTED_NORMAL -> SessionStateControllerAction.FinishDisconnecting
AudioClient.AUDIO_CLIENT_STATE_DISCONNECTED_NORMAL,
AudioClient.AUDIO_CLIENT_STATE_SERVER_HUNGUP -> SessionStateControllerAction.FinishDisconnecting
AudioClient.AUDIO_CLIENT_STATE_FAILED_TO_CONNECT,
AudioClient.AUDIO_CLIENT_STATE_DISCONNECTED_ABNORMAL,
AudioClient.AUDIO_CLIENT_STATE_SERVER_HUNGUP -> SessionStateControllerAction.Fail
AudioClient.AUDIO_CLIENT_STATE_DISCONNECTED_ABNORMAL -> SessionStateControllerAction.Fail
else -> SessionStateControllerAction.Unknown
}
}
Expand All @@ -532,7 +543,7 @@ class DefaultAudioClientObserver(
return when (internalAudioStatus) {
AudioClient.AUDIO_CLIENT_OK -> MeetingSessionStatusCode.OK
AudioClient.AUDIO_CLIENT_STATUS_NETWORK_IS_NOT_GOOD_ENOUGH_FOR_VOIP -> MeetingSessionStatusCode.NetworkBecamePoor
AudioClient.AUDIO_CLIENT_ERR_SERVER_HUNGUP -> MeetingSessionStatusCode.AudioDisconnected
AudioClient.AUDIO_CLIENT_ERR_SERVER_HUNGUP -> MeetingSessionStatusCode.AudioServerHungup
AudioClient.AUDIO_CLIENT_ERR_JOINED_FROM_ANOTHER_DEVICE -> MeetingSessionStatusCode.AudioJoinedFromAnotherDevice
AudioClient.AUDIO_CLIENT_ERR_INTERNAL_SERVER_ERROR -> MeetingSessionStatusCode.AudioInternalServerError
AudioClient.AUDIO_CLIENT_ERR_AUTH_REJECTED -> MeetingSessionStatusCode.AudioAuthenticationRejected
Expand All @@ -549,6 +560,12 @@ class DefaultAudioClientObserver(
private fun handleOnAudioSessionFailed(statusCode: MeetingSessionStatusCode?) {
if (audioClient != null) {
notifyFailed(statusCode)
}
handleAudioClientStop(statusCode)
}

private fun handleAudioClientStop(statusCode: MeetingSessionStatusCode?) {
if (audioClient != null) {
GlobalScope.launch {
audioClient?.stopSession()
DefaultAudioClientController.audioClientState = AudioClientState.STOPPED
Expand All @@ -571,4 +588,23 @@ class DefaultAudioClientObserver(
eventAnalyticsController.publishEvent(EventName.meetingFailed, attributes)
meetingStatsCollector.resetMeetingStats()
}

private fun notifyMeetingEnded(statusCode: MeetingSessionStatusCode?) {
val attributes: MutableMap<EventAttributeName, Any>? = statusCode?.let {
mutableMapOf(
EventAttributeName.meetingStatus to statusCode,
EventAttributeName.meetingErrorMessage to statusCode.toString()
)
}
eventAnalyticsController.publishEvent(EventName.meetingEnded, attributes)
meetingStatsCollector.resetMeetingStats()
}

private fun handleAudioSessionEndedByServer(statusCode: MeetingSessionStatusCode?) {
if (DefaultAudioClientController.audioClientState == AudioClientState.STOPPED) {
return
}
notifyMeetingEnded(statusCode)
handleAudioClientStop(statusCode)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,12 @@ enum class MeetingSessionStatusCode(val value: Int) {
/**
* Designated input device is not responding and timed out.
*/
AudioInputDeviceNotResponding(15);
AudioInputDeviceNotResponding(15),

/**
* Chime SDK audio server hung up.
*/
AudioServerHungup(16);

companion object {
fun from(intValue: Int): MeetingSessionStatusCode? = values().find { it.value == intValue }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,64 @@ class DefaultAudioClientObserverTest {
verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockAudioClient.stopSession() }
}

fun `onAudioClientStateChange should stop session and notify of session stop event when finish disconnecting while connecting`() {
every { mockAudioClient.stopSession() } returns 0

runBlockingTest {
audioClientObserver.onAudioClientStateChange(
AudioClient.AUDIO_CLIENT_STATE_CONNECTING,
AudioClient.AUDIO_CLIENT_OK
)

audioClientObserver.onAudioClientStateChange(
AudioClient.AUDIO_CLIENT_STATE_SERVER_HUNGUP,
AudioClient.AUDIO_CLIENT_ERR_SERVER_HUNGUP
)
}

verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockAudioVideoObserver.onAudioSessionStopped(any()) }
verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockAudioClient.stopSession() }
verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockEventAnalyticsController.publishEvent(EventName.meetingEnded, any()) }
}

fun `onAudioClientStateChange should stop session and notify of session stop event when finish disconnecting while connected`() {
every { mockAudioClient.stopSession() } returns 0

runBlockingTest {
audioClientObserver.onAudioClientStateChange(
AudioClient.AUDIO_CLIENT_STATE_CONNECTED,
AudioClient.AUDIO_CLIENT_OK
)

audioClientObserver.onAudioClientStateChange(
AudioClient.AUDIO_CLIENT_STATE_SERVER_HUNGUP,
AudioClient.AUDIO_CLIENT_ERR_SERVER_HUNGUP
)
}

verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockAudioVideoObserver.onAudioSessionStopped(any()) }
verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockAudioClient.stopSession() }
verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockEventAnalyticsController.publishEvent(EventName.meetingEnded, any()) }
}

fun `onAudioClientStateChange should stop session and notify of session stop event when finish disconnecting while reconnecting`() {
runBlockingTest {
audioClientObserver.onAudioClientStateChange(
AudioClient.AUDIO_CLIENT_STATE_RECONNECTING,
AudioClient.AUDIO_CLIENT_OK
)

audioClientObserver.onAudioClientStateChange(
AudioClient.AUDIO_CLIENT_STATE_SERVER_HUNGUP,
AudioClient.AUDIO_CLIENT_ERR_SERVER_HUNGUP
)
}

verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockAudioVideoObserver.onAudioSessionStopped(any()) }
verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockAudioClient.stopSession() }
verify(exactly = 1, timeout = TestConstant.globalScopeTimeoutMs) { mockEventAnalyticsController.publishEvent(EventName.meetingEnded, any()) }
}

@Test
fun `onAudioClientStateChange should notify of session fail event to EventAnalyticsController when failed to connect`() {
every { mockAudioClient.stopSession() } returns 0
Expand Down

0 comments on commit 427b402

Please sign in to comment.