Handling System Audio Interruptions

Audio interruptions are a common occurrence on mobile devices that can disrupt ongoing video calls. The Stream Video Flutter SDK provides built-in support for handling these interruptions gracefully, allowing you to maintain a smooth user experience even when external audio events occur.

What are Audio Interruptions?

Audio interruptions happen when the system or other applications take over the audio session, temporarily pausing or stopping your app’s audio. Common examples include:

iOS Interruptions

  • Incoming phone calls
  • Siri activation
  • Alarm or timer sounds
  • Audio from other apps taking over (e.g., voice memo, navigation apps)

Android Interruptions

The interruption sources depend on the configured AndroidInterruptionSource:

With Audio Focus:

  • Other media apps interrupting (e.g., Spotify, YouTube)
  • Assistant voice prompts (e.g., Google Assistant)
  • Alarms and notifications

With Telephony:

  • Phone calls (requires READ_PHONE_STATE permission)

Basic Implementation

The SDK provides the handleCallInterruptionCallbacks method through RtcMediaDeviceNotifier to manage audio interruptions.

In this example we disable the microphone during an interruption:

import 'package:stream_video_flutter/stream_video_flutter.dart';

bool? _microphoneEnabledBeforeInterruption;

void _handleMobileAudioInterruptions() {
  if (!CurrentPlatform.isMobile) return;

  RtcMediaDeviceNotifier.instance.handleCallInterruptionCallbacks(
    onInterruptionStart: () {
      // Mute the microphone when interruption start
      final call = StreamVideo.instance.activeCall;
      _microphoneEnabledBeforeInterruption =
            call?.state.value.localParticipant?.isAudioEnabled;
      
      call?.setMicrophoneEnabled(enabled: false);
    },
    onInterruptionEnd: () {
      // Unmute the microphone when interruption ends
      if (_microphoneEnabledBeforeInterruption == true) {
        StreamVideo.instance.activeCall?.setMicrophoneEnabled(enabled: true);
      }
      _microphoneEnabledBeforeInterruption = null;
    },
    androidInterruptionSource: AndroidInterruptionSource.audioFocusAndTelephony,
  );
}

When multiple active calls are enabled, use StreamVideo.instance.activeCalls instead.

Method Parameters

handleCallInterruptionCallbacks

Future<void> handleCallInterruptionCallbacks({
  void Function()? onInterruptionStart,
  void Function()? onInterruptionEnd,
  AndroidInterruptionSource androidInterruptionSource = 
      AndroidInterruptionSource.audioFocusAndTelephony,
})

Parameters:

  • onInterruptionStart: Callback function executed when an audio interruption starts
  • onInterruptionEnd: Callback function executed when an audio interruption ends
  • androidInterruptionSource: Specifies which interruption sources to monitor on Android

On Android you can filter interruptions for audio and/or telephony, but on iOS you can only enable all interruptions.

Android Interruption Sources

enum AndroidInterruptionSource {
  audioFocusOnly,           // Monitor audio focus changes only
  telephonyOnly,           // Monitor phone calls only
  audioFocusAndTelephony, // Monitor both (default)
}

Platform Setup

Android Permissions

To handle phone call interruptions on Android, add the following permissions to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_CALL_STATE" />

Runtime Permission Request

Request the phone permission at runtime for Android:

void _requestPermissions() async {
  if (CurrentPlatform.isAndroid) {
    await Permission.phone.request();
  }
}

Best Practices

1. Initialize Early

Set up interruption handling as early as possible in your app lifecycle:

@override
void initState() {
  super.initState();
  _handleMobileAudioInterruptions();
}

2. Platform Check

Always check if the platform is mobile before setting up interruption handling:

void _setupInterruptions() {
  if (!CurrentPlatform.isMobile) return;
  // Setup interruption handling
}

3. Graceful Degradation

Handle cases where permissions might not be granted:

void _setupWithPermissionCheck() async {
  if (CurrentPlatform.isAndroid) {
    final phonePermission = await Permission.phone.status;
    if (phonePermission.isDenied) {
      // Handle telephony interruptions only if permission is granted
      await Permission.phone.request();
    }
  }
  
  _handleMobileAudioInterruptions();
}

4. Explain permissions

Asking low level permissions, like READ_PHONE_STATE might worry users. It is better to first get users onboarded and explain why you need this permission before requesting it.

© Getstream.io, Inc. All Rights Reserved.