An end wakes a phone; it does not ring one
The matching half of the iOS fix, and this side is where the ghost was actually made for an iPhone receiving an Android hangup. iOS gives no choice: every VoIP push must report a call to CallKit or the app is killed. Ends were marked urgent, urgent means a VoIP push, so every hangup and every busy sent from here arrived on an iPhone as an incoming call. Ends were marked for a real reason: hanging up before the other phone was picked up left it ringing for a caller who had gone. What that missed is that the phone which needs to hear a hangup is, by definition, ringing, and therefore awake: it has a call up, the system keeps it running for one, it is already polling, and the relay answers a held poll through Redis in under half a second. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
255a14b7c2
commit
74429be153
@@ -1636,19 +1636,33 @@ class Conversations(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Whether this envelope has to reach a locked, suspended phone now
|
* Whether this envelope has to RING a locked, suspended phone now
|
||||||
* (spec §6.14). Held out here so it can be tested for what it decides
|
* (spec §6.14). Held out here so it can be tested for what it decides
|
||||||
* rather than for how it is written.
|
* rather than for how it is written.
|
||||||
*
|
*
|
||||||
* The end of a call is as time-critical as its start. Only the offer
|
* Only an offer. An end travels the ordinary wake path, and that is
|
||||||
* was marked, so hanging up before the other phone was picked up sent
|
* not a downgrade: it is the difference between waking a phone and
|
||||||
* the end as ordinary traffic: a backgrounded phone never woke for it
|
* ringing one.
|
||||||
* and went on ringing for a caller who had already gone.
|
*
|
||||||
|
* This matters on the other platform rather than this one. iOS gives
|
||||||
|
* no choice about it: every VoIP push must report a call to CallKit or
|
||||||
|
* the app is killed. So an urgent end from here arrived on an iPhone as
|
||||||
|
* an incoming call -- every hangup, every busy, every end for a call
|
||||||
|
* long over. The owner, on a real handset: "I place a call, it calls me
|
||||||
|
* back." Taking the ghost back afterwards is not a fix, because
|
||||||
|
* somebody has already seen it.
|
||||||
|
*
|
||||||
|
* Ends were marked for a real reason: hanging up before the other
|
||||||
|
* phone was picked up left it ringing for a caller who had gone. But a
|
||||||
|
* ringing phone is not asleep. It has a call up, the system keeps it
|
||||||
|
* running for one, it is already polling, and the relay answers a held
|
||||||
|
* poll through Redis in under half a second. The end arrives on its
|
||||||
|
* own, and a phone that never rang has nothing to stop.
|
||||||
*/
|
*/
|
||||||
fun ringsAPhone(body: String): Boolean {
|
fun ringsAPhone(body: String): Boolean {
|
||||||
if (!body.startsWith("""{"fnc":""")) return false
|
if (!body.startsWith("""{"fnc":""")) return false
|
||||||
val action = Content.parse(body).call?.action
|
val action = Content.parse(body).call?.action
|
||||||
return action == Content.CallSignal.OFFER || action == Content.CallSignal.END
|
return action == Content.CallSignal.OFFER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,13 +56,27 @@ class UrgentCallOfferTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* And so does the end of one. A call that is over has to stop the ring on
|
* But NOT the end of one, and this is the whole of the ghost ringing on
|
||||||
* a phone that is asleep, which takes the same push the offer took: this
|
* the other platform.
|
||||||
* is why hanging up first left the other phone ringing to nobody.
|
*
|
||||||
|
* iOS gives no choice: every VoIP push must report a call to CallKit or
|
||||||
|
* the app is killed. So an urgent end from here arrived on an iPhone as an
|
||||||
|
* incoming call -- every hangup, every busy, every end for a call long
|
||||||
|
* over. Taking the ghost back afterwards is not a fix; somebody has
|
||||||
|
* already seen it.
|
||||||
|
*
|
||||||
|
* Ends were marked for a real reason: hanging up before the other phone
|
||||||
|
* was picked up left it ringing for a caller who had gone. But a ringing
|
||||||
|
* phone is not asleep. It has a call up, the system keeps it running for
|
||||||
|
* one, it is already polling, and the relay answers a held poll through
|
||||||
|
* Redis in under half a second.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun hangingUpIsUrgentToo() {
|
fun hangingUpWakesAPhoneRatherThanRingingIt() {
|
||||||
assertTrue(Conversations.ringsAPhone(signal(Content.CallSignal.END)))
|
assertFalse(
|
||||||
|
Conversations.ringsAPhone(signal(Content.CallSignal.END)),
|
||||||
|
"an urgent end becomes a reported call on an iPhone",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Nothing else does. */
|
/** Nothing else does. */
|
||||||
|
|||||||
Reference in New Issue
Block a user