Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting ForegroundServiceStartNotAllowedException #111

Open
1 task
kevinguitar opened this issue Jul 8, 2022 · 21 comments
Open
1 task

Getting ForegroundServiceStartNotAllowedException #111

kevinguitar opened this issue Jul 8, 2022 · 21 comments
Assignees
Labels

Comments

@kevinguitar
Copy link

kevinguitar commented Jul 8, 2022

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
Copy link
Contributor

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?

  • Which methods have you overriden?
  • Are you letting MediaSessionService post notifications or do you override that functionality too?
  • What is the lifetime of your MediaControllers?
  • Do you release them every time the app goes into background?
  • How is playback started when the app is in the background?
  • Is it through a notification?
  • What are the Android OS versions of the devices that report the error?

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
Copy link
Contributor

chvp commented Jul 11, 2022

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
Copy link
Author

kevinguitar commented Jul 12, 2022

@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
Copy link
Contributor

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:

  • You said you're using a singleton player instance which you inject in your service. I also see that the service's onDestroy() stops but not releases the player. Why did you go for that design?
  • Do you release the player elsewhere?

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
Copy link
Author

@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
Copy link
Contributor

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
Copy link
Contributor

@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
Copy link

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
Copy link

phucynwa commented Dec 2, 2022

Is this issue fixed in the 1.0.0-beta03 version ?

@marcbaechinger
Copy link
Contributor

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
Copy link
Contributor

chvp commented Dec 2, 2022

I assumed from this comment that it was clear what the issue is: #111 (comment)

@vanniktech
Copy link

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
Copy link
Contributor

marcbaechinger commented Dec 2, 2022

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: #111 (comment)

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
Copy link
Contributor

chvp commented Dec 2, 2022

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

@JaCobbMedia
Copy link

@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
Copy link
Contributor

marcbaechinger commented Jan 27, 2023

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 ForegroundServiceStartNotAllowedException thrown when pressing play on chrome cast device #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.

@julianD77
Copy link

julianD77 commented Feb 8, 2023

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)

@phucynwa
Copy link

phucynwa commented Feb 8, 2023

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

@marcbaechinger
Copy link
Contributor

marcbaechinger commented Feb 13, 2023

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
Copy link

julianD77 commented Feb 15, 2023

@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
Copy link

tzugen commented Apr 13, 2023

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

Did you investigate this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants