ryanheise / audio_service

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

(AKA ForegroundServiceStartNotAllowedException) Player paused in background or screen lock on Samsung note 10, 20, Samsung S20 (on samsung devices) #932

Open deep1931 opened 2 years ago

deep1931 commented 2 years ago

Documented behaviour

Player should work on every device, and should not pause in background.

Actual behaviour

App works on most of the devices, no issue at all, but on Samsung S10, S20 etc, or Samsung Note devices, it paused when we lock the screen or app goes in background.

Minimal reproduction project

Official example: main.dart

Reproduction steps

  1. Download the sample project
  2. Make release APK
  3. Install on Samsung Note or Samsung S mobile
  4. Lock the screen and wait for sometime, then player will pause.
  5. Unlock and the it will start again
  6. Send app in background, or open any other app, wait for few seconds, playback will stop again.

    Output of flutter doctor

    [✓] Flutter (Channel stable, 2.10.3, on macOS 12.3.1 21E258 darwin-arm, locale en-IN)
    [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 13.3.1)
    [✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
    [!] Android Studio (not installed)
    [☠] IntelliJ IDEA Community Edition (the doctor check crashed)
    ✗ Due to an error, the doctor check did not complete. If the error message below is not helpful, please let us know about this issue at
      https://github.com/flutter/flutter/issues.
    ✗ FormatException: Unexpected extension byte (at offset 5)
    [✓] VS Code (version 1.66.0)
    [✓] Connected device (1 available)
    [✓] HTTP Host Availability

    Devices exhibiting the bug

    Android 12 Samsung S series Samsung Note

ryanheise commented 2 years ago

Documented behaviour

NA

So it is not a bug then? Surely you mean to reference some documentation that supports your expected behaviour.

I can't test this since I don't have a Samsung, but could it be Samsung's battery optimisation and something that you can override in the Samsung system settings for each app?

deep1931 commented 2 years ago

@ryanheise Thanks, I am testing on other devices too, i will update you once i will make sure what is the behaviour on other Android 12 devices. Because on Samsung i have checked all set all battery optimisation settings, other apps like spotify is working fine, but our app have issue.

And another strange thing i have noticed that, when we close all apps in memory and just open our app, then it works well in background as expected, and once we open any app and then it moved to background then paused.

Still i am trying to get proper use case so that it can be diagnosed.

Regards

CeesJanNolen commented 2 years ago

@deep1931 Did you make any progress on this one? I recently deployed my app to the Play Store and based on the reviews, my users experience the same behavior. Although I cannot reproduce it myself (yet). Already tried on some devices, but seems inconsistent.

As I added Crashlytics to my project, we also discovered that we do get some crashes on Android 12 while in background. Not sure if those are related, but I can imagine they might be. That one looks more or less the same as #918. Same goes for that one (Still not able to reproduce it).
For the latter we'll deploy our new version to the Play Store tonight with the foregroundServiceType in there. Maybe that solves some of the problems. https://stackoverflow.com/questions/69604951/getting-android-app-foregroundservicestartnotallowedexception-in-android-12-sdk. Will let you know if I see any difference.

ryanheise commented 2 years ago

Reproduction steps will be key to investigating further.

For the latter we'll deploy our new version to the Play Store tonight with the foregroundServiceType in there.

I should also update the official example with this attribute.

deep1931 commented 2 years ago

Hello,

I have tried to diagnose it, this issue is with Samsung Note and S series, also with Oppo, Xiomi high end devices. Actully this issue is belong to battery optimization. To fix this i have shown battery optimization settings after app start. When we disable battery optimization settings then player started working.

For samsung check this link... https://www.samsung.com/us/support/troubleshooting/TSG01200668/

And regarding crash on Android 12, I am not sure, because I have not faced this issue till date.

Thanks.

ryanheise commented 2 years ago

I've just updated the README Android setup instructions to include foregroundServiceType in the manifest. I could perhaps elaborate more on this, e.g. what to do if an app already has a different foregroundserviceType and needs to combine them, but this'll hopefully do for now.

I'd be interested to know how your latest deployment to the Play Store goes, or whether you end up needing to also open the battery optimisation settings.

ryanheise commented 2 years ago

@deep1931 On your Samsung, have you found any difference in behaviour from just setting the foregroundServiceType alone, and without opening the battery optimisation settings?

I am contemplating a note in the README about how to open the battery optimisation settings but only if foregroundServiceType alone does not help on the Samsung.

deep1931 commented 2 years ago

Hello @ryanheise

Right now I dnt have the Samsung device, but very soon i will check and revert back to you regarding this.

Thanks.

alexaung commented 2 years ago

It is also happening on Samsung Note 10 plus. We also got a lot of report from Realmi, Oppo and Xiaomi. For my Samsung Note 10 plus, power saving mode it off.

ryanheise commented 2 years ago

And foregroundServiceType is set too?

alexaung commented 2 years ago

Yes

<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>
ryanheise commented 2 years ago

Do you find this happens with the official example as well? That could help the investigation.

alexaung commented 2 years ago

I have tested with the official example_playlist and it only give me error on debug mode but working as expected in release mode.

Sample official example_playlist

Step to reproduce

  1. play
  2. Open facebook and scroll to see some video for a while (some time not happen immediately). Our app will be in background but I did not close it.
  3. In debug mode, you will get the following error when some audio or video play on facebook. In release mode, just paused and play again as expected.
Exception has occurred.
PlatformException (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
    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:6933)
    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 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:262)
    at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
    at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$DartMessenger(DartMessenger.java:319)
    at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
    at android.os.Handler.handleCallback(Handler.java:938)
    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:8663)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Caused by: android.os.RemoteException: Remote stack trace:
    at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:771)
    at com.android.server.am.ActiveServices.startServiceLocked(ActiveServices.java:679)
    at com.android.server.am.ActivityManagerService.startService(ActivityManagerService.java:14071)
    at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2951)
    at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3034)

))

[✓] Flutter (Channel stable, 3.0.5, on macOS 12.4 21F79 darwin-x64, locale en-GB) • Flutter version 3.0.5 at /Users/aungmyooo/Development/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision f1875d570e (6 days ago), 2022-07-13 11:24:16 -0700 • Engine revision e85ea0e79c • Dart version 2.17.6 • DevTools version 2.12.2

[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/aungmyooo/Library/Android/sdk • Platform android-33, build-tools 31.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1) • Xcode at /Applications/Xcode.app/Contents/Developer • CocoaPods version 1.11.3

[✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] VS Code (version 1.69.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.44.0

[✓] Connected device (4 available) • SM N975F (mobile) • RF8M82Y3K9A • android-arm64 • Android 12 (API 31) • iPhone (mobile) • 06e02964e429eeebf29550b03ef955abe09891ef • ios • iOS 15.5 19F77 • macOS (desktop) • macos • darwin-x64 • macOS 12.4 21F79 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 103.0.5060.114 ! Error: iPhone is busy: Fetching debug symbols for iPhone. Xcode will continue when iPhone is finished. (code -10)

[✓] HTTP Host Availability • All required HTTP hosts are available

• No issues found!

just_audio: ^0.9.28 audio_service: ^0.18.7 path_provider: ^2.0.11 audio_service: ^0.18.7

alexaung commented 2 years ago

Another input. It is not happening in android 11.

ryanheise commented 2 years ago

I'll look into the error in debug mode, but if the example is working in release mode, are you able to see what you're doing differently from the example?

alexaung commented 2 years ago

Nothing much. I just scrolling facebook news feed. In debug mode, when feed reach the video post and automatically play, then error come up. I did same in release mode. It is ok. I watch a lot of videos on news feed.

ryanheise commented 2 years ago

I just scrolling facebook news feed. In debug mode, when feed reach the video post and automatically play, then error come up. I did same in release mode. It is ok. I watch a lot of videos on news feed.

I understand that, but just to be clear, I'm asking about only release mode which you said is OK, so it would be interesting to compare what are the code differences between the working example (at least in release mode) and your own project. For example, comparing any differences in the AudioServiceConfig or in the implementation of certain methods that could stop the service, such as stop and onTaskRemoved.

alexaung commented 2 years ago

Ok. I did not change anything. Just tested with debug mode and release mode with same official example repo.

ryanheise commented 2 years ago

I guess you misunderstood my question. Just to be clear, I'm asking ONLY about release mode, which you said is OK (quote "it is ok"). So let's focus this question on release mode (not debug mode). The question is about why your app is failing in release mode (as reported from Realmi, Oppo and Xiaomi), but the official example works in release mode (as reported by you). What is the code difference to explain that? To find the answers, you could take a look at the difference in AudioServiceConfig parameters, and implementations of stop and onTaskRemoved in particular, to see if you're doing anything different in your project/app compared to the official example.

alexaung commented 2 years ago

Ok. Found the issue. There is androidStopForegroundOnPause: true, in AudioServiceConfig. I added because want to clear the notification when paused. I did not aware that os will killed even thought it is clearly mention in documentation.

ryanheise commented 2 years ago

Still ideally that configuration option should not make the OS so eager to kill the app. That issue has come up before but I haven't gotten to the bottom of it yet, unless the phone is truly running low on ram.

ryanheise commented 2 years ago

Actually, androidStopForegroundOnPause defaults to true anyway, and the example_playlist.dart example you said you tested just uses the default. So maybe that can't be the difference?

838 commented 1 year ago

@ryanheise

We also get lots of complaints from our android users that playback pauses after some minutes :(.

Seems that this issue is only on android 12 and 13.

Could this stackTrace (from Sentry) be related?

PlatformException: PlatformException(error, startForegroundService() not allowed due to mAllowStartForeground false: service dk.nota.lyt4/com.ryanheise.audioservice.AudioService, null, )
  File "message_codecs.dart", line 653, in StandardMethodCodec.decodeEnvelope
  File "platform_channel.dart", line 296, in MethodChannel._invokeMethod
  File "<asynchronous suspension>"
  File "method_channel_audio_service.dart", line 19, in MethodChannelAudioService.setState
  File "<asynchronous suspension>"
  File "audio_service.dart", line 1030, in AudioService._observePlaybackState
ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service dk.nota.lyt4/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:4804)
    at android.os.Parcel.readParcelable(Parcel.java:4772)
    at android.os.Parcel.createExceptionOrNull(Parcel.java:3035)
    at android.os.Parcel.createException(Parcel.java:3024)
    at android.os.Parcel.readException(Parcel.java:3007)
    at android.os.Parcel.readException(Parcel.java:2949)
    at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:5336)
    at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1901)
    at android.app.ContextImpl.startForegroundService(ContextImpl.java:1877)
    at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:832)
    at s1.a$f.b(Unknown Source:0)
    at s1.a.l(ContextCompat.java:2)
    at com.ryanheise.audioservice.AudioService.n(AudioService.java:1)
    at com.ryanheise.audioservice.AudioService.G(AudioService.java:29)
    at com.ryanheise.audioservice.a$c.onMethodCall(AudioServicePlugin.java:34)
    at xa.k$a.a(MethodChannel.java:2)
    at la.c.l(DartMessenger.java:2)
    at la.c.m(DartMessenger.java:2)
    at la.c.i(Unknown Source:0)
    at la.b.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:240)
    at android.os.Looper.loop(Looper.java:351)
    at android.app.ActivityThread.main(ActivityThread.java:8355)
    at java.lang.reflect.Method.invoke
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)
    at

audio_service: "0.18.9"

AndroidManifest.xml
    <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>

MainActivity.kt

import com.ryanheise.audioservice.AudioServiceFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : AudioServiceFragmentActivity() {
  private val channelName = "dk.nota.lyt4/flavor"

  override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    val messenger = flutterEngine.dartExecutor.binaryMessenger
    MethodChannel(messenger, channelName).setMethodCallHandler { _, result ->
      // Note: this method is invoked on the main thread.
      result.success(BuildConfig.FLAVOR)
    }
    super.configureFlutterEngine(flutterEngine);
  }
}
ryanheise commented 1 year ago

I think it has been reported above that on Samsung devices it also helps to disable the battery optimisation settings.

ryanheise commented 1 year ago

FYI there is a Flutter plugin called optimization_battery which allows you to launch the battery optimisation settings.

This implements the technique described in this StackOverflow answer.

There is another plugin called android_power_manager which implements a slightly different approach, although according to the same StackOverflow answer, using this approach might result in the Play Store rejecting your app. However, I don't know the full circumstances of that, and some people have reported each of these approaches working.

YMMV so I'd be interested to hear people's experience with whichever approach you use.

838 commented 1 year ago

@ryanheise

Thanks for the info, seems that the first option will be more suitable, but I'll get back when we have investigated both options :).