MusicService contains some logic that will promote the service to the foreground when it's actively playing audio. This logic depends on two things:
- whether the posted notification is
ongoing
- whether the service hasn't already been promoted to the foreground (described by the local flag
isForegroundService)
This is the relevant code:
|
if (ongoing && !isForegroundService) { |
|
ContextCompat.startForegroundService( |
|
applicationContext, |
|
Intent(applicationContext, this@MusicService.javaClass) |
|
) |
|
|
|
startForeground(notificationId, notification) |
|
isForegroundService = true |
|
} |
|
} |
There are two places where the service is 'demoted' to the background again:
- When the notification is cancelled
- When the player's playback state changes to paused (
playWhenReady == false)
Relevant code snippets:
|
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) { |
|
stopForeground(true) |
|
isForegroundService = false |
|
stopSelf() |
|
} |
|
if (!playWhenReady) { |
|
// If playback is paused we remove the foreground state which allows the |
|
// notification to be dismissed. An alternative would be to provide a |
|
// "close" button in the notification which stops playback and clears |
|
// the notification. |
|
stopForeground(false) |
|
} |
Notice the difference? When the playback state changes to paused, the service is demoted to the background but isForegroundService isn't updated accordingly (unlike when the notification gets cancelled). This means that when playback gets resumed, the service isn't promoted to the foreground again (from onNotificationPosted) because isForegroundService is still set to true.
This can also be evidenced through adb:
Initial playback:
> adb shell dmpsys activity services MusicService | grep isForeground
isForeground=true foregroundId=45881 foregroundNoti=Notification(pri=-1 contentView=com.example.android.uamp.next/0x109007f vibrate=null sound=null defaults=0x0 flags=0x62 color=0x00000000 category=transport actions=1 vis=PUBLIC)
After pausing and resuming playback:
> adb shell dmpsys activity services MusicService | grep isForeground
isForeground=false foregroundId=45881 foregroundNoti=Notification(pri=-1 contentView=com.example.android.uamp.next/0x109007f vibrate=null sound=null defaults=0x0 flags=0x62 color=0x00000000 category=transport actions=1 vis=PUBLIC)
Would you accept a PR to fix this?
Some context: what led me to dive into this is that we have a Podcast player app that's heavily based on UAMP. Our service implementation has a ~95% overlap with MusicService - the main difference is that we use a different layer of abstraction for resolving/loading audio (basically what MusicSource takes care of in UAMP).
We get a lot of reports from users - primarily with Samsung devices - that playback randomly stops while the app is the background. This is hard to reproduce on our development devices, and the fact that the reports our mostly from Samsung users can have different reasons: Samsung has a large market share in our geographic region, but Samsung is also known to impose 'battery optimisations' and 'background restrictions' that break valid background use cases for 3rd party apps.
We're somewhat optimistic/hopeful that correctly promoting the app to the foreground again will reduce the number of user reports concerning background playback issues, but the proof will be in the pudding.
MusicServicecontains some logic that will promote the service to the foreground when it's actively playing audio. This logic depends on two things:ongoingisForegroundService)This is the relevant code:
uamp/common/src/main/java/com/example/android/uamp/media/MusicService.kt
Lines 581 to 590 in 8dda0e8
There are two places where the service is 'demoted' to the background again:
playWhenReady == false)Relevant code snippets:
uamp/common/src/main/java/com/example/android/uamp/media/MusicService.kt
Lines 592 to 596 in 8dda0e8
uamp/common/src/main/java/com/example/android/uamp/media/MusicService.kt
Lines 615 to 621 in 8dda0e8
Notice the difference? When the playback state changes to paused, the service is demoted to the background but
isForegroundServiceisn't updated accordingly (unlike when the notification gets cancelled). This means that when playback gets resumed, the service isn't promoted to the foreground again (fromonNotificationPosted) becauseisForegroundServiceis still set totrue.This can also be evidenced through
adb:Initial playback:
After pausing and resuming playback:
Would you accept a PR to fix this?
Some context: what led me to dive into this is that we have a Podcast player app that's heavily based on UAMP. Our service implementation has a ~95% overlap with
MusicService- the main difference is that we use a different layer of abstraction for resolving/loading audio (basically whatMusicSourcetakes care of in UAMP).We get a lot of reports from users - primarily with Samsung devices - that playback randomly stops while the app is the background. This is hard to reproduce on our development devices, and the fact that the reports our mostly from Samsung users can have different reasons: Samsung has a large market share in our geographic region, but Samsung is also known to impose 'battery optimisations' and 'background restrictions' that break valid background use cases for 3rd party apps.
We're somewhat optimistic/hopeful that correctly promoting the app to the foreground again will reduce the number of user reports concerning background playback issues, but the proof will be in the pudding.