fix: callingx background handling#2310
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughThe 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. ChangesRef-counted background task API and dynamic FGS type promotion
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Bundle sizeBuilt package output. Sizes in KB; delta vs
|
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
packages/react-native-callingx/README.mdpackages/react-native-callingx/android/src/main/AndroidManifest.xmlpackages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.ktpackages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/utils/LifecycleListener.ktpackages/react-native-callingx/src/CallingxModule.tspackages/react-native-callingx/src/types.tspackages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.tspackages/react-native-sdk/src/utils/push/android.ts
|
🎉 The changes from this pull request have been released. Shipped with:
|
💡 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:
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
CallServiceran as aphoneCall-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 afterbackgrounding.
Fix: declare and use the
microphone/cameraFGS types. Since those types can't be activated from the background (the service starts from a push),CallServicestarts asphoneCalland is re-promoted toinclude mic/camera during the foreground answer window; the types then persist into the background.
2. JS timers suspended in background
Telecom + FGS with
phoneCallkeep 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.useAndroidKeepCallAliveEffectand 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
acquireBackgroundTask(owner)/releaseBackgroundTask(owner).