Skip to main content
Version: 0.x.x

Background Handling - Android

On Android, when your app moves to the background, the platform imposes restrictions—especially on accessing the microphone and camera.

Starting with Android 14 and above, these restrictions have become even stricter:

  • Background microphone and camera usage is restricted.
  • Battery optimization policies are more aggressive, often terminating background processes.

For seamless media transmission in VideoSDK, you must handle these restrictions carefully by configuring the right permissions and using a foreground service.

Alternatively, if you prefer not to implement a foreground service, you can use Picture-in-Picture (PiP) mode. PiP enables the app to remain visible in a small floating window, helping bypass some background restrictions while keeping media active.

If these steps aren’t correctly implemented, the app will lose the ability to send or receive media when backgrounded, disrupting core communication features.

Background Handling

Running video and audio capture in the background on Android presents multiple challenges:

  • Limited access to the microphone and camera when the app is backgrounded (usually capped at ~60 seconds).
  • Device/OEM-specific optimizations that aggressively kill background processes.

To address these challenges, your app should run a foreground service so Android recognizes it as actively running. This ensures consistent microphone and camera access even while the app is minimized.

Update your AndroidManifest.xml to declare permissions and the foreground service:

AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

<!-- Foreground Service Here Pass Your ForegroundService -->
<application>
<service android:name=".ForegroundService"
android:foregroundServiceType="camera|microphone"
android:stopWithTask="true" />
</application>
</manifest>

Implement a foreground service that shows a persistent notification and keeps audio/video capture running:

ForegroundService.kt
package com.example.example

import android.app.*
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import com.example.example.MainApplication
import com.example.example.R

class ForegroundService : Service() {

companion object {
const val NOTIFICATION_ID = 1001
const val CHANNEL_ID = "AppChannelID"
private const val TAG = "ForegroundService"

// Actions
const val ACTION_START = "com.example.example.START_FOREGROUND_SERVICE"
const val ACTION_STOP = "com.example.example.STOP_FOREGROUND_SERVICE"
}

override fun onCreate() {
super.onCreate()
Log.d(TAG, "Service onCreate")
createNotificationChannel()
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "Service onStartCommand")

when (intent?.action) {
ACTION_START -> {
startForegroundService()
}
ACTION_STOP -> {
stopForegroundService()
}
else -> {
// Default action - start the service
startForegroundService()
}
}

return START_STICKY
}

override fun onBind(intent: Intent?): IBinder? {
return null
}

override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "Service onDestroy")
}

private fun startForegroundService() {
Log.d(TAG, "Starting foreground service")

try {
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
Log.d(TAG, "Foreground service started successfully")
} catch (e: SecurityException) {
Log.e(TAG, "Security exception while starting foreground service", e)
// This usually means missing permissions
throw e
} catch (e: Exception) {
Log.e(TAG, "Error starting foreground service", e)
throw e
}
}

fun stopForegroundService() {
Log.d(TAG, "Stopping foreground service")
stopForeground(true)
stopSelf()
}

private fun createNotification(): Notification {
val notificationIntent = Intent(this, MainApplication::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_IMMUTABLE
)

return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("Camera and microphone are active")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.build()
}

private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Foreground Service",
NotificationManager.IMPORTANCE_LOW
).apply {
description = "Channel for foreground service notifications"
setShowBadge(false)
enableLights(false)
enableVibration(false)
}

val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager?.createNotificationChannel(channel)
Log.d(TAG, "Notification channel created")
}
}
}

By default, VideoSDK automatically stops the camera when the app goes into the background. To prevent this, you need to disable automatic activity lifecycle handling by calling setEnableActivityLifecycle(false) before initialize videosdk.

MainApplication.kt
package com.example.example

import android.app.Application
import live.videosdk.rtc.android.VideoSDK
import com.androidnetworking.AndroidNetworking

class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
VideoSDK.setEnableActivityLifecycle(false)
VideoSDK.initialize(applicationContext)
AndroidNetworking.initialize(applicationContext)
}
}

Start the foreground service automatically when a meeting begins and stop it once the user leaves the meeting.

MeetingActivity.kt
class MeetingActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_meeting)

val token = intent.getStringExtra("token")
val meetingId = intent.getStringExtra("meetingId")
val participantName = "John Doe"

VideoSDK.config(token)

meeting = VideoSDK.initMeeting(
this@MeetingActivity, meetingId, participantName,
true, true, null, null, false, null, null)

// Add event listener for listening upcoming events
meeting!!.addEventListener(meetingEventListener)

meeting!!.join()
}

// creating the MeetingEventListener
private val meetingEventListener: MeetingEventListener = object : MeetingEventListener() {
override fun onMeetingJoined() {
// start foreground service
startService(Intent(applicationContext, ForegroundService::class.java).apply {
action = ForegroundService.ACTION_START
})
}

override fun onMeetingLeft() {
// Stop foreground service
startService(Intent(applicationContext, ForegroundService::class.java).apply {
action = ForegroundService.ACTION_STOP
})
}
}
}
note

In above code com.example.example is our package name kindly replace with yours.

Got a Question? Ask us on discord


Was this helpful?