Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: terminate the SFT OneOnOneCall once the other person hangup the call (WPB-7153) #2923

2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ sqldelight = "2.0.1"
sqlcipher-android = "4.5.5"
pbandk = "0.14.2"
turbine = "1.0.0"
avs = "9.6.13"
avs = "9.6.15"
jna = "5.14.0"
core-crypto = "1.0.0-rc.56-hotfix.2"
core-crypto-multiplatform = "0.6.0-rc.3-multiplatform-pre1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,23 @@ package com.wire.kalium.logic.feature.call
import com.wire.kalium.calling.Calling
import com.wire.kalium.calling.types.Handle
import com.wire.kalium.logic.cache.SelfConversationIdProvider
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.VideoStateChecker
import com.wire.kalium.logic.data.call.mapper.CallMapperImpl
import com.wire.kalium.logic.data.conversation.ClientId
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.conversation.SubconversationRepository
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.CurrentClientIdProvider
import com.wire.kalium.logic.data.id.FederatedIdMapper
import com.wire.kalium.logic.data.id.QualifiedIdMapper
import com.wire.kalium.logic.data.message.Message
import com.wire.kalium.logic.data.message.MessageContent
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.data.id.CurrentClientIdProvider
import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater
import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider
import com.wire.kalium.logic.feature.message.MessageSender
import com.wire.kalium.logic.featureFlags.KaliumConfigs
import com.wire.kalium.logic.test_util.TestKaliumDispatcher
Expand All @@ -48,11 +52,6 @@ import kotlinx.coroutines.test.runTest
import kotlin.test.BeforeTest
import kotlin.test.Ignore
import kotlin.test.Test
import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater
import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider
import com.wire.kalium.network.NetworkStateObserver
import com.wire.kalium.util.DateTimeUtil.toIsoDateTimeString
import kotlinx.datetime.Instant

class CallManagerTest {

Expand All @@ -77,6 +76,12 @@ class CallManagerTest {
@Mock
private val conversationRepository = mock(classOf<ConversationRepository>())

@Mock
private val subconversationRepository = mock(classOf<SubconversationRepository>())

@Mock
private val userConfigRepository = mock(classOf<UserConfigRepository>())

@Mock
private val federatedIdMapper = mock(classOf<FederatedIdMapper>())

Expand Down Expand Up @@ -109,6 +114,8 @@ class CallManagerTest {
currentClientIdProvider = currentClientIdProvider,
selfConversationIdProvider = selfConversationIdProvider,
conversationRepository = conversationRepository,
subconversationRepository = subconversationRepository,
userConfigRepository = userConfigRepository,
messageSender = messageSender,
kaliumDispatchers = dispatcher,
federatedIdMapper = federatedIdMapper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
package com.wire.kalium.logic.feature.call

import com.wire.kalium.logic.cache.SelfConversationIdProvider
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.VideoStateChecker
import com.wire.kalium.logic.data.call.mapper.CallMapper
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.conversation.SubconversationRepository
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.data.id.FederatedIdMapper
import com.wire.kalium.logic.data.id.QualifiedID
Expand All @@ -43,6 +45,8 @@ actual class GlobalCallManager {
currentClientIdProvider: CurrentClientIdProvider,
selfConversationIdProvider: SelfConversationIdProvider,
conversationRepository: ConversationRepository,
subonversationRepository: SubconversationRepository,
userConfigRepository: UserConfigRepository,
messageSender: MessageSender,
callMapper: CallMapper,
federatedIdMapper: FederatedIdMapper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,22 @@ import com.wire.kalium.calling.types.Uint32_t
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.cache.SelfConversationIdProvider
import com.wire.kalium.logic.callingLogger
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.call.CallClient
import com.wire.kalium.logic.data.call.CallClientList
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.call.CallType
import com.wire.kalium.logic.data.call.EpochInfo
import com.wire.kalium.logic.data.call.MLSCallHelperImpl
import com.wire.kalium.logic.data.call.VideoState
import com.wire.kalium.logic.data.call.VideoStateChecker
import com.wire.kalium.logic.data.call.mapper.CallMapper
import com.wire.kalium.logic.data.call.mapper.ParticipantMapperImpl
import com.wire.kalium.logic.data.conversation.ClientId
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.conversation.SubconversationRepository
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.CurrentClientIdProvider
import com.wire.kalium.logic.data.id.FederatedIdMapper
Expand Down Expand Up @@ -105,6 +108,8 @@ class CallManagerImpl internal constructor(
private val videoStateChecker: VideoStateChecker,
private val conversationClientsInCallUpdater: ConversationClientsInCallUpdater,
private val getCallConversationType: GetCallConversationTypeProvider,
private val subconversationRepository: SubconversationRepository,
private val userConfigRepository: UserConfigRepository,
private val kaliumConfigs: KaliumConfigs,
private val json: Json = Json { ignoreUnknownKeys = true },
private val shouldRemoteMuteChecker: ShouldRemoteMuteChecker = ShouldRemoteMuteCheckerImpl(),
Expand Down Expand Up @@ -191,8 +196,16 @@ class CallManagerImpl internal constructor(
.keepingStrongReference(),
establishedCallHandler = OnEstablishedCall(callRepository, scope, qualifiedIdMapper)
.keepingStrongReference(),
closeCallHandler = OnCloseCall(callRepository, scope, qualifiedIdMapper)
.keepingStrongReference(),
closeCallHandler = OnCloseCall(
callRepository = callRepository,
mlsCallHelper = MLSCallHelperImpl(
callRepository = callRepository,
subconversationRepository = subconversationRepository,
userConfigRepository = userConfigRepository
),
scope = scope,
qualifiedIdMapper = qualifiedIdMapper
).keepingStrongReference(),
metricsHandler = metricsHandler,
callConfigRequestHandler = OnConfigRequest(calling, callRepository, scope)
.keepingStrongReference(),
Expand Down Expand Up @@ -464,6 +477,12 @@ class CallManagerImpl internal constructor(
qualifiedIdMapper = qualifiedIdMapper,
participantMapper = ParticipantMapperImpl(videoStateChecker, callMapper),
userRepository = userRepository,
mlsCallHelper = MLSCallHelperImpl(
callRepository = callRepository,
subconversationRepository = subconversationRepository,
userConfigRepository = userConfigRepository
),
endCall = { endCall(it) },
callingScope = scope
).keepingStrongReference()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ import com.wire.kalium.calling.ENVIRONMENT_DEFAULT
import com.wire.kalium.calling.callbacks.LogHandler
import com.wire.kalium.logic.cache.SelfConversationIdProvider
import com.wire.kalium.logic.callingLogger
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.VideoStateChecker
import com.wire.kalium.logic.data.call.mapper.CallMapper
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.conversation.SubconversationRepository
import com.wire.kalium.logic.data.id.FederatedIdMapper
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.id.QualifiedIdMapper
Expand Down Expand Up @@ -79,6 +81,8 @@ actual class GlobalCallManager(
currentClientIdProvider: CurrentClientIdProvider,
selfConversationIdProvider: SelfConversationIdProvider,
conversationRepository: ConversationRepository,
subconversationRepository: SubconversationRepository,
userConfigRepository: UserConfigRepository,
messageSender: MessageSender,
callMapper: CallMapper,
federatedIdMapper: FederatedIdMapper,
Expand All @@ -103,6 +107,8 @@ actual class GlobalCallManager(
videoStateChecker = videoStateChecker,
conversationClientsInCallUpdater = conversationClientsInCallUpdater,
getCallConversationType = getCallConversationType,
subconversationRepository = subconversationRepository,
userConfigRepository = userConfigRepository,
kaliumConfigs = kaliumConfigs
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@ import com.wire.kalium.calling.types.Uint32_t
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.callingLogger
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.call.MLSCallHelper
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.QualifiedIdMapper
import com.wire.kalium.logic.data.call.CallStatus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@Suppress("LongParameterList")
class OnCloseCall(
private val callRepository: CallRepository,
private val mlsCallHelper: MLSCallHelper,
private val scope: CoroutineScope,
private val qualifiedIdMapper: QualifiedIdMapper
) : CloseCallHandler {
Expand Down Expand Up @@ -67,15 +69,20 @@ class OnCloseCall(
status = callStatus
)

val conversationType =
callRepository.getCallMetadataProfile()[conversationIdWithDomain]?.conversationType

if (callRepository.getCallMetadataProfile()[conversationIdWithDomain]?.protocol is Conversation.ProtocolInfo.MLS) {
callRepository.leaveMlsConference(conversationIdWithDomain)
mlsCallHelper.handleCallTermination(conversationIdWithDomain, conversationType)
}

callingLogger.i("[OnCloseCall] -> ConversationId: ${conversationId.obfuscateId()} | callStatus: $callStatus")
}
}

private fun shouldPersistMissedCall(conversationId: ConversationId, callStatus: CallStatus): Boolean {
private fun shouldPersistMissedCall(
conversationId: ConversationId,
callStatus: CallStatus
): Boolean {
if (callStatus == CallStatus.MISSED)
return true
return callRepository.getCallMetadataProfile().data[conversationId]?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,29 @@ import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.callingLogger
import com.wire.kalium.logic.data.call.CallParticipants
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.MLSCallHelper
import com.wire.kalium.logic.data.call.Participant
import com.wire.kalium.logic.data.call.mapper.ParticipantMapper
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.QualifiedIdMapper
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.functional.onFailure
import com.wire.kalium.logic.functional.onSuccess
import com.wire.kalium.logic.kaliumLogger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json

// TODO: add tests for this class
@Suppress("LongParameterList")
class OnParticipantListChanged internal constructor(
private val callRepository: CallRepository,
private val qualifiedIdMapper: QualifiedIdMapper,
private val participantMapper: ParticipantMapper,
private val userRepository: UserRepository,
private val mlsCallHelper: MLSCallHelper,
private val endCall: suspend (conversationId: ConversationId) -> Unit,
private val callingScope: CoroutineScope
) : ParticipantChangedHandler {

Expand All @@ -65,6 +72,22 @@ class OnParticipantListChanged internal constructor(
participants.add(participant)
}
}
val callProtocol = callRepository.currentCallProtocol(conversationIdWithDomain)

val currentCall = callRepository.establishedCallsFlow().first().firstOrNull()
currentCall?.let {
val shouldEndSFTOneOnOneCall = mlsCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationIdWithDomain,
callProtocol = callProtocol,
conversationType = it.conversationType,
newCallParticipants = participants,
previousCallParticipants = it.participants
)
if (shouldEndSFTOneOnOneCall) {
kaliumLogger.i("[onParticipantChanged] - Ending MLS call due to participant leaving")
endCall(conversationIdWithDomain)
}
}

callRepository.updateCallParticipants(
conversationId = conversationIdWithDomain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ import com.wire.kalium.logic.data.conversation.ClientId
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.conversation.ConversationDetails
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.conversation.EpochChangesObserver
import com.wire.kalium.logic.data.conversation.JoinSubconversationUseCase
import com.wire.kalium.logic.data.conversation.LeaveSubconversationUseCase
import com.wire.kalium.logic.data.conversation.MLSConversationRepository
import com.wire.kalium.logic.data.conversation.SubconversationRepository
import com.wire.kalium.logic.data.id.ConversationId
Expand All @@ -50,9 +53,6 @@ import com.wire.kalium.logic.data.team.TeamRepository
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.di.MapperProvider
import com.wire.kalium.logic.data.conversation.JoinSubconversationUseCase
import com.wire.kalium.logic.data.conversation.LeaveSubconversationUseCase
import com.wire.kalium.logic.data.conversation.EpochChangesObserver
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.getOrNull
Expand Down Expand Up @@ -121,6 +121,7 @@ interface CallRepository {
fun updateParticipantsActiveSpeaker(conversationId: ConversationId, activeSpeakers: CallActiveSpeakers)
suspend fun getLastClosedCallCreatedByConversationId(conversationId: ConversationId): Flow<String?>
suspend fun updateOpenCallsToClosedStatus()
suspend fun leavePreviouslyJoinedMlsConferences()
suspend fun persistMissedCall(conversationId: ConversationId)
suspend fun joinMlsConference(
conversationId: ConversationId,
Expand All @@ -129,6 +130,7 @@ interface CallRepository {
suspend fun leaveMlsConference(conversationId: ConversationId)
suspend fun observeEpochInfo(conversationId: ConversationId): Either<CoreFailure, Flow<EpochInfo>>
suspend fun advanceEpoch(conversationId: ConversationId)
fun currentCallProtocol(conversationId: ConversationId): Conversation.ProtocolInfo?
}

@Suppress("LongParameterList", "TooManyFunctions")
Expand Down Expand Up @@ -426,7 +428,9 @@ internal class CallDataSource(
}
}

if (_callMetadataProfile.value[conversationId]?.protocol is Conversation.ProtocolInfo.MLS) {
if (_callMetadataProfile.value[conversationId]?.protocol is Conversation.ProtocolInfo.MLS &&
_callMetadataProfile.value[conversationId]?.conversationType == Conversation.Type.GROUP
) {
participants.forEach { participant ->
if (participant.hasEstablishedAudio) {
clearStaleParticipantTimeout(participant)
Expand Down Expand Up @@ -542,7 +546,7 @@ internal class CallDataSource(
}
}

private suspend fun leavePreviouslyJoinedMlsConferences() {
override suspend fun leavePreviouslyJoinedMlsConferences() {
callingLogger.i("Leaving previously joined MLS conferences")

callDAO.observeEstablishedCalls()
Expand Down Expand Up @@ -667,6 +671,9 @@ internal class CallDataSource(
} ?: callingLogger.w("[CallRepository] -> Requested new epoch but there's no conference subconversation")
}

override fun currentCallProtocol(conversationId: ConversationId): Conversation.ProtocolInfo? =
_callMetadataProfile.value.data[conversationId]?.protocol

companion object {
val STALE_PARTICIPANT_TIMEOUT = 190.toDuration(kotlin.time.DurationUnit.SECONDS)
}
Expand Down
Loading
Loading