Make a call that goes nowhere say so

Android's Session.sendCallSignal called the send path and threw the result
away. That result is the same Sent every other send returns - how many devices
took it, which failed - so an offer that reached NOBODY was indistinguishable
from one that rang. The caller watched "Calling..." for a signal that had gone
nowhere, and neither device had anything to say so. The same shape as the
message-loss bug fixed earlier today, on the one path where the UI never shows
a delivery banner because a call is placed from inside a WebRTC callback.

Also instruments the drops. An incoming offer can be discarded in five places
in iOS's CallManager - already-ended, older than the dial timeout, busy, wrong
state, unknown id - and every one was a bare `return`. Working out which of the
five happened costs more than the logging does. Same treatment E2EInbox.dropped
already gives ingest, and for the same reason.

Not a fix for "Android calls iOS and nothing happens" - I have not reproduced
that. It rules out and instruments: the wire shapes match, iOS builds its
CallManager eagerly at sign-in so onCallSignal is always hooked, Android sets no
from_addr so the federated fork is not swallowing it, and every clock (mac,
host, relay, db) agrees within a second so the staleness check is not
misfiring. The next attempt will say where it stopped instead of nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-08-27 19:20:16 -04:00
co-authored by Claude Opus 5
parent 780dc50652
commit b74cc1907f
3 changed files with 34 additions and 3 deletions
@@ -182,6 +182,8 @@ class CallManager(
private var pendingOffer: Pair<Long, Content.CallSignal>? = null
fun onSignal(fromUser: Long, incoming: Content.CallSignal, turn: JSONObject?) {
android.util.Log.i("fnmsg-call",
"call signal in: ${incoming.action} from $fromUser state=$state")
when (incoming.action) {
Content.CallSignal.OFFER -> {
// ⚠️ One call at a time. A second offer while busy is declined
@@ -1026,6 +1026,14 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
val current = session ?: return@launch
val manager = callManager()
val turn = withContext(Dispatchers.IO) { current.turnCredentials() }
// 🔴 **Whether the offer left, and to how many devices.** "I called
// them and nothing happened on the other phone" had no evidence on
// either side: the send path reports partial delivery to the UI as a
// banner, but a call places its offer from inside the WebRTC callback
// and that banner is never shown. A call to somebody with no registered
// device looked identical to a call that rang.
android.util.Log.i("fnmsg-call",
"placing call to $peerId, turn=${if (turn == null) "none" else "yes"}")
update {
it.copy(call = Phase.CallState(
state = CallManager.State.DIALLING,
@@ -397,9 +397,30 @@ class Session(
* what you typed in it is not acceptable. The delivery counts still tell
* the caller what happened.
*/
/** Send one call signal to a peer. */
fun sendCallSignal(peerUserId: Long, signal: io.flamenet.e2e.Content.CallSignal) {
conversations.sendCallSignal(peerUserId, signal)
/**
* Send one call signal to a peer.
*
* 🔴 **The result used to be discarded.** `sendCallSignal` returns the same
* `Sent` every other send does — how many devices took it, and which
* failed — and this threw it away. So an offer that reached NOBODY (the
* peer signed out, no registered device, a session that would not open)
* was indistinguishable from one that rang: the caller sat watching
* "Calling…" for a signal that had gone nowhere, and there was nothing on
* either device to say so. Exactly the shape of the message-loss bug on the
* text path, on the one path where the UI never shows a delivery banner.
*
* Returns it now, and logs the zero case, because a call is placed from
* inside a WebRTC callback where there is nowhere to put a banner.
*/
fun sendCallSignal(peerUserId: Long, signal: io.flamenet.e2e.Content.CallSignal):
Conversations.Sent {
val sent = conversations.sendCallSignal(peerUserId, signal)
if (!sent.reachedSomeone) {
android.util.Log.w("fnmsg-call",
"call signal ${signal.action} to $peerUserId reached NO devices: " +
sent.failed.entries.joinToString { "${it.key}=${it.value}" })
}
return sent
}
/** TURN credentials, or null when this relay has none configured. */