Skip to content

MediaController generates wrong onMediaItemTransition() callback on seek inside same media item #3248

@nift4

Description

@nift4

Version

Media3 1.10.1

More version details

No response

Devices that reproduce the issue

API 33 emulator

Devices that do not reproduce the issue

N/A

Reproducible in the demo app?

Yes

Reproduction steps

  1. Apply attached patch to session-demo app
  2. Play any song from an artist with more than one song, choose a song that is not the first in the list
  3. Seek inside this same song to some other position using seekbar
  4. onMediaItemTransition() is called on controller at this point

Note: to reproduce this, controller must be attached before setMediaItems happens, which is why I added a new controller in the patch. Also, index of song passed to setMediaItems must be >0.

diff --git a/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt b/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
index 81502f5ee8..f49cc17e60 100644
--- a/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
+++ b/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
@@ -23,6 +23,7 @@ import android.content.Context
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
 import android.os.Build
+import android.util.Log
 import androidx.annotation.OptIn
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
@@ -32,6 +33,7 @@ import androidx.datastore.dataStore
 import androidx.lifecycle.lifecycleScope
 import androidx.media3.cast.CastPlayer
 import androidx.media3.common.AudioAttributes
+import androidx.media3.common.MediaItem
 import androidx.media3.common.Player
 import androidx.media3.common.listenTo
 import androidx.media3.common.util.UnstableApi
@@ -39,6 +41,7 @@ import androidx.media3.demo.session.service.R
 import androidx.media3.exoplayer.ExoPlayer
 import androidx.media3.exoplayer.util.EventLogger
 import androidx.media3.session.CommandButton
+import androidx.media3.session.MediaController
 import androidx.media3.session.MediaLibraryService
 import androidx.media3.session.MediaSession
 import androidx.media3.session.MediaSession.ControllerInfo
@@ -54,6 +57,7 @@ import kotlinx.coroutines.launch
 open class DemoPlaybackService : MediaLibraryService() {
 
   private lateinit var mediaLibrarySession: MediaLibrarySession
+  private lateinit var controller: MediaController
 
   private val turnShuffleOnButton by
     lazy @OptIn(UnstableApi::class) {
@@ -146,6 +150,7 @@ open class DemoPlaybackService : MediaLibraryService() {
   @OptIn(UnstableApi::class)
   override fun onDestroy() {
     getBackStackedActivity()?.let { mediaLibrarySession.setSessionActivity(it) }
+    controller.release()
     mediaLibrarySession.release()
     mediaLibrarySession.player.release()
     clearListener()
@@ -165,6 +170,17 @@ open class DemoPlaybackService : MediaLibraryService() {
       MediaLibrarySession.Builder(this, player, createLibrarySessionCallback())
         .also { builder -> getSingleTopActivity()?.let { builder.setSessionActivity(it) } }
         .build()
+    controller = MediaController.Builder(this, mediaLibrarySession.token)
+      .buildAsync().get()
+    controller.addListener(object : Player.Listener {
+
+          override fun onMediaItemTransition(
+            mediaItem: MediaItem?,
+            reason: @Player.MediaItemTransitionReason Int
+          ) {
+            androidx.media3.common.util.Log.e("hi","[CONTROLLER] media item changed due to $reason")
+          }
+      })
 
     mediaLibrarySession.setCustomShuffleModeButton()
     lifecycleScope.launch {
@@ -181,6 +197,14 @@ open class DemoPlaybackService : MediaLibraryService() {
         .setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true)
         .build()
     exoPlayer.addAnalyticsListener(EventLogger())
+    exoPlayer.addListener(object : Player.Listener {
+      override fun onMediaItemTransition(
+        mediaItem: MediaItem?,
+        reason: @Player.MediaItemTransitionReason Int
+      ) {
+        Log.e("hi", "[PLAYER] media item transition due to $reason")
+      }
+    })
     return CastPlayer.Builder(/* context= */ this).setLocalPlayer(exoPlayer).build()
   }
 
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
index f1361dedec..7ad9006381 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
@@ -2530,6 +2530,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
     }
     MediaItem currentMediaItem = newPlayerInfo.getCurrentMediaItem();
     if (mediaItemTransitionReason != null) {
+      Log.i("hi","dispatch mit");
       listeners.queueEvent(
           /* eventFlag= */ Player.EVENT_MEDIA_ITEM_TRANSITION,
           listener -> listener.onMediaItemTransition(currentMediaItem, mediaItemTransitionReason));
@@ -2991,6 +2992,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
             || positionDiscontinuityReason == DISCONTINUITY_REASON_SEEK)) {
       if (oldPlayerInfo.newPositionInfo.mediaItemIndex
           != finalPlayerInfo.newPositionInfo.mediaItemIndex) {
+        Log.e("hi", "move from "+oldPlayerInfo.newPositionInfo.mediaItemIndex+ " to "+finalPlayerInfo.newPositionInfo.mediaItemIndex);
         mediaItemTransitionReason =
             positionDiscontinuityReason == DISCONTINUITY_REASON_AUTO_TRANSITION
                 ? MEDIA_ITEM_TRANSITION_REASON_AUTO

Expected result

No onMediaItemTransition() callback

Actual result

2026-05-29 18:54:24.382 12519-12519 hi                      androidx.media3.demo.session         E  move from 0 to 1
2026-05-29 18:54:24.382 12519-12519 hi                      androidx.media3.demo.session         I  dispatch mit
2026-05-29 18:54:24.382 12519-12519 hi                      androidx.media3.demo.session         E  [CONTROLLER] media item changed due to 2

Media

Anything works

Bug Report

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions