I have a media app that uses a MediaBrowserServiceCompat and ExoPlayer for playback. I start a foreground service with a foreground notification which should allow the service to run in the background indefinitely.
Everything works fine as long as the app is open but the service consistently stops around 10 minutes when the app goes to the background. It can't be a code issue because the service would crash whether the app was in the background or foreground.
I've tried other audio apps and built a minimal media service app and those don't stop so it's something specific to my app. The OS seems to think the service isn't a foreground service and shuts it down.
What is strange is the foreground notification remains so it's not a traditional crash. It's as if the app is in a frozen state. Restarting the app feels like a cold boot and it doesn't appear right away.
Here is a link to two bug report dumps taken right after the service stops if anyone can see anything I missed:
https://drive.google.com/file/d/1Sas2Pxo9331G7RcWrXgxC6PkM7GKSf0o/view?usp=sharing
https://drive.google.com/file/d/13s9NERqWek3y50NhSg4-yNIBKd8pOOC7/view?usp=sharing
These are devices running Android 11. I tried forcing the app into Doze and Standby Mode but that didn't recreate the issue. I checked that all battery optimizations are disabled.
Service:
public class MediaPlayerService extends MediaBrowserServiceCompat {
private MediaSessionCompat mMediaSessionCompat;
private ExoPlayer mPlayer;
@Override
public void onCreate() {
super.onCreate();
mMediaSessionCompat = new MediaSessionCompat(this, MediaPlayerService.class.getSimpleName());
mMediaSessionCompat.setCallback(mMediaSessionCallback);
mMediaSessionCompat.setActive(true);
setSessionToken(mMediaSessionCompat.getSessionToken());
}
private MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
super.onPlay();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.createNotificationChannel(new NotificationChannel("channelid", "Channel Name", NotificationManager.IMPORTANCE_DEFAULT));
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channelid")
...
.setSmallIcon(R.drawable.ic_notification);
startForegroundService(new Intent(getApplicationContext(), MediaPlayerService.class));
startForeground(101, builder.build());
mPlayer = new ExoPlayer.Builder(this).build();
mPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
mPlayer.setMediaItem(MediaItem.fromUri(GetItemUri()));
mPlayer.prepare();
mPlayer.play();
}
@Override
public void onPause() {
super.onPause();
Log.d(getPackageName(), "audio paused");
mPlayer.pause();
stopForeground(STOP_FOREGROUND_DETACH);
}
@Override
public void onStop() {
super.onStop();
Log.d(getPackageName(), "stop stopped");
mPlayer.stop();
stopForeground(STOP_FOREGROUND_REMOVE);
}
};
@Override
public void onDestroy() {
super.onDestroy();
Log.d(getPackageName(), "service destroyed");
mPlayer.releease();
stopForeground(STOP_FOREGROUND_REMOVE);
}
}
AndroidManifest entries:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<service
android:name=".MediaPlayerService"
android:exported="true"
android:foregroundServiceType="mediaPlayback"
android:permission="android.permission.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK">
<intent-filter>
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
Hopefully this will help someone in the future but if you call
stopForegroundyou have to callstartForegroundor the service will stop. The problem is that in Android 12,startForegroundcannot be called when the app is in the background.The solution is to check if battery optimization is disabled for you app:
Then prompt the user to disable optimizations and if they agree open this intent:
This opens the battery optimizations settings where the user can disable battery optimization for your app. It's a horrible user experience but that's the only solution.
Also use
startServiceinstead ofstartForegroundServicebecausestartForegroundServiceexpectsstartForegroundto be called within a couple seconds but if you are checking for battery optimizations thenstartForegroundmight not be called unless you add some additional logic.