Skip to content

fix: callingx background handling#2310

Merged
greenfrvr merged 4 commits into
mainfrom
android-bg-transition-issue
Jul 8, 2026
Merged

fix: callingx background handling#2310
greenfrvr merged 4 commits into
mainfrom
android-bg-transition-issue

Conversation

@greenfrvr

@greenfrvr greenfrvr commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

💡 Overview

Fixes two Android bugs where callingx calls break when the app is backgrounded (locked screen cases) — e.g. answering a ringing call on the lock screen and then tabbing out:

  1. Audio and video drop a few seconds after backgrounding with locked screen.
  2. Call drops 2 minutes after backgrounding with locked screen.

Both only affect the callingx-managed path (ringing calls, or non-ringing calls with enableOngoingCalls).

📝 Implementation notes

Two independent root causes, two fixes:

1. Background mic/camera behavior

CallService ran as a phoneCall-only foreground service, which on Android 14+ doesn't grant microphone/camera access in the background — so the mic went silent and the camera was cut shortly after
backgrounding.

Fix: declare and use the microphone/camera FGS types. Since those types can't be activated from the background (the service starts from a push), CallService starts as phoneCall and is re-promoted to
include mic/camera during the foreground answer window; the types then persist into the background.

2. JS timers suspended in background

Telecom + FGS with phoneCall keep the process alive, but without a background keep-alive task, React Native suspended JS timers while backgrounded. The client's healthcheck pings stopped, and the server dropped the participant.

Fix: run a headless JS task on callingx's existing CallService (no extra notification) to keep RN/timers alive. It's shared by the ringing-push handler and the keep-call-alive hook, and stops once the call ends. useAndroidKeepCallAliveEffect and the push flow now use this instead of bailing out, with guards so only one keep-alive mechanism runs per call.

🎫 Ticket: https://linear.app/stream/issue/RN-404/callingx-background-with-locked-screen-issues

📑 Docs: https://github.com/GetStream/docs-content/pull/1396

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added Android ref-counted background keep-alive via acquireBackgroundTask(owner) / releaseBackgroundTask(owner).
    • Foreground service now declares microphone/camera types when available, with automatic upgrading when the app returns to the foreground.
  • Bug Fixes
    • Improved foreground/background transition handling to prevent unintended call shutdowns.
    • Enhanced cleanup for ended calls, including ringing calls triggered from push events.
  • Documentation
    • Updated public API docs to reflect the new background keep-alive workflow.

@greenfrvr greenfrvr self-assigned this Jun 30, 2026
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9f7aa942-ac3c-4dce-8614-549abfff8d4c

📥 Commits

Reviewing files that changed from the base of the PR and between 11b5d63 and 622fe32.

📒 Files selected for processing (5)
  • packages/react-native-callingx/README.md
  • packages/react-native-callingx/android/src/main/AndroidManifest.xml
  • packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt
  • packages/react-native-callingx/src/CallingxModule.ts
  • packages/react-native-callingx/src/types.ts
💤 Files with no reviewable changes (1)
  • packages/react-native-callingx/README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/react-native-callingx/android/src/main/AndroidManifest.xml
  • packages/react-native-callingx/src/types.ts
  • packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt
  • packages/react-native-callingx/src/CallingxModule.ts

📝 Walkthrough

Walkthrough

The PR replaces the Android background-task API with owner-based acquire/release calls, adds foreground-state callbacks, and updates CallService to compute and re-promote foreground service types with camera and microphone support. The SDK hook and push handler are updated to use the new keep-alive flow.

Changes

Ref-counted background task API and dynamic FGS type promotion

Layer / File(s) Summary
Public API contract and docs update
packages/react-native-callingx/src/types.ts, packages/react-native-callingx/README.md
ICallingxModule replaces the background-task provider/start/stop methods with owner-based acquire/release methods, and the README documents the same API change.
LifecycleListener foreground callbacks
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/utils/LifecycleListener.kt
LifecycleListener adds foreground listener registration and notifies registered listeners on host resume with per-listener exception handling.
CallService dynamic FGS type and re-promotion
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt, packages/react-native-callingx/android/src/main/AndroidManifest.xml
CallService adds foreground-service permissions and service types, registers an app-foreground callback, computes a permission-aware foreground-service type, and re-issues startForeground when the app returns to the foreground.
CallingxModule ref-counted background task implementation
packages/react-native-callingx/src/CallingxModule.ts
CallingxModule replaces the old headless-task registration flow with owner tracking, deferred shutdown state, and Android-only acquire/release methods; several public methods are converted to class-field arrow functions.
useAndroidKeepCallAliveEffect callingx integration
packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
useAndroidKeepCallAliveEffect tracks a callingx keep-alive owner, acquires and releases background tasks around call state changes, and releases any held owner during unmount cleanup.
Push handler background task migration
packages/react-native-sdk/src/utils/push/android.ts
firebaseDataHandler acquires a callingx background task for ringing calls, uses a local release helper across termination paths, and releases the owner on failure.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: santhoshvai, oliverlaz

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately points to the main callingx background-handling change.
Description check ✅ Passed The description matches the template with Overview, Implementation notes, Ticket, and Docs, and covers the main fixes.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch android-bg-transition-issue

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

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

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown

Bundle size

Built package output. Sizes in KB; delta vs main@95bcc2a.

Package Unminified Minified Δ min vs main
@stream-io/video-react-native-sdk 401.3 KB 192.2 KB +663 B (+0.3%)
↳ install total (+ client + react-bindings) 1173.3 KB 464.3 KB +663 B (+0.1%)
@stream-io/react-native-callingx 16.7 KB 7.2 KB +504 B (+7.3%)

@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.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt`:
- Around line 592-611: `computeForegroundServiceType()` is still adding
microphone/camera foreground-service bits on Android 10, so gate those additions
behind an Android 11+ check while keeping
`ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL` unconditionally. Update
`CallService.computeForegroundServiceType()` to only OR in
`FOREGROUND_SERVICE_TYPE_MICROPHONE` and `FOREGROUND_SERVICE_TYPE_CAMERA` when
`Build.VERSION.SDK_INT >= Build.VERSION_CODES.R` and the permissions are
granted, and apply the same SDK gate anywhere the manifest-facing
foregroundServiceType for this service is defined.

In `@packages/react-native-callingx/src/CallingxModule.ts`:
- Around line 246-313: The changed CallingxModule methods are using class method
syntax instead of the required arrow-function field style. Update
registerBackgroundTask, acquireBackgroundTask, and releaseBackgroundTask to
arrow-function class fields so they preserve instance binding and match the
project’s coding guidelines for all class methods, including private ones.
- Around line 42-45: The keep-alive ownership tracking in CallingxModule is
incorrectly using a Set, which loses repeated acquires for the same owner and
breaks ref-counted behavior. Replace _keepAliveOwners with per-owner counts so
overlapping acquires from the same stable owner key are accumulated and only
fully removed on the final release. Update the keep-alive acquire/release logic
in CallingxModule, including the code around _keepAliveOwners and the related
keep-alive flow in the referenced acquire/release section, so the background
task is only torn down when all counted holds are released.
- Around line 268-290: The keep-alive acquire flow in CallingxModule should roll
back ownership if NativeCallingModule.startBackgroundTask fails, because owner
is added to _keepAliveOwners before the native start succeeds. Update the
acquire/registration path around registerBackgroundTask and
NativeCallingModule.startBackgroundTask so that a rejection removes the
just-added owner, clears any pending keep-alive state if needed, and preserves
the previous “not acquired” behavior. Make the fix in the keep-alive ownership
logic itself so future acquires don’t short-circuit when no HeadlessJS task is
actually running.

In `@packages/react-native-callingx/src/types.ts`:
- Around line 116-131: The public background-task API in the type declarations
has been renamed to acquireBackgroundTask/releaseBackgroundTask, but the old
consumer-facing methods are being removed too early. Keep the deprecated shim
methods alongside the new ones in types.ts, add `@deprecated` JSDoc with clear
replacement guidance pointing to acquireBackgroundTask and
releaseBackgroundTask, and ensure the existing aliases remain available for the
deprecation window before any next-major removal.

In `@packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts`:
- Around line 113-117: The keep-alive cleanup in useAndroidKeepCallAliveEffect
is swallowing failures from callingx.releaseBackgroundTask, which hides native
release errors and can leave the task running. Update both release paths in this
hook to stop using empty catch handlers, log the rejection with enough context,
and handle the promise result explicitly; if the call is made from a native
interaction path, wrap it in try-catch and make sure any rejected promise is
surfaced and recorded instead of being ignored.
- Around line 128-134: The keep-alive bookkeeping in
useAndroidKeepCallAliveEffect sets callingxKeepAliveOwnerRef.current before
callingx.acquireBackgroundTask succeeds, so a rejected acquire leaves stale
ownership state. Update the acquire path around acquireBackgroundTask to handle
the failure by clearing callingxKeepAliveOwnerRef.current (and only keeping the
owner on success), while still logging the warning through
videoLoggerSystem.getLogger('useAndroidKeepCallAliveEffect') so retry/release
logic for the active call stays consistent.

In `@packages/react-native-sdk/src/utils/push/android.ts`:
- Around line 167-172: The ringing-call cleanup path in the push handler starts
callFromPush.leave() and then immediately calls finishBackgroundTask(), which
can stop JS before the leave request completes. Update this branch so the
leave() promise is awaited (with the existing reject: false handling and error
logging preserved) before releasing the keep-alive via finishBackgroundTask(),
using the callFromPush and finishBackgroundTask symbols to locate the fix.
- Around line 124-139: The background-task lifecycle in android.ts is not
fail-safe because callingx.acquireBackgroundTask() errors are only logged and
execution continues, and callingx.releaseBackgroundTask() is invoked without
handling its returned promise. Update the acquire/release flow around
backgroundTaskOwner, finishBackgroundTask, and the call setup code to use
try-catch for native calls and explicitly await or catch both promises so
failures stop the dependent path and any release rejection is handled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3db974b7-5710-40c3-8079-4f78077ce43a

📥 Commits

Reviewing files that changed from the base of the PR and between 30b332c and 3d6908e.

📒 Files selected for processing (8)
  • packages/react-native-callingx/README.md
  • packages/react-native-callingx/android/src/main/AndroidManifest.xml
  • packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt
  • packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/utils/LifecycleListener.kt
  • packages/react-native-callingx/src/CallingxModule.ts
  • packages/react-native-callingx/src/types.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
  • packages/react-native-sdk/src/utils/push/android.ts

Comment thread packages/react-native-callingx/src/CallingxModule.ts
Comment thread packages/react-native-callingx/src/CallingxModule.ts Outdated
Comment thread packages/react-native-callingx/src/CallingxModule.ts Outdated
Comment thread packages/react-native-callingx/src/types.ts
Comment thread packages/react-native-sdk/src/utils/push/android.ts
Comment thread packages/react-native-sdk/src/utils/push/android.ts
@greenfrvr greenfrvr merged commit a96df4f into main Jul 8, 2026
11 checks passed
@greenfrvr greenfrvr deleted the android-bg-transition-issue branch July 8, 2026 15:26
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🎉 The changes from this pull request have been released.

Shipped with:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants