Open svenoaks opened 1 year ago
It's a design decision to take the service off the foreground when the player is paused. When in this state and the user does not resume playback within a given duration of time, the system will stop the idle service. This is working as intended and helps the system be in a healthy state instead having several services running silently in the background.
After that, the user has no way to resume playback besides going back into the app,
Your app can implement playback resumption. Then the system places a notification that allows the user to press the play button on that notification to restart your service and get back to the app. WIth playback resumption a user can also use the BT headset to resume.
This is how Android designs this for not having various idle services running and giving the user a way to resume playback after an app has terminated.
general recommended way to mimic this behavior with Media3?
You can override MediaSessionService.updateNotification()
and handle the entire foreground management on your own.
It's a design decision to take the service off the foreground when the player is paused. When in this state and the user does not resume playback within a given duration of time, the system will stop the idle service. This is working as intended and helps the system be in a healthy state instead having several services running silently in the background.
Couldn't it post the notification without keeping the service alive at all? As you said we could override MediaSessionService.updateNotification(), but I feel like keeping the notification would be what most media playing apps would want by default. At least, that's what most of the apps I use do, and mine did before changing to Media3
Couldn't it post the notification without keeping the service alive at all?
Only when the library knows that the app supports restarting without crashing, which is the case when an app ops-in to playback resumption.
Generally when not running, it's unclear what to do with controls like next/previous, seek and the like when the service is not running. Because, when the service gets started when not running, it needs to be started in the foreground which requires the service to start playback. That's only the case for the play button.
So from the library point of view, an app can implement playback resumption to get exactly that, a notification with just a play button. That's the system way and it has been designed like this for a reason.
With Media3 it's two simple steps:
androidx.media3.session.MediaButtonReceiver
to the manifest.Callback.onPlaybackResumption()
.That's for the reason that the library knows that playback resumption is supported by the app. If that's not the case and some actor tries to do playback resumption, you land on #167. No one wants to be there.
Media3 doesn't post such a notification at the end because there is an API for this that removes uncertainties. Media3 offers the System UI playback resumption with a play
button which is easy to implement and designed to work well together with system restrictions regarding starting a foreground service.
that's what most of the apps I use do, and mine did before changing to Media3
And, an app like yours is ultimately free to ignore all I've said here and what the library intents to do.
An app can post it's own notification at any time. You can for instance do this in onDestroy()
of your service (you could even reuse DefaultMediaNotificationProvider
. Just to store the last notification provided by it and re-post that when the app terminates. As mentioned, it's unclear how you'd respond to the buttons different than play
without crashing, but technically this is possible. I think I don't need to explain in detail though that we don't fancy giving too much support in case an app posts such a notification and then runs into problems with service restrictions imposed by the system. :)
So from the library point of view, an app can implement playback resumption to get exactly that, a notification with just a play button.
Not sure if this is intended, but this notification doesn't show with the two steps completed with my MediaSessionService
. It does show when I use MediaLibraryService
instead. Thought I would add this as a note. I read a lot of issue comments and did not find this mentioned anywhere.
@DimitarStoyanoff Thanks for your comment!
That's correct. System UI uses onGetRoot()
and onGetChildren()
of the MediaLibraryService
(or equivalent methods in the legacy MediaBrowserServiceCompat
) to ask whether to post the resumption notification. The MediaSessionService
does not have these domain methods and hence does not support this notification.
I will make this clear in the documentation.
I updated the docs and added a note in the 'Playback resumption' section of the DAC page about 'Background playback with a MediaSessionService'. The note says that the playback resumption notification is only available for a 'MediaLibraryService`.
Thank you, @marcbaechinger, for your quick responses and for all the work you put into this project.
Since I still have this problem even after implementing playback resumption with a MediaSessionService, I'm wondering why the library requires a MediaLibraryService and not just a MediaSessionService to display a notification with a play button, since playback resumption callback already requires a playlist to play for the play button.
Is it possible to make this happen the notification also appear with MediaSessionService, and if not what is the minimum amount needed to implement in MediaLibraryService to make this happen.
Also why does MediaSessionService with playback resumption implemented show the notification with play button on API 30, but not API 34? (haven't check the exact API range where it works / doesn't work). For example here's a screenshot of the working notification on API 30 emulator, it also works on a Samsung A50 with Android 11:
Thanks for your report!
Since I still have this problem even after implementing playback resumption
As mentioned in a comment above, the notification is removed when the system destroys the service after about 2 minutes of not being in the foreground which is when the player is paused. This is working as intended.
Is it possible to make this happen the notification also appear with MediaSessionService and if not what is the minimum amount needed to implement in MediaLibraryService to make this happen.
Under the hood, MediaLibraryService
offers the legacy API of the MediaBrowserService
that System UI uses to detect whether an app wants to show a resumption notification.
From your report I assume that you are advertising your MediaSessionService
in the manifest as a MediaSessionService
and MediaBrowserService
:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
I just noticed that the demo app is doing this as well. While this apparently works, I think it confusing to do so in the demo app. I'll send a CL to change this from MediaSessionService
to MediaLibraryService
.
Regarding your question, I guess that you are having a service that extends from MediaSessionService
. Then as above the manifest declares the MediaSessionService
and MediaBrowserService
. Please correct me if I'm wrong.
I think you kind of discovered a loop-hole in the implementation or, in a more positive way, you discovered the feature that we actually can offer a resumption notification for a MediaSessionService
as well. Thank you! :)
Lemme explain: System UI picks the MediaBrowserSerrvice
declaration in your manifest and Media3 returns your MediaSessionService
. System UI then tries the legacy service contract to figure out whether we want a resumption notification. Because playback resumption is enabled, Media3 responds to System UI under the hood, even if the service doesn't have the MediaLibrarySession.Callback
implemented properly (which is correct for a MediaSessionService
). Media3 does this here and here. Like you mention above, there is actually no need to implement MediaLibrarySession.Callback.onGetLibraryRoot()
and MediaLibrarySession.Callback.onGetChildren()
. Media3 responds to the calls from System UI that gets all the information about what to display in the resumption notification from the metadata of MediaSessionCompat
which is provided for the library and the session service in the same way.
While this is unexpected and confusing, I think we should keep this behaviour because it can easily be removed from a MediaSessionService
by not declaring MediaBrowserService
in the manifest. Unlike MediaLibraryService
where removing the declaration would mean that for instance Android Auto strops working, for a MediaSessionService
this works to prevent System UI doing a resumption notification by still allowing resumption over for instance Bluetooth.
So for this purpose, I think you can either remove MediaBrowserService
from the manifest if you wish the MediaSessionService
doesn't have the System UI resumption notification. I understand though you would actually want this to happen, so as you discovered this works.
MediaSessionService with playback resumption implemented show the notification with play button on API 30, but not API 34?
Guess the first part is explained above. I'm not sure why it doesn`t work on 34 without further information.
Can you repro this with the demo app?
I think since API 33 there is need for some additional permission regarding notifications that may have an impact. I assume you got this already as else you would possibly see some warnings from the system. The demo app has this here in the manifest and here in the MainActivivty
as well as here.
If you can repro that the notification is not shown with the notification permission in place, can you please make a bug report from your app that is made just after you started playback (aka when the first playback notification is displayed by System UI). You can also filter logcat for MediaResumeListener
(when looking at all logs, not only mine
). The interesting bit is whether you see
2023-12-29 12:54:23.247 877-877 MediaResumeListener com.android.systemui D Checking for service component for androidx.media3.demo.session
2023-12-29 12:54:23.252 877-958 MediaResumeListener com.android.systemui D Testing if we can connect to ComponentInfo{androidx.media3.demo.session/androidx.media3.demo.session.PlaybackService}
2023-12-29 12:54:23.762 877-958 MediaResumeListener com.android.systemui E Cannot resume with ComponentInfo{androidx.media3.demo.session/androidx.media3.demo.session.PlaybackService}
or
2023-12-29 12:52:14.017 877-877 MediaResumeListener com.android.systemui D Checking for service component for androidx.media3.demo.session
2023-12-29 12:52:14.018 877-958 MediaResumeListener com.android.systemui D Testing if we can connect to ComponentInfo{androidx.media3.demo.session/androidx.media3.demo.session.PlaybackService}
2023-12-29 12:52:14.518 877-958 MediaResumeListener com.android.systemui D Connected to ComponentInfo{androidx.media3.demo.session/androidx.media3.demo.session.PlaybackService}
2023-12-29 12:52:14.578 877-958 MediaResumeListener com.android.systemui D Can get resumable media for 0 from ComponentInfo{androidx.media3.demo.session/androidx.media3.demo.session.PlaybackService}
General way to replicate with demo-session:
After that, the user has no way to resume playback besides going back into the app, the notification is removed and app no longer responds to play command from bluetooth headset.
Seeing how other apps like YouTube Music persist the notification and continue to respond to media buttons, is there a general recommended way to mimic this behavior with Media3?