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

android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false #996

Open
serhii-k opened this issue Feb 26, 2023 · 17 comments

Comments

@serhii-k
Copy link

Documented behaviour

On Android 12+ it's not permitted to start a foreground service from the background.

Actual behaviour

Please follow the reproduction steps for a clear demonstration how to achieve this exception.

Precondition: the app is playing an audio in the background.
If we listen to the AudioSession.interruptionEventStream, and we pause for "begin" of the AudioInterruptionType.pause, and then we resume for "!begin". At this moment the exception happens. For some reason there is an attempt to start a foreground service (which actually is already running...) from the background.

Minimal reproduction project

Official example: example_multiple_handlers.dart

Reproduction steps

The steps should be done pretty fast, or you can add more numbers to say.

  1. Run the example_multiple_handlers.dart
  2. Switch to "Text-To-Speech"
  3. Press "Play"
  4. Hide the example app to the background (e.g. go to the Home Screen)
  5. Start the Google Assistant
    a) If you have new gestures (a horizontal bar at the bottom of the screen). Drag from the left bottom corner of the screen to the center of the screen.
    b) If there are old 3 buttons. Press and hold the middle "Circle" button.
  6. After Google Assistant is being visible for 2-3 seconds, close it by pressing outside of its shape. After closing the Google Assistant, the example app receives the temporarily withtaken audio focus, and tries to resume the audio playback. For some reason, there is an attempt to start the foreground service again. An because the example app is in the background, that triggers the ForegroundServiceStartNotAllowedException.

Output of flutter doctor

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.3)
[✓] VS Code (version 1.52.1)
[✓] Connected device (2 available)
[!] HTTP Host Availability
    ✗ HTTP host "https://cocoapods.org/" is not reachable. Reason: Failed to connect to host in 10 seconds

Devices exhibiting the bug

Android Emulator API Level 33 x86_64 (with Google Play)

@ruchit-7span
Copy link
Contributor

@serhii-k
Yes, Issue is there in package.
I found out a solution for this.Please follow below steps.
App Setting -> Battery saver or Battery performance -> change to no restriction.

With the above steps, My app is working fine.
Note: This is not a solution. It is only a temporary steps to the problem.

@ryanheise
Copy link
Owner

See #932 (comment) and let me know if it works for you. If it does, I'll reflect this suggestion in the README.

@serhii-k
Copy link
Author

serhii-k commented Mar 1, 2023

@ruchit-7span
Thank you for your suggestion. Yes, setting the unrestricted battery mode for the app lets it start a foreground service from the background. And this issue doesn't happen.
https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions

@ryanheise
However... I guess in this case there is a room for some improvements for the plugin. If that's okay in your opinion. I mean, it seems a serious issue for Android 12+.

If I understand correctly from the plugin code:

  1. There is AudioServiceConfig.androidStopForegroundOnPause.
    If it is set to true: when a user clicks Pause the foreground service stops, and then for Play the foreground service is being started once again.
  2. This leads to the ForegroundServiceStartNotAllowedException if the app happens to be in the background (!)
    For the cases when Pause-Play happen in the background (e.g. listeners of the audio focus events).
  3. If I switch androidStopForegroundOnPause to false:
    [background] during Pause the foreground service remains alive, and there is no such exception when Play occurs.
  4. For sure, it's preferable for androidStopForegroundOnPause to be true. However, in some situations (e.g. audio focus is just temporarily taken away by an incoming call) I would definitely like to be able to:
  • temporarily disable the androidStopForegroundOnPause behaviour
  • maybe by calling a new method preventAndroidStopForegroundOnPause(bool) that will set some new boolean field, which will be accounted inside the exitPlayingState() along with the config.androidStopForegroundOnPause

Or maybe some other more elegant, well thought solution... But that is really something that can be accounted...

@ryanheise
Copy link
Owner

I believe there is another feature request and pull request to make (4) possible but there is no settled-upon API design for it yet. That said, I agree the ideal is to set androidStopForegroundOnPause to true and then the goal for this present issue is to stop that exception from happening.

And although disabling the battery optimisation will prevent the exception, you're saying that it would also be good to stop the exception from happening in all cases, even if the battery optimisation is not disabled. Am I understanding that correctly?

@serhii-k
Copy link
Author

serhii-k commented Mar 1, 2023

@ryanheise Yes, if we aren't stopping the foreground service during Stop, then during Play this exception doesn't occur (as the foreground service is already started). So the user doesn't even have to disable the battery optimisation.

I completely agree that to make (4) possible there should be some good API. Argh... Android makes things so ugly as usual...

@ryanheise
Copy link
Owner

Unfortunately, I'm baffled as to why this exception occurs at all, because that Android documentation you linked to seems to indicate that we are one of the exemptions. I've tried to look into it deeply before but came up with nothing (except for programmatically sending users to the settings to disable battery optimisation).

@ground-beetle
Copy link

Commented in the related issue: #994 (comment)
Maybe you will find those resources useful 😅
Probably its possible to link these two issues since they seem to relate.

@skiluk
Copy link

skiluk commented Feb 19, 2024

@serhii-k I created PR #1054 which addresses this issue for our case. Adding androidStopForegroundOnCompleted. Maybe this would help you as well

@milesegan
Copy link

I'm also getting this error although only sporadically. I haven't found a simple reliable reproduction method for it yet.

Maybe something along these lines would help?
https://stackoverflow.com/a/72449084/3746945

@ryanheise
Copy link
Owner

Maybe something along these lines would help?
https://stackoverflow.com/a/72449084/3746945

That's an interesting approach... I haven't encountered the AlarmManager solution before.

@milesegan
Copy link

I'm not sure if that makes sense but it would be great if there was at least some way to catch this error. As it is now the whole app dies.

Thanks for all your work on audio_service and just_audio by the way! They're a huge time saver.

@ryanheise
Copy link
Owner

I could look into that as a fallback in case the app doesn't implement other measures to avoid the exception.

@milesegan
Copy link

I could look into that as a fallback in case the app doesn't implement other measures to avoid the exception.

That would be much appreciated! There's no way I can wrap my own exception handler around this now in my own code, right?

@ryanheise
Copy link
Owner

The fix/notification_npe branch adds the stream AudioService.asyncError which broadcasts any exceptions that occur asynchronously, such as when the foreground service fails to start due to this exception. Given that Android will crash your whole process if you forget to handle an exception in an event handler (not a good design if you ask me!), I thought it would be a good idea to go through all the code with a fine-toothed comb and ensure all exceptions are caught. In the process I identified a couple of other places where in theory this could happen, and these are also forwarded to asyncError in this branch.

Please try it out and let me know if it works. Alongside that, the recommended solution is still to change the battery optimisation settings.

The next thing I'd like to investigate is what influence the processing states might have on things.

@ryanheise
Copy link
Owner

I would like to avoid solutions based on AudioServiceConfig since those options are static, and what we're dealing with here is a requirement to change the foreground status dynamically. stopForegroundOnPause is like a hard coded strategy which is fine if we can only think of one strategy, but as it turns out, there may be numerous strategies in which case it may be best to expose an additional field in PlaybackState to control this dynamically.

I can see two options:

  • Add an androidForeground field where you explicitly set it when you do and don't want the service to be in the foreground.
  • Add an androidForceForeground field where you allow the existing strategy to do its thing except when you set this to false and you want to force it into the foreground.

I think the first option seems to provide more flexibility, but is too prone to misuse because people who are new to this will forget to set it and wonder why their app is not working.

Thoughts?

@milesegan
Copy link

The AudioService.asyncError approach makes a lot of sense to me. I will try this and get back to you ASAP. Thanks again for your great work on this!

I agree that crashing the entire app on an uncaught exception is a questionable design choice in the Android APIs.

@ryanheise
Copy link
Owner

I've just merged and published the fix/notification_npe branch which should allow the app to at least catch/listen to this error, and importantly, prevent an app crash.

I will keep this issue open pending a feedback on the ideas suggested in #996 (comment)

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

No branches or pull requests

6 participants