package com.example.app
import com.google.firebase.messaging.RemoteMessage
import io.getstream.rn.callingx.StreamMessagingService
class AppMessagingService : StreamMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// Stream call.ring + React Native Firebase JS background handler
super.onMessageReceived(remoteMessage)
// Optional — short-circuit other forwarders for Stream pushes.
// `isStreamCallRing` is provided for flexibility; Stream's `call.ring`
// has already been handled by `super.onMessageReceived(...)`.
if (StreamMessagingHelper.isStreamCallRing(remoteMessage)) return
// forward non-Stream pushes to other SDKs
ExampleSDK.passRemoteMessage(applicationContext, remoteMessage)
}
}Firebase Messaging Service overrides
The problem
The @stream-io/react-native-callingx package provides its own FirebaseMessagingService implementation (StreamMessagingService) and registers it in its manifest.
Android FCM only delivers each push to a single FirebaseMessagingService per app. If your merged manifest declares more than one — typically because another push SDK registers its own, or because your app ships a custom service — only the one PackageManager resolves first actually fires. Depending on which service wins the merge, you'll see either Stream ringing pushes stop showing or the other SDK stop receiving its pushes.
The manifest merger doesn't flag this as an error — declaring multiple MESSAGING_EVENT services is legal and the build succeeds. There is just no fan-out at runtime.
How to detect the conflict
In Android Studio, open AndroidManifest.xml and switch to the Merged Manifest tab. Search for MESSAGING_EVENT and count the <service> entries that declare it. More than one means you have a conflict.
Two patterns resolve this — pick based on whether you can subclass StreamMessagingService or need to host your own service.
Option 1 — Subclass StreamMessagingService
Pick this when you don't need to inherit from another SDK's service. Subclassing handles Stream's call.ring push notifications automatically, keeps the React Native Firebase background-message flow intact, and gives you a hook for custom forwarding.
Option 2 — Forward via StreamMessagingHelper
Pick this when your app already hosts its own FirebaseMessagingService. Remove Stream's service from the manifest and forward call.ring payloads via StreamMessagingHelper from your own service.
package com.example.app
import com.google.firebase.messaging.RemoteMessage
import io.getstream.rn.callingx.StreamMessagingHelper
import io.invertase.firebase.messaging.ReactNativeFirebaseMessagingService
class AppMessagingService : ReactNativeFirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// pass remote message to React Native Firebase JS background handler
super.onMessageReceived(remoteMessage)
// Optional gate — provided for finer control over the forwarding flow.
// `handleMessage` is also safe to call unconditionally; it no-ops for
// payloads that aren't a Stream `call.ring`.
if (StreamMessagingHelper.isStreamCallRing(remoteMessage)) {
StreamMessagingHelper.handleMessage(applicationContext, remoteMessage)
} else {
// forward to other SDKs
ExampleSDK.passRemoteMessage(applicationContext, remoteMessage)
}
}
}Your custom service must extend ReactNativeFirebaseMessagingService and chain super.onMessageReceived(...) — otherwise React Native Firebase's JS background handler (setBackgroundMessageHandler()) stops firing.
The helper exposes two methods:
| API | Purpose |
|---|---|
StreamMessagingHelper.isStreamCallRing(remoteMessage) | Returns true if the payload is a Stream Video call.ring. Useful when you want to short-circuit other SDK forwarders. |
StreamMessagingHelper.handleMessage(context, remoteMessage) | Handles a Stream call.ring payload (starts the incoming call flow). No-op for non-Stream payloads — safe to call unconditionally. |
Register your service in the manifest
Both options need the same change in android/app/src/main/AndroidManifest.xml — remove the default StreamMessagingService and register yours in its place:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<service
android:name="io.getstream.rn.callingx.StreamMessagingService"
tools:node="remove" />
<service
android:name=".AppMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>