How to handle ForegroundServiceStartNotAllowedException while retrying playback after a network timeout error
@marcbaechinger is already working on this.
Since May 7, 2024.
- Dominant language
- Java
- Stars
- 3k
- Forks
- 955
- Avg merge
- 12d 14h
- Merged PRs (30d)
- 2
Description
I want to retry playback when a player error is detected (SourceException indicating timeout). However, while attempting to retry, I get this exception. UI (MediaController) is intact (player in idle state). My retry code in the service determines that foreground service is not running (as determined by isServiceRunningInForeground()) in the following code. However, I see it is still running when I use the adb shell command.
adb shell dumpsys activity services com.myapp.MyMediaService | grep app=
app=ProcessRecord{dd404dc 7272:com.myapp.player/u0a172}
I also see allowStartForeground=PROC_STATE_TOP in the above command output (while playback is going on). And then it changes to DENIED which indicates
- Is the foreground service really running? not sure why I get inconsistent observation
- how do I make retry work in this case? are there any best practices document that explains how this can be achieved while using Media3?
Here is my code:
class MyMediaService : MediaSessionService() {
private var retryJob: Job? = null
private var mediaSession: MediaSession? = null
private val retryScope = CoroutineScope(Dispatchers.IO)
override fun onCreate() {
super.onCreate()
val customFactory = object : MediaSource.Factory { ... }
val player = ExoPlayer.Builder(this)
.setMediaSourceFactory(customFactory)
.build()
.apply {
addListener(object : Player.Listener {
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
when (error) {
is ExoPlaybackException -> {
if (error.type == ExoPlaybackException.TYPE_SOURCE) {
retryJob?.cancel()
retryJob = retryScope.launch { // Just retry once
delay(5000L)
restartPlayback()
}
}
}
}
}
})
playWhenReady = true
}
val intent = packageManager.getLaunchIntentForPackage(packageName)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, FLAG_IMMUTABLE)
mediaSession = MediaSession.Builder(this, player)
.setId("XXX")
.setSessionActivity(pendingIntent)
.build()
setListener(object : Listener {
override fun onForegroundServiceStartNotAllowedException() {
super.onForegroundServiceStartNotAllowedException()
Timber.d("Overriding onForegroundServiceStartNotAllowedException()")
}
})
}
override fun onTaskRemoved(rootIntent: Intent?) {
mediaSession?.player?.stop()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
super.onTaskRemoved(rootIntent)
}
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
return mediaSession
}
@Suppress("DEPRECATION")
private fun isServiceRunningInForeground(): Boolean {
val manager = getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
val runningServices = manager?.getRunningServices(Int.MAX_VALUE)
return runningServices?.any {
it.foreground && it.service.className == MyMediaService::class.java.name
} ?: false
}
private fun startForegroundService() {
val serviceIntent = Intent(this, MyMediaService::class.java)
try {
ContextCompat.startForegroundService(this, serviceIntent)
} catch (e: ForegroundServiceStartNotAllowedException) {
Timber.e("ForegroundServiceStartNotAllowedException caught: ${e.message}")
}
}
private suspend fun restartPlayback() = withContext(Dispatchers.Main) {
if (!isServiceRunningInForeground()) {
startForegroundService()
//startForeground()
mediaSession?.player?.let { player ->
if (player.playbackState == Player.STATE_IDLE || player.playbackState == Player.STATE_ENDED) {
player.prepare()
} else if (player.playbackState == Player.STATE_READY) {
player.play()
}
}
} else {
mediaSession?.player?.prepare()
mediaSession?.player?.play()
}
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.