Merge "Revert "Revert "MediaSession2: Add playlist parameter supports"""
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 6567cd0..a90ce8d 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -387,6 +387,11 @@
         return null;
     }
 
+    @Override
+    public void setPlaylistParams_impl(PlaylistParams params) {
+        // TODO(hdmoon): Implement
+    }
+
     ///////////////////////////////////////////////////
     // Protected or private methods
     ///////////////////////////////////////////////////
@@ -504,6 +509,24 @@
         }
 
         @Override
+        public void onPlaylistParamsChanged(Bundle params) throws RuntimeException {
+            final MediaController2Impl controller;
+            try {
+                controller = getController();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            controller.getCallbackExecutor().execute(() -> {
+                final MediaController2Impl impl = mController.get();
+                if (impl == null) {
+                    return;
+                }
+                impl.mCallback.onPlaylistParamsChanged(PlaylistParams.fromBundle(params));
+            });
+        }
+
+        @Override
         public void onConnectionChanged(IMediaSession2 sessionBinder, Bundle commandGroup)
                 throws RuntimeException {
             final MediaController2Impl controller;
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 28834a3..600fc95 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -68,6 +68,7 @@
     private MediaPlayerInterface mPlayer;
     @GuardedBy("mLock")
     private MyPlaybackListener mListener;
+    private PlaylistParams mPlaylistParams;
 
     /**
      * Can be only called by the {@link Builder#build()}.
@@ -237,6 +238,22 @@
         mSessionStub.notifyCustomLayoutNotLocked(controller, layout);
     }
 
+    @Override
+    public void setPlaylistParams_impl(PlaylistParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("PlaylistParams should not be null!");
+        }
+        mPlaylistParams = params;
+        mPlayer.setPlaylistParams(params);
+        mSessionStub.notifyPlaylistParamsChanged(params);
+    }
+
+    @Override
+    public PlaylistParams getPlaylistParams_impl() {
+        // TODO: Do we need to synchronize here for preparing Controller2.setPlaybackParams?
+        return mPlaylistParams;
+    }
+
     //////////////////////////////////////////////////////////////////////////////////////
     // TODO(jaewan): Implement follows
     //////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 69e8498..33cc78f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -344,4 +344,18 @@
             // TODO(jaewan): What to do when the controller is gone?
         }
     }
+
+    public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
+        final List<ControllerInfo> list = getControllers();
+        for (int i = 0; i < list.size(); i++) {
+            IMediaSession2Callback callbackBinder =
+                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
+            try {
+                callbackBinder.onPlaylistParamsChanged(params.toBundle());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Controller is gone", e);
+                // TODO(jaewan): What to do when the controller is gone?
+            }
+        }
+    }
 }
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 8329cf0..e7a0971 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -16,9 +16,14 @@
 
 package android.media;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
 import android.media.MediaSession2.Builder;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
+import android.os.Bundle;
 import android.os.Process;
 import android.support.annotation.NonNull;
 import android.support.test.filters.SmallTest;
@@ -30,6 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import static org.junit.Assert.*;
@@ -130,6 +136,30 @@
     }
 
     @Test
+    public void testSetPlaylistParams() throws Exception {
+        final PlaylistParams params = new PlaylistParams(
+                PlaylistParams.REPEAT_MODE_ALL,
+                PlaylistParams.SHUFFLE_MODE_ALL,
+                null /* PlaylistMetadata */);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaylistParamsChanged(PlaylistParams givenParams) {
+                TestUtils.equals(params.toBundle(), givenParams.toBundle());
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        mSession.setPlaylistParams(params);
+        assertTrue(mPlayer.mSetPlaylistParamsCalled);
+        TestUtils.equals(params.toBundle(), mPlayer.mPlaylistParams.toBundle());
+        TestUtils.equals(params.toBundle(), mSession.getPlaylistParams().toBundle());
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
     public void testPlaybackStateChangedListener() throws InterruptedException {
         // TODO(jaewan): Add equivalent tests again
         /*
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index 7834a42..99ed4b9 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -28,6 +28,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -57,7 +58,8 @@
     }
 
     interface TestControllerCallbackInterface {
-        // Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
+        // Add methods in ControllerCallback/BrowserCallback that you want to test.
+        default void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {}
 
         // Browser specific callbacks
         default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
@@ -191,6 +193,11 @@
                 assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
             }
         }
+
+        @Override
+        public void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
+            mCallbackProxy.onPlaylistParamsChanged(params);
+        }
     }
 
     public class TestMediaController extends MediaController2 implements TestControllerInterface {
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index d0d1178..9311410 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -44,8 +44,10 @@
     public long mSeekPosition;
     public boolean mSetCurrentPlaylistItemCalled;
     public int mItemIndex;
+    public boolean mSetPlaylistParamsCalled;
 
     public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+    public PlaylistParams mPlaylistParams;
     private PlaybackState2 mLastPlaybackState;
 
     public MockPlayer(int count) {
@@ -162,6 +164,17 @@
     }
 
     @Override
+    public void setPlaylistParams(PlaylistParams params) {
+        mSetPlaylistParamsCalled = true;
+        mPlaylistParams = params;
+    }
+
+    @Override
+    public PlaylistParams getPlaylistParams() {
+        return mPlaylistParams;
+    }
+
+    @Override
     public AudioAttributes getAudioAttributes() {
         return null;
     }