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.

android/app/src/main/java/.../AppMessagingService.kt
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)
  }
}

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.

android/app/src/main/java/.../AppMessagingService.kt
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:

APIPurpose
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:

android/app/src/main/AndroidManifest.xml
<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>