ryanheise / audio_service

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

Music notification not working properly #924

Closed dre8597 closed 1 year ago

dre8597 commented 2 years ago

Documented behaviour

A button to appear in the Android notification, lock screen, Android smart watch, or Android Auto device. The set of buttons you would like to display at any given moment should be streamed via AudioHandler.playbackState.

Actual behaviour

After being pressed, the play button remains, and the audio resumes playing instead of switching over to the pause button.

Runtime error

E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186): Failed to handle method call
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186): android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.ryanheise.audioserviceexample/com.ryanheise.audioservice.AudioService
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Parcel.readParcelableInternal(Parcel.java:4712)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Parcel.readParcelable(Parcel.java:4680)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Parcel.createExceptionOrNull(Parcel.java:2989)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Parcel.createException(Parcel.java:2978)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Parcel.readException(Parcel.java:2961)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Parcel.readException(Parcel.java:2903)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:5312)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1886)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.app.ContextImpl.startForegroundService(ContextImpl.java:1862)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:820)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at androidx.core.content.ContextCompat$Api26Impl.startForegroundService(ContextCompat.java:933)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at androidx.core.content.ContextCompat.startForegroundService(ContextCompat.java:701)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at com.ryanheise.audioservice.AudioService.enterPlayingState(AudioService.java:570)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at com.ryanheise.audioservice.AudioService.setState(AudioService.java:426)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at com.ryanheise.audioservice.AudioServicePlugin$AudioHandlerInterface.onMethodCall(AudioServicePlugin.java:888)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$DartMessenger(DartMessenger.java:319)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Handler.handleCallback(Handler.java:942)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Handler.dispatchMessage(Handler.java:99)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Looper.loopOnce(Looper.java:201)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.os.Looper.loop(Looper.java:288)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at android.app.ActivityThread.main(ActivityThread.java:7850)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/MethodChannel#com.ryanheise.audio_service.handler.methods( 1186):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
E/flutter ( 1186): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: PlatformException(error, startForegroundService() not allowed due to mAllowStartForeground false: service com.ryanheise.audioserviceexample/com.ryanheise.audioservice.AudioService, null, android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.ryanheise.audioserviceexample/com.ryanheise.audioservice.AudioService
E/flutter ( 1186):  at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
E/flutter ( 1186):  at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
E/flutter ( 1186):  at android.os.Parcel.readParcelableInternal(Parcel.java:4712)
E/flutter ( 1186):  at android.os.Parcel.readParcelable(Parcel.java:4680)
E/flutter ( 1186):  at android.os.Parcel.createExceptionOrNull(Parcel.java:2989)
E/flutter ( 1186):  at android.os.Parcel.createException(Parcel.java:2978)
E/flutter ( 1186):  at android.os.Parcel.readException(Parcel.java:2961)
E/flutter ( 1186):  at android.os.Parcel.readException(Parcel.java:2903)
E/flutter ( 1186):  at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:5312)
E/flutter ( 1186):  at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1886)
E/flutter ( 1186):  at android.app.ContextImpl.startForegroundService(ContextImpl.java:1862)
E/flutter ( 1186):  at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:820)
E/flutter ( 1186):  at androidx.core.content.ContextCompat$Api26Impl.startForegroundService(ContextCompat.java:933)
E/flutter ( 1186):  at androidx.core.content.ContextCompat.startForegroundService(ContextCompat.java:701)
E/flutter ( 1186):  at com.ryanheise.audioservice.AudioService.enterPlayingState(AudioService.java:570)
E/flutter ( 1186):  at com.ryanheise.audioservice.AudioService.setState(AudioService.java:426)
E/flutter ( 1186):  at com.ryanheise.audioservice.AudioServicePlugin$AudioHandlerInterface.onMethodCall(AudioServicePlugin.java:888)
E/flutter ( 1186):  at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)
E/flutter ( 1186):  at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
E/flutter ( 1186):  at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$DartMessenger(DartMessenger.java:319)
E/flutter ( 1186):  at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
E/flutter ( 1186):  at android.os.Handler.handleCallback(Handler.java:942)
E/flutter ( 1186):  at android.os.Handler.dispatchMessage(Handler.java:99)
E/flutter ( 1186):  at android.os.Looper.loopOnce(Looper.java:201)
E/flutter ( 1186):  at android.os.Looper.loop(Looper.java:288)
E/flutter ( 1186):  at android.app.ActivityThread.main(ActivityThread.java:7850)
E/flutter ( 1186):  at java.lang.reflect.Method.invoke(Native Method)
E/flutter ( 1186):  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/flutter ( 1186):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
E/flutter ( 1186): )
E/flutter ( 1186): #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter ( 1186): #1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)
E/flutter ( 1186): <asynchronous suspension>
E/flutter ( 1186): #2      MethodChannelAudioService.setState (package:audio_service_platform_interface/method_channel_audio_service.dart:19:5)
E/flutter ( 1186): <asynchronous suspension>
E/flutter ( 1186): #3      AudioService._observePlaybackState (package:audio_service/audio_service.dart:981:7)
E/flutter ( 1186): <asynchronous suspension>
E/flutter ( 1186): 

Minimal reproduction project

Official example: main.dart

Reproduction steps

  1. Play the audio in the app to trigger the music notification widget
  2. Pause the audio and leave the app running in the background
  3. Wait 10-30 seconds
  4. Press the play button in the music notification widget
  5. Audio plays but the play button remains.

    Output of flutter doctor

    [✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 13.3)
    [✓] Chrome - develop for the web
    [!] Android Studio
    ✗ Unable to find bundled Java version.
    [✓] Android Studio (version 2021.1)
    [!] Android Studio
    ✗ Unable to find bundled Java version.
    [✓] IntelliJ IDEA Ultimate Edition (version 2021.3.1)
    [✓] VS Code (version 1.66.0)
    Scanning for devices is taking a long time...[✓] Connected device (3 available)
    [✓] HTTP Host Availability

    Devices exhibiting the bug

    Pixel 6 pro - Android 13 DP2

ryanheise commented 2 years ago

I was not able to reproduce it on my Google Pixel 3a (Android 12).

Your bug report seems to be missing the section on which device you tested it on (Oh, wait, it's a formatting issue. You didn't put the closing 3 ticks on a separate line.)

Is it just the main.dart example that fails? Can you confirm whether the same issue occurs with example_multiple_handlers.dart?

dre8597 commented 2 years ago

I fixed the formatting. When using the multiple_handlers on the audio player setting, it behaves as described above; when using TTS, the audio restart to the beginning even when paused mid-speech, and the icon initially changes back to the pause icon, logs the same error as before, and the icon is stuck on pause.

ryanheise commented 2 years ago

Do you know if this behaviour is specific to Android 13?

dre8597 commented 2 years ago

From what I can tell, yes, it only occurs on Android 13. I tried to reproduce the error on my Galaxy S 21 Ultra running Android 12 OneUI 4.1, but it behaves appropriately. Re-attempted using a fresh emulator running Android 13, the error occurs just like on my physical phone.

ryanheise commented 2 years ago

Regarding your reproduction steps, you say "leave the app running in the background". To clarify, the app isn't presently in the background, so a step must exist to put the app into the background. Do you move the app into the background before or after hitting the pause button, and are you pausing from the notification or in the app?

Also, after the 10 to 30 second period you refer to, do you notice any visual changes to the notification, such as the next/previous buttons disappearing and only the play button remaining? Or do you see the full set of buttons still after the 30 seconds?

dre8597 commented 2 years ago

What I meant by in the background is swiping out of the app and returning to your phone's home screen. As for pausing, you can use the pause button within the app before swiping out or immediately pausing the audio from the notification after returning to the phone's home screen. The complete set of media controls is still visible after 30 seconds.

ryanheise commented 2 years ago

If I understand correctly, you mean that you get the same behaviour regardless of whether you pause before or after moving the app into the background.

I've just done some Googling and found a StackOverflow post that has a few helpful answers:

https://stackoverflow.com/questions/69604951/getting-android-app-foregroundservicestartnotallowedexception-in-android-12-sdk

One suggestion you may be able to try without hacking the audio_service code itself is the one about adding something to your manifest (although people have reported that not actually helping...)

As for hacking the code, I won't be able to do this in the immediate term since I have a hospital visit on Monday but hopefully next week.

It is puzzling that this error occurs, since clicking a notification is supposed to be one of the permitted cases for starting a foreground service. I can only guess that after 30 seconds the OS has purged the process from memory and has to restart it, but the FlutterEngine startup latency is too long and the OS therefore considers that it hasn't invoked startForegroundService soon enough. If that theory is correct, then we can't wait for the FlutterEngine to start up and we may need to heuristically predict it and call it sooner from the Android side.

dre8597 commented 2 years ago

Ok, I'll give this a try later on today. Thanks for taking the time to explore this. I hope all goes well during your hospital visit.

ryanheise commented 2 years ago

Is this specific to Android 13, and does it work for you on earlier versions of Android?

Edit: Strike that, I see I already asked that question.

ryanheise commented 2 years ago

I found this platform issue which may be related to yours:

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

It suggests it's a bug in the platform that has been fixed in the update.

huangcs427 commented 1 year ago

I found the same problem on Harmony OS 3.0 , And it is normal in Harmony OS 2.0

When I use audio_service to play music normally, I switch to another video player, and the music stops at this time (it is normal at this time). Then I switch to the background, and the video player pauses playback at this time (it did not switch to the application of audio_service), at this time audio_service plays music normally, but the icon in the background of audio_service stops at the play icon, and it does not work properly.

Version: flutter 3.3.0 Android SDK 33

NiuXiaoGuang commented 1 year ago

我也遇到同样的问题,andoud版本12

ryanheise commented 1 year ago

我也遇到同样的问题,andoud版本12

I'm sorry, I don't speak Chinese. If you have some important information to contribute, please use Google Translate to translate it into English.

ryanheise commented 1 year ago

It appears this issue is the same as #996 and a workaround is suggested there, so I'll close this and refer you to the other one.

ryanheise commented 1 year ago

Duplicate of #996

github-actions[bot] commented 1 year 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.