fix: allow self-managed calls to numbers Android classifies as emergency (WT-1730)#349
Merged
SERDUN merged 5 commits intoJul 16, 2026
Merged
Conversation
TelecomManager.placeCall() blocks a self-managed PhoneAccount from ever placing a call to a number the OS classifies as emergency, based purely on the tel: address matching the device/SIM-region emergency-number list - regardless of whether the number is actually reachable as one. This silently drops calls to legitimate internal PBX extensions that happen to collide with that list (e.g. an extension literally named 112 or 911). Building the outgoing Uri as a sip: address instead of tel: sidesteps the check entirely, since it only matches tel: addresses. The real number keeps flowing unchanged via CallMetadata into onCreateOutgoingConnection, which never reads the Uri - so this only affects what Telecom itself sees for its own emergency classification, not what actually gets dialed.
…coy sip: host Same RFC-reserved, never-resolving intent (RFC 9476 .internal instead of RFC 2606 .invalid), just a more identifiable name for this address in logs.
The sip: scheme change means Android's Telecom never classifies our outgoing calls as emergency anymore, so the code path that used to catch and report an emergency-number failure can never run: - removed EmergencyNumberException and its throw/catch sites - removed the now-single-case-only OutgoingFailureType.EMERGENCY_NUMBER - removed the now-unused TelephonyUtils.isEmergencyNumber() helper The shared PCallRequestErrorEnum.emergencyNumber / CallkeepCallRequestError.emergencyNumber wire values are left in place deliberately: the Kotlin side encodes this enum by explicit raw Int values while the Dart side decodes by list index, so removing an entry from the middle would require re-aligning every value after it by hand on both sides to avoid a silent wire-format mismatch. Nothing on the Android side sets this value anymore either way.
digiboridev
approved these changes
Jul 16, 2026
SERDUN
marked this pull request as ready for review
July 16, 2026 14:46
This was referenced Jul 16, 2026
SERDUN
added a commit
that referenced
this pull request
Jul 16, 2026
* fix: allow self-managed calls to numbers Android classifies as emergency (WT-1730) (#349) * fix: allow self-managed calls to numbers Android classifies as emergency TelecomManager.placeCall() blocks a self-managed PhoneAccount from ever placing a call to a number the OS classifies as emergency, based purely on the tel: address matching the device/SIM-region emergency-number list - regardless of whether the number is actually reachable as one. This silently drops calls to legitimate internal PBX extensions that happen to collide with that list (e.g. an extension literally named 112 or 911). Building the outgoing Uri as a sip: address instead of tel: sidesteps the check entirely, since it only matches tel: addresses. The real number keeps flowing unchanged via CallMetadata into onCreateOutgoingConnection, which never reads the Uri - so this only affects what Telecom itself sees for its own emergency classification, not what actually gets dialed. * build: use portadialer.internal instead of webtrit.invalid for the decoy sip: host Same RFC-reserved, never-resolving intent (RFC 9476 .internal instead of RFC 2606 .invalid), just a more identifiable name for this address in logs. * build: remove dead emergency-number exception handling on Android The sip: scheme change means Android's Telecom never classifies our outgoing calls as emergency anymore, so the code path that used to catch and report an emergency-number failure can never run: - removed EmergencyNumberException and its throw/catch sites - removed the now-single-case-only OutgoingFailureType.EMERGENCY_NUMBER - removed the now-unused TelephonyUtils.isEmergencyNumber() helper The shared PCallRequestErrorEnum.emergencyNumber / CallkeepCallRequestError.emergencyNumber wire values are left in place deliberately: the Kotlin side encodes this enum by explicit raw Int values while the Dart side decodes by list index, so removing an entry from the middle would require re-aligning every value after it by hand on both sides to avoid a silent wire-format mismatch. Nothing on the Android side sets this value anymore either way. * build: centralize outgoing decoy sip: Uri construction in TelephonyUtils * build: percent-encode the number in the outgoing sip: Uri * build: bump version build iteration to 1.3.0+2
SERDUN
added a commit
that referenced
this pull request
Jul 16, 2026
* fix: allow self-managed calls to numbers Android classifies as emergency (WT-1730) (#349) * fix: allow self-managed calls to numbers Android classifies as emergency TelecomManager.placeCall() blocks a self-managed PhoneAccount from ever placing a call to a number the OS classifies as emergency, based purely on the tel: address matching the device/SIM-region emergency-number list - regardless of whether the number is actually reachable as one. This silently drops calls to legitimate internal PBX extensions that happen to collide with that list (e.g. an extension literally named 112 or 911). Building the outgoing Uri as a sip: address instead of tel: sidesteps the check entirely, since it only matches tel: addresses. The real number keeps flowing unchanged via CallMetadata into onCreateOutgoingConnection, which never reads the Uri - so this only affects what Telecom itself sees for its own emergency classification, not what actually gets dialed. * build: use portadialer.internal instead of webtrit.invalid for the decoy sip: host Same RFC-reserved, never-resolving intent (RFC 9476 .internal instead of RFC 2606 .invalid), just a more identifiable name for this address in logs. * build: remove dead emergency-number exception handling on Android The sip: scheme change means Android's Telecom never classifies our outgoing calls as emergency anymore, so the code path that used to catch and report an emergency-number failure can never run: - removed EmergencyNumberException and its throw/catch sites - removed the now-single-case-only OutgoingFailureType.EMERGENCY_NUMBER - removed the now-unused TelephonyUtils.isEmergencyNumber() helper The shared PCallRequestErrorEnum.emergencyNumber / CallkeepCallRequestError.emergencyNumber wire values are left in place deliberately: the Kotlin side encodes this enum by explicit raw Int values while the Dart side decodes by list index, so removing an entry from the middle would require re-aligning every value after it by hand on both sides to avoid a silent wire-format mismatch. Nothing on the Android side sets this value anymore either way. * build: centralize outgoing decoy sip: Uri construction in TelephonyUtils * build: percent-encode the number in the outgoing sip: Uri * build: bump version build iteration to 1.3.1+1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
TelecomManager.placeCall()blocks a self-managedPhoneAccountfrom ever placing a call to a number Android classifies as emergency, based purely on thetel:address matching the device/SIM-region emergency-number list - regardless of whether that number is actually reachable as an emergency service. This silently drops calls to legitimate internal PBX extensions that happen to collide with that list (e.g. an extension literally named112or911).Building the outgoing
Urias asip:address instead oftel:sidesteps the check entirely, since Android's emergency-number matching only ever applies totel:addresses. The real number keeps flowing unchanged viaCallMetadataintoonCreateOutgoingConnection, which never reads theUri- so this only affects what Telecom itself sees for its own emergency classification, not what actually gets dialed or how the call is presented afterwards (PhoneConnection.updateData()sets the connection's visible address fromCallMetadata.numberdirectly, independent of thisUri).Verified against a mature SIP client (Linphone) placing a real call to a colliding number and inspecting Android's own Telecom system_server logs (
EmergencyCallHelper: ... not emergency) to confirm the mechanism, then reproduced and validated the same behavior for our own self-managed account on-device, both with Android'sadb emergency-number-test-modesynthetic override and with a real call against a live PortaSwitch test environment.Marked as draft pending the standard QA window before merge.
Test plan
adb shell cmd phone emergency-number-test-mode): call reachesonCreateOutgoingConnectionand connects, noonCreateOutgoingConnectionFailed