VoIP: PSTN support was done too early

This commit is contained in:
ganfra 2021-02-15 14:49:45 +01:00
parent 109a9e816b
commit 96b02d3154
4 changed files with 105 additions and 62 deletions

View File

@ -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<String, ThirdPartyProtocol> = 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
}
}

View File

@ -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<WebRtcCallManager.PSTNSupportListener>().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<String, ThirdPartyProtocol> = 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
}
}

View File

@ -59,7 +59,8 @@ import javax.inject.Singleton
@Singleton @Singleton
class WebRtcCallManager @Inject constructor( class WebRtcCallManager @Inject constructor(
private val context: Context, private val context: Context,
private val activeSessionDataSource: ActiveSessionDataSource private val activeSessionDataSource: ActiveSessionDataSource,
private val pstnProtocolChecker: PSTNProtocolChecker
) : CallListener, LifecycleObserver { ) : CallListener, LifecycleObserver {
private val currentSession: Session? private val currentSession: Session?
@ -74,13 +75,19 @@ class WebRtcCallManager @Inject constructor(
fun onPSTNSupportUpdated() fun onPSTNSupportUpdated()
} }
private val pstnSupportListeners = emptyList<PSTNSupportListener>().toMutableList() val supportedPSTNProtocol: String?
get() = pstnProtocolChecker.supportedPSTNProtocol
val supportsPSTNProtocol: Boolean
get() = supportedPSTNProtocol != null
fun addPstnSupportListener(listener: PSTNSupportListener) { fun addPstnSupportListener(listener: PSTNSupportListener) {
pstnSupportListeners.add(listener) pstnProtocolChecker.addPstnSupportListener(listener)
} }
fun removePstnSupportListener(listener: PSTNSupportListener) { fun removePstnSupportListener(listener: PSTNSupportListener) {
pstnSupportListeners.remove(listener) pstnProtocolChecker.removePstnSupportListener(listener)
} }
private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>() private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>()
@ -104,30 +111,16 @@ class WebRtcCallManager @Inject constructor(
private var peerConnectionFactory: PeerConnectionFactory? = null private var peerConnectionFactory: PeerConnectionFactory? = null
private val executor = Executors.newSingleThreadExecutor() private val executor = Executors.newSingleThreadExecutor()
private val dispatcher = executor.asCoroutineDispatcher() 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 val rootEglBase by lazy { EglUtils.rootEglBase }
private var isInBackground: Boolean = true 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) @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun entersForeground() { fun entersForeground() {
isInBackground = false isInBackground = false
checkForPSTNSupportIfNeeded()
} }
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
@ -167,6 +160,10 @@ class WebRtcCallManager @Inject constructor(
return callsByCallId.values.toList() return callsByCallId.values.toList()
} }
fun checkForPSTNSupportIfNeeded() {
pstnProtocolChecker.checkForPSTNSupportIfNeeded(currentSession)
}
/** /**
* @return a set of all advertised call during the lifetime of the app. * @return a set of all advertised call during the lifetime of the app.
*/ */

View File

@ -177,6 +177,7 @@ class RoomDetailViewModel @AssistedInject constructor(
// Inform the SDK that the room is displayed // Inform the SDK that the room is displayed
session.onRoomDisplayed(initialState.roomId) session.onRoomDisplayed(initialState.roomId)
callManager.addPstnSupportListener(this) callManager.addPstnSupportListener(this)
callManager.checkForPSTNSupportIfNeeded()
chatEffectManager.delegate = this chatEffectManager.delegate = this
} }