Open ItsBenyaamin opened 2 years ago
I've also seen this in Crashlytics, although I'm myself have been unable to reproduce this.
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?
So in my case, I have both target & compile SDK set to 33.
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)
@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?
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)
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.
Hi @marcbaechinger, When we can expect this bug fix on the repo? and any news about the next release?
This should be fixed with this commit. Please re-open if you think you still see this issue with this commit.
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?
Assigning to @christosts who is working on mapping commands to the MediaSessionCompat
actions.
The commit above restored to what we had before and has afterwards been removed by myself by mistake.
We should probably consider splitting COMMAND_PLAY_PAUSE
to COMMAND_PLAY
and COMMAND_PAUSE
?
@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.
Thanks for clarifying.
When can we expect a new release of the media3 library? This issue is affecting almost 2% of our users.
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.
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.
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?
Any update ?
If you still see this problem please expand your comment a bit and provide some more infos to make your ask actionable. You should at least tell us the version of the library you are using (should be 1.0.0-beta03
that has the latest fixes), what OS you are running on and what is the targetSdk of your app. Then please add the evidence of the issue that you are seeing. Best is a bug report right after you see this issue. Happy to look into this, else I consider this issue fixed as per the commits above and will close.
Our app release with media3 updated to 1.0.0-beta03
is just being rolled out. But we already received 2 crash reports. From Pixel 6 and 7, both on Android 13. targetSdk
is 33.
Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{2a2abb8 u0 com.package.name/.feature.player.service.PlayerService}
at android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException(ActivityThread.java:2006)
at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:1977)
at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException(ActivityThread.java)
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)
same here with beta3 and target sdk 33. Devices are 100% samsung with android 12 and 13
for Android 13 got this stacktrace:
Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{4d9015f u0 com.package.name/.feature.player.service.PlayerService}
android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException (ActivityThread.java:2242)
android.app.ActivityThread.throwRemoteServiceException (ActivityThread.java:2213)
android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException (ActivityThread.java)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2505)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:226)
android.os.Looper.loop (Looper.java:313)
android.app.ActivityThread.main (ActivityThread.java:8741)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)
and for android 12:
Fatal Exception: android.app.ForegroundServiceDidNotStartInTimeException
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{d5a5909 u0 com.package.name/.feature.player.service.PlayerService}
android.app.ActivityThread.throwRemoteServiceException (ActivityThread.java:2147)
android.app.ActivityThread.access$2900 (ActivityThread.java:310)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2376)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:226)
android.os.Looper.loop (Looper.java:313)
android.app.ActivityThread.main (ActivityThread.java:8663)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:567)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1135)
It occurs 100% in background and considered as a repeated crash
by firebase
@AndrazP @tfighieraFreebox
Is you app using a androidx.media.session.MediaButtonReceiver
? If your app is having a media button receiver, then the system may start your service and then sends an Intent with action android.intent.action.MEDIA_BUTTON
.
I'm asking because in such a case the service is allowed to be started as a foreground service (ContextCompat.startForegroundService(...)
) as per exemption. Your service then needs to handle that intent and immediately start playback to make the MediaNotificationManager
start in the foreground.
This is a potential source for a ForegroundServiceDidNotStartInTimeException
that I was just able to repro. Just adding this as for your information and investigation.
We don't use MediaButtonReceiver
but we use a custom media notification provider
internal class MusicNotificationProvider(private val context: Context, private val session: MediaSession) : MediaNotification.Provider {
private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private val coverLoader = MusicCoverLoader() // basically use SimpleBitmapLoader()
private val defaultCoverBitmap by lazy { ContextCompat.getDrawable(context, R.drawable.img_music)!!.toBitmap() }
override fun createNotification(
mediaSession: MediaSession,
customLayout: ImmutableList<CommandButton>,
actionFactory: ActionFactory,
onNotificationChangedCallback: MediaNotification.Provider.Callback
): MediaNotification {
ensureNotificationChannel()
val player = mediaSession.player
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
val mediaStyle = MediaStyleNotificationHelper.MediaStyle(session)
MediaNotificationCommandProvider.getMediaButtons(context, player.availableCommands, player.playWhenReady && player.isPlaying)
.also { buttons ->
val commandRange = List(min(buttons.size, 3)) { it } // initialize a list from 0 to N
mediaStyle.setShowActionsInCompactView(*commandRange.toIntArray())
}
.forEach { commandButton ->
if (commandButton.sessionCommand != null) {
actionFactory.createCustomActionFromCustomCommandButton(mediaSession, commandButton)
} else {
val icon = IconCompat.createWithResource(context, commandButton.iconResId)
actionFactory.createMediaAction(mediaSession, icon, commandButton.displayName, commandButton.playerCommand)
}.let(builder::addAction)
}
// Set metadata info in the notification.
val metadata = player.mediaMetadata
coverLoader.loadArtworkBitmap(metadata) { bitmap: Bitmap?, fromCache: Boolean ->
try {
if (bitmap == null) {
builder.setLargeIcon(defaultCoverBitmap)
} else {
builder.setLargeIcon(bitmap)
}
} catch (e: ExecutionException) {
Log.w(javaClass.simpleName, "Failed to load bitmap", e)
}
if (!fromCache) {
onNotificationChangedCallback.onNotificationChanged(MediaNotification(NOTIFICATION_ID, builder.build()))
}
}
val contentIntent = createHierarchy(player.currentMediaItem, player.mediaItemCount)
val notification = builder
.setContentTitle(metadata.title)
.setContentText(metadata.artist)
.setDeleteIntent(actionFactory.createMediaActionPendingIntent(mediaSession, Player.COMMAND_STOP.toLong()))
.setOnlyAlertOnce(true)
.setSmallIcon(smallIconResId)
.setStyle(mediaStyle)
.setContentIntent(contentIntent)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOngoing(false)
.build()
return MediaNotification(NOTIFICATION_ID, notification)
}
private fun createHierarchy(currentMediaItem: MediaItem?, mediaItemCount: Int): PendingIntent {
val resumeExtras = createResumeExtras(currentMediaItem, mediaItemCount)
return NavDeepLinkBuilder(context)
.setGraph(R.navigation.main)
.setDestination(R.id.main)
.setArguments(resumeExtras)
.createPendingIntent()
}
private fun createResumeExtras(currentMediaItem: MediaItem?, mediaItemCount: Int): Bundle? {
// (...)
return null
}
override fun handleCustomCommand(session: MediaSession, action: String, extras: Bundle): Boolean {
return false
}
private fun ensureNotificationChannel() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O || notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) != null) {
return
}
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW)
notificationManager.createNotificationChannel(channel)
}
I found two way to reproduce the crash. With demo-session from this project (100%) :
Since onTaskRemoved is called but onDestroy is not from PlaybackService, the mediaLibrarySession is not released and mediaButtonIntent is not canceled from MediaSessionImpl.
With uamp, branch media 3 :
This case is different, onTaskRemoved and onDestroy are not called. Since uamp doesn't release MediaBrowser in Activity onStop, the service is not destroyed.
A possible solution could be a combination of the two projects. In the demo-session app, you can release the mediaLibrarySession in onTaskRemoved, and in uamp, you can release the MediaBrowserService in onStop from the Activity.
However, this solution doesn't meet my needs and seems quite fragile.
Do you see any restrictions to stop using getForegroundService in MediaSessionImpl ?
I would sugest :
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, sessionUri);
intent.setComponent(mbrComponent);
mediaButtonIntent = PendingIntent.getService(context, 0, intent, pendingIntentFlagMutable);
broadcastReceiver = null;
Or same approach as before media 3 :
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, sessionUri);
intent.setComponent(mbrComponent);
mediaButtonIntent = PendingIntent.getBroadcast(context, 0/* requestCode, ignored */, intent, pendingIntentFlagMutable);
broadcastReceiver = null;
@marcbaechinger let me know if you need a more detailed bug report.
Thanks for your report. Please add the Android version the session demo app is running on and the version of Media3 which is specifically important for this topic. I will then assume you didn't manually change the session demos targetSdkVersion
.
With demo-session from this project (100%) :
- Play track
- Pause track
- Kill app
- Use a bluetooth command (e.g. play/pause from headphone)
- The app crashes after around 30 seconds.
Just for me to understand the theory. The session demo app does not use a MediaButtonReceiver
. So from your understanding, what is starting the session demo app after the step Kill the app
?
When you run adb shell dumpsys media_session
after the step kill app
, do you still see a session?
Since onTaskRemoved is called but onDestroy is not from PlaybackService,
This should not be indeed. Why do you think that onDestroy()
is not called? What version of the session demo are you running? Do you still see the service not being terminated properly with the newest version? See also this comment and this commit
I tested on Android 13 from the origin/release branch. I did not modify the code, only added some log statements.
From my understanding, since the MediaSession is not released, then the mediaButtonIntent is not cancelled, resulting in the mediaButtonIntent being the cause of the app starting.
The onDestroy() method is not called immediately after I close the app. If I send a Bluetooth command before it is called, the app crashes.
So yeah calling stopself() as you did in origin/main in onTaskremoved() solved the issue.
Additionally, my demo-session app has been crashing even though I wasn't using it and I had closed the app several hours before. I am unsure of how to reproduce this issue as I was not trying to do so
And to let you know, i wanted to share the log when i activate my bluetooth and the app is closed. It may explain why the app crashes after several hours.
Thanks for the logs.
I'm not sure whether changing the media button intent to use startService
is the right approach, because in the play
case we actually want to start the service in the foreground. We would then run into another exception in such a case.
For your reported case documented by the logs above I think the service has been crashed by the system:
2023-01-16 12:37:01.563 1529-4408 ActivityManager pid-1529 W Scheduling restart of crashed service androidx.media3.demo.session/.PlaybackService in 1000ms for start-requested
This happens because the service wasn't terminated when the user has removed the app from the list of tasks (aka. the user has killed the app). If the user does this while the service is not playing (aka the service is not in the foreground), the service needs to be properly terminated which you can do in onTaskRemoved
.
I think the log line from above tells us that the user has removed the app from the list of recent apps and the ActivityManager
crashes the service and restarts the service. I think then your service re-creates the session which puts the MediaButtonReceiver
back in place. When the user then uses the media button on the BT headset, the session tries to start playback. The session exists but the player is still in STATE_IDLE
, meaning it has an empty playlist and is not yet prepared. I conclude this from the logs at the moment when the BT command arrives that reports the session state:
2023-01-16 12:37:30.194 18892-18892 AudioMediaPlayerWrapper pid-18892 V onPlaybackStateChanged(): androidx.media3.demo.session : PlaybackState {state=1, position=0, buffered position=0, speed=0.0, updated=77569394, actions=3669967, custom actions=[Action:mName='Désactiver le mode aléatoire, mIcon=2131230853, mExtras=Bundle[EMPTY_PARCEL]], active item id=0, error=null}
state=1 => [STOPPED](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#STATE_STOPPED()), speed=0.0 => paused
So, the BT event now arrives and tries to start playback. If this would work, then the service is put in the foreground and we are all happy. However, the player is in state STATE_IDLE
and can't start playback and accordingly the service is not put to foreground and produces the ForegroundServiceDidNotStartInTimeException
that you report. I would expect this being a problem not only on Android 13 but since Android 8.
So unfortunately the answer to this report is that it's a bit more complicated and a bit more work for apps that want to support resuming playback after the app has been terminated by the user. The reasons is not that Media3 folks wants it like that, the reason is that Media3 needs to follow the platform restrictions to not make problems for apps. Full playback resumption support includes these three things:
If you see this line
2023-01-16 12:37:01.563 1529-4408 ActivityManager pid-1529 W Scheduling restart of crashed service androidx.media3.demo.session/.PlaybackService in 1000ms for start-requested
something isn't well.
MediaButtonReceiver
to enable playback resumption.MediaLibraryService/MediaBrowserService
.Summarizing this investigation I don't think there is a change in the service (like moving to an Intent
from getService()
) that would help. The current implementation works including playback resumption as explained above and there is no change that could make things easier for developers without running into exceptions that come from the restrictions around foreground services.
If there is a chance to make life easier for developers we are very happy to do that but as of now I don't know of such a case. Please tell me if you think differently.
In #299 the question was raised how an app can start playback when started by a BT headset play
command with a media button receiver in place. In such a case the service is started with an Intent
with action ACTION_MEDIA_BUTTON
and an app needs to start playback to not run into a ForegroundServiceDidNotStartInTimeException
.
There is currently no API for this. We are planning to add a media button receiver to Media3. When we do this we will probably add some API that makes the process easier. This is not a preview of that API, but simply a solution how apps can start playback after being started with a pending intent with action ACTION_MEDIA_BUTTON
from a BT headset to avoid the ForegroundServiceDidNotStartInTimeException
.
What worked for me in the session demo is the following. Roughly this includes:
SharedPreferences
to store and restore a basic media item.onStartCommand
and detects when the play
key event is sent by the BT headset and the player has an empty playlist.super.onStartCommand
. The library will then prepare the player and call play
with the first item. When this play
command succeeds, the service is started in the foreground that prevents the ForegroundServiceDidNotStartInTimeException
exception when started from BT headsets.The code below shows how I implemented this as a prototype with the PlaybackService
of the session demo app. This is not a recommendation of the best way to do it, but a proof of concept that worked well for me when I tested with the session demo app. If you experience the ForegroundServiceDidNotStartInTimeException
when started from BT headsets, this can be a way to avoid that.
This is a minimal sample that show the idea only. You may want to persist more data like the category, the current position and the like. You also want to sanitize (eg. null-checks when restoring data) the code better than this example :)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (isMediaButtonPlayEvent(intent) && player.mediaItemCount == 0) {
val mediaItem = restoreLastRecentPlayedItem()
// Set a media item here to not fall into ENDED state when the player is prepared with an empty playlist
player.setMediaItem(mediaItem)
scope.launch {
// load category async
val mediaItems = loadCategory("[artist]${mediaItem.mediaMetadata.artist}")
// add all items of the category except the playing one is untouched to not interrupt playback
var currentIndex = 0
for (item in mediaItems!!) {
if (item.mediaId == player.currentMediaItem?.mediaId) {
break
}
currentIndex++
}
if (currentIndex > 0) {
player.addMediaItems(0, mediaItems.subList(0, currentIndex))
}
if (currentIndex < mediaItems.size - 1) {
player.addMediaItems(
currentIndex + 1,
mediaItems.subList(currentIndex + 1, mediaItems.size)
)
}
}
}
return super.onStartCommand(intent, flags, startId)
}
private fun isMediaButtonPlayEvent(intent: Intent?): Boolean {
intent ?: return false
if (intent.action == Intent.ACTION_MEDIA_BUTTON) {
val keyEvent = intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
if (
keyEvent!!.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
keyEvent.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY
) {
return true
}
}
return false
}
private fun saveLastRecentPlayedItem(mediaItem: MediaItem?) {
mediaItem ?: return
val preferences = getSharedPreferences(RECENT_ITEM_SHARED_PREF_NAME, Context.MODE_PRIVATE)
val editor = preferences.edit()
editor.putString(RECENT_ITEM_SHARED_PREF_KEY_MEDIA_ID, mediaItem.mediaId)
editor.putString(RECENT_ITEM_SHARED_PREF_KEY_URI, mediaItem.localConfiguration!!.uri.toString())
editor.putString(RECENT_ITEM_SHARED_PREF_KEY_TITLE, mediaItem.mediaMetadata.title.toString())
editor.putString(RECENT_ITEM_SHARED_PREF_KEY_ARTIST, mediaItem.mediaMetadata.artist.toString())
editor.putString(
RECENT_ITEM_SHARED_PREF_KEY_ARTWORK_URI,
mediaItem.mediaMetadata.artworkUri.toString()
)
editor.apply()
}
private fun restoreLastRecentPlayedItem(): MediaItem {
val preferences = getSharedPreferences(RECENT_ITEM_SHARED_PREF_NAME, Context.MODE_PRIVATE)
val mediaUri = Uri.parse(preferences.getString(RECENT_ITEM_SHARED_PREF_KEY_URI, ""))
val artworkUri = Uri.parse(preferences.getString(RECENT_ITEM_SHARED_PREF_KEY_ARTWORK_URI, ""))
val mediaId =
preferences.getString(RECENT_ITEM_SHARED_PREF_KEY_MEDIA_ID, UUID.randomUUID().toString())
val mediaMetadata =
MediaMetadata.Builder()
.setTitle(preferences.getString(RECENT_ITEM_SHARED_PREF_KEY_TITLE, ""))
.setArtist(preferences.getString(RECENT_ITEM_SHARED_PREF_KEY_ARTIST, ""))
.setArtworkUri(artworkUri)
.build()
return MediaItem.Builder()
.setUri(mediaUri)
.setMediaId(mediaId!!)
.setMediaMetadata(mediaMetadata)
.build()
}
private suspend fun loadCategory(parentId: String): List<MediaItem>? {
return withContext(Dispatchers.IO) {
Thread.sleep(1_000L)
MediaItemTree.getChildren(parentId)
}
}
Thanks for the code example.
Note that there are more potential KEYCODEs that would trigger this service as onStartCommand
as well, besides KEYCODE_MEDIA_PLAY_PAUSE
and KEYCODE_MEDIA_PLAY
. Probably sufficient enough to check only on Intent.ACTION_MEDIA_BUTTON
as action as condition.
I'm not sure I agree. I would be careful with selecting other events than play. The reason for this is that only a PLAY
command will put the service into foreground. All other events will probably still result in a ForegroundServiceDidNotStartInTimeException
.
I'm actually not sure where these other commands would come from. Can you clarify who you think would send such a ACTION_MEDIA_BUTTON
that is not a play command when the app is not running? I haven't verified this, but I would expect that the system only delivers BT headset play
commands when there is no active session. If you see that other key events are delivered to your service when the service/app is not yet started, can you please take a bug report and upload here so I can investigate this case? The reason is simply that in such a case there is not much an app can do to avoid the crash (except probably immediately terminating the service), so I'd think the system should drop all other events except play
.
Not sure if someone mentioned it before, but here are the steps to reproduce ForegroundServiceDidNotStartInTimeException
/ ANR: Context.startForegroundService() did not then call Service.startForeground()... in the demo-session app:
adb shell am kill androidx.media3.demo.session
in your terminalThis is not a surprise. Without having tested this I think that in the scenario above onTaskRemoved
is not called which is important to properly shut down the app and the service. The stale media notification still being there indicates that this is the case and the service wasn't destroyed.
Given the only way a user can terminate the app is by dismissing the activity from the recent apps, this method would be called and the app is terminated correctly. If you kill an app with adb
which isn't a common way how users terminate the app, then the app is put in the inconsistent state that you describe.
The bug is that the notification is not removed and the reason for this is that the app is killed by a dev tool. I can imagine the same happens when you kill the app in the app info. There is a note that when doing this, apps may not properly work anymore.
We should do something about this or at least state in the docs about recommended way of handling the issue.
As currently it leads to dozens of crashes out of blue once an app hits production and devs have no way of expecting this behavior if they are not thinking about rare cases such as this.
Run adb shell am kill androidx.media3.demo.session in your terminal
Well, this is the way we can emulate how system kills the process due to some period of inactivity, nobody really expects users to do that.
I did the following with the session demo on SDK 29:
I got this logs (filtered for ActivityManager | ExoPlayerImpl | onDestroy
) :
13:16:15.635 13771-13771 ExoPlayerImpl I Init 7daf441 [AndroidXMedia3/1.0.0] [generic_x86, Android SDK built for x86, Google, 29]
13:18:05.756 2021-2064 ActivityManager W Stopping service due to app idle: u0a153 -1m50s450ms androidx.media3.demo.session/.PlaybackService
13:18:05.766 13771-13771 onDestroy D onDestroy()
13:18:05.792 13771-13771 ExoPlayerImpl I Release 7daf441 [AndroidXMedia3/1.0.0] [generic_x86, Android SDK built for x86, Google, 29] [media3.common, media3.exoplayer, media3.decoder, media3.session, media3.datasource, media3.ui, media3.extractor]
The activity manager kills the app indeed but onDestroy
of the service is called. In onDestroy
the player and session are released which then removed the notification.
Doing the same as above but not waiting for the system to kill. Instead I call
adb shell am kill androidx.media3.demo.session
This gives me the service crash that is well known to lead to the stale exception. From this it seems that these two types of killing the app are not the same and in the timeout case an app can handle this gracefully like the PlaybackService
does.
13:32:13.750 13771-13771 ExoPlayerImpl I Init 428846 [AndroidXMedia3/1.0.0] [generic_x86, Android SDK built for x86, Google, 29]
13:33:15.908 2021-3106 ActivityManager I Killing 13771:androidx.media3.demo.session/u0a153 (adj 905): kill background
13:33:15.925 2021-3106 ActivityManager W Scheduling restart of crashed service androidx.media3.demo.session/.PlaybackService in 1000ms
To me this is not really a solution because this is assuming that all apps that have a Browser Service and Library Service. Want apps to resume playback even when, in our case, it's a news app that also does podcasts.
If the player has never discovered the podcast feature of our app and he would press a BT Button. It doesn't make sense at all to start playing those.
I'm not sure if i get what you're saying about 'registered a receiver' afaik there is no way of registering a button receiver or deregistering it for that matter. Because if we could do that i would love to do that.
Do you know of a way to remove the registered intent from mediassession that comes up in
adb shell dumpsys media_session
Additionaly this error mainly occurs when targeting 33.
Note: for now we're investigating reverting the media3 investment because we have way to many users in production that are expierencing these crashes. 5k on a 5% rollout, which is unacceptable for a library that is marked as stable.
@tobyworks I think your issue is different from the above with the adb kill
. Your case is clearly a bug that is related to attempting to restart the app with BT headset. I filed #314 for this. Media3 is registering a broadcast receiver even if an app has not implemented playback resumption. This is a bug. Instead the Media3 should remove the pending intent when the session is released. See the other bug please
@marcbaechinger,
Hi, Marc. I would like to bring to your attention that I have tested the demo-session app on a Xiaomi Redmi Note 8 (Android 11) physical device, and I encountered a ForegroundServiceDidNotStartInTimeException error when attempting to play media from a stale media notification. Additionally, I also tested the app on a Pixel 2 API 30 emulator, and while the stale notification was present but did not result in a ForegroundServiceDidNotStartInTimeException.
To reproduce the issue, please follow these steps:
Thank you for your attention to this matter.
Thanks for reporting and precise repro steps including API version! I can repro this with an emulator API 30/Android.
For context, API 30/Android 11 introduced media controls including playback resumption with a notification provided by System UI. See sections Media controls
and Playback Resumption
of this blog post for the legacy API. This applies if your service is exported with action android.media.browse.MediaBrowserService
in the app manifest for backward compatibility.
Looks like this is an API 30 specific misbehavior that the PlaybackService
of the demo app does not properly handle the attempt of System UI to provide a playback resumption notification. The 'stale notification' is actually not a notification from the demo app or the Media3 library, but a notification provided by System UI.
After adding some additional log statements I can see that System UI attempts with package com.android.systemui
to initiate playback resumption:
04-23 18:03:27.970 4532 4532 I ExoPlayerImpl: Init ceb9f18 [AndroidXMedia3/1.0.1] [generic_x86, Android SDK built for x86, unknown, 30]
04-23 18:03:28.171 4532 4532 D ghi167 : onConnect: androidx.media3.demo.session
04-23 18:03:28.215 4532 4532 D ghi167 : onConnect: com.android.systemui
04-23 18:03:28.216 4532 4532 D ghi167 : onGetLibraryRoot: com.android.systemui with isRecent true
04-23 18:03:28.230 4532 4532 D ghi167 : onConnect: com.android.systemui
04-23 18:05:07.261 4742 4742 I ExoPlayerImpl: Init 44a157f [AndroidXMedia3/1.0.1] [generic_x86, Android SDK built for x86, unknown, 30]
04-23 18:05:08.289 4742 4742 D ghi167 : onConnect: androidx.media3.demo.session
04-23 18:05:08.305 4742 4742 D ghi167 : onConnect: androidx.media3.demo.session
04-23 18:05:08.312 4742 4742 D ghi167 : onGetLibraryRoot: androidx.media3.demo.session with isRecent null
04-23 18:05:54.844 4742 4742 D ghi167 : onConnect: androidx.media3.demo.session
04-23 18:05:54.851 4742 4742 D ghi167 : onGetItem: androidx.media3.demo.session with mediaId [artist]Kai Engel
04-23 18:05:57.786 4742 4742 D ghi167 : onConnect: androidx.media3.demo.session
04-23 18:05:57.881 4742 4742 D ghi167 : onConnect: com.android.systemui
04-23 18:05:57.882 4742 4742 D ghi167 : onGetLibraryRoot: com.android.systemui with isRecent true
04-23 18:05:57.902 4742 4742 D ghi167 : onConnect: com.android.systemui
04-23 18:05:57.908 4742 4742 D ghi167 : onGetLibraryRoot: com.android.systemui with isRecent true
04-23 18:06:02.443 4742 4742 I ExoPlayerImpl: Release 44a157f [AndroidXMedia3/1.0.1] [generic_x86, Android SDK built for x86, unknown, 30] [media3.common, media3.exoplayer, media3.decoder, media3.session, media3.datasource, media3.ui, media3.extractor]
// app terminated after step 5
04-23 18:06:13.500 4877 4877 I ExoPlayerImpl: Init ceb9f18 [AndroidXMedia3/1.0.1] [generic_x86, Android SDK built for x86, unknown, 30]
04-23 18:06:13.709 4877 4877 D ghi167 : onConnect: androidx.media3.demo.session
04-23 18:06:13.743 4877 4877 D ghi167 : onConnect: com.android.systemui
04-23 18:06:13.744 4877 4877 D ghi167 : onGetLibraryRoot: com.android.systemui with isRecent true
04-23 18:06:13.764 4877 4877 D ghi167 : onConnect: com.android.systemui
04-23 18:06:13.769 4877 4877 D ghi167 : receive play command from com.android.systemui
04-23 18:06:13.777 4877 4877 D ghi167 : receive play command from com.android.systemui
We can verify a few things (sorry for oversharing, it may be helpful for readers).
The log shows that the PlaybackService
has been accessed by system UI to get 'recently played information'. It also shows that after the click on the play button of the 'stale notification', the system attempts a restart by creating a MediaBrowserCompat
connecting against our service.
adb -s emulator-5560 shell dumpsys notification | grep -e "NotificationRecord"
before and after step 5 of the repro steps to see whether the notification of the demo app package got removed.
adb -s emulator-5560 shell dumpsys media_session
before and after step 5 shows that the session was released and media button receiver intent have been cleared.
Handling System UI playback resumption
To avoid problems with the observed attempts of System UI, the demo app and other apps needs to properly handle playback resumption attempts from System UI.
Currently, the session demo app does not support playback resumption and should not accept System UI's attempts to access the PlaybackService
. There are various ways to do that, some that work for me with the session demo are:
MediaLibraryService
can set export="false"
in the service in the manifest. The service is then not visible to SystemUI and it won't even try to connect:<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
android.media.browse.MediaBrowserService
from service in manifest if you do not want backwards compatibility with MediaBrowserServiceCompat
(for apps that only need to support Media3 controllers).Alternatively, apps can fully implement playback resumption as documented for the legacy API or implemented by UAMP in the Media3 branch like here and here. This includes implementing resuming playback when the service is started as described fro 1.0.0 in the comment above.
We are currently working on supporting playback resumption with BT headsets and System UI notification with library support and the session demo app for 1.1.0. and documenting this on DAC.
Thanks for your detailed explanation! Just wanted to let you know that I have also tested this on Samsung Galaxy A23 (Android 13) and the stale media notification was still present. I have not had a chance to check if the ForegroundServiceDidNotStartInTimeException is there in this case.
If that still happens with the fix above, please do a bug report right after pressing the play button in the notification and then upload here. If you're unable to share bug reports publicly, please send them to dev.exoplayer@gmail.com using a subject in the format "Issue #167".
I have not checked with the fix solution yet, but I will try it and let you know.
Pixel 2 API 33 emulator.
I did not notice any stale notifications on the demo-session application. I checked it on another project. The second method made the media notification completely non-functional even during the app's runtime (it didn't respond to anything), and the stale media notification remained as well. The first and third methods did not work either, and the stale media notification persisted.
Yes, indeed. Option 2 was rubbish. Sorry. I have removed it from the comment above. The other two options work for me though.
I'll investigate how to tell System UI not to do a notification on all API levels with the isRecent
contract. Will update here when I have news.
Thanks again for reporting @MaximillianLeonov . This is a bug on API 30 where the contract described in [1] is slightly different for rejecting the System UI playback resumption notification.
I have created #355 to track this.
[1] https://developer.android.com/guide/topics/media/media-controls#mediabrowserservice_implementation
I'm on version 1.0.2
. Seeing more than 100 of these crashes per day on Firebase and also getting user reports about playback randomly stopping when app is in background or phone is locked.
Mostly Samsung devices on android 13 but also seeing it for android 11 and 12.
Is there any plan to fix this?
And could the playback stopping be due to "battery optimization" settings killing the app on some of these devices? If so is there a solution for that?
@s6joui
I'm on 1.1.0-beta01
and I have a few crashes due to this. This is a background crash, the user interacts with the notification but the application can't start playback, and the crash happens. In this comment from one of contributors there is a solution for playback resumption which helped me on some devices. But I discovered that for some Samsung devices, We have to use WakeLock
to wake the device to playback actually starts playing.
I don't have Samsung devices with Android 13, and 99% of crashes happen on that devices and most of them are 5G. So I couldn't test and fix this for my app.
And Yes, Battery Optimization is the reason to kill your app. You have to check with PowerManager.isIgnoringBatteryOptimizations(packageName)
and if not, show a dialog and ask the user to disable Battery Optimization for your app.
I wonder two things:
https://github.com/androidx/media/commit/6a5ac19140253e7e78ea65745914b0746e527058
With this commit the demo now calls stopSelf in onTaskRemoved. But that alone should lead to a stale notfication as the session was never canceled (as described above). So:
I use PlaybackService same as demo.
91% crashes on Samsung device 99%~100% crashes on android 12 and android 13
This is firebase crashlytics
I have reproduced the crash on my Samsung device
I have tried doing the debug as below and I am not getting any more crashes. You can try
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val foregroundServiceBehavior = Notification.FOREGROUND_SERVICE_IMMEDIATE
notificationBuilder.foregroundServiceBehavior = foregroundServiceBehavior
}
Full my code
val notificationBuilder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.mipmap.icon_player_notification)
.setColor(ContextCompat.getColor(context, R.color.colorV2BlackFilter))
.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
.setContentTitle(title)
.setContentText(HtmlCompat.fromHtml(message, HtmlCompat.FROM_HTML_MODE_LEGACY))
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setDefaults(Notification.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val foregroundServiceBehavior = Notification.FOREGROUND_SERVICE_IMMEDIATE
notificationBuilder.foregroundServiceBehavior = foregroundServiceBehavior
}
val notificationManager = context.getSystemService(NotificationManager::class.java)
val channel = NotificationChannel(
channelId, title, NotificationManager.IMPORTANCE_HIGH
)
notificationManager?.createNotificationChannel(channel)
notificationManager?.notify(notificationId, notificationBuilder.build())
I will release on my app soon. I will update the latest information later Hope it's useful
@jackyhieu1211 Thank you!! Seeing no crashes after applying your fix on our beta channel. Have you found any downsides to your fix? and also can I ask you how you managed to reproduce the crash?
@s6joui
Have you found any downsides to your fix?
: looks like Samsung removed ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK in system device.
also can I ask you how you managed to reproduce the crash?
: because I don't have a Samsung device. so i used emulator Samsung Skin (Galaxy Note10+) Link here
NOTE: If not reproducible. you can try Don't Allow Post notification permissison (For Android 13)
@jackyhieu1211 where exactly did you put the code you posted above?
Media3 Version
1.0.0-beta02
Devices that reproduce the issue
Firebase crashlytics report these devices:
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
Media
-
Bug Report
adb bugreport
to dev.exoplayer@gmail.com after filing this issue.