Open kevinguitar opened 2 years ago
I can see you also filed #112 which looks related. I'll ask some information here to understand the usage of MediaSessionService in your app before going to #112.
Can you share how you're using MediaSessionService
?
MediaSessionService
post notifications or do you override that functionality too? MediaControllers
?For context: The stack trace shows that the player started playback while the service was not on the foreground anymore. Playback may start
from a notification: The notifications posted by MediaSessionService make sure to start the service in the foreground. If you're overriding the notification functionality, you'd need to ensure you create the notifications properly.
from a MediaController. In this case, the MediaController must first bind to the service in which case the service should be allowed to go the foreground (unless the app creating the MediaController is in the background..?)
You mention that you instantiate the media controller right after the user starts to playback
. May I assume then that playback starts from a notification?
I can reliably reproduce this issue by starting playback with the media controls of my smartwatch. (Android 12, Oneplus 6T) Smartwatch is a Garmin Vivoactive 4.
@christosts Thanks for the questions! They're very inspiring in many ways, I'll also recheck how those situations were handled in your sample app.
Here's how we implemented our playback service, please kindly check: https://gist.github.com/kevinguitar/5d2946e35ebf3f6836cbde778feb061e
We're using it for audio playlist streaming and local audio file playback, the ExoPlayer
is a singleton instance and the MediaNotificationManager
is our internal class which extends your DefaultMediaNotificationProvider
with a customized bitmap loader.
Are you letting MediaSessionService post notifications or do you override that functionality too?
We didn't override the method that related to notification posting.
Do you release them every time the app goes into background?
No, is it required to get media playback working without issue? 🤔
How is playback started when the app is in the background? Is it through a notification?
I assume that starting the playback from the notification in the background was already handled on your side, and we didn't override that functionality. As I see, the media controller creation is asynchronous, we do create it when the app is in the foreground, but I'm suspecting it may have a chance to be finished in the background, what's your thought on this?
What are the Android OS versions of the devices that report the error?
This crash only happens on Android 12 according to our stats.
You mention that you instantiate the media controller right after the user starts to playback. May I assume then that playback starts from a notification? What is the lifetime of your MediaControllers?
Actually no, I saw that the MediaSessionService
is a bound service, and it will be started automatically if there are any media controllers bound to it, so I simply instantiate a media controller when starting the playback in-app (by clicking the play button on audio), and we release it only when stopping the playback explicitly from the UI.
Thank you for your answers. My questions were for trying to understand how you are using the MediaSessionService
, in case we need to change its implementation or documentation.
Starting playback from notifications in the background is handled by the library. We haven't spotted a bug (yet) but maybe we missed something.
I looked at the code you shared (thanks again!). I noticed you have deviated from the reference service implementation. I wonder if that is causing some of the issues. Some suggestions/questions:
onDestroy()
stops but not releases the player. Why did you go for that design?In our reference implementation, the player lifetime coincides with the service lifetime, i.e. the player is created in the service's onCreate()
and released when onDestroy()
is called by the OS. If you don't release the player, it's possible that the player still issues events (the service is a listener of these events), therefore I wonder if it's possible the service is trying to handle a player event after the service onDestroy()
has been called, hence the issue you are facing.
You also override onTaskRemoved()
. Is there a reason you did that? The MediaSessionService
does not override onTaskRemoved()
on purpose - it could be that we have to add it, but so far we've seen it's not necessary to call stopSelf()
. What would happen in your app if you removed onTaskRemoved()
?
@christosts Yeah, I think the player lifetime is a potential issue as you said, it was designed this way because we wanted to have a global listener attached to the player, so the player UI can react accordingly. I'll have a deeper look at your sample project and see how we can adjust it to fit our requirements, huge thanks for the suggestions!
You also override onTaskRemoved(). Is there a reason you did that?
We want to stop the playback when the app is removed from the recent activities, so that's why we call stopSelf()
there, does that make sense?
We want to stop the playback when the app is removed from the recent activities, so that's why we call stopSelf() there, does that make sense?
It makes sense. Overall, my recommendation remains to start by releasing the player in the service's onDestroy(). For example, right now, calling stopSelf()
from onTaskRemoved
will trigger the OS to destroy the service, however the instance is still listening to player state changes.
Back to the exception/stack trace now: the issue is that the player started playback while the app/service are not in the foreground. If playback started from notification, then the play button in the notifications starts the service in the foreground. The question then is if our app started playback from another source (see comment below)
@kevinguitar & @chvp we know of a use-case where this error is being raised, when playback is started from other apps connected through MediaSession, i.e. playback did not start as a result of a notification posted by the library, specifically on Android 12. We're working on this use-case and I'll update the issue for any findings/solution/advice.
We're working on this use-case and I'll update the issue for any findings/solution/advice.
Has there been any update? I've got a similar stacktrace:
Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.vanniktech.rssreader/com.vanniktech.feature.rssreader.itemdownload.RssReaderItemDownloadBackgroundService
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 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:203)
at androidx.media3.session.MediaNotificationManager.updateNotification(MediaNotificationManager.java:179)
at androidx.media3.session.MediaSessionService.onUpdateNotification(MediaSessionService.java:409)
at androidx.media3.session.MediaNotificationManager$MediaControllerListener.onEvents(MediaNotificationManager.java:291)
at androidx.media3.session.MediaControllerImplBase.lambda$new$0(MediaControllerImplBase.java:204)
at androidx.media3.session.MediaControllerImplBase$$ExternalSyntheticLambda48.invoke(:4)
at androidx.media3.common.util.ListenerSet$ListenerHolder.iterationFinished(ListenerSet.java:294)
at androidx.media3.common.util.ListenerSet.handleMessage(ListenerSet.java:246)
at androidx.media3.common.util.ListenerSet.$r8$lambda$bio3pd12v5B_9b5UeFaPn9XBQ90()
at androidx.media3.common.util.ListenerSet$$ExternalSyntheticLambda0.handleMessage(:2)
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:8663)
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)
Is this issue fixed in the 1.0.0-beta03 version ?
Have you tried? If you are still seeing this, please expand your comment to make it actionable. Check my response to your comment in the other issue. We need the same details from you as I have explained there.
I assumed from this comment that it was clear what the issue is: https://github.com/androidx/media/issues/111#issuecomment-1191596778
I released my app with beta03 and so far I haven't gotten anything. So could be fixed or no one has ran into that particular issue. Gut feeling tells me it's better though.
Thanks @vannitech for the update. Please post here if you see something else!
@chvp
I assumed from this comment that it was clear what the issue is: https://github.com/androidx/media/issues/111#issuecomment-1191596778
Yeah, not sure I'm afraid. It's quite a jungle with API levels and targetSdk, so we would need to have all the details. That's why I'm commenting that placing an any updates/is this fixed
on multiple bugs here is not sufficient to make this actionable for us. Sorry when this has come across the wrong way.
Some more context.
In general for MediaLibraryService/MediaSessionService
, I assume this issue is fixed as far as it can be fixed on our end (Media3). If someone still has indication that this happens on a device that is not running Android 12 (API 31/32), then we need to investigate some further.
A) On Android 12 there is an issue in the framework that there is no exemption for calls coming through the legacy media session. One of the use case that is broken on these devices and is easy to repro is pausing and resuming playback by voice command. A fix for S has been sent far patching to OEMs. It's on their discretion how to go forward. We have to assume that it never lands on some devices I'm afraid.
What is on our plate for these cases is a) that we will catch such exceptions in the library (probably silently) to avoid a crash. We can't it work on the library side unfortunately. We further will b) change the MediaButtonReceiver
of the media1 library to catch such exceptions to avoid crashes. We will update this issue when this is release (in case of media1) or available in the main or release branch for Media3.
B) The fix mentioned is in Android 13. If someone sees a ForegroundServiceStartNotAllowedException
with Android 13 then this is either an random app sending media button event your way (for instance not from Bluetooth head sets) that is received with the MediaButtonReceiver
when the app is not running (see this doc how this is registered). All other cases would be a bug in Media3. Please report if you see that. Best is a repro with the session demo app.
That's clear, thanks. I did not know about the changes to the framework. I can confirm I can no longer repro this issue with my smartwatch on Android 13 (because this issue was not updated, I assumed this wasn't fixed yet, so I didn't try).
(This is also the only issue I am subscribed to, so I didn't see any other "is this fixed yet" comments. I get that those can be annoying :slightly_smiling_face:.)
@marcbaechinger I'm getting this crash on Android 13 as well. My case is that user goes to background, listens for around 20 minutes and it crashes when transitioning to other item. Item transition is handled by player (we only provide list of items). MediaController
is always released when app goes to background. Tried reproducing it myself but had no success :/
Summarizing the known causes for ForegroundServiceNotAllowedException
and work arounds if available:
Pausing and resuming playback from an external device like with CastPlayer
(see as reference issue #218). There isn't a satisfactory way to work around this yet.
Retrying after a playback error when the app is in background crashes the app when the problem is fixed asynchronously. The reason for this is that when the playback error occurs, the service is taken off the foreground. Resuming playback then needs to be done from the foreground again. There isn't a satisfactory way to work around this yet besides not retrying (or retry without async operations) in such a case. An app may want to post a notification when a playback error occurs to offer the user a way to resume playback by user interaction.
MediaButton Intents without exemption: Generally, only Bluetooth headphones are allowed to restart playback in the background with a media button Intent
. This is true either when the app is running in the background and when the app has been terminated and playback resumption via a MediaButtonReceiver
is attempted. The next release of the (legacy) androidx.media
library, will have the MediaButtonReceiver
updated to avoid crashes (see this change). Note that this is only required if an unauthorized app broadcast an media button event that is received by your receiver. The mentioned change aims to avoid the crash, but can't make the use case work. Instead apps other than Bluetooth need to use a controller instead of broadcasting intents (aside: a MediaButtonReceiver
for Media3 is planned for the 1.1.0 release).
Android 12: Voice commands can not resume playback. With the next release, such an exception will be caught by the Media3 library so that the app does not crash. The use case is still not working on Android 12 I'm afraid, but the app isn't crashed. An app can register a MediaSessionService.Listener
to be informed when such an exception is caught. An app can then for instance post a notification to inform the user and offer a notification to either go to the app or resume playback from the notification. This is implemented by the demo app of the main
branch and will land in the next release that is to come soon.
That's the state so far and all I know.
This is really helpful - thanks @marcbaechinger
If useful to others we also are seeing this on Android 12 and 13 only through Crashlytics and cannot (yet) reproduce locally. We are tracking the package IDs that are causing our MediaBrowserService
to start by overriding the onGetRoot
method and see that it is always either of these:
com.google.android.bluetooth
(playback triggered from remote controls of the BT headset) com.android.systemui
(we are assuming this is playback triggered from the Media Controls / Notification)@marcbaechinger Thank you. When is the next version released ?
If useful to others we also are seeing this on Android 12 and 13
@julianD77 What is 'this' in your case? Is it one of the cases above?
that are causing our
MediaBrowserService
to start by
This is expected behaviour. To be precise, these packages will not start
you service, they only bind
against your service and immediately unbind
again. If you are talking about MediaBrowserService
of the media1
library (as opposed to MediaSession
or MediaLibraryService
of Media3
) then you are handling foreground service modes yourself. We can't really help you much with this I'm afraid.
However, as I said it is expected that the service is created and accessed by the package names you are mentioning. I think though, they are accessing service methods (like onGetRoot
) only to get information about the media catalogue you are offering. In this stage there is no reason for your service to go into foreground. Just let them call your methods and let them unbind which makes the service exit immediately. You should not be required to start playback when browsing services are called I think. So you would not be required to ever put the service into started
or foreground
mode. Just let them bind and unbind and the service will exit automatically when the last client unbinds.
@julianD77 What is 'this' in your case? Is it one of the cases above?
Thanks @marcbaechinger yes, sorry I was referring to an issue we see when using the media1
library. We have not yet upgraded to media3
.
For info, we have just tracked down the issue with our MediaBrowserService
implementation (from the media1
lib) to a bug in our player code. We found that we were no longer closely following the logic suggested in the UAMP example regarding the calls to startForeground
here. A bug was allowing startForeground
to be called when the state was not PlaybackStateCompat.STATE_PLAYING
Thanks also for the clarification regarding processes accessing binding to the service temporarily for the purpose of retrieving information about the catalogue using onGetRoot
etc
@marcbaechinger In the platform bug report some users have reported a workaround, using setForegroundServiceBehavior() with FOREGROUND_SERVICE_IMMEDIATE
Did you investigate this?
I'm just getting this, described in https://github.com/androidx/media/issues/1715
@marcbaechinger In the platform bug report some users have reported a workaround, using setForegroundServiceBehavior() with FOREGROUND_SERVICE_IMMEDIATE
Did you investigate this?
I tried it, but it doesn't work.
Can we get some help on this? In my case, it happens the app plays a playlist while at background or with screen off, of music or podcasts. I detailed my finding in #1715 . With this issue, the app doesn't crash, but without foreground service, it's getting killed after a while.
Media3 Version
1.0.0-beta01
Devices that reproduce the issue
Samsung Galaxy S10+ Samsung Galaxy S32 5G Samsung Galaxy A51 OPPO A93
Happened only on Android 12
Devices that do not reproduce the issue
Can't reproduce manually, we got the stats from Crashlytics
Reproducible in the demo app?
Not tested
Reproduction steps
We extended our playback service by your
MediaSessionService
, and according to the crash stats in Crashlytics, the device states were 100% in the background, we actually instantiate the media controller right after the user starts to playback, but I'm not sure what's going on under the hood.Expected result
Since we don't have control over starting the service as foreground, I'm wondering does it make sense to catch the exception on the library side, at least to avoid crashing in this case?
Actual result
Getting the crash below
Media
Nothing particular, just normal audio streaming
Bug Report
adb bugreport
to dev.exoplayer@gmail.com after filing this issue.