mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-25 12:07:43 +00:00
feat: adding remainder of basic functionality to services and CallManager.kt
This commit is contained in:
@@ -141,10 +141,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
return (ApplicationContext) context.getApplicationContext();
|
return (ApplicationContext) context.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CallComponent getCallComponent() {
|
|
||||||
return EntryPoints.get(getApplicationContext(), CallComponent.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DatabaseComponent getDatabaseComponent() {
|
public DatabaseComponent getDatabaseComponent() {
|
||||||
return EntryPoints.get(getApplicationContext(), DatabaseComponent.class);
|
return EntryPoints.get(getApplicationContext(), DatabaseComponent.class);
|
||||||
}
|
}
|
||||||
|
@@ -43,10 +43,6 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
const val ACTION_ANSWER = "answer"
|
const val ACTION_ANSWER = "answer"
|
||||||
const val ACTION_END = "end-call"
|
const val ACTION_END = "end-call"
|
||||||
|
|
||||||
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 BUSY_SIGNAL_DELAY_FINISH = 5500L
|
const val BUSY_SIGNAL_DELAY_FINISH = 5500L
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +55,9 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
private lateinit var callAddress: Address
|
private lateinit var callAddress: Address
|
||||||
private lateinit var callId: UUID
|
private lateinit var callId: UUID
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
endCall()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
if (item.itemId == android.R.id.home) {
|
if (item.itemId == android.R.id.home) {
|
||||||
endCall()
|
finish()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
@@ -94,227 +86,19 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local_renderer.run {
|
|
||||||
setEnableHardwareScaler(true)
|
|
||||||
init(eglBase.eglBaseContext, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
remote_renderer.run {
|
|
||||||
setEnableHardwareScaler(true)
|
|
||||||
init(eglBase.eglBaseContext, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
end_call_button.setOnClickListener {
|
|
||||||
endCall()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch_camera_button.setOnClickListener {
|
|
||||||
videoCapturer?.switchCamera(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch_audio_button.setOnClickListener {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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]
|
|
||||||
peerConnection.setRemoteDescription(this, SessionDescription(SessionDescription.Type.OFFER, offerSdp))
|
|
||||||
peerConnection.createAnswer(this, MediaConstraints())
|
|
||||||
} else {
|
|
||||||
callAddress = intent.getParcelableExtra(EXTRA_ADDRESS) ?: run { finish(); return }
|
|
||||||
peerConnection.createOffer(this, MediaConstraints())
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
|
||||||
while (this.isActive) {
|
|
||||||
val answer = synchronized(WebRtcUtils.callCache) {
|
|
||||||
WebRtcUtils.callCache[callId]?.firstOrNull { it.type == SignalServiceProtos.CallMessage.Type.ANSWER }
|
|
||||||
}
|
|
||||||
if (answer != null) {
|
|
||||||
peerConnection.setRemoteDescription(
|
|
||||||
this@WebRtcCallActivity,
|
|
||||||
SessionDescription(SessionDescription.Type.ANSWER, answer.sdps[0])
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
delay(2_000L)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerReceiver(object: BroadcastReceiver() {
|
registerReceiver(object: BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
endCall()
|
finish()
|
||||||
}
|
}
|
||||||
}, IntentFilter(ACTION_END))
|
}, IntentFilter(ACTION_END))
|
||||||
|
|
||||||
lifecycleScope.launchWhenResumed {
|
|
||||||
while (this.isActive) {
|
|
||||||
delay(2_000L)
|
|
||||||
peerConnection.getStats(this@WebRtcCallActivity)
|
|
||||||
synchronized(WebRtcUtils.callCache) {
|
|
||||||
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 ->
|
|
||||||
peerConnection.addIceCandidate(candidate)
|
|
||||||
}
|
|
||||||
acceptedCallMessageHashes.add(callMessage.hashCode())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeResources() {
|
private fun initializeResources() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun endCall() {
|
|
||||||
if (isFinishing) return
|
|
||||||
val uuid = callId
|
|
||||||
|
|
||||||
MessageSender.sendNonDurably(
|
|
||||||
CallMessage.endCall(uuid),
|
|
||||||
callAddress
|
|
||||||
)
|
|
||||||
peerConnection.close()
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
endCall()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupStreams() {
|
private fun setupStreams() {
|
||||||
val videoSource = connectionFactory.createVideoSource(false)
|
|
||||||
|
|
||||||
videoCapturer?.initialize(surfaceHelper, local_renderer.context, videoSource.capturerObserver) ?: run {
|
|
||||||
finish()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
videoCapturer?.startCapture(HD_VIDEO_WIDTH, HD_VIDEO_HEIGHT, 10)
|
|
||||||
|
|
||||||
val audioTrack = connectionFactory.createAudioTrack(LOCAL_TRACK_ID + "_audio", audioSource)
|
|
||||||
val videoTrack = connectionFactory.createVideoTrack(LOCAL_TRACK_ID, videoSource)
|
|
||||||
videoTrack.addSink(local_renderer)
|
|
||||||
|
|
||||||
val stream = connectionFactory.createLocalMediaStream(LOCAL_STREAM_ID)
|
|
||||||
stream.addTrack(videoTrack)
|
|
||||||
stream.addTrack(audioTrack)
|
|
||||||
|
|
||||||
peerConnection.addStream(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSignalingChange(p0: PeerConnection.SignalingState?) {
|
|
||||||
Log.d("Loki-RTC", "onSignalingChange: $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
|
|
||||||
Log.d("Loki-RTC", "onIceConnectionChange: $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceConnectionReceivingChange(p0: Boolean) {
|
|
||||||
Log.d("Loki-RTC", "onIceConnectionReceivingChange: $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {
|
|
||||||
Log.d("Loki-RTC", "onIceGatheringChange: $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceCandidate(iceCandidate: IceCandidate?) {
|
|
||||||
Log.d("Loki-RTC", "onIceCandidate: $iceCandidate")
|
|
||||||
if (iceCandidate == null) return
|
|
||||||
// TODO: in a lokinet world, these might have to be filtered specifically to drop anything that is not .loki
|
|
||||||
peerConnection.addIceCandidate(iceCandidate)
|
|
||||||
candidates.add(iceCandidate)
|
|
||||||
iceDebouncer.publish {
|
|
||||||
MessageSender.sendNonDurably(
|
|
||||||
CallMessage(SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES,
|
|
||||||
candidates.map { it.sdp },
|
|
||||||
candidates.map { it.sdpMLineIndex },
|
|
||||||
candidates.map { it.sdpMid },
|
|
||||||
callId
|
|
||||||
),
|
|
||||||
callAddress
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
|
|
||||||
Log.d("Loki-RTC", "onIceCandidatesRemoved: $p0")
|
|
||||||
peerConnection.removeIceCandidates(p0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAddStream(remoteStream: MediaStream?) {
|
|
||||||
Log.d("Loki-RTC", "onAddStream: $remoteStream")
|
|
||||||
if (remoteStream == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteStream.videoTracks.firstOrNull()?.addSink(remote_renderer)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRemoveStream(p0: MediaStream?) {
|
|
||||||
Log.d("Loki-RTC", "onRemoveStream: $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDataChannel(p0: DataChannel?) {
|
|
||||||
Log.d("Loki-RTC", "onDataChannel: $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRenegotiationNeeded() {
|
|
||||||
Log.d("Loki-RTC", "onRenegotiationNeeded")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
|
|
||||||
Log.d("Loki-RTC", "onAddTrack: $p0: $p1")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateSuccess(sdp: SessionDescription) {
|
|
||||||
Log.d("Loki-RTC", "onCreateSuccess: ${sdp.type}")
|
|
||||||
when (sdp.type) {
|
|
||||||
SessionDescription.Type.OFFER -> {
|
|
||||||
peerConnection.setLocalDescription(this, sdp)
|
|
||||||
MessageSender.sendNonDurably(
|
|
||||||
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(),
|
|
||||||
callId
|
|
||||||
),
|
|
||||||
callAddress
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null, SessionDescription.Type.PRANSWER -> TODO("do the PR answer create success handling") // MessageSender.send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSetSuccess() {
|
|
||||||
Log.d("Loki-RTC", "onSetSuccess")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateFailure(p0: String?) {
|
|
||||||
Log.d("Loki-RTC", "onCreateFailure: $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSetFailure(p0: String?) {
|
|
||||||
Log.d("Loki-RTC", "onSetFailure: $p0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -187,17 +187,10 @@ object ConversationMenuHelper {
|
|||||||
.setTitle("Call")
|
.setTitle("Call")
|
||||||
.setMessage("Use relay?")
|
.setMessage("Use relay?")
|
||||||
.setPositiveButton("Use Relay") { d, w ->
|
.setPositiveButton("Use Relay") { d, w ->
|
||||||
val intent = Intent(context, WebRtcCallActivity::class.java)
|
TODO()
|
||||||
intent.putExtra(WebRtcCallActivity.EXTRA_CALL_ID, UUID.randomUUID().toString())
|
|
||||||
intent.putExtra(WebRtcCallActivity.EXTRA_ADDRESS, thread.address)
|
|
||||||
val activity = context as AppCompatActivity
|
|
||||||
activity.startActivity(intent)
|
|
||||||
}
|
}
|
||||||
.setNeutralButton("P2P only") { d, w ->
|
.setNeutralButton("P2P only") { d, w ->
|
||||||
val intent = Intent(context, WebRtcCallActivity::class.java)
|
TODO()
|
||||||
intent.putExtra(WebRtcCallActivity.EXTRA_ADDRESS, thread.address)
|
|
||||||
val activity = context as AppCompatActivity
|
|
||||||
activity.startActivity(intent)
|
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@@ -5,19 +5,19 @@ import dagger.Binds
|
|||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.components.ServiceComponent
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.android.scopes.ServiceScoped
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import org.session.libsession.database.CallDataProvider
|
import org.session.libsession.database.CallDataProvider
|
||||||
import org.thoughtcrime.securesms.database.Storage
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
import org.thoughtcrime.securesms.webrtc.CallManager
|
import org.thoughtcrime.securesms.webrtc.CallManager
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat
|
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
|
|
||||||
import org.thoughtcrime.securesms.webrtc.data.SessionCallDataProvider
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
abstract class CallModule {
|
object CallModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -25,11 +25,7 @@ abstract class CallModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideCallManager(@ApplicationContext context: Context, storage: Storage, audioManagerCompat: AudioManagerCompat) =
|
fun provideCallManager(@ApplicationContext context: Context, audioManagerCompat: AudioManagerCompat) =
|
||||||
CallManager(context, audioManagerCompat)
|
CallManager(context, audioManagerCompat)
|
||||||
|
|
||||||
@Binds
|
|
||||||
@Singleton
|
|
||||||
abstract fun bindCallDataProvider(sessionCallDataProvider: SessionCallDataProvider): CallDataProvider
|
|
||||||
|
|
||||||
}
|
}
|
@@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
|
|||||||
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider
|
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider
|
||||||
import org.thoughtcrime.securesms.database.*
|
import org.thoughtcrime.securesms.database.*
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.webrtc.data.SessionCallDataProvider
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
@@ -28,7 +28,8 @@ import org.thoughtcrime.securesms.webrtc.*
|
|||||||
import org.thoughtcrime.securesms.webrtc.CallManager.CallState.*
|
import org.thoughtcrime.securesms.webrtc.CallManager.CallState.*
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
||||||
import org.thoughtcrime.securesms.webrtc.locks.LockManager
|
import org.thoughtcrime.securesms.webrtc.locks.LockManager
|
||||||
import org.webrtc.SessionDescription
|
import org.webrtc.*
|
||||||
|
import org.webrtc.PeerConnection.IceConnectionState.*
|
||||||
import java.lang.AssertionError
|
import java.lang.AssertionError
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
@@ -37,7 +38,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class WebRtcCallService: Service() {
|
class WebRtcCallService: Service(), PeerConnection.Observer {
|
||||||
|
|
||||||
@Inject lateinit var callManager: CallManager
|
@Inject lateinit var callManager: CallManager
|
||||||
|
|
||||||
@@ -62,7 +63,6 @@ class WebRtcCallService: Service() {
|
|||||||
|
|
||||||
const val ACTION_RESPONSE_MESSAGE = "RESPONSE_MESSAGE"
|
const val ACTION_RESPONSE_MESSAGE = "RESPONSE_MESSAGE"
|
||||||
const val ACTION_ICE_MESSAGE = "ICE_MESSAGE"
|
const val ACTION_ICE_MESSAGE = "ICE_MESSAGE"
|
||||||
const val ACTION_ICE_CANDIDATE = "ICE_CANDIDATE"
|
|
||||||
const val ACTION_CALL_CONNECTED = "CALL_CONNECTED"
|
const val ACTION_CALL_CONNECTED = "CALL_CONNECTED"
|
||||||
const val ACTION_REMOTE_HANGUP = "REMOTE_HANGUP"
|
const val ACTION_REMOTE_HANGUP = "REMOTE_HANGUP"
|
||||||
const val ACTION_REMOTE_BUSY = "REMOTE_BUSY"
|
const val ACTION_REMOTE_BUSY = "REMOTE_BUSY"
|
||||||
@@ -118,6 +118,7 @@ class WebRtcCallService: Service() {
|
|||||||
|
|
||||||
private var callReceiver: IncomingPstnCallReceiver? = null
|
private var callReceiver: IncomingPstnCallReceiver? = null
|
||||||
private var wiredHeadsetStateReceiver: WiredHeadsetStateReceiver? = null
|
private var wiredHeadsetStateReceiver: WiredHeadsetStateReceiver? = null
|
||||||
|
private var uncaughtExceptionHandlerManager: UncaughtExceptionHandlerManager? = null
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun terminate() {
|
private fun terminate() {
|
||||||
@@ -155,7 +156,7 @@ class WebRtcCallService: Service() {
|
|||||||
action == ACTION_REMOTE_VIDEO_MUTE -> handleRemoteVideoMute(intent)
|
action == ACTION_REMOTE_VIDEO_MUTE -> handleRemoteVideoMute(intent)
|
||||||
action == ACTION_RESPONSE_MESSAGE -> handleResponseMessage(intent)
|
action == ACTION_RESPONSE_MESSAGE -> handleResponseMessage(intent)
|
||||||
action == ACTION_ICE_MESSAGE -> handleRemoteIceCandidate(intent)
|
action == ACTION_ICE_MESSAGE -> handleRemoteIceCandidate(intent)
|
||||||
action == ACTION_ICE_CANDIDATE -> handleLocalIceCandidate(intent)
|
action == ACTION_ICE_CONNECTED -> handleIceConnected(intent)
|
||||||
action == ACTION_CALL_CONNECTED -> handleCallConnected(intent)
|
action == ACTION_CALL_CONNECTED -> handleCallConnected(intent)
|
||||||
action == ACTION_CHECK_TIMEOUT -> handleCheckTimeout(intent)
|
action == ACTION_CHECK_TIMEOUT -> handleCheckTimeout(intent)
|
||||||
action == ACTION_IS_IN_CALL_QUERY -> handleIsInCallQuery(intent)
|
action == ACTION_IS_IN_CALL_QUERY -> handleIsInCallQuery(intent)
|
||||||
@@ -166,15 +167,18 @@ class WebRtcCallService: Service() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
// create audio manager
|
callManager.registerListener(this)
|
||||||
registerIncomingPstnCallReceiver()
|
registerIncomingPstnCallReceiver()
|
||||||
registerWiredHeadsetStateReceiver()
|
registerWiredHeadsetStateReceiver()
|
||||||
getSystemService(TelephonyManager::class.java)
|
getSystemService(TelephonyManager::class.java)
|
||||||
.listen(hangupOnCallAnswered, PhoneStateListener.LISTEN_CALL_STATE)
|
.listen(hangupOnCallAnswered, PhoneStateListener.LISTEN_CALL_STATE)
|
||||||
// reset call notification
|
registerUncaughtExceptionHandler()
|
||||||
// register uncaught exception handler
|
}
|
||||||
// register network receiver
|
|
||||||
// telephony listen to call state
|
private fun registerUncaughtExceptionHandler() {
|
||||||
|
uncaughtExceptionHandlerManager = UncaughtExceptionHandlerManager().apply {
|
||||||
|
registerHandler(ProximityLockRelease(lockManager))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerIncomingPstnCallReceiver() {
|
private fun registerIncomingPstnCallReceiver() {
|
||||||
@@ -215,7 +219,7 @@ class WebRtcCallService: Service() {
|
|||||||
private fun handleBusyMessage(intent: Intent) {
|
private fun handleBusyMessage(intent: Intent) {
|
||||||
val recipient = getRemoteRecipient(intent)
|
val recipient = getRemoteRecipient(intent)
|
||||||
val callId = getCallId(intent)
|
val callId = getCallId(intent)
|
||||||
if (callManager.currentConnectionState != STATE_DIALING || callManager.callId != callManager.callId || callManager.recipient != callManager.recipient) {
|
if (callManager.currentConnectionState != STATE_DIALING || callManager.callId != callId || callManager.recipient != recipient) {
|
||||||
Log.w(TAG,"Got busy message for inactive session...")
|
Log.w(TAG,"Got busy message for inactive session...")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -412,10 +416,25 @@ class WebRtcCallService: Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRemoteIceCandidate(intent: Intent) {
|
private fun handleRemoteIceCandidate(intent: Intent) {
|
||||||
|
val callId = getCallId(intent)
|
||||||
|
val sdpMids = intent.getStringArrayExtra(EXTRA_ICE_SDP_MID) ?: return
|
||||||
|
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")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val iceCandidates = (0 until sdpMids.size).map { index ->
|
||||||
|
IceCandidate(
|
||||||
|
sdpMids[index],
|
||||||
|
sdpLineIndexes[index],
|
||||||
|
sdps[index]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
callManager.handleRemoteIceCandidate(iceCandidates, callId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLocalIceCandidate(intent: Intent) {
|
private fun handleIceConnected(intent: Intent) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,12 +488,14 @@ class WebRtcCallService: Service() {
|
|||||||
System.currentTimeMillis() - intent.getLongExtra(EXTRA_TIMESTAMP, -1) > TimeUnit.MINUTES.toMillis(2)
|
System.currentTimeMillis() - intent.getLongExtra(EXTRA_TIMESTAMP, -1) > TimeUnit.MINUTES.toMillis(2)
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
callManager.unregisterListener(this)
|
||||||
callReceiver?.let { receiver ->
|
callReceiver?.let { receiver ->
|
||||||
unregisterReceiver(receiver)
|
unregisterReceiver(receiver)
|
||||||
}
|
}
|
||||||
callReceiver = null
|
callReceiver = null
|
||||||
// unregister exception handler
|
uncaughtExceptionHandlerManager?.unregister()
|
||||||
|
callManager.onDestroy()
|
||||||
|
super.onDestroy()
|
||||||
// shutdown audiomanager
|
// shutdown audiomanager
|
||||||
// unregister network receiver
|
// unregister network receiver
|
||||||
// unregister power button
|
// unregister power button
|
||||||
@@ -553,4 +574,37 @@ class WebRtcCallService: Service() {
|
|||||||
return expectedState == currentState && expectedCallId == currentCallId
|
return expectedState == currentState && expectedCallId == currentCallId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSignalingChange(p0: PeerConnection.SignalingState?) {}
|
||||||
|
|
||||||
|
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) {
|
||||||
|
if (newState in arrayOf(CONNECTED, COMPLETED)) {
|
||||||
|
val intent = Intent(this, WebRtcCallService::class.java)
|
||||||
|
.setAction(ACTION_ICE_CONNECTED)
|
||||||
|
startService(intent)
|
||||||
|
} else if (newState == FAILED) {
|
||||||
|
val intent = Intent(this, WebRtcCallService::class.java)
|
||||||
|
.setAction(ACTION_REMOTE_HANGUP)
|
||||||
|
.putExtra(EXTRA_CALL_ID, callManager.callId)
|
||||||
|
|
||||||
|
startService(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIceConnectionReceivingChange(p0: Boolean) {}
|
||||||
|
|
||||||
|
override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {}
|
||||||
|
|
||||||
|
override fun onIceCandidate(p0: IceCandidate?) {}
|
||||||
|
|
||||||
|
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {}
|
||||||
|
|
||||||
|
override fun onAddStream(p0: MediaStream?) {}
|
||||||
|
|
||||||
|
override fun onRemoveStream(p0: MediaStream?) {}
|
||||||
|
|
||||||
|
override fun onDataChannel(p0: DataChannel?) {}
|
||||||
|
|
||||||
|
override fun onRenegotiationNeeded() {}
|
||||||
|
|
||||||
|
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {}
|
||||||
}
|
}
|
@@ -10,6 +10,12 @@ open class AudioManagerCommand: Parcelable {
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
object Initialize: AudioManagerCommand()
|
object Initialize: AudioManagerCommand()
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
object Shutdown: AudioManagerCommand()
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
object UpdateAudioDeviceState: AudioManagerCommand()
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class StartOutgoingRinger(val type: OutgoingRinger.Type): AudioManagerCommand()
|
data class StartOutgoingRinger(val type: OutgoingRinger.Type): AudioManagerCommand()
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.webrtc
|
package org.thoughtcrime.securesms.webrtc
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
@@ -64,6 +63,16 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
|||||||
|
|
||||||
private val signalAudioManager: SignalAudioManager = SignalAudioManager(context, this, audioManager)
|
private val signalAudioManager: SignalAudioManager = SignalAudioManager(context, this, audioManager)
|
||||||
|
|
||||||
|
private val peerConnectionObservers = mutableSetOf<PeerConnection.Observer>()
|
||||||
|
|
||||||
|
fun registerListener(listener: PeerConnection.Observer) {
|
||||||
|
peerConnectionObservers.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unregisterListener(listener: PeerConnection.Observer) {
|
||||||
|
peerConnectionObservers.remove(listener)
|
||||||
|
}
|
||||||
|
|
||||||
private val _audioEvents = MutableStateFlow(StateEvent.AudioEnabled(false))
|
private val _audioEvents = MutableStateFlow(StateEvent.AudioEnabled(false))
|
||||||
val audioEvents = _audioEvents.asSharedFlow()
|
val audioEvents = _audioEvents.asSharedFlow()
|
||||||
private val _videoEvents = MutableStateFlow(StateEvent.VideoEnabled(false))
|
private val _videoEvents = MutableStateFlow(StateEvent.VideoEnabled(false))
|
||||||
@@ -183,47 +192,47 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onSignalingChange(newState: PeerConnection.SignalingState) {
|
override fun onSignalingChange(newState: PeerConnection.SignalingState) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onSignalingChange(newState) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState) {
|
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onIceConnectionChange(newState) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceConnectionReceivingChange(receiving: Boolean) {
|
override fun onIceConnectionReceivingChange(receiving: Boolean) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onIceConnectionReceivingChange(receiving) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceGatheringChange(newState: PeerConnection.IceGatheringState) {
|
override fun onIceGatheringChange(newState: PeerConnection.IceGatheringState) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onIceGatheringChange(newState) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceCandidate(p0: IceCandidate?) {
|
override fun onIceCandidate(iceCandidate: IceCandidate?) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onIceCandidate(iceCandidate) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
|
override fun onIceCandidatesRemoved(candidates: Array<out IceCandidate>?) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onIceCandidatesRemoved(candidates) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAddStream(p0: MediaStream?) {
|
override fun onAddStream(p0: MediaStream?) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onAddStream(p0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRemoveStream(p0: MediaStream?) {
|
override fun onRemoveStream(p0: MediaStream?) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onRemoveStream(p0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDataChannel(p0: DataChannel?) {
|
override fun onDataChannel(p0: DataChannel?) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onDataChannel(p0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRenegotiationNeeded() {
|
override fun onRenegotiationNeeded() {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onRenegotiationNeeded() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
|
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
|
||||||
|
peerConnectionObservers.forEach { listener -> listener.onAddTrack(p0, p1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBufferedAmountChange(l: Long) {
|
override fun onBufferedAmountChange(l: Long) {
|
||||||
@@ -476,4 +485,8 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onDestroy() {
|
||||||
|
signalAudioManager.handleCommand(AudioManagerCommand.Shutdown)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
package org.thoughtcrime.securesms.webrtc;
|
||||||
|
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows multiple default uncaught exception handlers to be registered
|
||||||
|
*
|
||||||
|
* Calls all registered handlers in reverse order of registration.
|
||||||
|
* Errors in one handler do not prevent subsequent handlers from being called.
|
||||||
|
*/
|
||||||
|
public class UncaughtExceptionHandlerManager implements Thread.UncaughtExceptionHandler {
|
||||||
|
private final Thread.UncaughtExceptionHandler originalHandler;
|
||||||
|
private final List<Thread.UncaughtExceptionHandler> handlers = new ArrayList<Thread.UncaughtExceptionHandler>();
|
||||||
|
|
||||||
|
public UncaughtExceptionHandlerManager() {
|
||||||
|
originalHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||||
|
registerHandler(originalHandler);
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerHandler(Thread.UncaughtExceptionHandler handler) {
|
||||||
|
handlers.add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister() {
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(originalHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||||
|
for (int i = handlers.size() - 1; i >= 0; i--) {
|
||||||
|
try {
|
||||||
|
handlers.get(i).uncaughtException(thread, throwable);
|
||||||
|
} catch(Throwable t) {
|
||||||
|
Log.e("UncaughtExceptionHandlerManager", "Error in uncaught exception handling", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,6 +8,7 @@ import android.telephony.TelephonyManager
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.service.WebRtcCallService
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||||
|
import org.thoughtcrime.securesms.webrtc.locks.LockManager
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
@@ -26,11 +27,11 @@ class HangUpRtcOnPstnCallAnsweredListener(private val hangupListener: ()->Unit):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AndroidEntryPoint
|
//@AndroidEntryPoint
|
||||||
class NetworkReceiver: BroadcastReceiver() {
|
class NetworkReceiver: BroadcastReceiver() {
|
||||||
|
|
||||||
@Inject
|
// @Inject
|
||||||
lateinit var callManager: CallManager
|
// lateinit var callManager: CallManager
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
@@ -47,13 +48,13 @@ class PowerButtonReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProximityLockRelease: Thread.UncaughtExceptionHandler {
|
class ProximityLockRelease(private val lockManager: LockManager): Thread.UncaughtExceptionHandler {
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = Log.tag(ProximityLockRelease::class.java)
|
private val TAG = Log.tag(ProximityLockRelease::class.java)
|
||||||
}
|
}
|
||||||
override fun uncaughtException(t: Thread, e: Throwable) {
|
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||||
Log.e(TAG,"Uncaught exception - releasing proximity lock", e)
|
Log.e(TAG,"Uncaught exception - releasing proximity lock", e)
|
||||||
// lockManager update phone state
|
lockManager.updatePhoneState(LockManager.PhoneState.IDLE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
private val androidAudioManager: AudioManagerCompat) {
|
private val androidAudioManager: AudioManagerCompat) {
|
||||||
|
|
||||||
private var commandAndControlThread: HandlerThread? = HandlerThread("call-audio").apply { start() }
|
private var commandAndControlThread: HandlerThread? = HandlerThread("call-audio").apply { start() }
|
||||||
private val handler = SignalAudioHandler(commandAndControlThread!!.looper)
|
private var handler = SignalAudioHandler(commandAndControlThread!!.looper)
|
||||||
|
|
||||||
private val signalBluetoothManager = SignalBluetoothManager(context, this, androidAudioManager, handler)
|
private val signalBluetoothManager = SignalBluetoothManager(context, this, androidAudioManager, handler)
|
||||||
|
|
||||||
@@ -69,6 +69,8 @@ class SignalAudioManager(private val context: Context,
|
|||||||
handler.post {
|
handler.post {
|
||||||
when (command) {
|
when (command) {
|
||||||
is AudioManagerCommand.Initialize -> initialize()
|
is AudioManagerCommand.Initialize -> initialize()
|
||||||
|
is AudioManagerCommand.Shutdown -> shutdown()
|
||||||
|
is AudioManagerCommand.UpdateAudioDeviceState -> updateAudioDeviceState()
|
||||||
is AudioManagerCommand.Start -> start()
|
is AudioManagerCommand.Start -> start()
|
||||||
is AudioManagerCommand.Stop -> stop(command.playDisconnect)
|
is AudioManagerCommand.Stop -> stop(command.playDisconnect)
|
||||||
is AudioManagerCommand.SetDefaultDevice -> setDefaultAudioDevice(command.device, command.clearUserEarpieceSelection)
|
is AudioManagerCommand.SetDefaultDevice -> setDefaultAudioDevice(command.device, command.clearUserEarpieceSelection)
|
||||||
@@ -84,6 +86,9 @@ class SignalAudioManager(private val context: Context,
|
|||||||
Log.i(TAG, "Initializing audio manager state: $state")
|
Log.i(TAG, "Initializing audio manager state: $state")
|
||||||
|
|
||||||
if (state == State.UNINITIALIZED) {
|
if (state == State.UNINITIALIZED) {
|
||||||
|
commandAndControlThread = HandlerThread("call-audio").apply { start() }
|
||||||
|
handler = SignalAudioHandler(commandAndControlThread!!.looper)
|
||||||
|
|
||||||
savedAudioMode = androidAudioManager.mode
|
savedAudioMode = androidAudioManager.mode
|
||||||
savedIsSpeakerPhoneOn = androidAudioManager.isSpeakerphoneOn
|
savedIsSpeakerPhoneOn = androidAudioManager.isSpeakerphoneOn
|
||||||
savedIsMicrophoneMute = androidAudioManager.isMicrophoneMute
|
savedIsMicrophoneMute = androidAudioManager.isMicrophoneMute
|
||||||
|
@@ -9,6 +9,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
|
import org.thoughtcrime.securesms.webrtc.AudioManagerCommand
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,7 +182,7 @@ class SignalBluetoothManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateAudioDeviceState() {
|
private fun updateAudioDeviceState() {
|
||||||
audioManager.updateAudioDeviceState()
|
audioManager.handleCommand(AudioManagerCommand.UpdateAudioDeviceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startTimer() {
|
private fun startTimer() {
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.webrtc.data
|
|
||||||
|
|
||||||
import org.session.libsession.database.CallDataProvider
|
|
||||||
import org.session.libsession.database.StorageProtocol
|
|
||||||
import org.thoughtcrime.securesms.webrtc.CallManager
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class SessionCallDataProvider @Inject constructor(private val storage: StorageProtocol,
|
|
||||||
private val callManager: CallManager): CallDataProvider {
|
|
||||||
|
|
||||||
}
|
|
Reference in New Issue
Block a user