Skip to content

Fix app doesn't go into reconnection mode in some cases#320

Merged
dimadesu merged 1 commit into
mainfrom
reconnect-mode-doesnt-start
May 25, 2026
Merged

Fix app doesn't go into reconnection mode in some cases#320
dimadesu merged 1 commit into
mainfrom
reconnect-mode-doesnt-start

Conversation

@dimadesu
Copy link
Copy Markdown
Owner

@dimadesu dimadesu commented May 24, 2026

Root cause — two problems combining to suppress reconnection:

  1. Race condition (CameraStreamerService sets NOT_STREAMING ~4ms before isStreamingFlow emits false). The old code read streamStatus.value as previousStatus, so by the time the isStreamingFlow observer fired, the status was already NOT_STREAMINGwasStreaming = false → reconnect skipped silently.

  2. Silent socket close — when you turn WiFi off in Android Settings, the OS closes the SRT UDP socket cleanly (no error), so no ClosedException is emitted to throwableFlow. The isStreamingFlow observer was the only remaining reconnect path — but bug Foreground service #1 broke it.

Fix — in the isStreamingFlow observer, replaced the unreliable previousStatus check with a var previouslyStreaming local variable that tracks the actual previous value of the flow itself. It's initialized from isStreamingFlow.value (handles already-streaming case on attach), set to true when isStreaming=true fires, used as wasStreaming when isStreaming=false fires, then reset to false. This is completely independent of the service's status updates, so the race condition can no longer suppress reconnection.


The diff is clean and minimal. Let me analyze potential behavioral changes:

Scenarios that now work differently:

Scenario Old behavior New behavior Concern?
WiFi off → SRT socket silently closed wasStreaming=false → no reconnect wasStreaming=true → reconnect triggers Desired fix
Service observer fires NOT_STREAMING before isStreamingFlow=false Race → reconnection suppressed Race irrelevant, tracked independently Desired fix

Scenarios that remain unchanged:

  1. User taps StopuserStoppedManually=true is set before anything else happens, so the wasStreaming check never reaches the reconnect branch. No change.

  2. Already reconnectingisReconnecting=true guard remains, prevents duplicate triggers. No change.

  3. Stream that never started (isStreamingFlow was never true) — previouslyStreaming initializes from isStreamingFlow.value which is false, and is never set to true, so wasStreaming=false → goes to "Stream never started" path. No change.

  4. Service rebind after process deathpreviouslyStreaming re-initializes from current isStreamingFlow.value. If the stream was running, it picks up true; if not, false. Same semantics as before.

One edge case worth noting:

If isStreamingFlow emits truefalsefalse rapidly (e.g., flicker), the first false sets previouslyStreaming = false, so the second false correctly gets wasStreaming=false. No double-reconnect. Safe.

Verdict: The fix is safe. The only behavioral change is that the reconnection now correctly triggers when WiFi drops silently — no other paths are affected.

**Root cause** — two problems combining to suppress reconnection:

1. **Race condition** (`CameraStreamerService` sets `NOT_STREAMING` ~4ms before `isStreamingFlow` emits `false`). The old code read `streamStatus.value` as `previousStatus`, so by the time the `isStreamingFlow` observer fired, the status was already `NOT_STREAMING` → `wasStreaming = false` → reconnect skipped silently.

2. **Silent socket close** — when you turn WiFi off in Android Settings, the OS closes the SRT UDP socket cleanly (no error), so no `ClosedException` is emitted to `throwableFlow`. The `isStreamingFlow` observer was the *only* remaining reconnect path — but bug #1 broke it.

**Fix** — in the `isStreamingFlow` observer, replaced the unreliable `previousStatus` check with a `var previouslyStreaming` local variable that tracks the actual previous value of the flow itself. It's initialized from `isStreamingFlow.value` (handles already-streaming case on attach), set to `true` when `isStreaming=true` fires, used as `wasStreaming` when `isStreaming=false` fires, then reset to `false`. This is completely independent of the service's status updates, so the race condition can no longer suppress reconnection.

---

The diff is clean and minimal. Let me analyze potential behavioral changes:

**Scenarios that now work differently:**

| Scenario | Old behavior | New behavior | Concern? |
|----------|-------------|-------------|----------|
| WiFi off → SRT socket silently closed | `wasStreaming=false` → no reconnect | `wasStreaming=true` → reconnect triggers | **Desired fix** |
| Service observer fires `NOT_STREAMING` before `isStreamingFlow=false` | Race → reconnection suppressed | Race irrelevant, tracked independently | **Desired fix** |

**Scenarios that remain unchanged:**

1. **User taps Stop** — `userStoppedManually=true` is set *before* anything else happens, so the `wasStreaming` check never reaches the reconnect branch. No change.

2. **Already reconnecting** — `isReconnecting=true` guard remains, prevents duplicate triggers. No change.

3. **Stream that never started** (`isStreamingFlow` was never `true`) — `previouslyStreaming` initializes from `isStreamingFlow.value` which is `false`, and is never set to `true`, so `wasStreaming=false` → goes to "Stream never started" path. No change.

4. **Service rebind after process death** — `previouslyStreaming` re-initializes from current `isStreamingFlow.value`. If the stream was running, it picks up `true`; if not, `false`. Same semantics as before.

**One edge case worth noting:**

If `isStreamingFlow` emits `true` → `false` → `false` rapidly (e.g., flicker), the first `false` sets `previouslyStreaming = false`, so the second `false` correctly gets `wasStreaming=false`. No double-reconnect. Safe.

**Verdict:** The fix is safe. The only behavioral change is that the reconnection now correctly triggers when WiFi drops silently — no other paths are affected.
@dimadesu dimadesu force-pushed the reconnect-mode-doesnt-start branch 2 times, most recently from 5e1f7c8 to a4ed89b Compare May 25, 2026 01:25
@dimadesu dimadesu merged commit 8d5bfe5 into main May 25, 2026
@dimadesu dimadesu deleted the reconnect-mode-doesnt-start branch May 25, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant