From f90c6477bb5d395473e70c154defe6a744ce776d Mon Sep 17 00:00:00 2001 From: Paul Hitt Date: Wed, 26 Aug 2026 09:05:31 -0400 Subject: [PATCH] Calling killed the app, twice over MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔴 **Three separate defects, and calling could not work past any of them.** Found by tapping the Call button, which is not covered by any test. **1. The button did nothing.** `requestCall` sets `callPrivacyPrompt`, and the only thing that renders it is `CallBanner` — which was composed inside `SignedIn`, the HOME screen. The chat screen REPLACES that. So the only Call button in the app set a flag nothing was on screen to show, and since the prompt gates placing the call, calling could not be started at all. The button looked simply dead. That also means an incoming call arriving while somebody was reading a thread displayed nothing — directly against the comment above it, which says an incoming call must be visible whatever screen somebody is on. CallBanner is now hoisted above the screen switch. **2. Placing a call SIGABRTed the process.** The manifest never declared ACCESS_NETWORK_STATE. WebRTC's NetworkMonitor calls ConnectivityManager.getNetworkState() on its own network thread, that throws without the permission, and the JNI layer immediately asserts RTC_CHECK(!env->ExceptionCheck()) — turning a Java exception into abort(). No AndroidRuntime trace, nothing catchable, the process just vanishes: Check failed: !env->ExceptionCheck() Fatal signal 6 (SIGABRT) in tid network_thread **3. Hanging up SIGABRTed it again, re-entrantly.** `onIceConnectionChange` treated CLOSED as a reason to hang up — but CLOSED is exactly what our own `close()` produces. So Hang up called hangUp, which closed the connection, which fired CLOSED on the signaling thread, which re-entered hangUp, which did a BLOCKING NETWORK SEND and disposed the connection it was running inside. hangUp is now re-entrancy-guarded, CLOSED is a no-op (we caused it), FAILED still hangs up, `dispose()` is guarded like the `close()` beside it already was, and the courtesy "call ended" signal is best-effort rather than something that can take the teardown down with it. The observer body cannot throw at all now, because WebRTC treats any escaping exception as fatal. Verified on the emulator: dialling brings up Mute/Hang up and the app survives both dialling and hanging up. ⚠️ Still unproven: whether AUDIO actually flows. That needs two live endpoints and a TURN server, and none is deployed. What is fixed is that calling is reachable and no longer kills the app. Co-Authored-By: Claude Opus 5 --- app/src/main/AndroidManifest.xml | 11 +++++ .../io/flamenet/messenger/app/CallManager.kt | 42 +++++++++++++++++-- .../io/flamenet/messenger/app/MainActivity.kt | 17 ++++++-- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3205b75..298a37d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,17 @@ + + +