androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
https://developer.android.com/media/media3
Apache License 2.0
1.69k stars 404 forks source link

Getting ForegroundServiceStartNotAllowedException #111

Open kevinguitar opened 2 years ago

kevinguitar commented 2 years ago

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

Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.bandlab.bandlab/com.bandlab.media.player.notification.MediaPlaybackService
       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:6965)
       at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1926)
       at android.app.ContextImpl.startForegroundService(ContextImpl.java:1892)
       at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:796)
       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: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

Nothing particular, just normal audio streaming

Bug Report

christosts commented 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?

For context: The stack trace shows that the player started playback while the service was not on the foreground anymore. Playback may start

  1. 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.

  2. 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?

chvp commented 2 years ago

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.

kevinguitar commented 2 years ago

@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.

Regarding your questions:

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.

christosts commented 2 years ago

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:

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()?

kevinguitar commented 2 years ago

@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?

christosts commented 2 years ago

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)

christosts commented 2 years ago

@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.

vanniktech commented 2 years ago

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)
phucynwa commented 1 year ago

Is this issue fixed in the 1.0.0-beta03 version ?

marcbaechinger commented 1 year ago

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.

chvp commented 1 year ago

I assumed from this comment that it was clear what the issue is: https://github.com/androidx/media/issues/111#issuecomment-1191596778

vanniktech commented 1 year ago

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.

marcbaechinger commented 1 year ago

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.

chvp commented 1 year ago

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:.)

JaCobbMedia commented 1 year ago

@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 :/

marcbaechinger commented 1 year ago

Summarizing the known causes for ForegroundServiceNotAllowedException and work arounds if available:

That's the state so far and all I know.

julianD77 commented 1 year ago

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:

phucynwa commented 1 year ago

@marcbaechinger Thank you. When is the next version released ?

marcbaechinger commented 1 year ago

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 commented 1 year ago

@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

tzugen commented 1 year ago

@marcbaechinger In the platform bug report some users have reported a workaround, using setForegroundServiceBehavior() with FOREGROUND_SERVICE_IMMEDIATE

Did you investigate this?

XilinJia commented 1 month ago

I'm just getting this, described in https://github.com/androidx/media/issues/1715

XilinJia commented 1 month ago

@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.

XilinJia commented 1 month ago

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.