Merge pull request #2121 from vitorpamplona/claude/fix-webrtc-answer-state-SaXhI
Handle WebRTC offer glare in call renegotiation
This commit is contained in:
@@ -539,11 +539,37 @@ class CallController(
|
||||
Log.d(TAG) { "Renegotiation offer from ${peerPubKey.take(8)}, sdpLength=${sdpOffer.length}" }
|
||||
|
||||
scope.launch {
|
||||
ps.session.setRemoteDescription(SessionDescription(SessionDescription.Type.OFFER, sdpOffer))
|
||||
ps.session.createAnswer { sdp ->
|
||||
scope.launch {
|
||||
callManager.sendRenegotiationAnswer(sdp.description, peerPubKey)
|
||||
val signalingState = ps.session.getSignalingState()
|
||||
|
||||
if (signalingState == PeerConnection.SignalingState.HAVE_LOCAL_OFFER) {
|
||||
// Glare: both sides sent offers simultaneously.
|
||||
// Use pubkey comparison as tiebreaker — higher pubkey wins.
|
||||
val myPubKey = signerProvider().pubKey
|
||||
if (myPubKey > peerPubKey) {
|
||||
Log.d(TAG) { "Glare with ${peerPubKey.take(8)}: I win (my offer takes priority), ignoring remote offer" }
|
||||
return@launch
|
||||
}
|
||||
Log.d(TAG) { "Glare with ${peerPubKey.take(8)}: I lose, rolling back my offer" }
|
||||
ps.session.rollback {
|
||||
scope.launch {
|
||||
applyRenegotiationOffer(ps, peerPubKey, sdpOffer)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
applyRenegotiationOffer(ps, peerPubKey, sdpOffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyRenegotiationOffer(
|
||||
ps: PeerSessionState,
|
||||
peerPubKey: HexKey,
|
||||
sdpOffer: String,
|
||||
) {
|
||||
ps.session.setRemoteDescription(SessionDescription(SessionDescription.Type.OFFER, sdpOffer))
|
||||
ps.session.createAnswer { sdp ->
|
||||
scope.launch {
|
||||
callManager.sendRenegotiationAnswer(sdp.description, peerPubKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +257,32 @@ class WebRtcCallSession(
|
||||
|
||||
fun getSignalingState(): PeerConnection.SignalingState? = peerConnection?.signalingState()
|
||||
|
||||
/**
|
||||
* Rolls back a local offer so an incoming remote offer can be accepted
|
||||
* (WebRTC offe glare handling).
|
||||
*/
|
||||
fun rollback(onDone: () -> Unit) {
|
||||
val rollbackSdp = SessionDescription(SessionDescription.Type.ROLLBACK, "")
|
||||
peerConnection?.setLocalDescription(
|
||||
object : SdpObserver {
|
||||
override fun onCreateSuccess(sdp: SessionDescription?) {}
|
||||
|
||||
override fun onCreateFailure(error: String?) {}
|
||||
|
||||
override fun onSetSuccess() {
|
||||
Log.d(TAG) { "Rollback SUCCESS" }
|
||||
onDone()
|
||||
}
|
||||
|
||||
override fun onSetFailure(error: String?) {
|
||||
Log.e(TAG, "Rollback FAILED: $error")
|
||||
error?.let { onError("Rollback failed: $it") }
|
||||
}
|
||||
},
|
||||
rollbackSdp,
|
||||
)
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
peerConnection?.close()
|
||||
peerConnection?.dispose()
|
||||
|
||||
Reference in New Issue
Block a user