Skip to content

fix(voip): use call ID for Telecom handle URI to prevent crash on Android 12+#7280

Merged
diegolmello merged 2 commits into
feat.voip-lib-newfrom
fix/voip-pr6918-01-telecom-handle-uri
Apr 30, 2026
Merged

fix(voip): use call ID for Telecom handle URI to prevent crash on Android 12+#7280
diegolmello merged 2 commits into
feat.voip-lib-newfrom
fix/voip-pr6918-01-telecom-handle-uri

Conversation

@diegolmello

@diegolmello diegolmello commented Apr 30, 2026

Copy link
Copy Markdown
Member

Proposed changes

Caller display names containing spaces, @, or other special characters caused Uri.fromParts(PhoneAccount.SCHEME_TEL, caller, null) to produce an invalid URI. On Android 12 and later, TelecomManager.addNewIncomingCall validates the handle URI strictly and throws IllegalArgumentException, crashing the incoming-call registration.

This fix uses the call's UUID (callId) as the URI's user-info component — a string that is always URI-safe — while the human-readable caller name is forwarded to Telecom via EXTRA_CALLER_DISPLAY_NAME. Telecom surfaces this extra on the lockscreen and the full-screen incoming-call UI without validating it as a URI component.

The existing in-app extras consumed by react-native-callkeep (EXTRA_CALLER_NAME, name, handle) are unchanged so JavaScript-side call handling is unaffected.

Issue(s)

Fixes incoming-call crash when caller's display name contains a space or @ on Android 12, 13, and 14 (blocker B1 in PR #6918).

How to test or reproduce

  1. Set up two devices / simulators with accounts on the same Rocket.Chat server.
  2. Have the callee's account receive an incoming VoIP call from a caller whose display name contains a space (e.g. "Alice Smith").
  3. Verify the full-screen incoming-call screen appears and no IllegalArgumentException is logged.
  4. Repeat with a caller display name containing @ (e.g. "alice@example").
  5. Verify the caller name shown on the lockscreen / in-call UI matches the FCM payload name.

Screenshots

N/A — no UI change; crash fix only.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

EXTRA_CALLER_DISPLAY_NAME is a TelecomManager constant available since API 18 — no new permission or min-SDK change required.

Summary by CodeRabbit

  • Bug Fixes
    • Improved incoming VoIP call notifications so calls reliably appear even when some caller details are empty.
    • Caller display names are now forwarded correctly without breaking the call connection, preventing issues caused by special characters in display names.

…roid 12+

Caller display names containing spaces or '@' caused Uri.fromParts to produce
an invalid URI, making TelecomManager throw IllegalArgumentException on Android 12
and later. Using the call UUID for the URI's user-info component avoids this; the
human-readable name now flows via EXTRA_CALLER_DISPLAY_NAME, which Telecom handles
separately and surfaces on the lockscreen and in-call UI.
@coderabbitai

coderabbitai Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dc418859-9c8a-4921-a117-b6ef83aa5d3f

📥 Commits

Reviewing files that changed from the base of the PR and between 86656f3 and 02d8f04.

📒 Files selected for processing (1)
  • android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt
📜 Recent review details
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-22T22:57:58.545Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP features in app/lib/services/voip/ directory using Zustand stores for WebRTC peer-to-peer audio calls with native CallKit (iOS) and Telecom (Android) integration
📚 Learning: 2026-04-22T22:57:58.545Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-22T22:57:58.545Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP features in app/lib/services/voip/ directory using Zustand stores for WebRTC peer-to-peer audio calls with native CallKit (iOS) and Telecom (Android) integration

Applied to files:

  • android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt
🔇 Additional comments (2)
android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt (2)

772-776: Good guard narrowing for Telecom registration input.

Validating callId (the value used in the Telecom handle URI) is the right constraint here and prevents rejecting valid calls with empty display names.


797-812: URI/display-name separation is implemented correctly.

Using callId for EXTRA_INCOMING_CALL_ADDRESS while keeping caller in display extras is a solid fix for invalid-handle crashes and keeps callkeep payload compatibility intact.


Walkthrough

Registration for incoming VoIP calls now requires a non-empty callId only; the tel: URI for TelecomManager.EXTRA_INCOMING_CALL_ADDRESS is constructed from callId (UUID) instead of the display caller. The human-readable caller is forwarded separately via EXTRA_CALLER_NAME while name/handle extras remain unchanged.

Changes

Cohort / File(s) Summary
VoIP Telecom Registration Logic
android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt
Validation tightened to reject empty callId only; logging narrowed for missing callId. EXTRA_INCOMING_CALL_ADDRESS now uses tel: URI with the callId as the user-info component (prevents invalid URIs from display names). Human-readable caller forwarded via EXTRA_CALLER_NAME; name/handle extras unchanged.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

type: bug

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing a VoIP crash on Android 12+ by using the call ID for Telecom's handle URI instead of the caller display name, which is the core problem and solution addressed in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 5/8 reviews remaining, refill in 22 minutes and 29 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@diegolmello diegolmello had a problem deploying to official_android_build April 30, 2026 18:19 — with GitHub Actions Error
@diegolmello diegolmello had a problem deploying to experimental_ios_build April 30, 2026 18:19 — with GitHub Actions Error
@diegolmello diegolmello temporarily deployed to experimental_android_build April 30, 2026 18:19 — with GitHub Actions Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt (1)

774-776: ⚡ Quick win

Harden the callId guard to reject blank values.

Line 774 uses isEmpty(), so whitespace-only IDs still pass and can flow into Telecom handle creation. Prefer isBlank() here to fail earlier on malformed payloads.

Proposed patch
-            if (callId.isEmpty()) {
-                Log.e(TAG, "Cannot register call with TelecomManager: callId is empty")
+            if (callId.isBlank()) {
+                Log.e(TAG, "Cannot register call with TelecomManager: callId is blank")
                 return
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt`
around lines 774 - 776, The guard checking callId should reject blank values: in
VoipNotification (where callId is validated before TelecomManager handle
creation) replace the callId.isEmpty() check with callId.isBlank() so
whitespace-only IDs are treated as invalid and trigger the early return (keeping
the Log.e(TAG, ...) and return flow intact) to prevent malformed payloads from
reaching Telecom registration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt`:
- Around line 774-776: The guard checking callId should reject blank values: in
VoipNotification (where callId is validated before TelecomManager handle
creation) replace the callId.isEmpty() check with callId.isBlank() so
whitespace-only IDs are treated as invalid and trigger the early return (keeping
the Log.e(TAG, ...) and return flow intact) to prevent malformed payloads from
reaching Telecom registration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 345b7573-382f-4890-bb4e-3d14de4415f8

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea0877 and 86656f3.

📒 Files selected for processing (1)
  • android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-22T22:57:58.545Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP features in app/lib/services/voip/ directory using Zustand stores for WebRTC peer-to-peer audio calls with native CallKit (iOS) and Telecom (Android) integration
📚 Learning: 2026-04-22T22:57:58.545Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-22T22:57:58.545Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP features in app/lib/services/voip/ directory using Zustand stores for WebRTC peer-to-peer audio calls with native CallKit (iOS) and Telecom (Android) integration

Applied to files:

  • android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt
🔇 Additional comments (1)
android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt (1)

797-812: Nice fix: separating URI handle from display name is the right approach.

Using callId for EXTRA_INCOMING_CALL_ADDRESS and caller for EXTRA_CALLER_DISPLAY_NAME cleanly addresses Android 12+ Telecom URI validation without changing JS-facing extras.

That constant is not part of the public Telecom API, so the previous
commit broke the Android build. The human-readable display name still
reaches the system: it travels through the in-app EXTRA_CALLER_NAME
extra, which the call-keep VoiceConnection constructor reads and applies
via Connection.setCallerDisplayName(), so Telecom surfaces it on the
lockscreen and in-call UI as required.
@diegolmello diegolmello temporarily deployed to experimental_android_build April 30, 2026 19:14 — with GitHub Actions Inactive
@diegolmello diegolmello had a problem deploying to experimental_ios_build April 30, 2026 19:14 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to official_android_build April 30, 2026 19:14 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to upload_experimental_android April 30, 2026 19:56 — with GitHub Actions Failure
@github-actions

Copy link
Copy Markdown

Android Build Available

Rocket.Chat Experimental 4.72.0.108726

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNQ6Cn53Mrq8W71JWGoKZlGQ2LQQvZWg32EhV8CmvZSGJ1-7X279bVNrEmbWIm25Q7X7ricJklMu6QXOs2ji

@diegolmello diegolmello merged commit 7e5c2b4 into feat.voip-lib-new Apr 30, 2026
8 of 13 checks passed
@diegolmello diegolmello deleted the fix/voip-pr6918-01-telecom-handle-uri branch April 30, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant