Skip to content

fix(call): fix native crashes, muted icons and state broadcast in calls#6261

Merged
mahibi merged 1 commit into
masterfrom
bugfix/noid/fixCrashWhenParticipantLeaves
May 29, 2026
Merged

fix(call): fix native crashes, muted icons and state broadcast in calls#6261
mahibi merged 1 commit into
masterfrom
bugfix/noid/fixCrashWhenParticipantLeaves

Conversation

@mahibi
Copy link
Copy Markdown
Collaborator

@mahibi mahibi commented May 29, 2026

This commit contains the code from #6039 (thank you @tareko !) as it needed to be combined with more fixes during it's review.

PeerConnectionWrapper — native crash fixes:

  • Call peerConnection.close() before dataChannel.dispose() so the WebRTC signaling thread stops dispatching callbacks before native objects are freed
  • Call peerConnection.close() outside the synchronized lock; holding the lock deadlocks because close() blocks waiting for the signaling thread, which itself needs the lock to run its callbacks
  • Null out peerConnection before calling close() so in-flight callbacks see null and return early instead of NPE-ing on peerConnection.hashCode() in onIceConnectionChange, onCreateFailure, and onSetFailure
  • Call dataChannel.unregisterObserver() before dispose() in both removePeerConnection() and onDataChannel() to prevent the native observer from calling back into a disposed Java DataChannel object

LocalStateBroadcasterNoMcu — initial mute state not sent to peers:

  • IceConnectionStateObserver.job was never started, so local audio/video/ speaking state was never sent to newly connected participants; only a manual mute+unmute triggered a broadcast
  • handleCallParticipantAdded now accepts the live StateFlow and uses first { it.isConnected } to send initial state exactly once when ICE connects; base class gets a concrete StateFlow overload that defaults to the existing snapshot path so MCU behaviour is unchanged
  • Fix ParticipantHandler initial isConnected = false (was true), which caused retrying when ICE actually connected

ParticipantHandler — guest audio not detected (cherry-pick of PR #6039):

  • handleStreamChange() now sets isAudioEnabled from audio track presence, not only isStreamEnabled from video tracks; guests whose DataChannel never opens were permanently shown as muted despite having an active audio track
  • handleIceConnectionStateChange() preserves audio/video state during ICE NEW/CHECKING if tracks are still present, rather than resetting to false unconditionally and relying on a DataChannel message to restore it
  • Add GuestAudioDetectionTest and diagnostic logging

AI-assistant: Claude Code v2.1.142 (Claude Sonnet 4.6)

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔖 Capability is checked or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

@mahibi mahibi self-assigned this May 29, 2026
@mahibi mahibi added this to the 24.1.0 milestone May 29, 2026
@mahibi mahibi added the 3. to review Waiting for reviews label May 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/26644548422/artifacts/7293967270
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

This commit contains the code from #6039 (thank you tareko!)
as it needed to be combined with more fixes during it's review.

  PeerConnectionWrapper — native crash fixes:
  - Call peerConnection.close() before dataChannel.dispose() so the WebRTC
    signaling thread stops dispatching callbacks before native objects are freed
  - Call peerConnection.close() outside the synchronized lock; holding the lock
    deadlocks because close() blocks waiting for the signaling thread, which
    itself needs the lock to run its callbacks
  - Null out peerConnection before calling close() so in-flight callbacks see
    null and return early instead of NPE-ing on peerConnection.hashCode() in
    onIceConnectionChange, onCreateFailure, and onSetFailure
  - Call dataChannel.unregisterObserver() before dispose() in both
    removePeerConnection() and onDataChannel() to prevent the native observer
    from calling back into a disposed Java DataChannel object

  LocalStateBroadcasterNoMcu — initial mute state not sent to peers:
  - IceConnectionStateObserver.job was never started, so local audio/video/
    speaking state was never sent to newly connected participants; only a
    manual mute+unmute triggered a broadcast
  - handleCallParticipantAdded now accepts the live StateFlow<ParticipantUiState>
    and uses first { it.isConnected } to send initial state exactly once when
    ICE connects; base class gets a concrete StateFlow overload that defaults
    to the existing snapshot path so MCU behaviour is unchanged
  - Fix ParticipantHandler initial isConnected = false (was true), which caused
    retrying when ICE actually connected

  ParticipantHandler — guest audio not detected (cherry-pick of PR #6039):
  - handleStreamChange() now sets isAudioEnabled from audio track presence,
    not only isStreamEnabled from video tracks; guests whose DataChannel never
    opens were permanently shown as muted despite having an active audio track
  - handleIceConnectionStateChange() preserves audio/video state during ICE
    NEW/CHECKING if tracks are still present, rather than resetting to false
    unconditionally and relying on a DataChannel message to restore it
  - Add GuestAudioDetectionTest and diagnostic logging

AI-assistant: Claude Code v2.1.142 (Claude Sonnet 4.6)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
@mahibi mahibi force-pushed the bugfix/noid/fixCrashWhenParticipantLeaves branch from 016f6d9 to 90e4f08 Compare May 29, 2026 15:17
@mahibi mahibi merged commit ade26b6 into master May 29, 2026
12 of 17 checks passed
@mahibi mahibi deleted the bugfix/noid/fixCrashWhenParticipantLeaves branch May 29, 2026 15:25
@mahibi
Copy link
Copy Markdown
Collaborator Author

mahibi commented May 29, 2026

/backport to stable-24.0

@github-actions
Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/26645620132/artifacts/7294400520
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant