Keeping The Call Alive In Background

Keep calls alive when the app goes to the background. Users continue hearing remote audio streams while the app is backgrounded. On Android, this is achieved using a foreground service. On iOS, the SDK uses CallKit integration to report ongoing calls to the system.

Install Dependencies

The @stream-io/react-native-callingx package is required for registering ongoing calls in iOS CallKit. This is what allows the system to keep the call alive in the background.

yarn add @stream-io/react-native-callingx

npx pod-install

If you already have @stream-io/react-native-callingx installed as part of the ringing setup, no additional installation is needed.

Android Setup

In Android, we use a foreground service to keep the call alive. The SDK will automatically create and manage the foreground service.

In order to be able to use the foreground service, some declarations need to be added to the AndroidManifest.xml. In app.json, in the plugins field, add true to the androidKeepCallAlive property in the @stream-io/video-react-native-sdk plugin. This will add the declarations automatically.

app.json
{
 "plugins": [
      [
        "@stream-io/video-react-native-sdk",
        {
           "androidKeepCallAlive": true
        }
      ],
      // your other plugins
  ]
}

If Expo EAS build is not used, please do npx expo prebuild --clean to edit the AndroidManifest.xml again after adding the config plugin property.

When uploading the app to the Play Store, it is essential to declare the permissions for foreground services in the Play Console and provide an explanation for their use. This includes adding a link to a video that demonstrates how the foreground service is utilized during video and audio calls. This procedure is required only once. For more details, click here. The added permissions are:

  • android.permission.FOREGROUND_SERVICE_CAMERA - To access camera when app goes to background
  • android.permission.FOREGROUND_SERVICE_MICROPHONE - To access microphone when app goes to background
  • android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK - To play video and audio tracks when the app goes to background

Request notification permissions

At an appropriate place in your app, request notification permissions from the user. Below is an example of how to request notification permissions using the react-native-permissions library:

import { requestNotifications } from "react-native-permissions";

// This will request POST_NOTIFICATION runtime permission for Anroid 13+
await requestNotifications(["alert", "sound"]);

For a comprehensive guide on requesting all required permissions (camera, microphone, bluetooth, and notifications), see Manage Native Permissions.

Optional: override the default configuration of the foreground service notifications

import { StreamVideoRN } from "@stream-io/video-react-native-sdk";

StreamVideoRN.updateConfig({
  foregroundService: {
    android: {
      channel: {
        id: "stream_call_foreground_service",
        name: "Service to keep call alive",
      },
      // you can edit the title and body of the notification here
      notificationTexts: {
        title: "Video call is in progress",
        body: "Tap to return to the call",
      },
      // you can optionally add a promise to run in the foreground service
      taskToRun: (call) =>
        new Promise(() => {
          console.log(
            "jumping to foreground service foreground service with call-cid",
            call.cid,
          );
        }),
    },
  },
});

iOS Setup

Enable the audio and voip background modes to keep the call alive when users lock their device or switch apps.

The Expo config plugin automatically adds the audio background mode. To also enable the voip background mode, set iosKeepCallAlive to true in the plugin configuration:

app.json
{
 "plugins": [
      [
        "@stream-io/video-react-native-sdk",
        {
           "iosKeepCallAlive": true
        }
      ],
      // your other plugins
  ]
}

If Expo EAS build is not used, please do npx expo prebuild --clean to regenerate the native project after adding the config plugin property.

  • audio — allows audio playback to continue in the background.
  • voip — allows the SDK to manage call lifecycle and report ongoing calls to the system via CallKit.

Enable ongoing calls

To enable keeping calls alive in the background, set enableOngoingCalls: true in setPushConfig and pass createStreamVideoClient so the SDK can create a StreamVideoClient when needed from the background. This lets the SDK manage events from CallKit.

import {
  StreamVideoClient,
  StreamVideoRN,
  User,
} from "@stream-io/video-react-native-sdk";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { STREAM_API_KEY } from "../constants"; // adjust import to your project

const createStreamVideoClient = async () => {
  const userId = await AsyncStorage.getItem("@userId");
  const userName = await AsyncStorage.getItem("@userName");
  if (!userId) return undefined;

  const tokenProvider = async (): Promise<string> =>
    yourServerAPI.getTokenForUser(userId).then((auth) => auth.token);

  const user: User = { id: userId, name: userName };
  return StreamVideoClient.getOrCreateInstance({
    apiKey: STREAM_API_KEY,
    user,
    tokenProvider,
  });
};

StreamVideoRN.setPushConfig({
  ios: {
    enableOngoingCalls: true,
  },
  createStreamVideoClient,
});

Always use StreamVideoClient.getOrCreateInstance(..) instead of new StreamVideoClient(..). Reusing the client instance preserves call accept/decline states changed while the app was in the background. The getOrCreateInstance method ensures the same user reuses the existing instance.