From 96b02d31540a10f343d3a19ea3e2d760566fc94b Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 15 Feb 2021 14:49:45 +0100 Subject: [PATCH] VoIP: PSTN support was done too early --- .../app/features/call/webrtc/PSTNProtocol.kt | 43 --------- .../call/webrtc/PSTNProtocolChecker.kt | 88 +++++++++++++++++++ .../features/call/webrtc/WebRtcCallManager.kt | 35 ++++---- .../home/room/detail/RoomDetailViewModel.kt | 1 + 4 files changed, 105 insertions(+), 62 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocol.kt create mode 100644 vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocolChecker.kt diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocol.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocol.kt deleted file mode 100644 index 3e6d2df690..0000000000 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocol.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.call.webrtc - -import kotlinx.coroutines.delay -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol - -private const val PSTN_VECTOR_KEY = "im.vector.protocol.pstn" -private const val PSTN_MATRIX_KEY = "m.protocol.pstn" - -suspend fun Session.getSupportedPSTN(maxTries: Int): String? { - val thirdPartyProtocols: Map = try { - thirdPartyService().getThirdPartyProtocols() - } catch (failure: Throwable) { - if (maxTries == 1) { - return null - } else { - // Wait for 10s before trying again - delay(10_000L) - return getSupportedPSTN(maxTries - 1) - } - } - return when { - thirdPartyProtocols.containsKey(PSTN_VECTOR_KEY) -> PSTN_VECTOR_KEY - thirdPartyProtocols.containsKey(PSTN_MATRIX_KEY) -> PSTN_MATRIX_KEY - else -> null - } -} diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocolChecker.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocolChecker.kt new file mode 100644 index 0000000000..6452bc3964 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocolChecker.kt @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.call.webrtc + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import timber.log.Timber +import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject +import javax.inject.Singleton + +private const val PSTN_VECTOR_KEY = "im.vector.protocol.pstn" +private const val PSTN_MATRIX_KEY = "m.protocol.pstn" + +@Singleton +class PSTNProtocolChecker @Inject constructor() { + + private var alreadyChecked = AtomicBoolean(false) + + private val pstnSupportListeners = emptyList().toMutableList() + fun addPstnSupportListener(listener: WebRtcCallManager.PSTNSupportListener) { + pstnSupportListeners.add(listener) + } + + fun removePstnSupportListener(listener: WebRtcCallManager.PSTNSupportListener) { + pstnSupportListeners.remove(listener) + } + + var supportedPSTNProtocol: String? = null + private set + + fun checkForPSTNSupportIfNeeded(currentSession: Session?) { + if (alreadyChecked.get()) return + GlobalScope.checkForPSTNSupport(currentSession) + } + + private fun CoroutineScope.checkForPSTNSupport(currentSession: Session?) = launch { + try { + supportedPSTNProtocol = currentSession?.getSupportedPSTN(3) + alreadyChecked.set(true) + if (supportedPSTNProtocol != null) { + pstnSupportListeners.forEach { + tryOrNull { it.onPSTNSupportUpdated() } + } + } + } catch (failure: Throwable) { + Timber.v("Fail to get supported PSTN, will check again next time.") + } + } +} + +suspend fun Session.getSupportedPSTN(maxTries: Int): String? { + val thirdPartyProtocols: Map = try { + thirdPartyService().getThirdPartyProtocols() + } catch (failure: Throwable) { + if (maxTries == 1) { + throw failure + } else { + // Wait for 10s before trying again + delay(10_000L) + return getSupportedPSTN(maxTries - 1) + } + } + return when { + thirdPartyProtocols.containsKey(PSTN_VECTOR_KEY) -> PSTN_VECTOR_KEY + thirdPartyProtocols.containsKey(PSTN_MATRIX_KEY) -> PSTN_MATRIX_KEY + else -> null + } +} diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index 95728e0a97..7f68a4bf5c 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -59,7 +59,8 @@ import javax.inject.Singleton @Singleton class WebRtcCallManager @Inject constructor( private val context: Context, - private val activeSessionDataSource: ActiveSessionDataSource + private val activeSessionDataSource: ActiveSessionDataSource, + private val pstnProtocolChecker: PSTNProtocolChecker ) : CallListener, LifecycleObserver { private val currentSession: Session? @@ -74,13 +75,19 @@ class WebRtcCallManager @Inject constructor( fun onPSTNSupportUpdated() } - private val pstnSupportListeners = emptyList().toMutableList() + val supportedPSTNProtocol: String? + get() = pstnProtocolChecker.supportedPSTNProtocol + + val supportsPSTNProtocol: Boolean + get() = supportedPSTNProtocol != null + + fun addPstnSupportListener(listener: PSTNSupportListener) { - pstnSupportListeners.add(listener) + pstnProtocolChecker.addPstnSupportListener(listener) } fun removePstnSupportListener(listener: PSTNSupportListener) { - pstnSupportListeners.remove(listener) + pstnProtocolChecker.removePstnSupportListener(listener) } private val currentCallsListeners = CopyOnWriteArrayList() @@ -104,30 +111,16 @@ class WebRtcCallManager @Inject constructor( private var peerConnectionFactory: PeerConnectionFactory? = null private val executor = Executors.newSingleThreadExecutor() private val dispatcher = executor.asCoroutineDispatcher() - var supportedPSTNProtocol: String? = null - private set - val supportsPSTNProtocol: Boolean - get() = supportedPSTNProtocol != null private val rootEglBase by lazy { EglUtils.rootEglBase } private var isInBackground: Boolean = true - init { - GlobalScope.launch { - supportedPSTNProtocol = currentSession?.getSupportedPSTN(3) - if (supportedPSTNProtocol != null) { - pstnSupportListeners.forEach { - tryOrNull { it.onPSTNSupportUpdated() } - } - } - } - } - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun entersForeground() { isInBackground = false + checkForPSTNSupportIfNeeded() } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) @@ -167,6 +160,10 @@ class WebRtcCallManager @Inject constructor( return callsByCallId.values.toList() } + fun checkForPSTNSupportIfNeeded() { + pstnProtocolChecker.checkForPSTNSupportIfNeeded(currentSession) + } + /** * @return a set of all advertised call during the lifetime of the app. */ diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index cb93db1d72..acc295b242 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -177,6 +177,7 @@ class RoomDetailViewModel @AssistedInject constructor( // Inform the SDK that the room is displayed session.onRoomDisplayed(initialState.roomId) callManager.addPstnSupportListener(this) + callManager.checkForPSTNSupportIfNeeded() chatEffectManager.delegate = this }