Repository navigation
CastPlayer produces ForegroundServiceStartNotAllowedException on media item transition #245
Copy link
Copy link
Closed
Labels
Description
Media3 Version
1.0.0-beta03
Devices that reproduce the issue
Various devices running android 12 and android 13
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Not tested
Reproduction steps
Wasn't able to reproduce it myself but I'm still reporting this because it was mentioned in #111 that we should report it if it happens on Android 13. According to crashlytics this happens 100% in background. By the logs I see various scenarios how this happens but most common one is when player is auto transitioning to next item.
Some additional info that might help indicate the problem:
- We are releasing
MediaControllerwhen user goes to background - On media item transition we are saving current item to DB, if it was listened before we are seeking to it's last listened position and adjusting
playWhenReadyif user has turned off/on autoplay. (But doubt this is causing any issues since crashes happen not only when item is transitioning) onTaskRemovedwe are doingstopSelfifplayer.playWhenReady != true- We do not override
onUpdateNotification, but we are setting a customMediaNotification.Provider
I can provide any additional info or logs if needed
Expected result
Not getting ForegroundServiceStartNotAllowedException and successfully continuing playback in background.
Actual result
Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.podimo/.app.core.media.MediaService
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
at android.os.Parcel.readParcelable(Parcel.java:3345)
at android.os.Parcel.createExceptionOrNull(Parcel.java:2432)
at android.os.Parcel.createException(Parcel.java:2421)
at android.os.Parcel.readException(Parcel.java:2404)
at android.os.Parcel.readException(Parcel.java:2346)
at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:6968)
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1927)
at android.app.ContextImpl.startForegroundService(ContextImpl.java:1893)
at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:798)
at com.podimo.app.core.media.MediaService.startForegroundService(MediaService.kt:153)
at androidx.core.content.ContextCompat$Api26Impl.startForegroundService(ContextCompat.java:931)
at androidx.core.content.ContextCompat.startForegroundService(ContextCompat.java:703)
at androidx.media3.session.MediaNotificationManager.updateNotificationInternal(MediaNotificationManager.java:206)
at androidx.media3.session.MediaNotificationManager.updateNotification(MediaNotificationManager.java:182)
at androidx.media3.session.MediaSessionService.onUpdateNotification(MediaSessionService.java:411)
at androidx.media3.session.MediaNotificationManager$MediaControllerListener.onEvents(MediaNotificationManager.java:299)
at androidx.media3.session.MediaControllerImplBase.lambda$new$0(MediaControllerImplBase.java:151)
at androidx.media3.session.MediaControllerImplBase$$InternalSyntheticLambda$3$10aed4c532fdc841d63f0af013a047ffd34a0b4b9b1d6da420408b3dbfb435a8$0.invoke(MediaControllerImplBase.java:1)
at androidx.media3.common.util.ListenerSet$ListenerHolder.iterationFinished(ListenerSet.java:295)
at androidx.media3.common.util.ListenerSet.handleMessage(ListenerSet.java:246)
at androidx.media3.common.util.ListenerSet$$InternalSyntheticLambda$1$0a1dffe138e30e7ad8eb5a169bfb4852f73945d310b0f0a49ed6bc74e8c308ab$0.handleMessage$bridge(ListenerSet.java:57)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8669)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit
$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Media
Bug Report
- You will email the zip file produced by
adb bugreportto [email protected] after filing this issue.
Reactions are currently unavailable
Activity
marcbaechinger commentedon Jan 24, 2023
Thanks for the report.
The service is put to/removed from foreground when
playWhenReadychanges.I can't think of why that should happen when the player transitions playback from one item to the next. Even when a seek is performed within or across media items,
playWhenReadyis not changed by the player internally.Accordingly, this seems to be a potential source of the problem, but I'm not sure I understand how. As far as I understand, we have the following scenarios:
playWhenReadyis false: In this case we would not see an automatic transition because playback is paused.playWhenReadyis true andautoplay off: In this case the transition would happen and thenplayWhenReadyis set to false. This would cause the service to be removed from the foreground. This does not result in the reportedForegroundServiceStartNotAllowedException.playWhenReadyis true andautoplay on: In this caseplayWhenReadyis not changed and the service remains in the foreground. No change in the state of the service is requested.The only thing I can think of is that 'adjusting playWhenReady' involves switching
playWhenReadyto false and then to true again, which would be like resuming playback but I actually wouldn't expect you doing that.One way to verify what is happening would be that you add an
EventLoggerto the app. This would make visible what happens on the player side. However, this would only help in case we get a bug report from such a device just after the crash has occured. We would then be able to see the logs ofEventLoggerto see whether the crash was following a change in the player state.JaCobbMedia commentedon Jan 25, 2023
Thank you for the response @marcbaechinger . I'll investigate more our autoplay logic, but it's pretty basic oneliner -
player?.playWhenReady = player?.playWhenReady == true && autoplay. Also got some logs from latest release and noticed few scenarios:STATE_IDLEand right back toSTATE_READYERROR_CODE_DECODER_INIT_FAILEDwhich caused same state change as previously described. I will look into it more, but ifPlaybackExceptionis thrown we just callplayer.stop()and send some events to UI, we don't really retry restarting playback without user interaction.I added
EventLoggerto new app version, so I should get more info when users crash on that version. Will update this issue more thenmarcbaechinger commentedon Jan 25, 2023
Yeah, agreed the
playWhenReadything should be trivial.But good call regarding the playback error! I haven't taken this case into account. We (the Media3 team) need to think about this case well. Auto re-starting in the background wouldn't work like you say. This would remove the service from and put it back to the foreground. Thanks for bringing this up!
Having the logs from
EventLoggerwould give as some more insight in any case!JaCobbMedia commentedon Jan 27, 2023
Okay, so I got some logs from latest release.
EventLoggerand then I realised it was happening onCastPlayer(confirmed that by looking into our events). Then tried reproducing it by myself and it was pretty easy - I just let one item transition to other item while casting and immediately got FgS exception. Then tried it with UAMP project on media3 branch, same happened there :(java.net.SocketTimeoutExceptionwhile in background, might be because of their poor internet connection.DataSourcetries couple of times to retry the connection and fails, then 10s later goes to buffering state andisPlayingchanges to false and immediately FgS exception.I'll be looking into more cases, but if you need any full logs from these cases, I can send it via email.
marcbaechinger commentedon Jan 27, 2023
Thanks for the analysis.
We definitely should look into
CastPlayerand why the state is changing when transitioning. It defo sounds like a bug to me as this should not happen. I'll change the title of this issue accordingly if that's ok with you.googlequicksearchbox - Yeah, as you mention. Known issue on Android 12.
InternalError - This is actually an obvious one when thinking about it. I haven't noticed this yet though, so thanks for reporting. There is not much we can do about it with the current best practice implementation that is to take the service out of the foreground when not playing. The exception, and the resumption of playback after the error causes interrupts the playback and a retry will cause the
ForegroundServiceStartNotAllowedException. We are internally advocating for a different implementation approach that would possible help in this case as well. So thanks for giving us another argument! On the other hand, I don't think we can possibly do something right now for this case. The only thing an app can do is posting aNotificationto ask the user to start the retry by user intervention. This would then give a way to resume from the foreground.I have updated #111 accordingly and I rename this issue to focus on the
CastPlayertransitions only. Please shout if you thing that doesn't make sense. :)[-]ForegroundServiceStartNotAllowedException on Android 13 devices[/-][+]`CastPlayer` produces `ForegroundServiceStartNotAllowedException` on media item transition[/+]JaCobbMedia commentedon Jan 27, 2023
Thank you once again.
I just have to mention few things. After receiving an internal error we don't actually try restarting playback by ourselves. We have
DefaultLoadErrorHandlingPolicyadded to ourDefaultMediaSourceFactory. In the logs I can't see thatPlaybackExceptionhas been thrown yet because then we would have stopped player, so my guess is that crash happened during load error handling process. Could that be the case?And
googlequicksearchboxcase happened on Android 13 actually (Galaxy XCover 5)marcbaechinger commentedon Jan 27, 2023
Thanks for clarification. What is the playback state doing when this happens? Because I'd only expect a fatal error changing the playback state to
IDLEas a consequence ofstopbeing called. I think we need to investigate this a bit more in this case. I think when we retry (as in terms ofLoadErrorHandlingPolicy) and in the end successfully start loading again we should somehow be able to not getting the service off the foreground, so this should actually work.The case I mentioned regarding retry after a
PlaybackExceptionis harder to fix I think.That's odd. I'll test this. Thanks for mentioning.
JaCobbMedia commentedon Jan 27, 2023
What I can see from the logs:
STATE_READYplayWhenReady = true,isPlaying = trueMediaControllerinternalErrorseach with 8s intervalinternalError8s passed and state changed toSTATE_BUFFERINGand isPlaying changed tofalsemarcbaechinger commentedon Jan 27, 2023
With the scenario above, the service should not be taken off the foreground as far as I can tell. Being in state
BUFFERINGisn't enough to get off the foreground. The player either needs to be inSTATE_IDLEorSTATE_ENDEDorplayWhenReady==falseto take the service off the foreground.We need to investigate this. Many thanks for your detailed report. Much appreciated.
marcbaechinger commentedon Jan 30, 2023
I can confirm that the
CastPlayertransitions toSTATE_IDLEwhen playback transitions automatically from one item in the playlist to the next. Then it immediately goes toSTATE_READYagain.This explains why the service is taken off the foreground and then produces the reported exception. We will look into
CastPlayerand we will update this issue when we have submitted a change regarding this problem. Thanks for reporting again.Map `PLAYER_STATE_LOADING` to `STATE_BUFFERING`
Map `PLAYER_STATE_LOADING` to `STATE_BUFFERING`
Map `PLAYER_STATE_LOADING` to `STATE_BUFFERING`
Map `PLAYER_STATE_LOADING` to `STATE_BUFFERING`
marcbaechinger commentedon Mar 4, 2023
Closing this issue as it is fixed as per commits above.
JaCobbMedia commentedon Mar 6, 2023
Great news, thank you!