ryanheise / audio_service

Flutter plugin to play audio in the background while the screen is off.
790 stars 471 forks source link

ForegroundServiceStartNotAllowedException #994

Closed Josaldy closed 5 months ago

Josaldy commented 1 year ago

Documented behaviour

n/a

Actual behaviour

see error below

Runtime error

Exception has occurred.
PlatformException (PlatformException(error, startForegroundService() not allowed due to mAllowStartForeground false: service inc.josrainc.distractionfmdemo/com.ryanheise.audioservice.AudioService, null, android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service inc.josrainc.distractionfmdemo/com.ryanheise.audioservice.AudioService
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
at android.os.Parcel.readParcelableInternal(Parcel.java:4787)
at android.os.Parcel.readParcelable(Parcel.java:4755)
at android.os.Parcel.createExceptionOrNull(Parcel.java:3018)
at android.os.Parcel.createException(Parcel.java:3007)
at android.os.Parcel.readException(Parcel.java:2990)
at android.os.Parcel.readException(Parcel.java:2932)
at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:6201)
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1967)
at android.app.ContextImpl.startForegroundService(ContextImpl.java:1933)
at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:839)
at androidx.core.content.ContextCompat$Api26Impl.startForegroundService(ContextCompat.java:931)
at androidx.core.content.ContextCompat.startForegroundService(ContextCompat.java:703)
at com.ryanheise.audioservice.AudioService.enterPlayingState(AudioService.java:649)
at com.ryanheise.audioservice.AudioService.setState(AudioService.java:505)
at com.ryanheise.audioservice.AudioServicePlugin$AudioHandlerInterface.onMethodCall(AudioServicePlugin.java:885)
at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:258)
at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8741)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
))

Minimal reproduction project

Official example: main.dart

Reproduction steps

Try watching whatsapp status for a few minutes while app is playing audio

(Edit by Ryan: adding the detail given below in the comments)

-Run the app on a real device -Press Play -While the app is running in the background, Go to whatsapp to watch status. Normally while watching whatsapp status some status might be images so the app will continue playing audio. If a whatsapp status is a video the app will momentarily pause and resume after the video status is completed, which is how it's supposed to behave. -The app will work fine for a few minutes (pause for video status and resume after video is completed) and if continue watching status for a few minutes the exception will trigger and a black screen will be shown. -Same behavior is observed either if the app is running in the background or if app was closed while audio continues playing while the app was closed. -Similar behavior is noted as well during phone calls. The app will behave normally during phone calls. Audio will be paused and resumed after the call. However, if the call last too long it will not resume playing afterwards and the exception will be thrown as well. -The exception is not thrown when using youtube for example. The app will pause the first time youtube takes over the audio session and remained paused even if youtube video is paused or completed.

Output of flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[!] Flutter (Channel stable, 3.7.0, on macOS 13.1 22C65 darwin-x64, locale en-US)
    ! Warning: `dart` on your path resolves to /usr/local/Cellar/dart/2.18.7/libexec/bin/dart,
      which is not inside your current Flutter SDK checkout at
      /Users/roro/Documents/Dev/flutter. Consider adding /Users/roro/Documents/Dev/flutter/bin
      to the front of your path.
[!] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc4)
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[!] Android Studio (version 2022.1)
    ✗ Unable to find bundled Java version.
[✓] IntelliJ IDEA Community Edition (version 2018.3.2)
[✓] VS Code (version 1.74.3)
[✓] VS Code (version 1.41.1)
[✓] Connected device (5 available)
[✓] HTTP Host Availability

Devices exhibiting the bug

Samssung S21Ultra running Android 13

ryanheise commented 1 year ago

Did you go through the new issue form?

https://ryanheise.github.io/audio_service/issue-form/issue.html

Josaldy commented 1 year ago

updated using the new form

ryanheise commented 1 year ago

It's not a new form, it's the "new issue" form. That is, if you click "New issue" up above, it takes you to that link. I was asking you the question because I wondered how you created this issue without going through that form. What did you click so that it didn't give you the correct form?

Also, you have now created a second issue and hence now have two issues open. And the second issue is not valid. Despite now going through that form, you were presented with these options:

Then you selected "compile time" but you presented me with an issue that happens at run time, not compile time.

Do you not understand the difference between compile-time and run-time?

Josaldy commented 1 year ago

I'm not sure why the new issue form wasn't generated Is there a way to delete the duplicate? Isn't compile time an issue that happens before you run the program and at run time it happens after the program is launched on the device? Possibly I selected thee wrong choice by mistake.

ryanheise commented 1 year ago

Isn't compile time an issue that happens before you run the program and at run time it happens after the program is launched on the device?

Your error happened after it was installed onto the device, in response to a user interaction on the device. It is literally impossible for it to be a compile-time error because the error did not happen while you were running the compiler.

I'm not sure why the new issue form wasn't generated

As I clarified in my previous comment, "new issue" actually means "new issue", so you should not be surprised that it creates a new issue.

Is there a way to delete the duplicate?

The GitHub way is to close the issue. There's a close button.

Possibly I selected thee wrong choice by mistake.

Or by not taking enough time to read and understand the instructions.

Josaldy commented 1 year ago

My apologies for screwing up and not following the instructions properly.

ryanheise commented 1 year ago

Please use the form to prepare a new issue, but before submitting the new issue, you can select all, then copy and paste it into this issue (via edit).

Josaldy commented 1 year ago

done

ryanheise commented 1 year ago

Reproduction steps

Try watching whatsapp status for a few minutes while app is playing audio

Can you explain how to reproduce it in steps? I don't know exactly what to do. Is Whatsapp required, or can the bug be reproduced with other apps that don't require a sign in?

Josaldy commented 1 year ago

-Run the app on a real device -While the app is running in the background, Go to whatsapp to watch status. Normally while watching whatsapp status some status might be images so the app will continue playing audio. If a whatsapp status is a video the app will momentarily pause and resume after the video status is completed, which is how it's supposed to behave. -The app will work fine for a few minutes (pause for video status and resume after video is completed) and if continue watching status for a few minutes the exception will trigger and a black screen will be shown. -Same behavior is observed either if the app is running in the background or if app was closed while audio continues playing while the app was closed. -Similar behavior is noted as well during phone calls. The app will behave normally during phone calls. Audio will be paused and resumed after the call. However, if the call last too long it will not resume playing afterwards and the exception will be thrown as well. -The exception is not thrown when using youtube for example. The app will pause the first time youtube takes over the audio session and remained paused even if youtube video is paused or completed.

ryanheise commented 1 year ago

I edited your issue for you by copying those steps into the correct section of the bug report.

Is there a missing step where the user should press the play button? I assume after step 1?

JoseBarreto1 commented 1 year ago

when androidStopForegroundOnPause is false how do i close the notification. I needed to use it as false, but I couldn't find a way to close the notification after displaying it for the first time?

ryanheise commented 1 year ago

androidStopForegroundOnPause is relevant to this issue only in that if you set it to false, you can probably avoid this error because it means that you are forcing Android to keep your app alive even during long pauses.

However, by definition, this option requires the notification to always be shown. The idea is that you need to show a notification so that the user is aware that your app is still running and consuming battery.

So the two choices are really to set androidStopForegroundOnPause to false and accept its consequences, or allow androidStopForegroundOnPause to be true and fix this current bug.

Josaldy commented 1 year ago

-androidStopForegroundOnPause set to true does not fix the issue. However, I tried using Just_audio_background as an alternative and it doesn't seem to have this issue of ForgroundServiceStartNotAllowedException. -WIth the code below is how the notification is updated when streaming radio station every time the song is changed using audio_service.

_player.icyMetadataStream.listen((event) async { MediaItem media = MediaItem( id: 'https://stream-uk1.radioparadise.com/aac-320', album: "Playing Live Radio", title: event?.info?.title ?? 'Live Radio', artist: 'Artist', artUri: Uri.parse( 'https://s53.radiolize.com/static/img/generic_song.png'), );

    //Update notification here with MediaItem ---> mediaItem.add(media);

  });

-Is there a way to update the notification when using just_audio_background? ---> mediaItem.add(media); cannot be called in just_audio_background.

ryanheise commented 1 year ago

That's interesting given that just_audio_background is just a convenience package implemented on top of audio_service, so anything just_audio_background can do, audio_service can also do.

Maybe the way to investigate this is to change your current code to use audio_service in the same way that just_audio_background uses audio_service.

Josaldy commented 1 year ago

I thought about that too, but it wasn't obvious to me where to update the notification inside the just_audi_background.dart file. Could you point me to the right direction, please?

ryanheise commented 1 year ago

I think you've misunderstood me. I'm saying that the way to investigate this issue would be to continue using audio_service directly, not just_audio_background, but to simply try to emulate whatever it is that just_audio_background was doing successfully.

letiagoalves commented 1 year ago

@Josaldy Please define too long. I did the same steps, played a video on whatsapp for a few minutes but couldn't reproduce the crash. I also tried with a call.

However, if the call last too long it will not resume playing afterwards and the exception will be thrown as well.

Josaldy commented 1 year ago

@letiagoalves For phone calls that last more than 10 minutes the exception was thrown. While watching status on whatsapp when it's pausing and resuming playing due to statuses being video and images the exception would be thrown. @ryanheise I have a workaround. I edit the onPause in AudioHandler and instead of _player.pause() i replace it with _player.stop(). That seems to solve the issue. When onPlay is triggered again manually it just restart the player.

ground-beetle commented 1 year ago

At first, thanks @ryanheise for the amazing work you do! 🙇

Yes, this issue is quite a nasty one. I'm using Android 13 and for me the above ForegroundServiceStartNotAllowedException exception causes the standard system Android media controls (that are outside of the app) to stop working after a while of playing audio files in background. When that happens - I have to restart the app.

The "fix" that I found and which works for me - is to disable Battery Optimizations for the app - set Unrestricted battery usage mode for it. When battery optimizations are disabled - I can listen to music in background without the above ForegroundServiceStartNotAllowedException exception.

I've excavated a bit to find a solution/workaround for this problem and here are the resources I've found:

snipd-mikel commented 1 year ago

Hi! We have also run into this problem and a few users have complained to us

The solution that we ended implementing is showing a message to the user if we detect that the user has battery optimzation enabled for our app (using optimize_battery). We do that when the user plays something on the app for 5 times, and if we detect that ForegroundServiceStartNotAllowedException was thrown, we will show it again.

This solution is the same that PocketCasts seem to be implementing as you can see here, here and here

Sadly we didn't find a better solution for now. Let's see how the users react to this

ryanheise commented 1 year ago

Thanks, @snipd-mikel .

Looking through past issues, it looks likely to be a duplicate of #932 and #996 where the battery optimisation setting is also discussed:

https://github.com/ryanheise/audio_service/issues/932#issuecomment-1382729296

@Josaldy does this work for you? If so, I'll close some of these as duplicates.

As to why this error should be happening at all, it feels like a bug in Android. The documentation seems to indicate that this error arises only if an app tries to start a foreground service from the background "while" not being on the list of exempted cases. But, according to the documentation, hitting a play button in the notification is one of those exemptions:

https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions

The user performs an action on a UI element related to your app. For example, they might interact with a bubble, notification, widget, or activity.

The last exception is the battery optimisation one:

The user turns off battery optimizations for your app. You can help users find this option by sending them to your app's App info page in system settings. To do so, invoke an intent that contains the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent action.

There are a couple of Flutter packages that implement Android battery optimisation in different ways, but optimize_battery is the one that exactly implements the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS approach.

Josaldy commented 1 year ago

Hi guys, changing the battery settings seems to fix that issue. But telling users to change their battery settings would probably raised a red flag and make them think this app is going to eat away all your battery. Any thoughts on that?

On Fri, Jun 9, 2023, 21:53 ryanheise @.***> wrote:

Thanks, @snipd-mikel https://github.com/snipd-mikel .

Looking through past issues, it looks likely to be a duplicate of #932 https://github.com/ryanheise/audio_service/issues/932 and #996 https://github.com/ryanheise/audio_service/issues/996 where the battery optimisation setting is also discussed:

932 (comment)

https://github.com/ryanheise/audio_service/issues/932#issuecomment-1382729296

@Josaldy https://github.com/Josaldy does this work for you? If so, I'll close some of these as duplicates.

As to why this error should be happening at all, it feels like a bug in Android. The documentation seems to indicate that this error arises only if an app tries to start a foreground service from the background "while" not being on the list of exempted cases. But, according to the documentation, hitting a play button in the notification is one of those exemptions:

https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions

The user performs an action on a UI element related to your app. For example, they might interact with a bubble https://developer.android.com/guide/topics/ui/bubbles, notification https://developer.android.com/develop/ui/views/notifications, widget https://developer.android.com/guide/topics/appwidgets/overview, or activity.

The last exception is the battery optimisation one:

The user turns off battery optimizations for your app. You can help users find this option by sending them to your app's App info page in system settings. To do so, invoke an intent that contains the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS https://developer.android.com/reference/android/provider/Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent action.

There are a couple of Flutter packages that implement Android battery optimisation in different ways, but optimize_battery is the one that exactly implements the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS approach.

— Reply to this email directly, view it on GitHub https://github.com/ryanheise/audio_service/issues/994#issuecomment-1585420795, or unsubscribe https://github.com/notifications/unsubscribe-auth/A5LTEOHPC4ZUICG5D3SY6N3XKPHSTANCNFSM6AAAAAAUSKRMKE . You are receiving this because you were mentioned.Message ID: @.***>

EarningsCall commented 12 months ago

Hi guys, changing the battery settings seems to fix that issue. But telling users to change their battery settings would probably raised a red flag and make them think this app is going to eat away all your battery. Any thoughts on that? On Fri, Jun 9, 2023, 21:53 ryanheise @.> wrote: Thanks, @snipd-mikel https://github.com/snipd-mikel . Looking through past issues, it looks likely to be a duplicate of #932 <#932> and #996 <#996> where the battery optimisation setting is also discussed: #932 (comment) <#932 (comment)> @Josaldy https://github.com/Josaldy does this work for you? If so, I'll close some of these as duplicates. As to why this error should be happening at all, it feels like a bug in Android. The documentation seems to indicate that this error arises only if an app tries to start a foreground service from the background "while" not being on the list of exempted cases. But, according to the documentation, hitting a play button in the notification is one of those exemptions: https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions The user performs an action on a UI element related to your app. For example, they might interact with a bubble https://developer.android.com/guide/topics/ui/bubbles, notification https://developer.android.com/develop/ui/views/notifications, widget https://developer.android.com/guide/topics/appwidgets/overview, or activity. The last exception is the battery optimisation one: The user turns off battery optimizations for your app. You can help users find this option by sending them to your app's App info page in system settings. To do so, invoke an intent that contains the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS https://developer.android.com/reference/android/provider/Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent action. There are a couple of Flutter packages that implement Android battery optimisation in different ways, but optimize_battery is the one that exactly implements the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS approach. — Reply to this email directly, view it on GitHub <#994 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/A5LTEOHPC4ZUICG5D3SY6N3XKPHSTANCNFSM6AAAAAAUSKRMKE . You are receiving this because you were mentioned.Message ID: @.>

Agreed.

EArminjon commented 10 months ago

@ryanheise

On Android doc, startForeground seems to have an additional parameter : foregroundServiceType. Maybe the AndroidManifest android:foregroundServiceType part is not enough...

Capture d’écran 2023-10-10 à 14 45 09

https://developer.android.com/reference/android/app/Service#startForeground(int,%20android.app.Notification,%20int)

I saw on this package code that foregroundServiceType was not used : (line 737 from AudioService.java)

private void internalStartForeground() {
   startForeground(NOTIFICATION_ID, buildNotification());
   notificationCreated = true;
}

Maybe a coode like that can fix this issue ? (A Stack overflow post which detail that)

startForeground(NOTIFICATION_ID, buildNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
ryanheise commented 10 months ago

This option is set in the manifest without needing to be set in the Java code:

    <!-- ADD THIS "SERVICE" element -->
    <service android:name="com.ryanheise.audioservice.AudioService"
        android:foregroundServiceType="mediaPlayback"
        android:exported="true" tools:ignore="Instantiatable">
      <intent-filter>
        <action android:name="android.media.browse.MediaBrowserService" />
      </intent-filter>
    </service>

See https://github.com/ryanheise/audio_service/issues/932#issuecomment-1382729296

Cosminnv commented 9 months ago

image

Error keeps happening, mostly on Samsung devices, from Android 12 onwards and 100% in background.

From my experience this happens only when these are set to true:

androidNotificationOngoing: true,
androidStopForegroundOnPause: true,

Does anyone have any potential fix? Also found this possible fix in react, maybe we can try to emulate it?

ryanheise commented 9 months ago

Does anyone have any potential fix?

When asking about a potential fix, make sure you have read the preceding discussion in which we just described a fix. It is important for us to understand whether you haven't read the discussion above, or whether you have read it but don't consider what we just discussed as a fix. If the latter, it would be helpful to explain why you don't.

Also found this https://github.com/doublesymmetry/react-native-track-player/issues/1986#issuecomment-1579227746, maybe we can try to emulate it?

I would be interested if there were actually a way to get this to work without having to use the solution already discussed above, although I am not clear about 1) whether it actually fixes it since people have reported in the comments of your linked issue that it did not resolve the problem, and I am also not clear about 2) what part of the code in that snippet is actually responsible for supposedly fixing the issue. All of the methods used in that code snippet (startForeground, stopForeground, stopSelf, notify) are already used in audio_service with the same parameters. What would be the key part of the fix?

ryanheise commented 5 months ago

Duplicate of #932

ryanheise commented 5 months ago

I'm in the process of merging the duplicates into #932 but for those who are subscribed to this issue, please consider starring the official issue in the Android issue tracker:

https://issuetracker.google.com/issues/235172948

I haven't had much success with the Android issue tracker in the past. Issues are known to stay open for 10 years and get forgotten. But just as an experiment we can see if mass starring it will make a difference. There is also the link from that page to a similar issue in UAMP (Note that UAMP is referenced from the official Android developer tutorials in order to explain how to build a media app properly, but it suffers from the same exception. There is no word from Android on this.)

In the meantime, the recommended workaround is in https://github.com/ryanheise/audio_service/issues/932#issuecomment-1382729296

github-actions[bot] commented 5 months ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.