Skip to content

Conversation

@aleksandar-apostolov
Copy link
Contributor

@aleksandar-apostolov aleksandar-apostolov commented Dec 19, 2025

Goal

Improve visibility on reconnect flow and stats.

Implementation

Added more traces.
Send stats when leaving
Send stats when rejoining
Send stats to track app lifecycle - foreground and background
Improve fast reconnect logic (and when its called)
Modified delta compression logic to include all fields on initial stats
Introduced iceTricklePendingEvents for buffering ICE candidates until web socket connection ready.
Introduced connectionFailed: suspend (Throwable) -> Unit callback parameter to whenConnected() methods.
Enhanced isFailedOrClosed() to check both PeerConnection and ICE connection states.

Testing

E2E Test + Run manual tests and check stats.

Summary by CodeRabbit

  • Bug Fixes

    • Better detection of failed peer/ICE states and safer socket/SFU connection handling to reduce unexpected disconnects.
  • Improvements

    • Reconnect and leave flows now include contextual reason strings for clearer diagnostics and telemetry.
    • Connection workflow gains an explicit failure callback to surface errors without crashing.
    • Enhanced tracing, stats reporting, lifecycle handling and logging for more reliable media/session recovery.
  • Chores

    • Wiring updates to SFU-related components for improved reconnect behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 19, 2025

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled.

🎉 Great job! This PR is ready for review.

@aleksandar-apostolov aleksandar-apostolov added the pr:improvement Enhances an existing feature or code label Dec 19, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 19, 2025

Walkthrough

Reconnection and leave APIs now require/rely on explicit reason strings; socket whenConnected gains a connectionFailed callback and is wrapped in try/catch; RtcSession exposes sfuTracer, buffers ICE candidates, propagates unified_session_id, and mediaScope; Subscriber/Factory wired with SfuConnectionModule; stats delta includes all fields on first report.

Changes

Cohort / File(s) Summary
Call lifecycle and call-sites
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt, .../CallState.kt, .../ExternalCallRejectionHandler.kt, .../StreamVideoClient.kt, .../notifications/internal/receivers/LeaveCallBroadcastReceiver.kt, .../notifications/internal/service/CallService.kt
fastReconnect, rejoin, and leave now accept reason: String (with defaults at call sites). Replaced parameterless leave() calls with contextual reason strings and propagated reason through reconnect/leave paths and stats/traces.
Socket whenConnected error handling
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/common/SocketActions.kt, .../coordinator/CoordinatorSocketConnection.kt, .../sfu/SfuSocketConnection.kt
whenConnected signature extended to add connectionFailed: suspend (Throwable) -> Unit = {}. Implementations wrap connection flow in try/catch and invoke connectionFailed on exceptions while preserving connected callback on success.
RtcSession, tracing and ICE buffering
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
sfuTracer made internal, added mediaScope and lifecycle observer, introduced iceTricklePendingEvents to queue ICE candidates until SFU connection, include unified_session_id in join payloads, adjust stats/tracing and ensure mediaScope/lifecycle cleanup on leave.
Peer connection DI and behavior
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnection.kt, .../StreamPeerConnectionFactory.kt, .../connection/Subscriber.kt
makeSubscriber() and Subscriber gain sfuConnectionModule: SfuConnectionModule parameter. isFailedOrClosed() now considers ICE state; removed triggering fast-reconnect on PC FAILED; negotiation failure may invoke rejoin().
Statistics delta behavior
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/stats/StatsTracer.kt
Delta compression updated to include all fields when oldStats is empty (first report); subsequent diffs remain value-change-based.
Logging improvements
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/SerialProcessor.kt
Track and log currentJobName for job start and worker crash messages.
API surface / generated API declarations
stream-video-android-core/api/stream-video-android-core.api
Public API signatures updated (and synthetic $default overloads added) to reflect added parameters for fastReconnect, rejoin, leave, and three-arg whenConnected.
Tests
stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/SubscriberTest.kt, .../rtc/RtcSessionTest2.kt
Tests updated to supply SfuConnectionModule mocks, adapt to three-parameter whenConnected (added failure callback), and align constructor argument ordering.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Call
    participant RtcSession
    participant SocketConn as SocketConnection
    participant SFU

    User->>Call: fastReconnect(reason)
    Call->>Call: collectStats(oldSessionStats)
    Call->>RtcSession: request reconnect(reason, oldSessionStats)

    RtcSession->>SocketConn: whenConnected(timeout, connectionFailed, connected)
    alt Socket connects
        SocketConn->>SFU: establish connection
        SFU-->>SocketConn: ok
        SocketConn->>RtcSession: connected(connectionId)
        RtcSession->>Call: sendCallStats(oldSessionStats)
    else Socket errors
        SocketConn->>SFU: establish connection
        SFU--x SocketConn: error
        SocketConn->>RtcSession: connectionFailed(error)
        RtcSession->>Call: handle reconnect failure(reason, error)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay extra attention to:
    • RtcSession: mediaScope lifecycle, lifecycleObserver registration/removal, concurrency around iceTricklePendingEvents flush.
    • Correct propagation and sanitization of reason strings across all call/leave/reconnect paths.
    • Consistent connectionFailed wiring and error handling across socket implementations and callers.
    • StatsTracer first-report inclusion behavior and any serialization/consumer expectations.
    • DI change: SfuConnectionModule wiring into Subscriber/Factory and related test adjustments.

Poem

I nibble bytes and trace the thread,
I name the reasons, soft and spread,
I queue the ice till SFU's near,
I catch the fall and whisper clear,
Reconnect, report — then hop to bed. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.30% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main objective: improving stats collection and reconnect flow, which aligns with the PR's primary focus on tracing, stats transmission, and reconnection logic.
Description check ✅ Passed The PR description includes Goal, Implementation, and Testing sections covering key changes. However, it lacks UI Changes (N/A here), Contributor Checklist completion, and Reviewer Checklist items required by the template.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch stats-improvement

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt (1)

846-849: Consider a more descriptive reason string.

The else branch handles the "ongoing call" scenario per the comment on line 847, but the reason string says "unknown". A more specific reason like "call-service-end-call-ongoing" would improve traceability.

🔎 Proposed fix
                 } else {
                     // If I'm in an ongoing call, end the call for me
-                    call.leave("call-service-end-call-unknown")
+                    call.leave("call-service-end-call-ongoing")
                     logger.i { "[onTaskRemoved] Ended ongoing call for me." }
                 }
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt (1)

344-348: Consider removing or completing the TODO block.

This commented-out code introduces dead code into the codebase. If this is planned work, consider either completing it or tracking it in an issue and removing the commented code.

Would you like me to open an issue to track this socket connectivity enhancement?

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 45f7088 and 7526af8.

📒 Files selected for processing (17)
  • stream-video-android-core/api/stream-video-android-core.api (6 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt (14 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt (5 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/ExternalCallRejectionHandler.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (30 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnection.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnectionFactory.kt (2 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt (4 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/stats/StatsTracer.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/receivers/LeaveCallBroadcastReceiver.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/common/SocketActions.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/coordinator/CoordinatorSocketConnection.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/sfu/SfuSocketConnection.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/SerialProcessor.kt (1 hunks)
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/SubscriberTest.kt (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{kt,java}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,java}: Use Kotlin with JVM toolchain 17; Java is legacy-only
Use 4-space indentation with no trailing whitespace
Avoid wildcard imports

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/sfu/SfuSocketConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/common/SocketActions.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/SerialProcessor.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnectionFactory.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/SubscriberTest.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/coordinator/CoordinatorSocketConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/receivers/LeaveCallBroadcastReceiver.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/ExternalCallRejectionHandler.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/stats/StatsTracer.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,kts}: Use PascalCase for types and Composables (e.g., StreamCallActivity, ParticipantGrid)
Use camelCase for functions and values
Use UPPER_SNAKE_CASE for constants only when truly constant
Prefer explicit visibility modifiers; limit internal leakage across modules
Keep critical RTC paths off the main thread; prefer coroutines with structured scopes
Monitor logging verbosity; rely on StreamVideoImpl.developmentMode for guardrails
Use KDoc (/** ... */) for public APIs and complex subsystems; link to Stream docs when relevant
Group large files with // region judiciously; keep commentary purposeful
Sanitize logs to avoid dumping JWTs, ICE tokens, or call IDs in verbose logs
Pause/resume capture on lifecycle changes; ensure background audio routing is intentional
Validate orientation, aspect ratio, and dynascale handling for both portrait/landscape phones and tablets
Keep concurrency deterministic—use structured coroutines and avoid global scope
Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/sfu/SfuSocketConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/common/SocketActions.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/SerialProcessor.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnectionFactory.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/SubscriberTest.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/coordinator/CoordinatorSocketConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/receivers/LeaveCallBroadcastReceiver.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/ExternalCallRejectionHandler.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/stats/StatsTracer.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
**/*.{kt,java,kts,gradle.kts}

📄 CodeRabbit inference engine (AGENTS.md)

Follow Spotless formatting; ensure custom license headers are in spotless/ directory

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/sfu/SfuSocketConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/common/SocketActions.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/SerialProcessor.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnectionFactory.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/SubscriberTest.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/coordinator/CoordinatorSocketConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/receivers/LeaveCallBroadcastReceiver.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/ExternalCallRejectionHandler.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnection.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/stats/StatsTracer.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
**/src/test/**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

**/src/test/**/*.{kt,kts}: Use descriptive backtick test names (e.g., fun `joining a call publishes participant tracks`())
Use TestBase for fast unit tests and IntegrationTestBase for end-to-end call flows

Files:

  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/SubscriberTest.kt
🧠 Learnings (6)
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/sfu/SfuSocketConnection.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Pause/resume capture on lifecycle changes; ensure background audio routing is intentional

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Keep concurrency deterministic—use structured coroutines and avoid global scope

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Keep critical RTC paths off the main thread; prefer coroutines with structured scopes

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Prefer Jetpack Compose for UI (`stream-video-android-ui-compose`); XML views supported via `stream-video-android-ui-xml`

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to stream-video-android-previewdata/**/*.{kt,kts} : Keep test fixtures in `stream-video-android-previewdata`; avoid duplicating builder logic

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
🧬 Code graph analysis (2)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (3)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/AndroidUtils.kt (1)
  • safeCall (129-139)
stream-video-android-core/src/main/proto/video/sfu/event/events.pb.go (6)
  • ICETrickle (682-689)
  • ICETrickle (704-704)
  • ICETrickle (719-721)
  • JoinRequest (1180-1216)
  • JoinRequest (1231-1231)
  • JoinRequest (1246-1248)
stream-video-android-core/src/main/proto/video/sfu/models/models.pb.go (3)
  • ICETrickle (1750-1758)
  • ICETrickle (1773-1773)
  • ICETrickle (1788-1790)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt (4)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (2)
  • fastReconnect (1766-1819)
  • cleanup (953-1000)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/AndroidUtils.kt (1)
  • safeCall (129-139)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt (1)
  • cleanup (219-243)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/scope/ScopeProviderImpl.kt (1)
  • cleanup (91-99)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: build
  • GitHub Check: Spotless check
  • GitHub Check: API check
  • GitHub Check: Unit Tests
  • GitHub Check: Build / compose apks
🔇 Additional comments (34)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/ExternalCallRejectionHandler.kt (1)

71-71: LGTM: Reason parameter improves traceability.

The addition of "rejected-on-wearable" as a reason parameter to the call.leave() invocation enhances visibility into call rejection flows triggered from wearable devices, supporting the PR's goal of improving visibility and stats collection. The kebab-case format is consistent with the predominant pattern used across other reason parameters in the codebase (e.g., "accepted-on-another-device", "start-ringing-timeout", "call-service-end-call-incomming").

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/receivers/LeaveCallBroadcastReceiver.kt (1)

40-40: LGTM!

Passing a descriptive reason string to leave() improves traceability and aligns with the PR objective to enhance stats collection and visibility of the disconnect/leave flow.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt (1)

242-242: LGTM!

Adding the "client-cleanup" reason string provides clear context for tracing and stats when the call is left due to client cleanup.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnection.kt (1)

133-150: Good enhancement to connection health detection.

Expanding isFailedOrClosed() to also consider IceConnectionState.FAILED and IceConnectionState.CLOSED is a robust improvement. ICE layer failures can occur independently of PeerConnection state transitions, so checking both ensures more accurate failure detection for reconnect logic.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/stats/StatsTracer.kt (1)

242-244: LGTM!

This fix ensures the first stats report includes all fields rather than an empty diff. When oldStats.isEmpty(), skipping the value comparison correctly captures the complete initial state for delta compression.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/common/SocketActions.kt (1)

81-85: LGTM!

Clean addition of the connectionFailed callback with a sensible default empty lambda, maintaining backward compatibility.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/sfu/SfuSocketConnection.kt (1)

148-164: LGTM with a minor observation.

The error handling correctly routes exceptions to the connectionFailed callback. However, consider adding a debug log before invoking connectionFailed to aid troubleshooting connection issues.

The 500ms delay at line 157 with the comment "Wait for the connection to settle" seems like a workaround. If this is intentional, consider documenting why this settling period is necessary.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt (5)

748-748: LGTM!

Adding the reason string "accepted-on-another-device" improves traceability for debugging cross-device call scenarios.


776-776: LGTM!

Reason string correctly identifies this leave path.


785-790: LGTM!

Both CallEndedEvent and CallEndedSfuEvent leave paths now have clear reason strings for diagnostics.


1192-1192: LGTM!

Reason string clarifies this is a rejection-triggered leave.


1304-1304: LGTM!

Timeout-based leave is now clearly identified.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/socket/coordinator/CoordinatorSocketConnection.kt (1)

209-224: LGTM!

The error handling correctly routes exceptions to the connectionFailed callback. Implementation is consistent with the interface contract.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (9)

232-239: LGTM!

Visibility change from private to internal for sfuTracer is appropriate to allow access from other components within the module for enhanced tracing capabilities.


544-564: Lifecycle observer registration looks correct.

The observer is properly guarded against null and registered on the Main dispatcher. Good use of safeCall to handle potential exceptions during lifecycle registration.


625-634: Pending ICE trickle events flush logic is correct.

The pattern of copying the list, clearing it, then iterating over the copy prevents concurrent modification issues. The synchronized list backing ensures thread-safe access.


1434-1435: LGTM!

Using Collections.synchronizedList for iceTricklePendingEvents ensures thread-safe access for the ICE candidate buffering.


1444-1464: ICE candidate buffering logic is well implemented.

The check for SfuSocketState.Connected before sending, and buffering candidates otherwise, prevents race conditions during connection establishment. This aligns well with the PR objective of improving the reconnect flow.


1580-1586: Good defensive pattern for tracer rollback.

Capturing tracer slices before serialization and rolling them back on failure (lines 1624-1631) prevents data loss when stats transmission fails.


1082-1086: Empty callbacks intentionally defer handling.

The empty callbacks for rejoin and fastReconnect in createSubscriber and createPublisher are documented with comments. The actual reconnect logic is handled elsewhere based on SFU error events.


1770-1786: Fast reconnect tracing is comprehensive.

Good addition of tracing for the fast reconnect flow and join request details. This will significantly improve debugging of reconnection scenarios.


1840-1846: Leave with reason properly cleans up resources.

The mediaScope.cancel() and tracing with the reason string provide good cleanup and observability for session termination.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt (1)

280-286: LGTM! Good error recovery mechanism.

Calling rejoin() when setRemoteDescription fails during subscriber negotiation is a reasonable recovery strategy to re-establish the session. This aligns with the PR objective of improving the reconnect flow.

stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/call/connection/SubscriberTest.kt (1)

75-76: LGTM! Test correctly updated to match constructor changes.

The test appropriately adds a RelaxedMockK for SfuConnectionModule and passes it to the Subscriber constructor, maintaining test consistency with the production code changes.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/StreamPeerConnectionFactory.kt (1)

469-481: LGTM! Dependency correctly threaded through factory.

The sfuConnectionModule parameter is properly added to makeSubscriber and forwarded to the Subscriber constructor. This follows good dependency injection patterns.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt (3)

682-694: LGTM! Symmetric ICE monitoring for publisher.

The publisher ICE state monitoring mirrors the existing subscriber pattern, providing consistent reconnection behavior for both peer connections. The implementation correctly restarts ICE on FAILED or DISCONNECTED states.


755-755: LGTM! Reason parameters improve observability.

Adding reason parameters to fastReconnect, rejoin, and leave methods with sensible defaults enhances debugging and tracing capabilities, aligning with the PR objective of improving visibility.

Also applies to: 784-784, 925-925


1742-1745: LGTM!

The Debug helper correctly exposes the reason parameter for testing reconnect scenarios.

stream-video-android-core/api/stream-video-android-core.api (6)

7574-7575: API signature change with default parameter support looks correct.

The addition of a String reason parameter to fastReconnect with the corresponding $default synthetic method maintains backward compatibility for Kotlin consumers. Java consumers will need to update their calls to provide the reason parameter explicitly.


7612-7613: LGTM!

The leave method now accepts a reason string with default parameter support, consistent with the other connection lifecycle methods.


7631-7632: LGTM!

The rejoin method follows the same pattern as fastReconnect and leave, adding a reason parameter for improved visibility into the reconnect flow.


12984-12985: Interface method updated with error handling callback.

The whenConnected method now accepts a second Function2 parameter (presumably for error/timeout handling based on the AI summary mentioning "fallback/alternative path"). The $default synthetic method allows existing Kotlin callers to omit this parameter.


13163-13163: LGTM!

Implementation correctly matches the updated SocketActions interface signature.


13373-13373: LGTM!

Implementation correctly matches the updated SocketActions interface signature, ensuring consistency across all socket connection types.

Note on API compatibility: These changes add new required parameters to public API methods (fastReconnect, leave, rejoin, whenConnected). While Kotlin consumers benefit from default parameter support via the $default synthetic methods, Java consumers will experience a breaking change and must update their calls to provide the new parameters. Ensure this is documented in release notes/changelog if this library is consumed externally.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 19, 2025

SDK Size Comparison 📏

SDK Before After Difference Status
stream-video-android-core 11.94 MB 11.94 MB 0.00 MB 🟢
stream-video-android-ui-xml 5.68 MB 5.68 MB 0.00 MB 🟢
stream-video-android-ui-compose 6.27 MB 6.27 MB 0.00 MB 🟢

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (1)

1624-1631: Consider safeguarding rollback operations in error handler.

If slice.rollback() throws an exception, it could mask the original send failure. Consider wrapping the rollback loop in safeCall or handling rollback failures gracefully.

🔎 Proposed safeguard
             } catch (e: Exception) {
                 sfuTracer.trace("send-stats-failed", "${e.message}")
-                tracerSlices.forEach { slice -> slice.rollback() }
+                tracerSlices.forEach { slice -> 
+                    safeCall { slice.rollback() }
+                }
             }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7526af8 and 79290ea.

📒 Files selected for processing (4)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (30 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt (1 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/SerialProcessor.kt (2 hunks)
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/rtc/RtcSessionTest2.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/SerialProcessor.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{kt,java}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,java}: Use Kotlin with JVM toolchain 17; Java is legacy-only
Use 4-space indentation with no trailing whitespace
Avoid wildcard imports

Files:

  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/rtc/RtcSessionTest2.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,kts}: Use PascalCase for types and Composables (e.g., StreamCallActivity, ParticipantGrid)
Use camelCase for functions and values
Use UPPER_SNAKE_CASE for constants only when truly constant
Prefer explicit visibility modifiers; limit internal leakage across modules
Keep critical RTC paths off the main thread; prefer coroutines with structured scopes
Monitor logging verbosity; rely on StreamVideoImpl.developmentMode for guardrails
Use KDoc (/** ... */) for public APIs and complex subsystems; link to Stream docs when relevant
Group large files with // region judiciously; keep commentary purposeful
Sanitize logs to avoid dumping JWTs, ICE tokens, or call IDs in verbose logs
Pause/resume capture on lifecycle changes; ensure background audio routing is intentional
Validate orientation, aspect ratio, and dynascale handling for both portrait/landscape phones and tablets
Keep concurrency deterministic—use structured coroutines and avoid global scope
Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Files:

  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/rtc/RtcSessionTest2.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
**/*.{kt,java,kts,gradle.kts}

📄 CodeRabbit inference engine (AGENTS.md)

Follow Spotless formatting; ensure custom license headers are in spotless/ directory

Files:

  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/rtc/RtcSessionTest2.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
**/src/test/**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

**/src/test/**/*.{kt,kts}: Use descriptive backtick test names (e.g., fun `joining a call publishes participant tracks`())
Use TestBase for fast unit tests and IntegrationTestBase for end-to-end call flows

Files:

  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/rtc/RtcSessionTest2.kt
🧠 Learnings (7)
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/src/test/**/*.{kt,kts} : Use descriptive backtick test names (e.g., ``fun `joining a call publishes participant tracks`()``)

Applied to files:

  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/rtc/RtcSessionTest2.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Pause/resume capture on lifecycle changes; ensure background audio routing is intentional

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Keep critical RTC paths off the main thread; prefer coroutines with structured scopes

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Keep concurrency deterministic—use structured coroutines and avoid global scope

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Prefer Jetpack Compose for UI (`stream-video-android-ui-compose`); XML views supported via `stream-video-android-ui-xml`

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to stream-video-android-previewdata/**/*.{kt,kts} : Keep test fixtures in `stream-video-android-previewdata`; avoid duplicating builder logic

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
🧬 Code graph analysis (1)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (3)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/AndroidUtils.kt (1)
  • safeCall (129-139)
stream-video-android-core/src/main/proto/video/sfu/event/events.pb.go (9)
  • ICETrickle (682-689)
  • ICETrickle (704-704)
  • ICETrickle (719-721)
  • JoinRequest (1180-1216)
  • JoinRequest (1231-1231)
  • JoinRequest (1246-1248)
  • LeaveCallRequest (880-887)
  • LeaveCallRequest (902-902)
  • LeaveCallRequest (917-919)
stream-video-android-core/src/main/proto/video/sfu/models/models.pb.go (3)
  • ICETrickle (1750-1758)
  • ICETrickle (1773-1773)
  • ICETrickle (1788-1790)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: Build / compose apks
  • GitHub Check: build
  • GitHub Check: Spotless check
  • GitHub Check: Unit Tests
  • GitHub Check: API check
🔇 Additional comments (13)
stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/rtc/RtcSessionTest2.kt (1)

464-473: LGTM! Test updated to match new whenConnected API.

The mock correctly reflects the updated whenConnected signature with three parameters (timeout, connectionFailed, connected). The test simulates the success path by invoking the third callback with a connection ID.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (12)

625-634: LGTM! ICE candidate buffering implemented correctly.

The code properly flushes buffered ICE candidates when the WebSocket connection is established. The snapshot-clear-process pattern prevents concurrent modification issues.


781-788: LGTM! Connection failure callback properly integrated.

The connectionFailed callback correctly traces the failure and sends stats for visibility into connection issues.


985-994: LGTM! Lifecycle observer cleanup properly implemented.

The cleanup correctly removes the observer on the main thread and nulls out the reference. The assignment bug from the previous review has been fixed.


1351-1354: LGTM! ICE restart tracing improved with reason string.

The addition of a reason parameter to restartIce() improves visibility into why ICE restarts occur.


1434-1435: LGTM! ICE candidate buffering properly implemented.

The iceTricklePendingEvents buffer ensures ICE candidates are queued until the WebSocket connection is ready, preventing lost candidates during connection establishment.


1445-1464: LGTM! ICE candidate sending logic properly gated on connection state.

The logic correctly buffers ICE candidates when the WebSocket is not connected and sends them immediately when connected. The retry mechanism handles transient failures gracefully.


1793-1798: LGTM! Fast reconnect flow properly enhanced with failure handling.

The connectionFailed callback ensures stats are collected and traced even when fast reconnect fails, improving visibility into reconnection issues.


249-249: LGTM! Reconnect calls enhanced with explicit reason strings.

The addition of reason parameters improves visibility into why reconnections occur. Error messages are included for debugging without apparent PII leakage.

Also applies to: 662-662, 666-666


763-763: LGTM! Unified session ID properly propagated to SFU.

The unified_session_id field helps the SFU identify users across reconnects, as documented in the proto definition.

Also applies to: 1777-1777


1770-1770: LGTM! Enhanced tracing improves visibility into session lifecycle.

The additional trace events for fast reconnect, join requests, and leave operations provide better observability without exposing sensitive data.

Also applies to: 1786-1786, 1846-1846


544-564: Observer is properly cleaned up; no race condition exists.

The lifecycle observer is explicitly removed in cleanup() at lines 985–994 via lifecycle.removeObserver(), wrapped in the same coroutineScope and Main dispatcher as the setup. Both setup and cleanup are tied to supervisorJob, so when cleanup() calls supervisorJob.cancel() (line 997), any pending setup coroutine is cancelled before the observer is added. If setup completes first, cleanup safely removes it. No leak or race condition occurs.

Likely an incorrect or invalid review comment.


1080-1086: Empty reconnect callbacks are properly delegated to higher-level handlers.

The rejoin and fastReconnect callbacks in the Subscriber are intentionally empty—reconnection logic is centralized at the RtcSession and Call levels. The connectionFailed callback in sfuConnectionModule.socketConnection.whenConnected() handles socket failures by tracing and collecting stats, while successful connections validate peer state and delegate to call.rejoin() when needed. All reconnection paths flow through Call.fastReconnect() and Call.rejoin(), ensuring consistent handling.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt (1)

283-286: Consider passing an explicit reason to rejoin().

The rejoin call on negotiation failure is appropriate for recovery. However, passing an explicit reason (e.g., rejoin("negotiate-setRemoteDescription-failed")) would improve observability and tracing, helping diagnose reconnect triggers in production.

🔎 Proposed enhancement
            .onErrorSuspend {
                tracer.trace("negotiate-error-setremotedescription", it.message ?: "unknown")
-               rejoin()
+               rejoin("negotiate-setRemoteDescription-failed")
            }
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (2)

625-634: Consider synchronizing the drain operation.

The pending ICE trickle event drain creates a race condition window between toList() (line 629) and clear() (line 630). If a new candidate arrives on another thread during this window, it could be lost.

🔎 Proposed fix
                    is SfuSocketState.Connected -> {
                        call.state._connection.value =
                            RealtimeConnection.Connected

-                       val pendingTrickleEvents = iceTricklePendingEvents.toList()
-                       iceTricklePendingEvents.clear()
-                       pendingTrickleEvents.forEach {
-                           sendIceCandidate(it.first, it.second)
-                       }
+                       synchronized(iceTricklePendingEvents) {
+                           val pendingTrickleEvents = iceTricklePendingEvents.toList()
+                           iceTricklePendingEvents.clear()
+                           pendingTrickleEvents.forEach {
+                               sendIceCandidate(it.first, it.second)
+                           }
+                       }
                    }

814-814: Consider tying mediaScope to the supervisor job.

The mediaScope correctly isolates media-related coroutines. However, it's currently independent of the supervisorJob, which means media coroutines might outlive the RtcSession if cleanup() is called without leaveWithReason().

Alternative approach
-   internal val mediaScope = CoroutineScope(Dispatchers.Default)
+   internal val mediaScope = CoroutineScope(Dispatchers.Default + supervisorJob)

This would ensure mediaScope is automatically cancelled when supervisorJob.cancel() is called in cleanup() (line 997), providing more robust cleanup semantics while still preserving the ability to explicitly cancel via leaveWithReason().

Note: Based on past review comments, the current design intentionally keeps mediaScope alive during rejoin. This suggestion offers an alternative that ties lifecycle more explicitly to the supervisor job.

Based on learnings, ...

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 79290ea and 7df7f37.

📒 Files selected for processing (2)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (30 hunks)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{kt,java}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,java}: Use Kotlin with JVM toolchain 17; Java is legacy-only
Use 4-space indentation with no trailing whitespace
Avoid wildcard imports

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,kts}: Use PascalCase for types and Composables (e.g., StreamCallActivity, ParticipantGrid)
Use camelCase for functions and values
Use UPPER_SNAKE_CASE for constants only when truly constant
Prefer explicit visibility modifiers; limit internal leakage across modules
Keep critical RTC paths off the main thread; prefer coroutines with structured scopes
Monitor logging verbosity; rely on StreamVideoImpl.developmentMode for guardrails
Use KDoc (/** ... */) for public APIs and complex subsystems; link to Stream docs when relevant
Group large files with // region judiciously; keep commentary purposeful
Sanitize logs to avoid dumping JWTs, ICE tokens, or call IDs in verbose logs
Pause/resume capture on lifecycle changes; ensure background audio routing is intentional
Validate orientation, aspect ratio, and dynascale handling for both portrait/landscape phones and tablets
Keep concurrency deterministic—use structured coroutines and avoid global scope
Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
**/*.{kt,java,kts,gradle.kts}

📄 CodeRabbit inference engine (AGENTS.md)

Follow Spotless formatting; ensure custom license headers are in spotless/ directory

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
🧠 Learnings (7)
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Pause/resume capture on lifecycle changes; ensure background audio routing is intentional

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Keep critical RTC paths off the main thread; prefer coroutines with structured scopes

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Keep concurrency deterministic—use structured coroutines and avoid global scope

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/src/test/**/*.{kt,kts} : Use descriptive backtick test names (e.g., ``fun `joining a call publishes participant tracks`()``)

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Prefer Jetpack Compose for UI (`stream-video-android-ui-compose`); XML views supported via `stream-video-android-ui-xml`

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to stream-video-android-previewdata/**/*.{kt,kts} : Keep test fixtures in `stream-video-android-previewdata`; avoid duplicating builder logic

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt
🧬 Code graph analysis (2)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt (1)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt (2)
  • rejoin (784-846)
  • rejoin (1722-1726)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (3)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/AndroidUtils.kt (1)
  • safeCall (129-139)
stream-video-android-core/src/main/proto/video/sfu/event/events.pb.go (6)
  • ICETrickle (682-689)
  • ICETrickle (704-704)
  • ICETrickle (719-721)
  • JoinRequest (1180-1216)
  • JoinRequest (1231-1231)
  • JoinRequest (1246-1248)
stream-video-android-core/src/main/proto/video/sfu/models/models.pb.go (3)
  • ICETrickle (1750-1758)
  • ICETrickle (1773-1773)
  • ICETrickle (1788-1790)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: Build / compose apks
  • GitHub Check: Unit Tests
  • GitHub Check: API check
  • GitHub Check: Spotless check
  • GitHub Check: build
🔇 Additional comments (16)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/connection/Subscriber.kt (2)

30-30: LGTM!

The import addition correctly supports the new sfuConnectionModule constructor parameter, enabling SFU connection module integration into the Subscriber.


80-80: LGTM!

The sfuConnectionModule parameter correctly integrates the SFU connection module into Subscriber's constructor, supporting enhanced signaling and reconnect flows.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt (14)

29-33: LGTM!

The lifecycle-related imports and coroutine utilities correctly support the new lifecycle observer integration and mediaScope management introduced in this PR.

Also applies to: 88-88, 102-102, 107-107, 120-120


232-239: LGTM!

The visibility change from private to internal appropriately exposes the sfuTracer for use across reconnect and stats collection flows, improving tracing visibility as intended by the PR.


249-249: LGTM!

Adding explicit reason strings to fastReconnect() and rejoin() calls significantly improves observability of reconnect triggers, making it easier to diagnose connection issues in production logs.

Also applies to: 662-662, 666-666


511-512: LGTM!

The lifecycle observer correctly traces foreground/background transitions on the Main thread. The implementation properly uses withContext(Dispatchers.Main) for lifecycle operations and safeCall for exception handling.

Minor observation: The null check at line 546 is redundant since lifecycleObserver is always null during initialization, but this doesn't affect correctness.

Also applies to: 544-564


1444-1465: LGTM!

The rewritten sendIceCandidate properly handles connection state by buffering ICE candidates when the SFU socket is not connected and sending them immediately when connected. The retry logic (3 attempts) provides appropriate resilience.


763-763: LGTM!

Including unified_session_id in JoinRequest constructions correctly propagates session context to the SFU, enabling proper user identification across reconnects as intended by the PR.

Also applies to: 1777-1777


781-788: LGTM!

The connectionFailed callback enhances error visibility by tracing connection failures and collecting stats when connection attempts fail, directly supporting the PR's stats collection improvements.


985-994: LGTM!

The lifecycle observer cleanup correctly removes the observer on the Main thread using withContext(Dispatchers.Main) and properly nullifies the reference. The previously reported bug (using == instead of =) has been fixed.


1071-1090: LGTM!

The sfuConnectionModule is correctly wired into the Subscriber, and the empty rejoin/fastReconnect callbacks indicate intentional centralization of reconnect logic elsewhere in the call flow, reducing duplication.


1165-1183: LGTM!

The rejoin() call now includes an explicit reason ("Publisher#ERROR_CODE_REQUEST_VALIDATION_FAILED"), improving observability. The empty fastReconnect callback aligns with the centralized reconnect handling pattern.


1347-1361: LGTM!

The ICE restart handling now properly handles null publisher by queuing the event for later processing, and includes an explicit reason string for better tracing.


1544-1641: LGTM!

The stats collection improvements correctly implement transactional semantics by collecting tracer slices and rolling them back on failure. This prevents inconsistent state and improves reliability of stats reporting.


1766-1819: LGTM!

The fast reconnect flow enhancements add comprehensive tracing, proper connection failure handling, and explicit reason strings. The dual peer connection health check (line 1800) correctly validates both subscriber and publisher states before proceeding.


1840-1846: LGTM!

The leaveWithReason() method correctly cancels the mediaScope when truly leaving the call (as opposed to rejoining) and adds appropriate tracing for the leave operation.

…ascope is cancelled

2. Some renaming of variables and method
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7df7f37 and a5792a8.

📒 Files selected for processing (1)
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt (13 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{kt,java}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,java}: Use Kotlin with JVM toolchain 17; Java is legacy-only
Use 4-space indentation with no trailing whitespace
Avoid wildcard imports

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{kt,kts}: Use PascalCase for types and Composables (e.g., StreamCallActivity, ParticipantGrid)
Use camelCase for functions and values
Use UPPER_SNAKE_CASE for constants only when truly constant
Prefer explicit visibility modifiers; limit internal leakage across modules
Keep critical RTC paths off the main thread; prefer coroutines with structured scopes
Monitor logging verbosity; rely on StreamVideoImpl.developmentMode for guardrails
Use KDoc (/** ... */) for public APIs and complex subsystems; link to Stream docs when relevant
Group large files with // region judiciously; keep commentary purposeful
Sanitize logs to avoid dumping JWTs, ICE tokens, or call IDs in verbose logs
Pause/resume capture on lifecycle changes; ensure background audio routing is intentional
Validate orientation, aspect ratio, and dynascale handling for both portrait/landscape phones and tablets
Keep concurrency deterministic—use structured coroutines and avoid global scope
Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
**/*.{kt,java,kts,gradle.kts}

📄 CodeRabbit inference engine (AGENTS.md)

Follow Spotless formatting; ensure custom license headers are in spotless/ directory

Files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
🧠 Learnings (1)
📚 Learning: 2025-12-19T09:15:37.256Z
Learnt from: CR
Repo: GetStream/stream-video-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T09:15:37.256Z
Learning: Applies to **/*.{kt,kts} : Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries)

Applied to files:

  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: Unit Tests
  • GitHub Check: build
  • GitHub Check: API check
  • GitHub Check: Spotless check
  • GitHub Check: Build / compose apks
🔇 Additional comments (6)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt (6)

366-372: LGTM!

Adding descriptive reason strings to reconnect calls improves observability and aligns with the PR's goal of enhancing reconnect flow visibility.


580-580: LGTM!

Including the error message in the leave reason provides useful context for debugging ring failures.


682-694: LGTM - Publisher ICE monitoring added symmetrically with subscriber.

The implementation mirrors the subscriber monitoring pattern (lines 696-709), providing consistent handling of ICE connection failures for both peer connections. The null-safe chain ensures graceful handling if session or publisher is unavailable.


755-778: LGTM!

The reason propagation to ReconnectDetails enables better tracing. The stats collection timing (after fastReconnect) was discussed in previous reviews and confirmed as intentional since the stats are sent with the new session ID.


926-982: LGTM - Clean teardown with proper error handling.

The refactored leave/internalLeave functions properly:

  • Cancel and null out monitor jobs (lines 933-936)
  • Use safeCall to protect stats collection from exceptions
  • Sequence cleanup after stats are sent

The safeCall wrapper ensures that if tracing or stats collection fails, it won't prevent the cleanup from executing. Based on learnings, this aligns with ensuring cleanup/teardown paths handle cancellation and failure.


1743-1747: LGTM!

Propagating the reason parameter through the Debug API maintains consistency with the updated fastReconnect signature. The default value "Debug" appropriately identifies the source.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
22.3% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@aleksandar-apostolov aleksandar-apostolov merged commit 13454a4 into develop Dec 19, 2025
11 of 12 checks passed
@aleksandar-apostolov aleksandar-apostolov deleted the stats-improvement branch December 19, 2025 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Enhances an existing feature or code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants