From 97de2868962dd99552da6d113942e2bf0d4d9913 Mon Sep 17 00:00:00 2001 From: Harris Date: Fri, 22 Oct 2021 08:52:57 +1100 Subject: [PATCH] feat: update to utilise call ID --- app/build.gradle | 4 +- .../securesms/calls/WebRtcTestsActivity.kt | 42 ++++++++++----- .../v2/menus/ConversationMenuHelper.kt | 2 + .../securesms/home/HomeActivity.kt | 51 +++++++++++++++++++ .../securesms/webrtc/CallBottomSheet.kt | 9 +++- .../messaging/messages/control/CallMessage.kt | 15 ++++-- .../messaging/utilities/WebRtcUtils.kt | 4 +- 7 files changed, 103 insertions(+), 24 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 547b97ddd8..a70a81e479 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -154,8 +154,8 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.4' } -def canonicalVersionCode = 229 -def canonicalVersionName = "1.11.11" +def canonicalVersionCode = 230 +def canonicalVersionName = "1.12.0" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcTestsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcTestsActivity.kt index 5e1920b5cc..0b4cf83268 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcTestsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcTestsActivity.kt @@ -23,6 +23,7 @@ import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.permissions.Permissions import org.webrtc.* +import java.util.* class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection.Observer, @@ -40,6 +41,7 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection const val EXTRA_SDP = "WebRtcTestsActivity_EXTRA_SDP" const val EXTRA_ADDRESS = "WebRtcTestsActivity_EXTRA_ADDRESS" + const val EXTRA_CALL_ID = "WebRtcTestsActivity_EXTRA_CALL_ID" const val EXTRA_RELAY_USED = "WebRtcTestsActivity_EXTRA_RELAY_USED" const val EXTRA_SDP_MLINE_INDEXES = "WebRtcTestsActivity_EXTRA_SDP_MLINE_INDEXES" const val EXTRA_SDP_MIDS = "WebRtcTestsActivity_EXTRA_SDP_MIDS" @@ -89,20 +91,19 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection } private lateinit var callAddress: Address + private lateinit var callId: UUID private var relayUsed: Boolean = true private val peerConnection by lazy { // TODO: in a lokinet world, ice servers shouldn't be needed as .loki addresses should suffice to p2p - val server = PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer() - val server1 = PeerConnection.IceServer.builder("stun:stun1.l.google.com:19302").createIceServer() - val server2 = PeerConnection.IceServer.builder("stun:stun2.l.google.com:19302").createIceServer() - val server3 = PeerConnection.IceServer.builder("stun:stun3.l.google.com:19302").createIceServer() - val server4 = PeerConnection.IceServer.builder("stun:stun4.l.google.com:19302").createIceServer() - val iceServers = mutableListOf(server,server1,server2,server3,server4) + val server = PeerConnection.IceServer.builder("turn:freyr.getsession.org:5349").setUsername("session").setPassword("session").createIceServer() + val iceServers = mutableListOf(server) if (relayUsed) { // add relay server } - val rtcConfig = PeerConnection.RTCConfiguration(iceServers) + val rtcConfig = PeerConnection.RTCConfiguration(iceServers).apply { + this.iceTransportsType = PeerConnection.IceTransportsType.RELAY + } rtcConfig.keyType = PeerConnection.KeyType.ECDSA connectionFactory.createPeerConnection(rtcConfig, this)!! } @@ -156,6 +157,7 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection } // create either call or answer + callId = intent.getStringExtra(EXTRA_CALL_ID).let(UUID::fromString) if (intent.action == ACTION_ANSWER) { callAddress = intent.getParcelableExtra(EXTRA_ADDRESS) ?: run { finish(); return } val offerSdp = intent.getStringArrayExtra(EXTRA_SDP)!![0] @@ -169,7 +171,7 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection lifecycleScope.launchWhenCreated { while (this.isActive) { val answer = synchronized(WebRtcUtils.callCache) { - WebRtcUtils.callCache[callAddress]?.firstOrNull { it.type == SignalServiceProtos.CallMessage.Type.ANSWER } + WebRtcUtils.callCache[callId]?.firstOrNull { it.type == SignalServiceProtos.CallMessage.Type.ANSWER } } if (answer != null) { peerConnection.setRemoteDescription( @@ -193,7 +195,7 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection delay(2_000L) peerConnection.getStats(this@WebRtcTestsActivity) synchronized(WebRtcUtils.callCache) { - val set = WebRtcUtils.callCache[callAddress] ?: mutableSetOf() + val set = WebRtcUtils.callCache[callId] ?: mutableSetOf() set.filter { it.hashCode() !in acceptedCallMessageHashes && it.type == SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES }.forEach { callMessage -> callMessage.iceCandidates().forEach { candidate -> @@ -228,9 +230,10 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection private fun endCall() { if (isFinishing) return + val uuid = callId MessageSender.sendNonDurably( - CallMessage.endCall(), + CallMessage.endCall(uuid), callAddress ) peerConnection.close() @@ -319,7 +322,8 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection CallMessage(SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES, candidates.map { it.sdp }, candidates.map { it.sdpMLineIndex }, - candidates.map { it.sdpMid } + candidates.map { it.sdpMid }, + callId ), callAddress ) @@ -362,18 +366,28 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection SessionDescription.Type.OFFER -> { peerConnection.setLocalDescription(this, sdp) MessageSender.sendNonDurably( - CallMessage(SignalServiceProtos.CallMessage.Type.OFFER, listOf(sdp.description), listOf(), listOf()), + CallMessage(SignalServiceProtos.CallMessage.Type.OFFER, + listOf(sdp.description), + listOf(), + listOf(), + callId + ), callAddress ) } SessionDescription.Type.ANSWER -> { peerConnection.setLocalDescription(this, sdp) MessageSender.sendNonDurably( - CallMessage(SignalServiceProtos.CallMessage.Type.ANSWER, listOf(sdp.description), listOf(), listOf()), + CallMessage(SignalServiceProtos.CallMessage.Type.ANSWER, + listOf(sdp.description), + listOf(), + listOf(), + callId + ), callAddress ) } - SessionDescription.Type.PRANSWER -> TODO("do the PR answer create success handling") // MessageSender.send() + null, SessionDescription.Type.PRANSWER -> TODO("do the PR answer create success handling") // MessageSender.send() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt index 920ca652c1..a4b20b3ab7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt @@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.groups.EditClosedGroupActivity.Companion.group import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.getColorWithID import java.io.IOException +import java.util.* object ConversationMenuHelper { @@ -187,6 +188,7 @@ object ConversationMenuHelper { .setMessage("Use relay?") .setPositiveButton("Use Relay") { d, w -> val intent = Intent(context, WebRtcTestsActivity::class.java) + intent.putExtra(WebRtcTestsActivity.EXTRA_CALL_ID, UUID.randomUUID().toString()) intent.putExtra(WebRtcTestsActivity.EXTRA_ADDRESS, thread.address) intent.putExtra(WebRtcTestsActivity.EXTRA_RELAY_USED, false) val activity = context as AppCompatActivity diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index 6cf4aeafbe..7d30987463 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -12,13 +12,16 @@ import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.view.View import android.widget.Toast +import androidx.core.os.bundleOf import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope import androidx.loader.app.LoaderManager import androidx.loader.content.Loader import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.seed_reminder_stub.* @@ -33,13 +36,16 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.sending_receiving.MessageSender +import org.session.libsession.messaging.utilities.WebRtcUtils import org.session.libsession.utilities.* import org.session.libsession.utilities.Util +import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.MuteDialog import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.calls.WebRtcTestsActivity import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils import org.thoughtcrime.securesms.crypto.IdentityKeyUtil @@ -58,6 +64,7 @@ import org.thoughtcrime.securesms.onboarding.SeedActivity import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate import org.thoughtcrime.securesms.preferences.SettingsActivity import org.thoughtcrime.securesms.util.* +import org.thoughtcrime.securesms.webrtc.CallBottomSheet import java.io.IOException import java.util.* import javax.inject.Inject @@ -141,6 +148,50 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis } this.broadcastReceiver = broadcastReceiver LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged")) + lifecycleScope.launchWhenCreated { + // web rtc channel handling + for (message in WebRtcUtils.SIGNAL_QUEUE) { + // TODO: check errors here in the + val sender = Address.fromSerialized(message.sender!!) + val callId = message.callId!! + synchronized(WebRtcUtils.callCache) { + val set = WebRtcUtils.callCache[callId] ?: mutableSetOf() + set += message + WebRtcUtils.callCache[callId] = set + } + when (message.type) { + SignalServiceProtos.CallMessage.Type.OFFER -> { + // show bottom sheet + if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { + CallBottomSheet().apply { + arguments = bundleOf( + CallBottomSheet.ARGUMENT_ADDRESS to sender, + CallBottomSheet.ARGUMENT_SDP to message.sdps.toTypedArray(), + CallBottomSheet.ARGUMENT_TYPE to message.type!!.number, + CallBottomSheet.ARGUMENT_CALL_ID to callId.toString() + ) + show(this@HomeActivity.supportFragmentManager,"call-sheet") + } + } + } + SignalServiceProtos.CallMessage.Type.END_CALL -> { + // dismiss the call sheet + supportFragmentManager.findFragmentByTag("call-sheet")?.let { callSheet -> + if (callSheet is BottomSheetDialogFragment) { + callSheet.dismiss() + } + } + // clear the callCache for this sender + synchronized(WebRtcUtils.callCache) { + WebRtcUtils.callCache[callId] = mutableSetOf() + } + sendBroadcast(Intent(WebRtcTestsActivity.ACTION_END)) + } + else -> { /* do nothing */ } + } + } + } + lifecycleScope.launchWhenStarted { launch(Dispatchers.IO) { // Double check that the long poller is up diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallBottomSheet.kt index 3bff96f107..b71099fd3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallBottomSheet.kt @@ -17,6 +17,7 @@ import org.session.libsession.utilities.Address import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.calls.WebRtcTestsActivity import org.thoughtcrime.securesms.mms.GlideApp +import java.util.* class CallBottomSheet: BottomSheetDialogFragment() { @@ -24,6 +25,7 @@ class CallBottomSheet: BottomSheetDialogFragment() { const val ARGUMENT_ADDRESS = "CallBottomSheet_ADDRESS" const val ARGUMENT_SDP = "CallBottomSheet_SDP" const val ARGUMENT_TYPE = "CallBottomSheet_TYPE" + const val ARGUMENT_CALL_ID = "CallBottomSheet_CALL_ID" } private lateinit var address: Address @@ -41,6 +43,8 @@ class CallBottomSheet: BottomSheetDialogFragment() { address = arguments?.getParcelable(ARGUMENT_ADDRESS) ?: return dismiss() val sdp = arguments?.getStringArray(ARGUMENT_SDP) ?: return dismiss() + val callId = arguments?.getString(ARGUMENT_CALL_ID) ?: return dismiss() + val callUUID = UUID.fromString(callId) val recipient = Recipient.from(requireContext(), address, false) profilePictureView.publicKey = address.serialize() profilePictureView.glide = GlideApp.with(this) @@ -53,7 +57,8 @@ class CallBottomSheet: BottomSheetDialogFragment() { val intent = Intent(requireContext(), WebRtcTestsActivity::class.java) val bundle = bundleOf( WebRtcTestsActivity.EXTRA_ADDRESS to address, - WebRtcTestsActivity.EXTRA_RELAY_USED to relaySwitch.isChecked + WebRtcTestsActivity.EXTRA_RELAY_USED to relaySwitch.isChecked, + WebRtcTestsActivity.EXTRA_CALL_ID to callId ) intent.action = WebRtcTestsActivity.ACTION_ANSWER bundle.putStringArray(WebRtcTestsActivity.EXTRA_SDP, sdp) @@ -64,7 +69,7 @@ class CallBottomSheet: BottomSheetDialogFragment() { } declineButton.setOnClickListener { - MessageSender.sendNonDurably(CallMessage.endCall(), address) + MessageSender.sendNonDurably(CallMessage.endCall(callUUID), address) dismiss() } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/CallMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/CallMessage.kt index 81354d8cf8..db9c16e49e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/CallMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/CallMessage.kt @@ -2,16 +2,18 @@ package org.session.libsession.messaging.messages.control import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.utilities.Log +import java.util.* class CallMessage(): ControlMessage() { var type: SignalServiceProtos.CallMessage.Type? = null var sdps: List = listOf() var sdpMLineIndexes: List = listOf() var sdpMids: List = listOf() + var callId: UUID? = null override val isSelfSendValid: Boolean = false - override val ttl: Long = 5 * 60 * 1000 + override val ttl: Long = 300000L // 30s override fun isValid(): Boolean = super.isValid() && type != null && (!sdps.isNullOrEmpty() || type == SignalServiceProtos.CallMessage.Type.END_CALL) @@ -19,17 +21,19 @@ class CallMessage(): ControlMessage() { constructor(type: SignalServiceProtos.CallMessage.Type, sdps: List, sdpMLineIndexes: List, - sdpMids: List) : this() { + sdpMids: List, + callId: UUID) : this() { this.type = type this.sdps = sdps this.sdpMLineIndexes = sdpMLineIndexes this.sdpMids = sdpMids + this.callId = callId } companion object { const val TAG = "CallMessage" - fun endCall() = CallMessage(SignalServiceProtos.CallMessage.Type.END_CALL, emptyList(), emptyList(), emptyList()) + fun endCall(callId: UUID) = CallMessage(SignalServiceProtos.CallMessage.Type.END_CALL, emptyList(), emptyList(), emptyList(), callId) fun fromProto(proto: SignalServiceProtos.Content): CallMessage? { val callMessageProto = if (proto.hasCallMessage()) proto.callMessage else return null @@ -37,7 +41,8 @@ class CallMessage(): ControlMessage() { val sdps = callMessageProto.sdpsList val sdpMLineIndexes = callMessageProto.sdpMLineIndexesList val sdpMids = callMessageProto.sdpMidsList - return CallMessage(type,sdps, sdpMLineIndexes, sdpMids) + val callId = UUID.fromString(callMessageProto.uuid) + return CallMessage(type,sdps, sdpMLineIndexes, sdpMids, callId) } } @@ -52,6 +57,7 @@ class CallMessage(): ControlMessage() { .addAllSdps(sdps) .addAllSdpMLineIndexes(sdpMLineIndexes) .addAllSdpMids(sdpMids) + .setUuid(callId!!.toString()) return SignalServiceProtos.Content.newBuilder() .setCallMessage( @@ -66,6 +72,7 @@ class CallMessage(): ControlMessage() { other as CallMessage + if (callId != other.callId) return false if (type != other.type) return false if (sdps != other.sdps) return false if (sdpMLineIndexes != other.sdpMLineIndexes) return false diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/WebRtcUtils.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/WebRtcUtils.kt index 2be483a90f..e828b2bcbd 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/WebRtcUtils.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/WebRtcUtils.kt @@ -2,12 +2,12 @@ package org.session.libsession.messaging.utilities import kotlinx.coroutines.channels.Channel import org.session.libsession.messaging.messages.control.CallMessage -import org.session.libsession.utilities.Address +import java.util.* object WebRtcUtils { // TODO: move this to a better place that is persistent val SIGNAL_QUEUE = Channel(Channel.UNLIMITED) - val callCache: MutableMap> = mutableMapOf() + val callCache: MutableMap> = mutableMapOf() } \ No newline at end of file