Stop the ring on this account's other devices when one answers

The answer goes to the caller only, so a second device on the same
account kept ringing for its full minute after the first picked up, and
then filed the call as missed. The device that answers or declines now
sends call-end to its own account's other devices, reason
answered-elsewhere or declined-elsewhere. A device still ringing for that
call stops and files nothing; one already on the call ignores it.

A send to this account's own devices carries no delivery key or key-log
prelude and is not refused for going unsealed. Its failures are logged
and never touch the call on this device.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-09-11 12:50:59 -04:00
co-authored by Claude Opus 5
parent f6506292e7
commit 43baac80b7
5 changed files with 160 additions and 6 deletions
@@ -568,6 +568,10 @@ class Conversations(
toUserId: Long,
text: String,
): Sent {
// This account's own other devices. They need no delivery key or
// key-log gossip from us, and sealing hides nothing from a relay that
// already knows the account is talking to itself.
val toSelf = toUserId == myUserId
// The DIRECTORY, not the key route.
val served = relay.listDirectory(toUserId)
val directory = served.devices
@@ -652,13 +656,17 @@ class Conversations(
// ios#16, refuse rather than degrade, for the people who would rather a
// message did not go than go attributable.
if (!sealed && requireSealed()) throw RelayException.SealingRequired()
if (!sealed && !toSelf && requireSealed()) throw RelayException.SealingRequired()
val preludes =
listOfNotNull(
ourDeliveryKeyEnvelopeFor(toUserId),
ourKeyLogHeadEnvelopeFor(toUserId),
)
if (toSelf) {
emptyList()
} else {
listOfNotNull(
ourDeliveryKeyEnvelopeFor(toUserId),
ourKeyLogHeadEnvelopeFor(toUserId),
)
}
for (bundle in bundles) {
if (bundle.deviceId == identity.deviceId) continue // never talk to ourselves