From 72127c0871bd4e991bcd5b9adfacd861f2edd5ce Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 23 Apr 2026 22:45:37 -0700 Subject: [PATCH 1/2] Fix P2P text chat timeout on WebRTC regions and delay voice renegotiation on disconnect Text chat: On WebRTC regions, getOutgoingCallInterface() returns nullptr, causing mP2PAsAdhocCall to be true for all P2P sessions including text-only IMs. This routed text chat through startP2PVoiceCoro which sent a "start p2p voice" request and waited for a server reply that never came, resulting in a 30-second session initialization timeout. Fix by gating the p2p-as-adhoc server init on mStartedAsIMCall so text-only sessions initialize immediately. WebRTC: Split kFailed and kDisconnected handling in OnConnectionChange. kFailed still renegotiates immediately. kDisconnected now waits 10 seconds before renegotiating, giving the connection time to recover on its own. Uses a sequence counter to ensure only the most recent disconnect transition can trigger renegotiation, preventing stale delayed tasks from firing early after disconnect/reconnect cycles. Co-Authored-By: Claude Opus 4.6 (1M context) --- indra/llwebrtc/llwebrtc.cpp | 29 +++++++++++++++++++++++++++-- indra/llwebrtc/llwebrtc_impl.h | 11 +++++++++++ indra/newview/llimview.cpp | 3 ++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 6bf38cc1f68..a286f75f424 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -811,6 +811,8 @@ LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() : mPeerConnection(nullptr), mMute(MUTE_INITIAL), mAnswerReceived(false), + mPeerConnectionState(webrtc::PeerConnectionInterface::PeerConnectionState::kNew), + mDisconnectCount(0), mPendingJobs(0) { } @@ -1237,11 +1239,15 @@ void LLWebRTCPeerConnectionImpl::OnIceGatheringChange(webrtc::PeerConnectionInte } } +static const webrtc::TimeDelta DISCONNECT_RENEGOTIATE_DELAY = webrtc::TimeDelta::Millis(10000); + // Called any time the PeerConnectionState changes. void LLWebRTCPeerConnectionImpl::OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState new_state) { RTC_LOG(LS_ERROR) << __FUNCTION__ << " Peer Connection State Change " << new_state; + mPeerConnectionState = new_state; + switch (new_state) { case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected: @@ -1257,13 +1263,32 @@ void LLWebRTCPeerConnectionImpl::OnConnectionChange(webrtc::PeerConnectionInterf break; } case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: - case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: { for (auto &observer : mSignalingObserverList) { observer->OnRenegotiationNeeded(); } - + break; + } + case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: + { + // Wait 10 seconds before renegotiating in case the connection recovers on its own. + // Use a sequence count so that only the most recent disconnect transition can trigger + // a renegotiation, avoiding stale delayed tasks from earlier disconnect/reconnect cycles. + uint32_t disconnect_count = ++mDisconnectCount; + mWebRTCImpl->PostDelayedSignalingTask( + [this, disconnect_count]() + { + if (disconnect_count == mDisconnectCount + && mPeerConnectionState == webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected) + { + for (auto &observer : mSignalingObserverList) + { + observer->OnRenegotiationNeeded(); + } + } + }, + DISCONNECT_RENEGOTIATE_DELAY); break; } default: diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 2ff85c92eeb..bd7a2e0bcfc 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -480,6 +480,13 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceO mSignalingThread->PostTask(std::move(task), location); } + void PostDelayedSignalingTask(absl::AnyInvocable task, + webrtc::TimeDelta delay, + const webrtc::Location& location = webrtc::Location::Current()) + { + mSignalingThread->PostDelayedTask(std::move(task), delay, location); + } + void PostNetworkTask(absl::AnyInvocable task, const webrtc::Location& location = webrtc::Location::Current()) { @@ -676,6 +683,10 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface, std::vector mDataObserverList; webrtc::scoped_refptr mDataChannel; + // connection state tracking for delayed renegotiation on disconnect + webrtc::PeerConnectionInterface::PeerConnectionState mPeerConnectionState; + uint32_t mDisconnectCount; + std::atomic mPendingJobs; }; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index ad01e11d483..872548c87ba 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -775,7 +775,8 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, //we need to wait for session initialization for outgoing ad-hoc and group chat session //correct session id for initiated ad-hoc chat will be received from the server - if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, mInitialTargetIDs, mType, mP2PAsAdhocCall)) + //only use p2p-as-adhoc server init when this is actually a voice call, not a text-only IM + if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, mInitialTargetIDs, mType, mP2PAsAdhocCall && mStartedAsIMCall)) { //we don't need to wait for any responses //so we're already initialized From 20bbb860be3653572594e658c3bc7cc0191fabf2 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 27 Apr 2026 08:14:42 -0700 Subject: [PATCH 2/2] Revert im-change for not using the voice subsystem when doing a text-only IM --- indra/newview/llimview.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 872548c87ba..ad01e11d483 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -775,8 +775,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, //we need to wait for session initialization for outgoing ad-hoc and group chat session //correct session id for initiated ad-hoc chat will be received from the server - //only use p2p-as-adhoc server init when this is actually a voice call, not a text-only IM - if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, mInitialTargetIDs, mType, mP2PAsAdhocCall && mStartedAsIMCall)) + if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, mInitialTargetIDs, mType, mP2PAsAdhocCall)) { //we don't need to wait for any responses //so we're already initialized