@ -7,10 +7,13 @@ import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.media.AudioManager
import android.os.Build
import android.os.IBinder
import android.os.ResultReceiver
import android.telephony.PhoneStateListener
import android.telephony.PhoneStateListener.LISTEN_NONE
import android.telephony.TelephonyManager
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
@ -28,30 +31,13 @@ import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_IN
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_INCOMING_PRE_OFFER
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_INCOMING_RINGING
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_OUTGOING_RINGING
import org.thoughtcrime.securesms.webrtc.AudioManagerCommand
import org.thoughtcrime.securesms.webrtc.CallManager
import org.thoughtcrime.securesms.webrtc.CallViewModel
import org.thoughtcrime.securesms.webrtc.HangUpRtcOnPstnCallAnsweredListener
import org.thoughtcrime.securesms.webrtc.IncomingPstnCallReceiver
import org.thoughtcrime.securesms.webrtc.NetworkChangeReceiver
import org.thoughtcrime.securesms.webrtc.PeerConnectionException
import org.thoughtcrime.securesms.webrtc.PowerButtonReceiver
import org.thoughtcrime.securesms.webrtc.ProximityLockRelease
import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager
import org.thoughtcrime.securesms.webrtc.WiredHeadsetStateReceiver
import org.thoughtcrime.securesms.webrtc.*
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
import org.thoughtcrime.securesms.webrtc.data.Event
import org.thoughtcrime.securesms.webrtc.locks.LockManager
import org.webrtc.DataChannel
import org.webrtc.IceCandidate
import org.webrtc.MediaStream
import org.webrtc.PeerConnection
import org.webrtc.PeerConnection.IceConnectionState.CONNECTED
import org.webrtc.PeerConnection.IceConnectionState.DISCONNECTED
import org.webrtc.PeerConnection.IceConnectionState.FAILED
import org.webrtc.RtpReceiver
import org.webrtc.SessionDescription
import java.util.UUID
import org.webrtc.*
import org.webrtc.PeerConnection.IceConnectionState.*
import java.util.*
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
@ -60,7 +46,7 @@ import javax.inject.Inject
import org.thoughtcrime.securesms.webrtc.data.State as CallState
@AndroidEntryPoint
class WebRtcCallService : Service ( ) , CallManager . WebRtcListener {
class WebRtcCallService : Service ( ) , CallManager . WebRtcListener {
companion object {
@ -108,62 +94,82 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private const val RECONNECT _SECONDS = 5L
private const val MAX _RECONNECTS = 5
fun cameraEnabled ( context : Context , enabled : Boolean ) = Intent ( context , WebRtcCallService :: class . java )
fun cameraEnabled ( context : Context , enabled : Boolean ) =
Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _SET _MUTE _VIDEO )
. putExtra ( EXTRA _MUTE , ! enabled )
fun flipCamera ( context : Context ) = Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _FLIP _CAMERA )
. setAction ( ACTION _FLIP _CAMERA )
fun acceptCallIntent ( context : Context ) = Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _ANSWER _CALL )
. setAction ( ACTION _ANSWER _CALL )
fun microphoneIntent ( context : Context , enabled : Boolean ) = Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _SET _MUTE _AUDIO )
. putExtra ( EXTRA _MUTE , ! enabled )
fun microphoneIntent ( context : Context , enabled : Boolean ) =
Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _SET _MUTE _AUDIO )
. putExtra ( EXTRA _MUTE , ! enabled )
fun createCall ( context : Context , recipient : Recipient ) = Intent ( context , WebRtcCallService :: class . java )
fun createCall ( context : Context , recipient : Recipient ) =
Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _OUTGOING _CALL )
. putExtra ( EXTRA _RECIPIENT _ADDRESS , recipient . address )
fun incomingCall ( context : Context , address : Address , sdp : String , callId : UUID , callTime : Long ) =
Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _INCOMING _RING )
. putExtra ( EXTRA _RECIPIENT _ADDRESS , address )
. putExtra ( EXTRA _CALL _ID , callId )
. putExtra ( EXTRA _REMOTE _DESCRIPTION , sdp )
. putExtra ( EXTRA _TIMESTAMP , callTime )
fun incomingCall (
context : Context ,
address : Address ,
sdp : String ,
callId : UUID ,
callTime : Long
) =
Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _INCOMING _RING )
. putExtra ( EXTRA _RECIPIENT _ADDRESS , address )
. putExtra ( EXTRA _CALL _ID , callId )
. putExtra ( EXTRA _REMOTE _DESCRIPTION , sdp )
. 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 )
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 preOffer ( context : Context , address : Address , callId : UUID , callTime : Long ) =
Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _PRE _OFFER )
. putExtra ( EXTRA _RECIPIENT _ADDRESS , address )
. putExtra ( EXTRA _CALL _ID , callId )
. putExtra ( EXTRA _TIMESTAMP , callTime )
fun iceCandidates ( context : Context , address : Address , 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 ( ) )
. putExtra ( EXTRA _RECIPIENT _ADDRESS , address )
fun denyCallIntent ( context : Context ) = Intent ( context , WebRtcCallService :: class . java ) . setAction ( ACTION _DENY _CALL )
fun remoteHangupIntent ( context : Context , callId : UUID ) = Intent ( context , WebRtcCallService :: class . java )
Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _PRE _OFFER )
. putExtra ( EXTRA _RECIPIENT _ADDRESS , address )
. putExtra ( EXTRA _CALL _ID , callId )
. putExtra ( EXTRA _TIMESTAMP , callTime )
fun iceCandidates (
context : Context ,
address : Address ,
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 ( ) )
. putExtra ( EXTRA _RECIPIENT _ADDRESS , address )
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 hangupIntent ( context : Context ) =
Intent ( context , WebRtcCallService :: class . java ) . setAction ( ACTION _LOCAL _HANGUP )
fun sendAudioManagerCommand ( context : Context , command : AudioManagerCommand ) {
val intent = Intent ( context , WebRtcCallService :: class . java )
@ -174,7 +180,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
fun broadcastWantsToAnswer ( context : Context , wantsToAnswer : Boolean ) {
val intent = Intent ( ACTION _WANTS _TO _ANSWER )
. putExtra ( EXTRA _WANTS _TO _ANSWER , wantsToAnswer )
. putExtra ( EXTRA _WANTS _TO _ANSWER , wantsToAnswer )
LocalBroadcastManager . getInstance ( context ) . sendBroadcast ( intent )
}
@ -182,13 +188,14 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
@JvmStatic
fun isCallActive ( context : Context , resultReceiver : ResultReceiver ) {
val intent = Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _IS _IN _CALL _QUERY )
. putExtra ( EXTRA _RESULT _RECEIVER , resultReceiver )
. setAction ( ACTION _IS _IN _CALL _QUERY )
. putExtra ( EXTRA _RESULT _RECEIVER , resultReceiver )
context . startService ( intent )
}
}
@Inject lateinit var callManager : CallManager
@Inject
lateinit var callManager : CallManager
private var wantsToAnswer = false
private var currentTimeouts = 0
@ -199,8 +206,17 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private val lockManager by lazy { LockManager ( this ) }
private val serviceExecutor = Executors . newSingleThreadExecutor ( )
private val timeoutExecutor = Executors . newScheduledThreadPool ( 1 )
private val hangupOnCallAnswered = HangUpRtcOnPstnCallAnsweredListener {
ContextCompat . startForegroundService ( this , hangupIntent ( this ) )
private val hangupOnCallAnswered by lazy {
HangUpRtcOnPstnCallAnsweredListener {
ContextCompat . startForegroundService ( this , hangupIntent ( this ) )
}
}
private val hangupTelephonyCallback by lazy {
HangUpRtcTelephonyCallback {
ContextCompat . startForegroundService ( this , hangupIntent ( this ) )
}
}
private var networkChangedReceiver : NetworkChangeReceiver ? = null
@ -258,7 +274,9 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
val action = intent . action
Log . i ( " Loki " , " Handling ${intent.action} " )
when {
action == ACTION _INCOMING _RING && isSameCall ( intent ) && callManager . currentConnectionState == CallState . Reconnecting -> handleNewOffer ( intent )
action == ACTION _INCOMING _RING && isSameCall ( intent ) && callManager . currentConnectionState == CallState . Reconnecting -> handleNewOffer (
intent
)
action == ACTION _PRE _OFFER && isIdle ( ) -> handlePreOffer ( intent )
action == ACTION _INCOMING _RING && isBusy ( intent ) -> handleBusyCall ( intent )
action == ACTION _INCOMING _RING && isPreOffer ( ) -> handleIncomingRing ( intent )
@ -272,7 +290,9 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
action == ACTION _FLIP _CAMERA -> handleSetCameraFlip ( intent )
action == ACTION _WIRED _HEADSET _CHANGE -> handleWiredHeadsetChanged ( intent )
action == ACTION _SCREEN _OFF -> handleScreenOffChange ( intent )
action == ACTION _RESPONSE _MESSAGE && isSameCall ( intent ) && callManager . currentConnectionState == CallState . Reconnecting -> handleResponseMessage ( intent )
action == ACTION _RESPONSE _MESSAGE && isSameCall ( intent ) && callManager . currentConnectionState == CallState . Reconnecting -> handleResponseMessage (
intent
)
action == ACTION _RESPONSE _MESSAGE -> handleResponseMessage ( intent )
action == ACTION _ICE _MESSAGE -> handleRemoteIceCandidate ( intent )
action == ACTION _ICE _CONNECTED -> handleIceConnected ( intent )
@ -293,8 +313,15 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
registerIncomingPstnCallReceiver ( )
registerWiredHeadsetStateReceiver ( )
registerWantsToAnswerReceiver ( )
getSystemService ( TelephonyManager :: class . java )
. listen ( hangupOnCallAnswered , PhoneStateListener . LISTEN _CALL _STATE )
if ( checkSelfPermission ( android . Manifest . permission . READ _PHONE _STATE ) == PackageManager . PERMISSION _GRANTED ) {
if ( Build . VERSION . SDK _INT < Build . VERSION_CODES . S ) {
getSystemService ( TelephonyManager :: class . java )
. listen ( hangupOnCallAnswered , PhoneStateListener . LISTEN _CALL _STATE )
} else {
getSystemService ( TelephonyManager :: class . java )
. registerTelephonyCallback ( serviceExecutor , hangupTelephonyCallback )
}
}
registerUncaughtExceptionHandler ( )
networkChangedReceiver = NetworkChangeReceiver ( :: networkChange )
networkChangedReceiver !! . register ( this )
@ -318,7 +345,8 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
}
wantsToAnswerReceiver = receiver
LocalBroadcastManager . getInstance ( this ) . registerReceiver ( receiver , IntentFilter ( ACTION _WANTS _TO _ANSWER ) )
LocalBroadcastManager . getInstance ( this )
. registerReceiver ( receiver , IntentFilter ( ACTION _WANTS _TO _ANSWER ) )
}
private fun registerWiredHeadsetStateReceiver ( ) {
@ -339,7 +367,11 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private fun handleUpdateAudio ( intent : Intent ) {
val audioCommand = intent . getParcelableExtra < AudioManagerCommand > ( EXTRA _AUDIO _COMMAND ) !!
if ( callManager . currentConnectionState !in arrayOf ( CallState . Connected , * CallState . PENDING _CONNECTION _STATES ) ) {
if ( callManager . currentConnectionState !in arrayOf (
CallState . Connected ,
* CallState . PENDING _CONNECTION _STATES
)
) {
Log . w ( TAG , " handling audio command not in call " )
return
}
@ -419,8 +451,15 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
callManager . initializeAudioForCall ( )
callManager . startOutgoingRinger ( OutgoingRinger . Type . RINGING )
setCallInProgressNotification ( TYPE _OUTGOING _RINGING , callManager . recipient )
callManager . insertCallMessage ( recipient . address . serialize ( ) , CallMessageType . CALL _OUTGOING )
scheduledTimeout = timeoutExecutor . schedule ( TimeoutRunnable ( callId , this ) , TIMEOUT _SECONDS , TimeUnit . SECONDS )
callManager . insertCallMessage (
recipient . address . serialize ( ) ,
CallMessageType . CALL _OUTGOING
)
scheduledTimeout = timeoutExecutor . schedule (
TimeoutRunnable ( callId , this ) ,
TIMEOUT _SECONDS ,
TimeUnit . SECONDS
)
callManager . setAudioEnabled ( true )
val expectedState = callManager . currentConnectionState
@ -429,15 +468,21 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
try {
val offerFuture = callManager . onOutgoingCall ( this )
offerFuture . fail { e ->
if ( isConsistentState ( expectedState , expectedCallId , callManager . currentConnectionState , callManager . callId ) ) {
Log . e ( TAG , e )
if ( isConsistentState (
expectedState ,
expectedCallId ,
callManager . currentConnectionState ,
callManager . callId
)
) {
Log . e ( TAG , e )
callManager . postViewModelState ( CallViewModel . State . NETWORK _FAILURE )
callManager . postConnectionError ( )
terminate ( )
}
}
} catch ( e : Exception ) {
Log . e ( TAG , e )
Log . e ( TAG , e )
callManager . postConnectionError ( )
terminate ( )
}
@ -476,7 +521,11 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
callManager . silenceIncomingRinger ( )
callManager . postViewModelState ( CallViewModel . State . CALL _INCOMING )
scheduledTimeout = timeoutExecutor . schedule ( TimeoutRunnable ( callId , this ) , TIMEOUT _SECONDS , TimeUnit . SECONDS )
scheduledTimeout = timeoutExecutor . schedule (
TimeoutRunnable ( callId , this ) ,
TIMEOUT _SECONDS ,
TimeUnit . SECONDS
)
callManager . initializeAudioForCall ( )
callManager . initializeVideo ( this )
@ -487,7 +536,13 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
try {
val answerFuture = callManager . onIncomingCall ( this )
answerFuture . fail { e ->
if ( isConsistentState ( expectedState , expectedCallId , callManager . currentConnectionState , callManager . callId ) ) {
if ( isConsistentState (
expectedState ,
expectedCallId ,
callManager . currentConnectionState ,
callManager . callId
)
) {
Log . e ( TAG , e )
insertMissedCall ( recipient , true )
callManager . postConnectionError ( )
@ -497,7 +552,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
lockManager . updatePhoneState ( LockManager . PhoneState . PROCESSING )
callManager . setAudioEnabled ( true )
} catch ( e : Exception ) {
Log . e ( TAG , e )
Log . e ( TAG , e )
callManager . postConnectionError ( )
terminate ( )
}
@ -518,6 +573,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private fun handleRemoteHangup ( intent : Intent ) {
if ( callManager . callId != getCallId ( intent ) ) {
Log . e ( TAG , " Hangup for non-active call... " )
stopForeground ( true )
return
}
@ -555,7 +611,11 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
val callId = getCallId ( intent )
val description = intent . getStringExtra ( EXTRA _REMOTE _DESCRIPTION )
callManager . handleResponseMessage ( recipient , callId , SessionDescription ( SessionDescription . Type . ANSWER , description ) )
callManager . handleResponseMessage (
recipient ,
callId ,
SessionDescription ( SessionDescription . Type . ANSWER , description )
)
} catch ( e : PeerConnectionException ) {
terminate ( )
}
@ -567,14 +627,14 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
val sdpLineIndexes = intent . getIntArrayExtra ( EXTRA _ICE _SDP _LINE _INDEX ) ?: return
val sdps = intent . getStringArrayExtra ( EXTRA _ICE _SDP ) ?: return
if ( sdpMids . size != sdpLineIndexes . size || sdpLineIndexes . size != sdps . size ) {
Log . w ( TAG , " sdp info not of equal length " )
Log . w ( TAG , " sdp info not of equal length " )
return
}
val iceCandidates = sdpMids . indices . map { index ->
IceCandidate (
sdpMids [ index ] ,
sdpLineIndexes [ index ] ,
sdps [ index ]
sdpMids [ index ] ,
sdpLineIndexes [ index ] ,
sdps [ index ]
)
}
callManager . handleRemoteIceCandidate ( iceCandidates , callId )
@ -597,7 +657,11 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private fun handleIsInCallQuery ( intent : Intent ) {
val listener = intent . getParcelableExtra < ResultReceiver > ( EXTRA _RESULT _RECEIVER ) ?: return
val currentState = callManager . currentConnectionState
val isInCall = if ( currentState in arrayOf ( * CallState . PENDING _CONNECTION _STATES , CallState . Connected ) ) 1 else 0
val isInCall = if ( currentState in arrayOf (
* CallState . PENDING _CONNECTION _STATES ,
CallState . Connected
)
) 1 else 0
listener . send ( isInCall , bundleOf ( ) )
}
@ -616,10 +680,21 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
if ( callId == getCallId ( intent ) && isNetworkAvailable && numTimeouts <= MAX _RECONNECTS ) {
Log . i ( " Loki " , " Trying to re-connect " )
callManager . networkReestablished ( )
scheduledTimeout = timeoutExecutor . schedule ( TimeoutRunnable ( callId , this ) , TIMEOUT _SECONDS , TimeUnit . SECONDS )
scheduledTimeout = timeoutExecutor . schedule (
TimeoutRunnable ( callId , this ) ,
TIMEOUT _SECONDS ,
TimeUnit . SECONDS
)
} else if ( numTimeouts < MAX _RECONNECTS ) {
Log . i ( " Loki " , " Network isn't available, timeouts == $numTimeouts out of $MAX _RECONNECTS " )
scheduledReconnect = timeoutExecutor . schedule ( CheckReconnectedRunnable ( callId , this ) , RECONNECT _SECONDS , TimeUnit . SECONDS )
Log . i (
" Loki " ,
" Network isn't available, timeouts == $numTimeouts out of $MAX _RECONNECTS "
)
scheduledReconnect = timeoutExecutor . schedule (
CheckReconnectedRunnable ( callId , this ) ,
RECONNECT _SECONDS ,
TimeUnit . SECONDS
)
} else {
Log . i ( " Loki " , " Network isn't available, timing out " )
handleLocalHangup ( intent )
@ -627,12 +702,15 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
private fun handleCheckTimeout ( intent : Intent ) {
val callId = callManager . callId ?: return
val callState = callManager . currentConnectionState
if ( callId == getCallId ( intent ) && ( callState !in arrayOf ( CallState . Connected , CallState . Connecting ) ) ) {
if ( callId == getCallId ( intent ) && ( callState !in arrayOf (
CallState . Connected ,
CallState . Connecting
) )
) {
Log . w ( TAG , " Timing out call: $callId " )
handleLocalHangup ( intent )
}
@ -640,8 +718,8 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private fun setCallInProgressNotification ( type : Int , recipient : Recipient ? ) {
startForeground (
CallNotificationBuilder . WEBRTC _NOTIFICATION ,
CallNotificationBuilder . getCallInProgressNotification ( this , type , recipient )
CallNotificationBuilder . WEBRTC _NOTIFICATION ,
CallNotificationBuilder . getCallInProgressNotification ( this , type , recipient )
)
if ( ! CallNotificationBuilder . areNotificationsEnabled ( this ) && type == TYPE _INCOMING _PRE _OFFER ) {
// start an intent for the fullscreen
@ -661,14 +739,14 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
private fun getRemoteRecipient ( intent : Intent ) : Recipient {
val remoteAddress = intent . getParcelableExtra < Address > ( EXTRA _RECIPIENT _ADDRESS )
?: throw AssertionError ( " No recipient in intent! " )
?: throw AssertionError ( " No recipient in intent! " )
return Recipient . from ( this , remoteAddress , true )
}
private fun getCallId ( intent : Intent ) : UUID {
private fun getCallId ( intent : Intent ) : UUID {
return intent . getSerializableExtra ( EXTRA _CALL _ID ) as ? UUID
?: throw AssertionError ( " No callId in intent! " )
?: throw AssertionError ( " No callId in intent! " )
}
private fun insertMissedCall ( recipient : Recipient , signal : Boolean ) {
@ -680,10 +758,13 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
private fun isIncomingMessageExpired ( intent : Intent ) =
System . currentTimeMillis ( ) - intent . getLongExtra ( EXTRA _TIMESTAMP , - 1 ) > TimeUnit . SECONDS . toMillis ( TIMEOUT _SECONDS )
System . currentTimeMillis ( ) - intent . getLongExtra (
EXTRA _TIMESTAMP ,
- 1
) > TimeUnit . SECONDS . toMillis ( TIMEOUT _SECONDS )
override fun onDestroy ( ) {
Log . d ( TAG , " onDestroy() " )
Log . d ( TAG , " onDestroy() " )
callManager . unregisterListener ( this )
callReceiver ?. let { receiver ->
unregisterReceiver ( receiver )
@ -698,6 +779,16 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
wantsToAnswer = false
currentTimeouts = 0
isNetworkAvailable = false
if ( checkSelfPermission ( android . Manifest . permission . READ _PHONE _STATE ) == PackageManager . PERMISSION _GRANTED ) {
val telephonyManager = getSystemService ( TelephonyManager :: class . java )
with ( telephonyManager ) {
if ( Build . VERSION . SDK _INT < Build . VERSION_CODES . S ) {
this . listen ( hangupOnCallAnswered , LISTEN _NONE )
} else {
this . unregisterTelephonyCallback ( hangupTelephonyCallback )
}
}
}
super . onDestroy ( )
}
@ -709,7 +800,8 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
}
private class CheckReconnectedRunnable ( private val callId : UUID , private val context : Context ) : Runnable {
private class CheckReconnectedRunnable ( private val callId : UUID , private val context : Context ) :
Runnable {
override fun run ( ) {
val intent = Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _CHECK _RECONNECT )
@ -718,7 +810,8 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
}
private class ReconnectTimeoutRunnable ( private val callId : UUID , private val context : Context ) : Runnable {
private class ReconnectTimeoutRunnable ( private val callId : UUID , private val context : Context ) :
Runnable {
override fun run ( ) {
val intent = Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _CHECK _RECONNECT _TIMEOUT )
@ -727,26 +820,29 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
}
private class TimeoutRunnable ( private val callId : UUID , private val context : Context ) : Runnable {
private class TimeoutRunnable ( private val callId : UUID , private val context : Context ) :
Runnable {
override fun run ( ) {
val intent = Intent ( context , WebRtcCallService :: class . java )
. setAction ( ACTION _CHECK _TIMEOUT )
. putExtra ( EXTRA _CALL _ID , callId )
. setAction ( ACTION _CHECK _TIMEOUT )
. putExtra ( EXTRA _CALL _ID , callId )
context . startService ( intent )
}
}
private abstract class FailureListener < V > (
expectedState : CallState ,
expectedCallId : UUID ? ,
getState : ( ) -> Pair < CallState , UUID ? > ) : StateAwareListener < V > ( expectedState , expectedCallId , getState ) {
expectedState : CallState ,
expectedCallId : UUID ? ,
getState : ( ) -> Pair < CallState , UUID ? >
) : StateAwareListener < V > ( expectedState , expectedCallId , getState ) {
override fun onSuccessContinue ( result : V ) { }
}
private abstract class SuccessOnlyListener < V > (
expectedState : CallState ,
expectedCallId : UUID ? ,
getState : ( ) -> Pair < CallState , UUID > ) : StateAwareListener < V > ( expectedState , expectedCallId , getState ) {
expectedState : CallState ,
expectedCallId : UUID ? ,
getState : ( ) -> Pair < CallState , UUID >
) : StateAwareListener < V > ( expectedState , expectedCallId , getState ) {
override fun onFailureContinue ( throwable : Throwable ? ) {
Log . e ( TAG , throwable )
throw AssertionError ( throwable )
@ -754,9 +850,10 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
private abstract class StateAwareListener < V > (
private val expectedState : CallState ,
private val expectedCallId : UUID ? ,
private val getState : ( ) -> Pair < CallState , UUID ? > ) : FutureTaskListener < V > {
private val expectedState : CallState ,
private val expectedCallId : UUID ? ,
private val getState : ( ) -> Pair < CallState , UUID ? >
) : FutureTaskListener < V > {
companion object {
private val TAG = Log . tag ( StateAwareListener :: class . java )
@ -764,7 +861,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
override fun onSuccess ( result : V ) {
if ( !is ConsistentState ( ) ) {
Log . w ( TAG , " State has changed since request, aborting success callback... " )
Log . w ( TAG , " State has changed since request, aborting success callback... " )
} else {
onSuccessContinue ( result )
}
@ -773,7 +870,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
override fun onFailure ( exception : ExecutionException ? ) {
if ( !is ConsistentState ( ) ) {
Log . w ( TAG , exception )
Log . w ( TAG , " State has changed since request, aborting failure callback... " )
Log . w ( TAG , " State has changed since request, aborting failure callback... " )
} else {
exception ?. let {
onFailureContinue ( it . cause )
@ -792,10 +889,10 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
}
private fun isConsistentState (
expectedState : CallState ,
expectedCallId : UUID ? ,
currentState : CallState ,
currentCallId : UUID ?
expectedState : CallState ,
expectedCallId : UUID ? ,
currentState : CallState ,
currentCallId : UUID ?
) : Boolean {
return expectedState == currentState && expectedCallId == currentCallId
}
@ -817,17 +914,29 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
val intent = Intent ( this , WebRtcCallService :: class . java )
. setAction ( ACTION _ICE _CONNECTED )
startService ( intent )
} else if ( newState in arrayOf ( FAILED , DISCONNECTED ) && ( scheduledReconnect == null && scheduledTimeout == null ) ) {
} else if ( newState in arrayOf (
FAILED ,
DISCONNECTED
) && ( scheduledReconnect == null && scheduledTimeout == null )
) {
callManager . callId ?. let { callId ->
callManager . postConnectionEvent ( Event . IceDisconnect ) {
callManager . postViewModelState ( CallViewModel . State . CALL _RECONNECTING )
if ( callManager . isInitiator ( ) ) {
Log . i ( " Loki " , " Starting reconnect timer " )
scheduledReconnect = timeoutExecutor . schedule ( CheckReconnectedRunnable ( callId , this ) , RECONNECT _SECONDS , TimeUnit . SECONDS )
scheduledReconnect = timeoutExecutor . schedule (
CheckReconnectedRunnable ( callId , this ) ,
RECONNECT _SECONDS ,
TimeUnit . SECONDS
)
} else {
Log . i ( " Loki " , " Starting timeout, awaiting new reconnect " )
callManager . postConnectionEvent ( Event . PrepareForNewOffer ) {
scheduledTimeout = timeoutExecutor . schedule ( TimeoutRunnable ( callId , this ) , TIMEOUT _SECONDS , TimeUnit . SECONDS )
scheduledTimeout = timeoutExecutor . schedule (
TimeoutRunnable ( callId , this ) ,
TIMEOUT _SECONDS ,
TimeUnit . SECONDS
)
}
}
}
@ -855,7 +964,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener {
override fun onDataChannel ( p0 : DataChannel ? ) { }
override fun onRenegotiationNeeded ( ) {
Log . w ( TAG , " onRenegotiationNeeded was called! " )
Log . w ( TAG , " onRenegotiationNeeded was called! " )
}
override fun onAddTrack ( p0 : RtpReceiver ? , p1 : Array < out MediaStream > ? ) { }