151 lines
6.8 KiB
Kotlin
151 lines
6.8 KiB
Kotlin
package org.thoughtcrime.securesms.webrtc
|
|
|
|
import android.app.NotificationManager
|
|
import android.content.Context
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.lifecycle.Lifecycle
|
|
import androidx.lifecycle.coroutineScope
|
|
import kotlinx.coroutines.Dispatchers.IO
|
|
import kotlinx.coroutines.isActive
|
|
import kotlinx.coroutines.launch
|
|
import org.session.libsession.database.StorageProtocol
|
|
import org.session.libsession.messaging.calls.CallMessageType
|
|
import org.session.libsession.messaging.messages.control.CallMessage
|
|
import org.session.libsession.messaging.utilities.WebRtcUtils
|
|
import org.session.libsession.utilities.Address
|
|
import org.session.libsession.utilities.TextSecurePreferences
|
|
import org.session.libsession.utilities.recipients.Recipient
|
|
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.ANSWER
|
|
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.END_CALL
|
|
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES
|
|
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.OFFER
|
|
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.PRE_OFFER
|
|
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.PROVISIONAL_ANSWER
|
|
import org.session.libsignal.utilities.Log
|
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder
|
|
import org.webrtc.IceCandidate
|
|
|
|
|
|
class CallMessageProcessor(private val context: Context, private val textSecurePreferences: TextSecurePreferences, lifecycle: Lifecycle, private val storage: StorageProtocol) {
|
|
|
|
init {
|
|
lifecycle.coroutineScope.launch(IO) {
|
|
while (isActive) {
|
|
val nextMessage = WebRtcUtils.SIGNAL_QUEUE.receive()
|
|
Log.d("Loki", nextMessage.type?.name ?: "CALL MESSAGE RECEIVED")
|
|
val sender = nextMessage.sender ?: continue
|
|
val approvedContact = Recipient.from(context, Address.fromSerialized(sender), false).isApproved
|
|
Log.i("Loki", "Contact is approved?: $approvedContact")
|
|
if (!approvedContact && storage.getUserPublicKey() != sender) continue
|
|
|
|
if (!textSecurePreferences.isCallNotificationsEnabled()) {
|
|
Log.d("Loki","Dropping call message if call notifications disabled")
|
|
if (nextMessage.type != PRE_OFFER) continue
|
|
val sentTimestamp = nextMessage.sentTimestamp ?: continue
|
|
if (textSecurePreferences.setShownCallNotification()) {
|
|
// first time call notification encountered
|
|
val notification = CallNotificationBuilder.getFirstCallNotification(context)
|
|
context.getSystemService(NotificationManager::class.java).notify(CallNotificationBuilder.WEBRTC_NOTIFICATION, notification)
|
|
insertMissedCall(sender, sentTimestamp, isFirstCall = true)
|
|
} else {
|
|
insertMissedCall(sender, sentTimestamp)
|
|
}
|
|
continue
|
|
}
|
|
when (nextMessage.type) {
|
|
OFFER -> incomingCall(nextMessage)
|
|
ANSWER -> incomingAnswer(nextMessage)
|
|
END_CALL -> incomingHangup(nextMessage)
|
|
ICE_CANDIDATES -> handleIceCandidates(nextMessage)
|
|
PRE_OFFER -> incomingPreOffer(nextMessage)
|
|
PROVISIONAL_ANSWER, null -> {} // TODO: if necessary
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun insertMissedCall(sender: String, sentTimestamp: Long, isFirstCall: Boolean = false) {
|
|
val currentUserPublicKey = storage.getUserPublicKey()
|
|
if (sender == currentUserPublicKey) return // don't insert a "missed" due to call notifications disabled if it's our own sender
|
|
if (isFirstCall) {
|
|
storage.insertCallMessage(sender, CallMessageType.CALL_FIRST_MISSED, sentTimestamp)
|
|
} else {
|
|
storage.insertCallMessage(sender, CallMessageType.CALL_MISSED, sentTimestamp)
|
|
}
|
|
}
|
|
|
|
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 sender = callMessage.sender ?: return
|
|
|
|
val iceCandidates = callMessage.iceCandidates()
|
|
if (iceCandidates.isEmpty()) return
|
|
|
|
val iceIntent = WebRtcCallService.iceCandidates(
|
|
context = context,
|
|
iceCandidates = iceCandidates,
|
|
callId = callId,
|
|
address = Address.fromSerialized(sender)
|
|
)
|
|
context.startService(iceIntent)
|
|
}
|
|
|
|
private fun incomingPreOffer(callMessage: CallMessage) {
|
|
// handle notification state
|
|
val recipientAddress = callMessage.sender ?: return
|
|
val callId = callMessage.callId ?: return
|
|
val incomingIntent = WebRtcCallService.preOffer(
|
|
context = context,
|
|
address = Address.fromSerialized(recipientAddress),
|
|
callId = callId,
|
|
callTime = callMessage.sentTimestamp!!
|
|
)
|
|
ContextCompat.startForegroundService(context, incomingIntent)
|
|
}
|
|
|
|
private fun incomingCall(callMessage: CallMessage) {
|
|
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,
|
|
callTime = callMessage.sentTimestamp!!
|
|
)
|
|
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])
|
|
}
|
|
}
|
|
|
|
} |