mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-24 22:17:25 +00:00
feat: some connection and service launching / ring lifecycle
This commit is contained in:
@@ -10,6 +10,7 @@ import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -17,6 +18,7 @@ import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||
import org.thoughtcrime.securesms.webrtc.CallViewModel
|
||||
import org.webrtc.IceCandidate
|
||||
import java.util.*
|
||||
@@ -72,7 +74,11 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
||||
// repeat on start or something
|
||||
}
|
||||
|
||||
|
||||
if (intent.action == ACTION_ANSWER) {
|
||||
// answer via ViewModel
|
||||
val answerIntent = WebRtcCallService.acceptCallIntent(this)
|
||||
startService(answerIntent)
|
||||
}
|
||||
|
||||
registerReceiver(object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
|
@@ -13,6 +13,8 @@ import android.telephony.PhoneStateListener
|
||||
import android.telephony.TelephonyManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.FutureTaskListener
|
||||
import org.session.libsession.utilities.Util
|
||||
@@ -46,7 +48,7 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
|
||||
private val TAG = Log.tag(WebRtcCallService::class.java)
|
||||
|
||||
const val ACTION_INCOMING_CALL = "CALL_INCOMING"
|
||||
const val ACTION_INCOMING_RING = "RING_INCOMING"
|
||||
const val ACTION_OUTGOING_CALL = "CALL_OUTGOING"
|
||||
const val ACTION_ANSWER_CALL = "ANSWER_CALL"
|
||||
const val ACTION_DENY_CALL = "DENY_CALL"
|
||||
@@ -90,16 +92,35 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
.setAction(ACTION_OUTGOING_CALL)
|
||||
.putExtra(EXTRA_RECIPIENT_ADDRESS, recipient.address)
|
||||
|
||||
fun incomingCall(context: Context, address: Address, sdp: String, callId: UUID) =
|
||||
fun incomingCall(context: Context, address: Address, sdp: String, callId: UUID, callTime: Long) =
|
||||
Intent(context, WebRtcCallService::class.java)
|
||||
.setAction(ACTION_INCOMING_CALL)
|
||||
.setAction(ACTION_INCOMING_RING)
|
||||
.putExtra(EXTRA_RECIPIENT_ADDRESS, address)
|
||||
.putExtra(EXTRA_CALL_ID, callId)
|
||||
.putExtra(EXTRA_REMOTE_DESCRIPTION, sdp)
|
||||
.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis())
|
||||
.putExtra(EXTRA_TIMESTAMP, callTime)
|
||||
|
||||
fun incomingAnswer(context: Context, address: Address, sdp: String, callId: UUID) =
|
||||
Intent(context, WebRtcCallService::class.java)
|
||||
.setAction(ACTION_RESPONSE_MESSAGE)
|
||||
.putExtra(EXTRA_RECIPIENT_ADDRESS, address)
|
||||
.putExtra(EXTRA_CALL_ID, callId)
|
||||
.putExtra(EXTRA_REMOTE_DESCRIPTION, sdp)
|
||||
|
||||
fun iceCandidates(context: Context, iceCandidates: List<IceCandidate>, callId: UUID) =
|
||||
Intent(context, WebRtcCallService::class.java)
|
||||
.setAction(ACTION_ICE_MESSAGE)
|
||||
.putExtra(EXTRA_CALL_ID, callId)
|
||||
.putExtra(EXTRA_ICE_SDP, iceCandidates.map(IceCandidate::sdp).toTypedArray())
|
||||
.putExtra(EXTRA_ICE_SDP_LINE_INDEX, iceCandidates.map(IceCandidate::sdpMLineIndex).toIntArray())
|
||||
.putExtra(EXTRA_ICE_SDP_MID, iceCandidates.map(IceCandidate::sdpMid).toTypedArray())
|
||||
|
||||
fun denyCallIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_DENY_CALL)
|
||||
|
||||
fun remoteHangupIntent(context: Context, callId: UUID) = Intent(context, WebRtcCallService::class.java)
|
||||
.setAction(ACTION_REMOTE_HANGUP)
|
||||
.putExtra(EXTRA_CALL_ID, callId)
|
||||
|
||||
fun hangupIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_LOCAL_HANGUP)
|
||||
|
||||
fun sendAudioManagerCommand(context: Context, command: AudioManagerCommand) {
|
||||
@@ -149,10 +170,11 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
if (intent == null || intent.action == null) return START_NOT_STICKY
|
||||
serviceExecutor.execute {
|
||||
val action = intent.action
|
||||
Log.d("Loki", "Handling ${intent.action}")
|
||||
when {
|
||||
action == ACTION_INCOMING_CALL && isBusy() -> handleBusyCall(intent)
|
||||
action == ACTION_INCOMING_RING && isBusy() -> handleBusyCall(intent)
|
||||
action == ACTION_REMOTE_BUSY -> handleBusyMessage(intent)
|
||||
action == ACTION_INCOMING_CALL -> handleIncomingCall(intent)
|
||||
action == ACTION_INCOMING_RING && isIdle() -> handleIncomingRing(intent)
|
||||
action == ACTION_OUTGOING_CALL && isIdle() -> handleOutgoingCall(intent)
|
||||
action == ACTION_ANSWER_CALL -> handleAnswerCall(intent)
|
||||
action == ACTION_DENY_CALL -> handleDenyCall(intent)
|
||||
@@ -204,7 +226,6 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
|
||||
private fun handleBusyCall(intent: Intent) {
|
||||
val recipient = getRemoteRecipient(intent)
|
||||
val callId = getCallId(intent)
|
||||
val callState = callManager.currentConnectionState
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -215,7 +236,6 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
STATE_ANSWERING -> setCallInProgressNotification(TYPE_INCOMING_CONNECTING, callManager.recipient)
|
||||
STATE_LOCAL_RINGING -> setCallInProgressNotification(TYPE_INCOMING_RINGING, callManager.recipient)
|
||||
STATE_CONNECTED -> setCallInProgressNotification(TYPE_ESTABLISHED, callManager.recipient)
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,48 +264,19 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
}, WebRtcCallActivity.BUSY_SIGNAL_DELAY_FINISH)
|
||||
}
|
||||
|
||||
private fun handleIncomingCall(intent: Intent) {
|
||||
if (callManager.currentConnectionState != STATE_IDLE) throw IllegalStateException("Incoming on non-idle")
|
||||
private fun handleIncomingRing(intent: Intent) {
|
||||
if (callManager.currentConnectionState != STATE_IDLE) throw IllegalStateException("Incoming ring on non-idle")
|
||||
|
||||
val offer = intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION) ?: return
|
||||
callManager.postConnectionEvent(STATE_ANSWERING)
|
||||
val callId = getCallId(intent)
|
||||
callManager.callId = callId
|
||||
callManager.clearPendingIceUpdates()
|
||||
val recipient = getRemoteRecipient(intent)
|
||||
callManager.recipient = recipient
|
||||
if (isIncomingMessageExpired(intent)) {
|
||||
insertMissedCall(recipient, true)
|
||||
terminate()
|
||||
return
|
||||
}
|
||||
|
||||
val offer = intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION) ?: return
|
||||
val timestamp = intent.getLongExtra(EXTRA_TIMESTAMP, -1)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
setCallInProgressNotification(TYPE_INCOMING_CONNECTING, recipient)
|
||||
}
|
||||
|
||||
timeoutExecutor.schedule(TimeoutRunnable(callId, this), 2, TimeUnit.MINUTES)
|
||||
|
||||
callManager.initializeVideo(this)
|
||||
|
||||
val expectedState = callManager.currentConnectionState
|
||||
val expectedCallId = callManager.callId
|
||||
|
||||
try {
|
||||
val answerFuture = callManager.onIncomingCall(offer, this) // add is always turn here
|
||||
answerFuture.fail { e ->
|
||||
if (isConsistentState(expectedState,expectedCallId, callManager.currentConnectionState, callManager.callId)) {
|
||||
Log.e(TAG, e)
|
||||
insertMissedCall(recipient, true)
|
||||
terminate()
|
||||
}
|
||||
}
|
||||
callManager.postViewModelState(CallViewModel.State.CALL_INCOMING)
|
||||
// lock manager update phone state processing here
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG,e)
|
||||
terminate()
|
||||
setCallInProgressNotification(TYPE_INCOMING_RINGING, recipient)
|
||||
}
|
||||
callManager.onIncomingRing(offer, callId, recipient, timestamp)
|
||||
callManager.clearPendingIceUpdates()
|
||||
callManager.postConnectionEvent(STATE_LOCAL_RINGING)
|
||||
}
|
||||
|
||||
private fun handleOutgoingCall(intent: Intent) {
|
||||
@@ -304,7 +295,7 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
callManager.startOutgoingRinger(OutgoingRinger.Type.RINGING)
|
||||
setCallInProgressNotification(TYPE_OUTGOING_RINGING, callManager.recipient)
|
||||
// TODO: DatabaseComponent.get(this).insertOutgoingCall(callManager.recipient!!.address)
|
||||
timeoutExecutor.schedule(TimeoutRunnable(callId, this), 2, TimeUnit.MINUTES)
|
||||
timeoutExecutor.schedule(TimeoutRunnable(callId, this), 5, TimeUnit.MINUTES)
|
||||
|
||||
val expectedState = callManager.currentConnectionState
|
||||
val expectedCallId = callManager.callId
|
||||
@@ -330,17 +321,50 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
return
|
||||
}
|
||||
|
||||
if (callManager.callNotSetup()) {
|
||||
throw AssertionError("assert")
|
||||
}
|
||||
|
||||
// DatabaseComponent.get(this).smsDatabase().insertReceivedCall(recipient)
|
||||
|
||||
val (callId, recipient) = callManager.handleAnswerCall()
|
||||
val pending = callManager.pendingOffer ?: return
|
||||
val callId = callManager.callId ?: return
|
||||
val recipient = callManager.recipient ?: return
|
||||
val timestamp = callManager.pendingOfferTime
|
||||
|
||||
intent.putExtra(EXTRA_CALL_ID, callId)
|
||||
intent.putExtra(EXTRA_RECIPIENT_ADDRESS, recipient.address)
|
||||
handleCallConnected(intent)
|
||||
intent.putExtra(EXTRA_REMOTE_DESCRIPTION, pending)
|
||||
intent.putExtra(EXTRA_TIMESTAMP, timestamp)
|
||||
|
||||
callManager.postConnectionEvent(STATE_ANSWERING)
|
||||
|
||||
if (isIncomingMessageExpired(intent)) {
|
||||
insertMissedCall(recipient, true)
|
||||
terminate()
|
||||
return
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
setCallInProgressNotification(TYPE_INCOMING_CONNECTING, recipient)
|
||||
}
|
||||
|
||||
timeoutExecutor.schedule(TimeoutRunnable(callId, this), 5, TimeUnit.MINUTES)
|
||||
|
||||
callManager.initializeVideo(this)
|
||||
|
||||
val expectedState = callManager.currentConnectionState
|
||||
val expectedCallId = callManager.callId
|
||||
|
||||
try {
|
||||
val answerFuture = callManager.onIncomingCall(this) // add is always turn here
|
||||
answerFuture.fail { e ->
|
||||
if (isConsistentState(expectedState,expectedCallId, callManager.currentConnectionState, callManager.callId)) {
|
||||
Log.e(TAG, e)
|
||||
insertMissedCall(recipient, true)
|
||||
terminate()
|
||||
}
|
||||
}
|
||||
callManager.postViewModelState(CallViewModel.State.CALL_INCOMING)
|
||||
lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG,e)
|
||||
terminate()
|
||||
}
|
||||
// DatabaseComponent.get(this).smsDatabase().insertReceivedCall(recipient)
|
||||
}
|
||||
|
||||
private fun handleDenyCall(intent: Intent) {
|
||||
@@ -367,16 +391,18 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
private fun handleRemoteHangup(intent: Intent) {
|
||||
if (callManager.callId != getCallId(intent)) {
|
||||
Log.e(TAG, "Hangup for non-active call...")
|
||||
terminate()
|
||||
return
|
||||
}
|
||||
|
||||
callManager.handleRemoteHangup()
|
||||
|
||||
if (callManager.currentConnectionState in arrayOf(STATE_ANSWERING, STATE_LOCAL_RINGING)) {
|
||||
if (callManager.currentConnectionState in arrayOf(STATE_REMOTE_RINGING, STATE_ANSWERING, STATE_LOCAL_RINGING)) {
|
||||
callManager.recipient?.let { recipient ->
|
||||
insertMissedCall(recipient, true)
|
||||
}
|
||||
}
|
||||
terminate()
|
||||
}
|
||||
|
||||
private fun handleSetMuteAudio(intent: Intent) {
|
||||
@@ -445,7 +471,10 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||
}
|
||||
|
||||
private fun handleIceConnected(intent: Intent) {
|
||||
|
||||
if (callManager.currentConnectionState == STATE_ANSWERING) {
|
||||
val recipient = callManager.recipient ?: return
|
||||
callManager.postConnectionEvent(STATE_CONNECTED)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCallConnected(intent: Intent) {
|
||||
|
@@ -9,9 +9,11 @@ import kotlinx.serialization.json.put
|
||||
import nl.komponents.kovenant.Promise
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
import org.session.libsession.utilities.Debouncer
|
||||
import org.session.libsession.utilities.Util
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat
|
||||
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
||||
@@ -86,12 +88,15 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
private var localCameraState: CameraState = CameraState.UNKNOWN
|
||||
private var bluetoothAvailable = false
|
||||
|
||||
val currentConnectionState = (_connectionEvents.value as StateEvent.CallStateUpdate).state
|
||||
val currentConnectionState
|
||||
get() = (_connectionEvents.value as StateEvent.CallStateUpdate).state
|
||||
|
||||
private val networkExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
private var eglBase: EglBase? = null
|
||||
|
||||
var pendingOffer: String? = null
|
||||
var pendingOfferTime: Long = -1
|
||||
var callId: UUID? = null
|
||||
var recipient: Recipient? = null
|
||||
|
||||
@@ -103,6 +108,8 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
private val pendingOutgoingIceUpdates = ArrayDeque<IceCandidate>()
|
||||
private val pendingIncomingIceUpdates = ArrayDeque<IceCandidate>()
|
||||
|
||||
private val outgoingIceDebouncer = Debouncer(2_000L)
|
||||
|
||||
private var localRenderer: SurfaceViewRenderer? = null
|
||||
private var remoteRenderer: SurfaceViewRenderer? = null
|
||||
private var peerConnectionFactory: PeerConnectionFactory? = null
|
||||
@@ -117,6 +124,9 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
}
|
||||
|
||||
fun startOutgoingRinger(ringerType: OutgoingRinger.Type) {
|
||||
if (ringerType == OutgoingRinger.Type.RINGING) {
|
||||
signalAudioManager.handleCommand(AudioManagerCommand.UpdateAudioDeviceState)
|
||||
}
|
||||
signalAudioManager.handleCommand(AudioManagerCommand.StartOutgoingRinger(ringerType))
|
||||
}
|
||||
|
||||
@@ -209,16 +219,49 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
peerConnectionObservers.forEach { listener -> listener.onIceGatheringChange(newState) }
|
||||
}
|
||||
|
||||
override fun onIceCandidate(iceCandidate: IceCandidate?) {
|
||||
override fun onIceCandidate(iceCandidate: IceCandidate) {
|
||||
peerConnectionObservers.forEach { listener -> listener.onIceCandidate(iceCandidate) }
|
||||
val expectedCallId = this.callId ?: return
|
||||
val expectedRecipient = this.recipient ?: return
|
||||
pendingOutgoingIceUpdates.add(iceCandidate)
|
||||
outgoingIceDebouncer.publish {
|
||||
val currentCallId = this.callId ?: return@publish
|
||||
val currentRecipient = this.recipient ?: return@publish
|
||||
if (currentCallId == expectedCallId && expectedRecipient == currentRecipient) {
|
||||
val currentPendings = mutableSetOf<IceCandidate>()
|
||||
while (pendingOutgoingIceUpdates.isNotEmpty()) {
|
||||
currentPendings.add(pendingOutgoingIceUpdates.pop())
|
||||
}
|
||||
val sdps = currentPendings.map { it.sdp }
|
||||
val sdpMLineIndexes = currentPendings.map { it.sdpMLineIndex }
|
||||
val sdpMids = currentPendings.map { it.sdpMid }
|
||||
|
||||
MessageSender.sendNonDurably(CallMessage(
|
||||
ICE_CANDIDATES,
|
||||
sdps = sdps,
|
||||
sdpMLineIndexes = sdpMLineIndexes,
|
||||
sdpMids = sdpMids,
|
||||
currentCallId
|
||||
), currentRecipient.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIceCandidatesRemoved(candidates: Array<out IceCandidate>?) {
|
||||
peerConnectionObservers.forEach { listener -> listener.onIceCandidatesRemoved(candidates) }
|
||||
}
|
||||
|
||||
override fun onAddStream(p0: MediaStream?) {
|
||||
peerConnectionObservers.forEach { listener -> listener.onAddStream(p0) }
|
||||
override fun onAddStream(stream: MediaStream) {
|
||||
peerConnectionObservers.forEach { listener -> listener.onAddStream(stream) }
|
||||
for (track in stream.audioTracks) {
|
||||
track.setEnabled(true)
|
||||
}
|
||||
|
||||
if (stream.videoTracks != null && stream.videoTracks.size == 1) {
|
||||
val videoTrack = stream.videoTracks.first()
|
||||
videoTrack.setEnabled(true)
|
||||
videoTrack.addSink(remoteRenderer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRemoveStream(p0: MediaStream?) {
|
||||
@@ -254,13 +297,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
signalAudioManager.handleCommand(AudioManagerCommand())
|
||||
}
|
||||
|
||||
private fun CallMessage.iceCandidates(): List<IceCandidate> {
|
||||
val candidateSize = sdpMids.size
|
||||
return (0 until candidateSize).map { i ->
|
||||
IceCandidate(sdpMids[i], sdpMLineIndexes[i], sdps[i])
|
||||
}
|
||||
}
|
||||
|
||||
private fun CallState.withState(vararg expected: CallState, transition: () -> Unit) {
|
||||
if (this in expected) transition()
|
||||
else Log.w(TAG,"Tried to transition state $this but expected $expected")
|
||||
@@ -293,9 +329,19 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
localCameraState = newCameraState
|
||||
}
|
||||
|
||||
fun onIncomingCall(offer: String, context: Context, isAlwaysTurn: Boolean = false): Promise<Unit, Exception> {
|
||||
fun onIncomingRing(offer: String, callId: UUID, recipient: Recipient, callTime: Long) {
|
||||
if (currentConnectionState != CallState.STATE_IDLE) return
|
||||
|
||||
this.callId = callId
|
||||
this.recipient = recipient
|
||||
this.pendingOffer = offer
|
||||
this.pendingOfferTime = callTime
|
||||
}
|
||||
|
||||
fun onIncomingCall(context: Context, isAlwaysTurn: Boolean = false): Promise<Unit, Exception> {
|
||||
val callId = callId ?: return Promise.ofFail(NullPointerException("callId is null"))
|
||||
val recipient = recipient ?: return Promise.ofFail(NullPointerException("recipient is null"))
|
||||
val offer = pendingOffer ?: return Promise.ofFail(NullPointerException("pendingOffer is null"))
|
||||
val factory = peerConnectionFactory ?: return Promise.ofFail(NullPointerException("peerConnectionFactory is null"))
|
||||
val local = localRenderer ?: return Promise.ofFail(NullPointerException("localRenderer is null"))
|
||||
val base = eglBase ?: return Promise.ofFail(NullPointerException("eglBase is null"))
|
||||
@@ -310,6 +356,8 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
)
|
||||
peerConnection = connection
|
||||
localCameraState = connection.getCameraState()
|
||||
val dataChannel = connection.createDataChannel(DATA_CHANNEL_NAME)
|
||||
dataChannel.registerObserver(this)
|
||||
connection.setRemoteDescription(SessionDescription(SessionDescription.Type.OFFER, offer))
|
||||
val answer = connection.createAnswer(MediaConstraints())
|
||||
connection.setLocalDescription(answer)
|
||||
@@ -323,7 +371,10 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
val candidate = pendingIncomingIceUpdates.pop() ?: break
|
||||
connection.addIceCandidate(candidate)
|
||||
}
|
||||
return answerMessage // TODO: maybe add success state update
|
||||
return answerMessage.success {
|
||||
pendingOffer = null
|
||||
pendingOfferTime = -1
|
||||
} // TODO: maybe add success state update
|
||||
}
|
||||
|
||||
fun onOutgoingCall(context: Context, isAlwaysTurn: Boolean = false): Promise<Unit, Exception> {
|
||||
@@ -346,6 +397,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
isAlwaysTurn
|
||||
)
|
||||
|
||||
peerConnection = connection
|
||||
localCameraState = connection.getCameraState()
|
||||
val dataChannel = connection.createDataChannel(DATA_CHANNEL_NAME)
|
||||
dataChannel.registerObserver(this)
|
||||
@@ -363,14 +415,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
fun callNotSetup(): Boolean =
|
||||
peerConnection == null || dataChannel == null || recipient == null || callId == null
|
||||
|
||||
fun handleAnswerCall(): Pair<UUID, Recipient> {
|
||||
peerConnection?.let { connection ->
|
||||
connection.setAudioEnabled(true)
|
||||
connection.setVideoEnabled(true)
|
||||
}
|
||||
return callId!! to recipient!!
|
||||
}
|
||||
|
||||
fun handleDenyCall() {
|
||||
val callId = callId ?: return
|
||||
val recipient = recipient ?: return
|
||||
@@ -476,6 +520,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||
fun handleRemoteIceCandidate(iceCandidates: List<IceCandidate>, callId: UUID) {
|
||||
if (callId != this.callId) {
|
||||
Log.w(TAG, "Got remote ice candidates for a call that isn't active")
|
||||
return
|
||||
}
|
||||
|
||||
peerConnection?.let { connection ->
|
||||
|
@@ -1,18 +1,18 @@
|
||||
package org.thoughtcrime.securesms.webrtc
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import org.session.libsession.messaging.utilities.WebRtcUtils
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.OFFER
|
||||
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.*
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||
import javax.inject.Inject
|
||||
import org.webrtc.IceCandidate
|
||||
|
||||
|
||||
class CallMessageProcessor(private val context: Context, lifecycle: Lifecycle) {
|
||||
@@ -21,25 +21,75 @@ class CallMessageProcessor(private val context: Context, lifecycle: Lifecycle) {
|
||||
lifecycle.coroutineScope.launch {
|
||||
while (isActive) {
|
||||
val nextMessage = WebRtcUtils.SIGNAL_QUEUE.receive()
|
||||
when {
|
||||
// TODO: handle messages as they come in
|
||||
nextMessage.type == OFFER -> incomingCall(nextMessage)
|
||||
Log.d("Loki", nextMessage.toString())
|
||||
when (nextMessage.type) {
|
||||
OFFER -> incomingCall(nextMessage)
|
||||
ANSWER -> incomingAnswer(nextMessage)
|
||||
END_CALL -> incomingHangup(nextMessage)
|
||||
ICE_CANDIDATES -> handleIceCandidates(nextMessage)
|
||||
PRE_OFFER -> incomingCall(nextMessage)
|
||||
PROVISIONAL_ANSWER -> {} // TODO: if necessary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun incomingHangup(callMessage: CallMessage) {
|
||||
val callId = callMessage.callId ?: return
|
||||
val hangupIntent = WebRtcCallService.remoteHangupIntent(context, callId)
|
||||
context.startService(hangupIntent)
|
||||
}
|
||||
|
||||
private fun incomingAnswer(callMessage: CallMessage) {
|
||||
val recipientAddress = callMessage.sender ?: return
|
||||
val callId = callMessage.callId ?: return
|
||||
val sdp = callMessage.sdps.firstOrNull() ?: return
|
||||
val answerIntent = WebRtcCallService.incomingAnswer(
|
||||
context = context,
|
||||
address = Address.fromSerialized(recipientAddress),
|
||||
sdp = sdp,
|
||||
callId = callId
|
||||
)
|
||||
context.startService(answerIntent)
|
||||
}
|
||||
|
||||
private fun handleIceCandidates(callMessage: CallMessage) {
|
||||
val callId = callMessage.callId ?: return
|
||||
|
||||
val iceCandidates = callMessage.iceCandidates()
|
||||
if (iceCandidates.isEmpty()) return
|
||||
|
||||
val iceIntent = WebRtcCallService.iceCandidates(
|
||||
context = context,
|
||||
iceCandidates = iceCandidates,
|
||||
callId = callId
|
||||
)
|
||||
context.startService(iceIntent)
|
||||
}
|
||||
|
||||
private fun incomingCall(callMessage: CallMessage) {
|
||||
val recipientAddress = callMessage.recipient ?: return
|
||||
val recipientAddress = callMessage.sender ?: return
|
||||
val callId = callMessage.callId ?: return
|
||||
val sdp = callMessage.sdps.firstOrNull() ?: return
|
||||
val incomingIntent = WebRtcCallService.incomingCall(
|
||||
context = context,
|
||||
address = Address.fromSerialized(recipientAddress),
|
||||
sdp = sdp,
|
||||
callId = callId
|
||||
callId = callId,
|
||||
callTime = callMessage.sentTimestamp ?: -1L
|
||||
)
|
||||
context.startService(incomingIntent)
|
||||
ContextCompat.startForegroundService(context, incomingIntent)
|
||||
|
||||
}
|
||||
|
||||
private fun CallMessage.iceCandidates(): List<IceCandidate> {
|
||||
if (sdpMids.size != sdpMLineIndexes.size || sdpMLineIndexes.size != sdps.size) {
|
||||
return listOf() // uneven sdp numbers
|
||||
}
|
||||
val candidateSize = sdpMids.size
|
||||
return (0 until candidateSize).map { i ->
|
||||
IceCandidate(sdpMids[i], sdpMLineIndexes[i], sdps[i])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,14 +1,8 @@
|
||||
package org.thoughtcrime.securesms.webrtc
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import org.webrtc.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
Reference in New Issue
Block a user