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

MediaLibraryService throws ForegroundServiceDidNotStartInTimeException #167

Closed
1 task
ItsBenyaamin opened this issue Sep 19, 2022 · 240 comments
Closed
1 task

Comments

@ItsBenyaamin
Copy link

ItsBenyaamin commented Sep 19, 2022

Media3 Version

1.0.0-beta02

Devices that reproduce the issue

Firebase crashlytics report these devices:

  • Galaxy Note20 running Android 12
  • Galaxy S21 FE 5G running Android 12
  • Xiaomi 11T Pro running Android 12
  • Redmi Note 9 Pro running Android 11

Devices that do not reproduce the issue

No response

Reproducible in the demo app?

Not tested

Reproduction steps

It's happened on some devices. I think It has related to MediaController not properly handling the service.

Expected result

not crash?

Actual result

Fatal Exception: android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{ac0645b u0 player.PlaybackService}
       at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2147)
       at android.app.ActivityThread.access$2900(ActivityThread.java:310)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2376)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       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

Bug Report

  • You will email the zip file produced by adb bugreport to [email protected] after filing this issue.
@vanniktech
Copy link

I've also seen this in Crashlytics, although I'm myself have been unable to reproduce this.

@marcbaechinger marcbaechinger self-assigned this Sep 19, 2022
@marcbaechinger
Copy link
Contributor

Thanks for reporting!

Do you have some more context what command has been sent to the service when this happens?

I'm asking because I probably have found an execution path that could cause this problem, but I can not repro this and hence I can not verify a fix.

I think the reason for this could be that the pending intent of 'pause' is starting a foreground service and then does not call Service.startForeground().

I will provide a fix for the case described above. However, I can't be really sure whether this is the root case of the exception above because I can't repro now. I think it may be a corner case that sends the pause command when the service actually has been destroyed, but that's a wild guess.

You have already reported that this happens on Android 12. Can you let me know the targetSkdVersion that you use for the app that is producing the stack trace above?

@vanniktech
Copy link

So in my case, I have both target & compile SDK set to 33.

Screen Shot 2022-09-20 at 21 10 05

Full stacktrace:

Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{ad7d394 u0 com.vanniktech.rssreader/com.vanniktech.feature.rssreader.itemdownload.RssReaderItemDownloadBackgroundService}
       at android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException(ActivityThread.java:2006)
       at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:1977)
       at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2242)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:7898)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

@ItsBenyaamin
Copy link
Author

ItsBenyaamin commented Sep 21, 2022

@marcbaechinger
My project is an online streaming application that streams songs. So basic commands are play/pause/seek/stop.
and the targetSkdVersion is 32

This crash is reported a lot
And when are we gonna get stable v1? any news?

@ItsBenyaamin
Copy link
Author

ItsBenyaamin commented Sep 23, 2022

***** EDIT *****
I found the reason. when you play a song and then pause it and close the app the notification remains
If you click on play, after a few seconds it will crash.
service is not foreground but notification is not dismissed and the problem is when pressing the play button it will crash


Hi, I reproduce the crash with media session demo app
here is the stack trace. I don't know if It's the exact same, but in bottom of crash it contains the ForegroundServiceDidNotStartInTimeException exception.
It happens in the background playing when pressing play/pause a few times On both the Lock screen and unlocked.
Also, the crash is insanely reporting in crashlytics for me!

2022-09-23 11:59:43.721 7087-7087/androidx.media3.demo.session E/AbstractFuture: RuntimeException while executing runnable androidx.media3.demo.session.MainActivity$$ExternalSyntheticLambda2@51ce0f3 with executor MoreExecutors.directExecutor()
    java.util.concurrent.CancellationException: Task was cancelled.
        at com.google.common.util.concurrent.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1496)
        at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:586)
        at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:547)
        at androidx.media3.demo.session.MainActivity.getBrowser(MainActivity.kt:42)
        at androidx.media3.demo.session.MainActivity.pushRoot(MainActivity.kt:160)
        at androidx.media3.demo.session.MainActivity.initializeBrowser$lambda-3(MainActivity.kt:108)
        at androidx.media3.demo.session.MainActivity.$r8$lambda$zcr2almQqmFxiCBoT1PwekCQLVg(Unknown Source:0)
        at androidx.media3.demo.session.MainActivity$$ExternalSyntheticLambda2.run(Unknown Source:2)
        at com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:31)
        at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1277)
        at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:1038)
        at com.google.common.util.concurrent.AbstractFuture.cancel(AbstractFuture.java:665)
        at androidx.media3.session.MediaController.releaseFuture(MediaController.java:479)
        at androidx.media3.demo.session.MainActivity.releaseBrowser(MainActivity.kt:112)
        at androidx.media3.demo.session.MainActivity.onStop(MainActivity.kt:97)
        at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1496)
        at android.app.Activity.performStop(Activity.java:8502)
        at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:5298)
        at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:5278)
        at android.app.ActivityThread.handleStopActivity(ActivityThread.java:5343)
        at android.app.servertransaction.StopActivityItem.execute(StopActivityItem.java:43)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2282)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:210)
        at android.os.Looper.loop(Looper.java:299)
        at android.app.ActivityThread.main(ActivityThread.java:8250)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)
2022-09-23 11:59:43.800 7087-7087/androidx.media3.demo.session E/AbstractFuture: RuntimeException while executing runnable androidx.media3.demo.session.PlayerActivity$$ExternalSyntheticLambda3@82c155b with executor MoreExecutors.directExecutor()
    java.util.concurrent.CancellationException: Task was cancelled.
        at com.google.common.util.concurrent.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1496)
        at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:586)
        at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:547)
        at androidx.media3.demo.session.PlayerActivity.getController(PlayerActivity.kt:44)
        at androidx.media3.demo.session.PlayerActivity.setController(PlayerActivity.kt:128)
        at androidx.media3.demo.session.PlayerActivity.initializeController$lambda-4(PlayerActivity.kt:120)
        at androidx.media3.demo.session.PlayerActivity.$r8$lambda$_NhsfsieB1wweFfLvjToeliIJGM(Unknown Source:0)
        at androidx.media3.demo.session.PlayerActivity$$ExternalSyntheticLambda3.run(Unknown Source:2)
        at com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:31)
        at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1277)
        at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:1038)
        at com.google.common.util.concurrent.AbstractFuture.cancel(AbstractFuture.java:665)
        at androidx.media3.session.MediaController.releaseFuture(MediaController.java:479)
        at androidx.media3.demo.session.PlayerActivity.releaseController(PlayerActivity.kt:124)
        at androidx.media3.demo.session.PlayerActivity.onStop(PlayerActivity.kt:102)
        at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1496)
        at android.app.Activity.performStop(Activity.java:8502)
        at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:5298)
        at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:5278)
        at android.app.ActivityThread.handleStopActivity(ActivityThread.java:5343)
        at android.app.servertransaction.StopActivityItem.execute(StopActivityItem.java:43)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2282)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:210)
        at android.os.Looper.loop(Looper.java:299)
        at android.app.ActivityThread.main(ActivityThread.java:8250)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)
2022-09-23 11:59:48.639 8393-8393/androidx.media3.demo.session E/AndroidRuntime: FATAL EXCEPTION: main
    Process: androidx.media3.demo.session, PID: 8393
    android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{73168c0 u0 androidx.media3.demo.session/.PlaybackService}
        at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:1995)
        at android.app.ActivityThread.access$2800(ActivityThread.java:271)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2220)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:210)
        at android.os.Looper.loop(Looper.java:299)
        at android.app.ActivityThread.main(ActivityThread.java:8250)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)

@marcbaechinger
Copy link
Contributor

Thanks for digging some further. That give me some further data points.

when you play a song and then pause it and close the app the notification remains.

Yes, indeed, these cases are always about closing the app and having only the service playing in the background. As soon as your app is in the foreground with a controller connected you are bound to the service, and you will never see such an exception.

If you click on play, after a few seconds it will crash.

With the default behaviour of the MediaSessionService (your app does not override updateNotification()), I don't think that play should produce a ForegroundServiceDidNotStartInTimeException, because after you pressed play, playWhenReady is true and the player is not STATE_IDLE which results in service.startForeground() being called. With play I would only expect a ForegroundServiceStartNotAllowedException in some cases but not when pressing PLAY on a notification on Android 12 (PendingIntent with exception) and Android 13 (MediaSession with exemption).

I still think it's the PAUSE case for which we will provide a fix.

@ItsBenyaamin
Copy link
Author

Hi @marcbaechinger,
When we can expect this bug fix on the repo? and any news about the next release?

@marcbaechinger
Copy link
Contributor

This should be fixed with this commit. Please re-open if you think you still see this issue with this commit.

@yschimke
Copy link
Contributor

yschimke commented Oct 3, 2022

I'm still a little bit concerned it's not a complete fix.

    if (Util.SDK_INT >= 26 && command == COMMAND_PLAY_PAUSE) {
    if (Util.SDK_INT >= 26
        && command == COMMAND_PLAY_PAUSE
        && !mediaSession.getPlayer().getPlayWhenReady()) {

Seems racy, the pending intent in the notification can be clicked on after something else has just toggled this.

Also do you have a test for playback stopped by suppression? I assume this will work because it's temporary and the session is still active?

@marcbaechinger

This comment was marked as off-topic.

@marcbaechinger
Copy link
Contributor

@yschimke This issue is about a ForegroundServiceDidNotStartInTimeException of the MediaSessionService. My understanding/hypothesis is that this is related to the first command arriving in onStartCommand as a pending intent when the service is not yet running (onCreate called immediately before onStartCommand). My understanding is based on a possibly faulty code path that I found and described above and that the exception is observed on Android 12 and 11 where the notification still sends pending intents (as opposed to Android 13). Where do you see the risk of a race condition with another intent or media session command in such a case when the service is not yet started?

By design, the service is started by creating a controller/browser and binding to the service. The service shouldn't be started by a pending intent (not Context.startForegroundService(intent)) that are only coming from the notification - which needs a running service.

For this reason, I agree that this is about a race condition or an invalid state but elsewhere (stale notification?). I couldn't repro, because I do not understand from where a pending intent for PAUSE arrives when the service is not running. When the service is stopped, the notification is removed and there shouldn't be a way to send a PAUSE intent in this case. If such an intent is sent by an app, the app is just not using the service correctly I think. Not saying this is the case, as I have no evidence that this is an app problem.

I agree that I'm not sure that this fixes the problem, but I think it removes the ForegroundServiceDidNotStartInTimeException (admittedly to the expense of another Exception that we hopefully better understand). Because in such a case of a PAUSE command when the service is not yet started, we probably now are at risk of a ForegroundStartNotAllowedException.

If you have any way to repro this ForegroundServiceDidNotStartInTimeException please let me know. I'm thankful for any input that makes me understand better how this exception is produced.

@yschimke
Copy link
Contributor

yschimke commented Oct 3, 2022

Thanks for clarifying.

marcbaechinger pushed a commit that referenced this issue Oct 17, 2022
A follow up to stopping speaker playback with a Player decorator from
#15.

It looks like we will need to change to using playback suppression to avoid
errors like #167, when we don't start
a foreground service.

We may not have this implemented by 1.0, but would like it in the API and it seems to be appropriate.

PiperOrigin-RevId: 478835686
marcbaechinger pushed a commit to google/ExoPlayer that referenced this issue Oct 20, 2022
A follow up to stopping speaker playback with a Player decorator from
androidx/media#15.

It looks like we will need to change to using playback suppression to avoid
errors like androidx/media#167, when we don't start
a foreground service.

We may not have this implemented by 1.0, but would like it in the API and it seems to be appropriate.

PiperOrigin-RevId: 478835686
@AndrazP
Copy link

AndrazP commented Oct 21, 2022

When can we expect a new release of the media3 library?
This issue is affecting almost 2% of our users.

@marcelpallares
Copy link

marcelpallares commented Oct 26, 2022

We have many reports in Crashlytics of users facing this issue, however I believe it's not causing crashes (although they get reported as one), as I can see in our database that the users who face the crash can listen to the app audios and complete the full playback. We have not been able to reproduce it ourselves so far.

Another annoying thing is that we get different reports in Crashlytics that are not getting grouped as the same issue, and it's spamming us with many different issues that are essentially the same.

Android 12 examples:

Fatal Exception: android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{eac8ccc u0 xxx/.feature.player.service.PlayerService}
       at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2152)
       at android.app.ActivityThread.access$2800(ActivityThread.java:315)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2381)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8751)
       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)

Different report, same issue:

Fatal Exception: android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{24fb927 u0 xxx/.feature.player.service.PlayerService}
       at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2147)
       at android.app.ActivityThread.access$2900(ActivityThread.java:310)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2376)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       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:567)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

Another:

Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{9086cb9 u0 xxx/.feature.player.service.PlayerService}
       at android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException(ActivityThread.java:2206)
       at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2177)
       at android.app.ActivityThread.access$2900(ActivityThread.java:324)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2435)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8855)
       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)

And many more.

Android 11 example:

Fatal Exception: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{5632ffb u0 xxx/.feature.player.service.PlayerService}
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2248)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:246)
       at android.app.ActivityThread.main(ActivityThread.java:8550)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)

We have around 15 different reports of this same issue with slightly different line numbers throwing the error in the last 7 days. Do you have any insights or recommendations on what to do?

@kelmer44
Copy link

Absolutely #1764

I do have further questions if I may: i have tried explicitly requesting playback in onplaybackresumption for all scenarios, then rebooting the phone, but playback isnt started (as I assumed its supposed to, because of MEdiaLibrarySession. getRecentMediaItemAtDeviceBootTime).

Additionally some user reports for my app complain the app starts playing at random times without necessarily being preceded by a reboot.

Are there any more occasions where the MediaLibrarySession path might be called? Do some other conditions need to be met? Trying to understand the behavior here :)

@marcbaechinger
Copy link
Contributor

explicitly requesting playback in onplaybackresumption for all scenarios,
then rebooting the phone, but playback isnt started (as I assumed its supposed to,
because of MEdiaLibrarySession. getRecentMediaItemAtDeviceBootTime).

Yeah, I see. This is something we haven't anticipated apps would want to do and that's why #1764 is a valid request I think. We need some API to clarify/express whether the media items returned by onPlaybackResumption() are used for actually setting up the player and resume playback, or to just hand it out to a browser without involving the player. Or, we alternatively need to better document things and for instance say that an app shouldn't call play() in onPlaybackResumption. :)

At boot time, onPlaybackResumption() is called and the media items you return with MediaItemsWithStartPosition are just delivered to System UI that called MediaLibrarySession.Callback.onGetChildren() through a MediaBrowser. SysUI then creates the media notification with it that a user may use after boot completed.

In contrast and in the case when onPlaybackResumption() is called for actual playback resumption (when BT sends a PLAY or when the user presses play on the System UI resumption notification), then Media3 takes the media items of MediaItemsWithStartPosition, calls player.setMediaItems(mediaItems, startIndex, startPosition), player.prepare() and player.play() to resume playback from there.

Are there any more occasions where the MediaLibrarySession path might be called?
Do some other conditions need to be met? Trying to understand the behavior here :)

Not that I'm aware of. Media3 calls onPlaybackResumption in these two cases we discussed already:

1 - when a play command is received and the playlist of the session player is empty (here).
2 - when a MediaBrowser attempts to call MediaLibrarySession.Callback.getChildren(parentId) and parentId == "androidx.media3.session.recent.root" (here). The value "androidx.media3.session.recent.root" is provided as the root item by getLibraryRoot(ControllerInfo, LibraryParams) for a caller with packageId="com.android.systemui" and LibraryParam.recent == true (here).

Additionally some user reports for my app complain the app starts playing at random
times without necessarily being preceded by a reboot.

from the above, the intended behavior that follows is:

a - any browser that calls getChildren() with "androidx.media3.session.recent.root" trigger onPlaybackResumption()
b - any controller that you allow to connect and that calls play when the player has an empty playlist may call initiate a call to onPlaybackResumptiopn()

I think for b) it's either System UI or a user that intentionally requests play, so it wouldn't be surprising for the user.

For a) and if your app calls player.play() in onPlaybackResumption() then playback could start when System UI calls the service at a moment when the player is in STATE_IDLE. I'm not aware of other occasions then after boot and just after starting the session, but I'm not certain to be honest. I think not calling play yourself in onPlaybackResumption() would verify this if the user complaints go away.

lovegaoshi added a commit to lovegaoshi/react-native-track-player that referenced this issue Sep 30, 2024
@johngray1965
Copy link

Additionally some user reports for my app complain the app starts playing at random times without necessarily being preceded by a reboot.

I'm getting reports of this as well.

@debz-eight
Copy link

So according to last 7 days of crashlytics report, I had 150 crashes due to ForegroundServiceDidNotStartInTimeException on majorly Samsung's Android 14, 70% of them to be exact. My app is now 99.73% crash free as of now after added the code to play a silent 0.5s audio onPlaybackResumption().

@marcbaechinger
Copy link
Contributor

@debz-eight Can you give me the Samsung models that are reported? It's all about finding why and how these Samsung devices trigger playback resumption I think. I can give it a shot again if I get such a device in my hands.

@debz-eight
Copy link

@marcbaechinger These is the whole list of 150 crashes of Samsung: https://batbin.me/lavers

@ksvslk
Copy link

ksvslk commented Oct 3, 2024

Mostly Samsung A, G, S, F series - but that could be that there are more of these out there. There also some Sony and Xiaomis.
And Android version is in the range of Android 11 - 14.

EDIT: I can test with A55 - Android 14. But I cannot reproduce it. If anyone can please let me know of the steps.

@Flyktsodan
Copy link

Flyktsodan commented Oct 10, 2024

Since migrating to Media3, we’ve seen a significant increase in crashes, dropping from a 99.9% crash-free user rate to around 99.4%. Notably, we do not have a MediaButtonReceiver registered in the manifest, so this issue doesn’t seem related to playback resumption. Our implementation is quite straightforward and closely mirrors the demo app.

I’ve only been able to reproduce this issue on a Samsung device running Android 11. When pausing playback and swiping the app away from the recent apps screen, the notification remains active. Interacting with the notification triggers a “silent” crash, as reported in other issues.

Observations:

  • This issue has persisted since the beta release and remains unresolved, which suggests there might not be a straightforward solution.
  • Workarounds like playing silent audio or force-starting/stopping the service feel somewhat extreme and unsustainable in the long run.

Questions:

  • I’m curious why this was a “solved problem” in the deprecated ExoPlayer implementation, but now presents a challenge in Media3? Are there specific design changes in Media3 that could explain this?

Suggestions:

  • Could the team provide more context on whether the architecture of Media3 requires developers to implement these workarounds?

Any guidance on addressing this issue, or insights into future improvements, would be greatly appreciated. Thanks for your continued work on the project!

@marcbaechinger
Copy link
Contributor

I’ve only been able to reproduce this issue on a Samsung device running Android 11.

Can you tell me what device this was exactly?

I'm not sure if this is the same issue, but if we can repro this on a device, that sounds like great. So far we only have telemetry reports without knowing how to repro.

I still think there may be two different issues:

  • the one you report here that leaves a stale notification on Android11. That's actually quite obvious, so I would expect if the other issues discussed here would be related to that it would have been reported.
  • the rest of the issues seems specific to a certain vendor on Android 14 that triggers a playback resumption from an unknown sources.

I’m curious why this was a “solved problem” in the deprecated ExoPlayer

That's media session not an ExoPlayer issue. MediaBrowserServiceCompat was replaced with MediaLibraryService. These are completely different, so almost everything in the design has changed.

This issue has persisted since the beta release and remains unresolved, which
suggests there might not be a straightforward solution.

From what I understand, this issue is specific to devices of a certain vendor. I'm afraid, but when trying to get into contact with the vendor I didn't get a response. When following up I was told that I get a response soon. It seems that the vendor isn't really interested in supporting us I'm afraid. For various technical reasons, it is difficult for us to inspect OS behavior of devices from other vendors. The situation we are having with such devices is similar to what you have with Media3 - we don't know what is going on there and in contrast to Media3 we don't have the source code of the changes applied to for instance AOSP/Pixel builds where we don't see that issue.

If you want to help me, then making noise at the side of the vendor would be helpful, but I'm afraid I don't know myself how to get through them. Please accept my apologies, I can understand your frustration.

@Flyktsodan
Copy link

@marcbaechinger Thanks for the quick reply! I agree with your assessment that we are likely dealing with two separate issues here.

The first issue, related to stale notifications, produced the same stack trace but was resolved by handling onTaskRemoved (as described in other issues). This workaround seems to work well for that particular case.

However, the second issue (or possibly others) is still occurring frequently. Unfortunately, we haven’t been able to reproduce these crashes ourselves, which is one of the reasons I’m hesitant to try the various workarounds mentioned in this and other discussions.

Looking at the crash reports, the majority of occurrences are on Samsung, Xiaomi, and Huawei devices running Android 11 and 12. Given that we can’t reproduce it on our end, I’m unsure how to proceed in contacting the vendors directly. If you—or anyone else—could provide guidance on how to reproduce these crashes reliably, that would be an incredibly helpful starting point.

@jackyhieu1211
Copy link

@marcbaechinger

I have an app that has been having this error for a long time. But the problem is not solved,
Recently i have been running a service for my own work.

i have been using when click button to startService. When the service has stopped. I try to restart the Service. And I got error : Fatal Exception: android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground()

fun startTask() {
    startForeground(
        SERVICE_CODE,
        createNotification().build(),
    )
    // Code here
}

I fixed it by calling startForeground in OnCreate in Service

  override fun onCreate() {
    super.onCreate()

    startForeground(
        SERVICE_CODE,
        createNotification().build(),
    )
 }

I looked it up and it seems like you called startForeground like me. I have not tried the above for Media library. Because I have to clone a lot of Modules to do this. Please check this. Thanks.

@marcbaechinger
Copy link
Contributor

@jackyhieu1211 Thanks for the suggestion.

MedisSessionService/MediaLibraryService need to be started by building a controller or browser. With that, the service is a bound service as long as the controller/browser is connected. You'll never get a ForegroundServiceDidNotStartInTimeException in the bound state.

The problem of ForegroundServiceDidNotStartInTimeException is only relevant when starting the service for playback resumption, because then, no controller is bound and the service needs to be started into the foreground immediately. That's why playback has to start immediately in the case of playback resumption, because when playing the service is started into the foreground.

From this your approach isn't really matching the problem I think. In onCreate(), we don't have a notification. A notification can be created when there is a MediaItem available that gives the metadata for the notification.

For these reasons I think the current approach is the right thing to do:

  1. Playback resumption should be disabled if the app can't start playback when started externally without a binding controller/browser.
  2. If playaback resumption is enabled, the app needs to start playback which is ensured by overriding onPlaybackResumption().

@johngray1965
Copy link

However, the second issue (or possibly others) is still occurring frequently. Unfortunately, we haven’t been able to reproduce these crashes ourselves, which is one of the reasons I’m hesitant to try the various workarounds mentioned in this and other discussions.

Looking at the crash reports, the majority of occurrences are on Samsung, Xiaomi, and Huawei devices running Android 11 and 12. Given that we can’t reproduce it on our end, I’m unsure how to proceed in contacting the vendors directly. If you—or anyone else—could provide guidance on how to reproduce these crashes reliably, that would be an incredibly helpful starting point.

I'm seeing exactly this. Same devices, same versions of Android. Always in the background. Its accounts for the vast majority of my crashes.

@ksvslk
Copy link

ksvslk commented Oct 22, 2024

@jackyhieu1211 Thanks for the suggestion.

MedisSessionService/MediaLibraryService need to be started by building a controller or browser. With that, the service is a bound service as long as the controller/browser is connected. You'll never get a ForegroundServiceDidNotStartInTimeException in the bound state.

The problem of ForegroundServiceDidNotStartInTimeException is only relevant when starting the service for playback resumption, because then, no controller is bound and the service needs to be started into the foreground immediately. That's why playback has to start immediately in the case of playback resumption, because when playing the service is started into the foreground.

From this your approach isn't really matching the problem I think. In onCreate(), we don't have a notification. A notification can be created when there is a MediaItem available that gives the metadata for the notification.

For these reasons I think the current approach is the right thing to do:

  1. Playback resumption should be disabled if the app can't start playback when started externally without a binding controller/browser.
  2. If playaback resumption is enabled, the app needs to start playback which is ensured by overriding onPlaybackResumption().

So is the approach described possible with 1.5.0-beta01?

@debz-eight
Copy link

Any update on this issue.

@johngray1965
Copy link

I'm working with 1.5.0-rc01 now. I was able to reproduce this locally by removing the recently played item. Once I implemented shouldStartForegroundService, I could not reproduce it locally. But I still see crashes on Android 14, and some of Android 13. On Samsung and Xiaomi devices (so far).

I don't have a test device that matches those that are showing the crash.

It seems to me we would expect the crash if

  1. We don't have an item to play (should be handled with shouldStartForegroundService)
  2. We have an item to play, but we didn't load it and start playing it fast enough.

I've tried to find something to that I would expect to be particularly expensive to load, but I can always load and start well within the limit. But again, it only seems to happen on certain devices and I don't currently have one. Of course, you never know what the load on the users device might be.

It's disappointing the number of crashes I still see in crashlytics with shouldStartForegroundService implemented.

Is there something other than the cases I listed above? Perhaps there's something else in my code. Perhaps the problematic devices have a shorter timeout? Other possibilities.

I am adding some additional logging so I can see if some edge cases that I don't believe should happen are in fact happening. They are basically cases where we failed to load the item, that I believe should be caught by shouldStartForegroundService.

@Tr736
Copy link

Tr736 commented Dec 12, 2024

So for me this is happening when the app is has been playing in the background for an extended period of time usually around 1 hour.

Once triggered the position from the player always returns the same value.

im using a controller like so...

` init {
Log.d("MediaController", "onCreate")
_playerListener = PlayerListener()
val sessionToken =
SessionToken(context, ComponentName(context, PlaybackService::class.java))
initController(sessionToken)
initBrowser(sessionToken)
}

private fun initController(sessionToken: SessionToken) {
    _controllerFuture =
        MediaController.Builder(context, sessionToken).buildAsync()

    _controllerFuture.addListener({
        // Log.d("MediaController", "controllerFuture")
        _controller = _controllerFuture.get()
        _controller?.addListener(_playerListener)
        playerController = PlayerController(_controller)
    },
        MoreExecutors.directExecutor()
    )
}`

looks like the controller somehow gets destroyed however Im still able to play/pause

I think I need to figure a way to bind controller to prevent the os from destroying it.

@marcbaechinger
Copy link
Contributor

@Tr736 I think this is something different than ForegroundServiceDidNotStartInTimeException.

I think I need to figure a way to bind controller to prevent the os from destroying it.

You already do the right thing with MediaController.Builder(context, sessionToken).buildAsync() which creates a service binding and when playing the service is in the foreground. Else the service wouldn't survive an hour.

It looks like you can repro this on your device. If that's the case it would be great if you please file a new bug. If you do this best would be to do a bug report right after you see that it happened. Please upload the bug report to the new bug . If you're unable to share bug reports publicly, please send them to [email protected] using a subject in the format "Issue #1234", where "#1234" should be replaced with your issue number. Please also update the issue to indicate you’ve done this.

I think we see in the logcat why the app/service was terminated.

I'm interested in the entire logcat and in the output of

adb shell dumpsys activity exit-info $APP_PACKAGE

that all we be included in the bug report.

@andrewille
Copy link

andrewille commented Dec 13, 2024

I am currently starting to write a media player app and face the same Exception. I just followed the basic guide on developer.android.com. The background service runs and starts playing but stops after a few seconds when the Exception is thrown. There is no notification appearing at all and startForeground is never called apparently.
I use Android 15 on Xiaomi Poco F3 but I even tried it in the device emulator! It failed as well.

AndroidManifest.xml:

<service
    android:name=".PlayerService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaSessionService"/>
        </intent-filter>
</service>

PlayerService.java

private ExoPlayer player;
private MediaSession mediaSession;

@Override
public void onCreate() {
    super.onCreate();
    startPlayer();
}

@Nullable
@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
    return mediaSession;
}

@Override
public void onDestroy() {
    super.onDestroy();
    if (player != null) {
        player.release();
        player = null;
    }
    if (mediaSession != null) {
        mediaSession.release();
        mediaSession = null;
    }
    stopSelf();
}

private void startPlayer() {
    player = new ExoPlayer.Builder(this).build();
    mediaSession = new MediaSession.Builder(this, player)
            .build();
    MediaItem item = MediaItem.fromUri("/sdcard/test123.mp3");
    player.setMediaItem(item);
    player.prepare();
    player.play();
}

MainActivity.java

@Override
protected void onResume() {
    super.onResume();

    /* getting storage permission here (omitted) */

    Intent serviceIntent = new Intent(this, PlayerService.class);
    ContextCompat.startForegroundService(this, serviceIntent);
}

So I got this very simple basic setup by following the guide and it is still crashing even on the emulator :x What is wrong with that?

@marcbaechinger
Copy link
Contributor

You should build a MediaController instead of starting the service manually. Read on in the docs a bit and it will tell (sorry, for not sending a link as I'm afk at the moment)

@debz-eight
Copy link

debz-eight commented Dec 16, 2024

So for me this is happening when the app is has been playing in the background for an extended period of time usually around 1 hour.

Once triggered the position from the player always returns the same value.

im using a controller like so...

` init { Log.d("MediaController", "onCreate") _playerListener = PlayerListener() val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java)) initController(sessionToken) initBrowser(sessionToken) }

private fun initController(sessionToken: SessionToken) {
    _controllerFuture =
        MediaController.Builder(context, sessionToken).buildAsync()

    _controllerFuture.addListener({
        // Log.d("MediaController", "controllerFuture")
        _controller = _controllerFuture.get()
        _controller?.addListener(_playerListener)
        playerController = PlayerController(_controller)
    },
        MoreExecutors.directExecutor()
    )
}`

looks like the controller somehow gets destroyed however Im still able to play/pause

I think I need to figure a way to bind controller to prevent the os from destroying it.

So I have faced somewhat of a similar thing where my controller is getting destroyed after the user is in paused state for extended period of time. I have solved this issue by doing the below:

I have a single source for initialization of my controller and I have made the function to initialize the controller public. So whenever the user is trying to play something, I check if the controller is connected or null, if so, I re-initialize the controller, else I use the existing controller instance to do the necessary.

So whenever my controller is disconnected or killed, I just re-initialize it. I don't know if its the correct way or not but it works. Perhaps @marcbaechinger can provide some insights on this.

@XilinJia
Copy link

My app has been getting this issue (noticed from time to time). From the documentation, I can't figure out a solution. Google Play Console complains about this. But it's amazing to see this issue standing here for over 2 years and many people suffering on the same problem. Would I get more comfort just by visiting this page to find company?

Anyone looking to reproduce this, feel free to check out my open-source Youtube/podcast app Podcini: https://github.com/XilinJia/Podcini, currently being launched on Google Play.

@marcbaechinger
Copy link
Contributor

getting this issue @XilinJia

Yeah, I agree we should close this issue. If you need support, please start a new issue where you describe what you are facing or then add some more information. Saying this issue is unclear unfortunately people describe all sorts of problems they have with the service here.

@XilinJia
Copy link

getting this issue @XilinJia

Yeah, I agree we should close this issue. If you need support, please start a new issue where you describe what you are facing or then add some more information. Saying this issue is unclear unfortunately people describe all sorts of problems they have with the service here.

To me, this is an in-fighting between media3 and Android, and we app developers are unfortunately caught in the middle. My app just uses MediaLibraryService and a MediaController according to what has been described, so I expect media3 to settle down any Android complaints, but apparently that's not what's happening. And my issue doesn't appear unique from the large list of issues reported here. I'm not sure how to report anything new.

@marcbaechinger
Copy link
Contributor

@XilinJia Thanks for your opinion. If you want us to look into your issue, please file a new issue and explain it in terms of the output of your app (like bugreport). Thanks.

@marcbaechinger
Copy link
Contributor

I'm closing this issue. It doesn't work anymore as it has grown too big.

As a general advise on this issue:

  1. The only way to start the service by an app is connecting a controller or browser because this creates a service binding: https://developer.android.com/media/media3/session/connect-to-media-app

  2. The only exception for that is when playback resumption is triggered by a key event or the Sytem UI resumption notification. This is only expected when playback resumption is implemented and then playback must start immediately when the service is started else the service is not started into the foreground and a ForegroundServiceDidNotStartInTimeException is thrown: https://developer.android.com/media/media3/session/background-playback#resumption

If you haven't enabled playback resumption and you see that an OEM device is starting the service either way, please file a new bug report and describe how the OS starts the service.

If you think you are still affected by a ForegroundServiceDidNotStartInTimeException after doing it right, please file a new bug and describe exactly what the app is doing and why you think the problem you see happens. Best is to repro with the demo app because then people like me can't say it's an app issue.

Thanks to everyone who helped us with this issue and contributed to make the service more robust on all available API levels, OEM devices and in the many use cases apps use the service. You helped us to make Media3 better. That's very much appreciated. Thank you! ♥︎

@androidx androidx locked and limited conversation to collaborators Dec 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests