Merge "SessionPlaylistAgent: Handle setDataSource" into pi-dev
diff --git a/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java b/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
index 693e137..dfbe56a 100644
--- a/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
+++ b/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
@@ -23,6 +23,7 @@
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.util.ArrayMap;
@@ -47,8 +48,8 @@
 
     private final Object mLock = new Object();
     private final MediaSession2Impl mSessionImpl;
+    private final MyPlayerEventCallback mPlayerCallback;
 
-    // TODO: Set data sources properly into mPlayer (b/74090741)
     @GuardedBy("mLock")
     private MediaPlayerBase mPlayer;
     @GuardedBy("mLock")
@@ -69,6 +70,22 @@
     @GuardedBy("mLock")
     private PlayItem mCurrent;
 
+    // Called on session callback executor.
+    private class MyPlayerEventCallback extends PlayerEventCallback {
+        public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
+                @Nullable DataSourceDesc dsd) {
+            if (mPlayer != mpb) {
+                return;
+            }
+            synchronized (mLock) {
+                if (dsd == null && mCurrent != null) {
+                    mCurrent = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
+                    updateCurrentIfNeededLocked();
+                }
+            }
+        }
+    }
+
     private class PlayItem {
         int shuffledIdx;
         DataSourceDesc dsd;
@@ -127,14 +144,23 @@
         }
         mSessionImpl = sessionImpl;
         mPlayer = player;
+        mPlayerCallback = new MyPlayerEventCallback();
+        mPlayer.registerPlayerEventCallback(mSessionImpl.getCallbackExecutor(), mPlayerCallback);
     }
 
-    public void setPlayer(MediaPlayerBase player) {
+    public void setPlayer(@NonNull MediaPlayerBase player) {
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
         synchronized (mLock) {
+            if (player == mPlayer) {
+                return;
+            }
+            mPlayer.unregisterPlayerEventCallback(mPlayerCallback);
             mPlayer = player;
+            mPlayer.registerPlayerEventCallback(
+                    mSessionImpl.getCallbackExecutor(), mPlayerCallback);
+            updatePlayerDataSourceLocked();
         }
     }
 
@@ -172,6 +198,7 @@
 
             mMetadata = metadata;
             mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+            updatePlayerDataSourceLocked();
         }
         notifyPlaylistChanged();
     }
@@ -210,6 +237,7 @@
             }
             if (!hasValidItem()) {
                 mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+                updatePlayerDataSourceLocked();
             } else {
                 updateCurrentIfNeededLocked();
             }
@@ -249,6 +277,7 @@
             mPlaylist.set(index, item);
             if (!hasValidItem()) {
                 mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+                updatePlayerDataSourceLocked();
             } else {
                 updateCurrentIfNeededLocked();
             }
@@ -291,7 +320,7 @@
     @Override
     public void skipToNextItem() {
         synchronized (mLock) {
-            if (!hasValidItem()) {
+            if (!hasValidItem() || mCurrent == mEopPlayItem) {
                 return;
             }
             PlayItem next = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
@@ -318,6 +347,23 @@
                 return;
             }
             mRepeatMode = repeatMode;
+            switch (repeatMode) {
+                case MediaPlaylistAgent.REPEAT_MODE_ONE:
+                    if (mCurrent != null && mCurrent != mEopPlayItem) {
+                        mPlayer.loopCurrent(true);
+                    }
+                    break;
+                case MediaPlaylistAgent.REPEAT_MODE_ALL:
+                case MediaPlaylistAgent.REPEAT_MODE_GROUP:
+                    if (mCurrent == mEopPlayItem) {
+                        mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+                        updatePlayerDataSourceLocked();
+                    }
+                    // pass through
+                case MediaPlaylistAgent.REPEAT_MODE_NONE:
+                    mPlayer.loopCurrent(false);
+                    break;
+            }
         }
         notifyRepeatModeChanged();
     }
@@ -339,6 +385,7 @@
             }
             mShuffleMode = shuffleMode;
             applyShuffleModeLocked();
+            updateCurrentIfNeededLocked();
         }
         notifyShuffleModeChanged();
     }
@@ -373,6 +420,7 @@
         return dsd;
     }
 
+    // TODO: consider to call updateCurrentIfNeededLocked inside (b/74090741)
     private PlayItem getNextValidPlayItemLocked(int curShuffledIdx, int direction) {
         int size = mPlaylist.size();
         if (curShuffledIdx == END_OF_PLAYLIST) {
@@ -414,9 +462,21 @@
                 mCurrent = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
             }
         }
+        updatePlayerDataSourceLocked();
         return;
     }
 
+    private void updatePlayerDataSourceLocked() {
+        if (mCurrent == null || mCurrent == mEopPlayItem) {
+            return;
+        }
+        if (mPlayer.getCurrentDataSource() != mCurrent.dsd) {
+            mPlayer.setDataSource(mCurrent.dsd);
+            mPlayer.loopCurrent(mRepeatMode == MediaPlaylistAgent.REPEAT_MODE_ONE);
+        }
+        // TODO: Call setNextDataSource (b/74090741)
+    }
+
     private void applyShuffleModeLocked() {
         mShuffledList.clear();
         mShuffledList.addAll(mPlaylist);
diff --git a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
index ca941ab..55a34fd 100644
--- a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
+++ b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
@@ -24,6 +24,7 @@
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.OnDataSourceMissingHelper;
@@ -35,6 +36,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Matchers;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -56,6 +58,7 @@
     private Context mContext;
     private MediaSession2Impl mSessionImpl;
     private MediaPlayerBase mPlayer;
+    private PlayerEventCallback mPlayerEventCallback;
     private SessionPlaylistAgent mAgent;
     private OnDataSourceMissingHelper mDataSourceHelper;
     private MyPlaylistEventCallback mEventCallback;
@@ -229,6 +232,12 @@
         };
 
         mPlayer = mock(MockPlayer.class);
+        doAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            mPlayerEventCallback = (PlayerEventCallback) args[1];
+            return null;
+        }).when(mPlayer).registerPlayerEventCallback(Matchers.any(), Matchers.any());
+
         mSessionImpl = mock(MediaSession2Impl.class);
         mDataSourceHelper = new MyDataSourceHelper();
         mAgent = new SessionPlaylistAgent(mContext, mSessionImpl, mPlayer);
@@ -567,6 +576,33 @@
         assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
     }
 
+    @Test
+    public void testPlaylistAfterOnCurrentDataSourceChanged() throws Exception {
+        int listSize = 2;
+        verify(mPlayer).registerPlayerEventCallback(Matchers.any(), Matchers.any());
+
+        createAndSetPlaylist(listSize);
+        assertEquals(0, mAgent.getCurShuffledIndex());
+
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(1, mAgent.getCurShuffledIndex());
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
+
+        mAgent.skipToNextItem();
+        assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
+
+        mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ONE);
+        assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
+
+        mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ALL);
+        assertEquals(0, mAgent.getCurShuffledIndex());
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(1, mAgent.getCurShuffledIndex());
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(0, mAgent.getCurShuffledIndex());
+    }
+
     private List<MediaItem2> createAndSetPlaylist(int listSize) throws Exception {
         List<MediaItem2> items = new ArrayList<>();
         for (int i = 0; i < listSize; ++i) {