Repository navigation
feat(android): foregroundServiceType and endCallsOnHostDestroy options - #905
Merged
Merged
Conversation
Two options for apps whose calls are not tied to the React activity. foregroundService.foregroundServiceType is passed to startForeground (Android 10 and later). Without a type, Android starts the service with every type its manifest entry declares, so an app that declares phoneCall|microphone could not ring with phoneCall alone and add the microphone once the call is answered. Without the option, nothing changes. endCallsOnHostDestroy: false makes onHostDestroy leave the calls and the process. By default it still ends every call and kills the process, which is right when the activity is the call's only owner, but ends a call that native code (a service, a call screen in its own task) still holds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 17, 2026
- docs/setup.md and the changelog say that CallKeep starts the service once and keeps the given types for the whole call: with phoneCall alone, adding microphone after the answer is up to the app's native code, with startForeground on currentConnectionService. - The type test asserts that the host declares phoneCall|microphone. With phoneCall alone, master also starts the service as phoneCall and the test passed without proving anything; its comment said the host declares phoneCall. - startForeground's first argument is the notification's id, but it was named FOREGROUND_SERVICE_TYPE_MICROPHONE, which reads as a type next to a real one. Constants.FOREGROUND_SERVICE_NOTIFICATION_ID names it; the old constant keeps the same value for apps that use it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Two Android options for apps whose calls aren't tied to the React activity. Both come from the patch Luge Mobile carries on 4.4.0. Without them, nothing changes.
foregroundService.foregroundServiceTypeThe value is passed to
startForegroundon Android 10 and later, as a mask ofServiceInfo.FOREGROUND_SERVICE_TYPE_*values. Without a type, Android starts the service with every type its manifest entry declares. An app that declaresphoneCall|microphonetherefore couldn't ring withphoneCallalone. Android 14+ refuses amicrophoneservice withoutRECORD_AUDIO(SecurityException: Starting FGS with type microphone ... requires permissions), and doesn't give the microphone to a service started from the background.CallKeep starts the service once, when the call is created, and keeps the given types for the whole call. With
4alone, addingmicrophoneafter the answer is up to the app's native code: it callsstartForegroundagain onVoiceConnectionService.currentConnectionService.docs/setup.mdsays so. Luge rings with4and restarts the service withphoneCall|microphonewhen the call is answered.Each type must be declared on the service in the manifest.
startForeground's first argument is the notification's id, but it was namedFOREGROUND_SERVICE_TYPE_MICROPHONE, which reads as a type next to a real one.Constants.FOREGROUND_SERVICE_NOTIFICATION_IDnow names it. The old constant keeps the same value for apps that use it.endCallsOnHostDestroy: falseonHostDestroyends every call and kills the process. That is right when the activity is the call's only owner. It also ends a call that something else still holds, such as native code or a call screen in its own task. Withfalse, the calls and the process stay, and ending them is up to the app. The default staystrue.Tests
New
tests/android/CallKeepCallLifetimeOptionsTest.kt, run in the example app (React Native 0.87.1) on a Galaxy S24, Android 16:theForegroundServiceStartsWithTheGivenTypeexpected:<4> but was:<132>(the manifest'sphoneCall|microphone)theCallOutlivesTheActivityWhenAskedonHostDestroy(SIGKILL, reported as a crash)The type test asserts that the host declares
phoneCall|microphoneon the service. WithphoneCallalone, master also starts the service asphoneCall, and the test would prove nothing../gradlew :react-native-callkeep:lintDebugreports no errors, andnpm run checkpasses.index.d.ts,docs/setup.mdandCHANGELOG.mdare updated.The tests live in a new file so that this branch doesn't conflict with #904, which changes
CallKeepForegroundServiceTest.kt.🤖 Generated with Claude Code