Repository navigation
fix(android): calls outlive the activity, as in other calling apps - #908
Merged
Merged
Conversation
onHostDestroy, added in #724 to "stop all calls and processes when the app is killed", ended every call and killed the process whenever the React activity was destroyed. That is not the app being killed: a configuration change the activity doesn't handle (font size, language), Back on Android 11 and earlier, or swiping the app away from Recents destroy the activity while the process and React Native's runtime keep running. On Android 10 and 16 emulators, each of these ended an active call and closed the app. A call now lasts as long as something can handle it: - the activity is destroyed: the call goes on, as in WhatsApp or Teams. Telecom keeps the process running during a call, with or without the foreground service (checked on both versions); - the process dies: Telecom ends its calls itself (checked: a crash leaves no call); - React Native's instance stops while the process runs (a reload, an over-the-air update): the module's invalidate() ends the calls on the main thread. On master a reload left the call active in Telecom with no JavaScript to handle it. endCallsWhenReactNativeStops: false keeps them for apps whose native code owns the calls. It replaces the unreleased endCallsOnHostDestroy option. The example keeps its call state and CallKeep's listeners outside the screen, registered once from index.js, so a new activity shows the call that goes on; setup() still runs from the screen, and the events replayed from before it wait for it. docs/setup.md explains the lifecycle.
Merged
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.
The problem
onHostDestroy, added in #724 to "stop all calls and processes when app is killed", ends every call and kills the process (killProcess) whenever the React activity is destroyed. That is not the app being killed. On Android 10 and Android 16 emulators, with the example app and an answered call:In the first three cases the process and React Native's runtime keep running (React reuses its instance and only mounts the screen again). Telecom keeps the process running during a call, with or without the foreground service: swiping the app away without
foregroundServicealso leaves the process and the call (checked on both versions).Service.onTaskRemovedis never called on the ConnectionService when the app is swiped away, so it's no signal either.The change
A call lasts as long as something can handle it, as in WhatsApp or Teams:
invalidate()ends the calls, on the main thread (React Native may invalidate elsewhere).endCallsWhenReactNativeStops: falsekeeps them, for apps whose native code owns the calls (feat: show an incoming call from native code, and observe events natively on iOS #906). It replacesendCallsOnHostDestroyfrom feat(android): foregroundServiceType and endCallsOnHostDestroy options #905, which hasn't been released.The example keeps its call state and CallKeep's listeners outside the screen, registered once from
index.js: before, its calls lived in the component's state, so a recreated activity showed no call even though it went on.setup()still runs from the screen (it can ask for permissions on Android), once per mode, and the events replayed from before it (didLoadWithEvents) wait for it: a call started from Recents on an iOS cold start needs it.docs/setup.mdhas a Calls and the app's lifecycle (Android) section; the changelog lists it under Changed.Tests
tests/android/CallKeepCallLifetimeOptionsTest.kt:theCallOutlivesTheActivity(master: the process is killed, reported as a crash),theCallEndsWhenReactNativeStops(master: the call outlives React Native),nativeCallsOutliveReactNative; with the type test, OK (4 tests). The other eight fixtures, including feat: show an incoming call from native code, and observe events natively on iOS #906's native setup, pass on Android 10.dumpsys telecom. The mode switch still runssetup()again.tscand ESLint. Its JavaScript changed on iOS too (listeners at startup, the replay waiting forsetup()): on an iPhone 15 Pro Max with iOS 27, an incoming call works, and calling back from the Phone app's Recents with the app not running opens it and starts the call.