MediaSession2: Implement PlaylistParams replacements

This includes following changes
  - Remove PlaylistParams
  - Implement PlaylistParams replacements. Here's the list.
    - get/setShuffleMode()
    - get/setRepeatMode()
    - Note: Playlist metadata APIs are already submitted (b/74174649)

Bug: 74116823, Bug: 74118768
Test: Run CTS with MediaComponents/runcts.sh
Change-Id: I712a2c3fe959b8496f3d80310579af872419f486
diff --git a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
index d6c8e21..0e003c0 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
@@ -37,12 +37,13 @@
     void onBufferedPositionChanged(long bufferedPositionMs);
     void onPlaylistChanged(in List<Bundle> playlist, in Bundle metadata);
     void onPlaylistMetadataChanged(in Bundle metadata);
-    void onPlaylistParamsChanged(in Bundle params);
     void onPlaybackInfoChanged(in Bundle playbackInfo);
+    void onRepeatModeChanged(int repeatMode);
+    void onShuffleModeChanged(int shuffleMode);
 
     void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup,
             int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed,
-            long bufferedPositionMs, in Bundle playbackInfo, in Bundle params,
+            long bufferedPositionMs, in Bundle playbackInfo, int repeatMode, int shuffleMode,
             in List<Bundle> playlist, in PendingIntent sessionActivity);
     void onDisconnected();
 
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 7317b44..664467d 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -67,6 +67,8 @@
     void skipToPlaylistItem(IMediaController2 caller, in Bundle mediaItem);
     void skipToPreviousItem(IMediaController2 caller);
     void skipToNextItem(IMediaController2 caller);
+    void setRepeatMode(IMediaController2 caller, int repeatMode);
+    void setShuffleMode(IMediaController2 caller, int shuffleMode);
 
     //////////////////////////////////////////////////////////////////////////////////////////////
     // library service specific
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 15922f7..45f8751 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -22,6 +22,8 @@
 import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM;
 import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST;
 import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE;
 import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID;
 import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH;
 import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI;
@@ -40,11 +42,12 @@
 import android.media.MediaController2.PlaybackInfo;
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
+import android.media.MediaPlaylistAgent.RepeatMode;
+import android.media.MediaPlaylistAgent.ShuffleMode;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
-import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSessionService2;
 import android.media.Rating2;
 import android.media.SessionToken2;
@@ -87,7 +90,9 @@
     @GuardedBy("mLock")
     private MediaMetadata2 mPlaylistMetadata;
     @GuardedBy("mLock")
-    private PlaylistParams mPlaylistParams;
+    private @RepeatMode int mRepeatMode;
+    @GuardedBy("mLock")
+    private @ShuffleMode int mShuffleMode;
     @GuardedBy("mLock")
     private int mPlayerState;
     @GuardedBy("mLock")
@@ -706,9 +711,41 @@
     }
 
     @Override
-    public PlaylistParams getPlaylistParams_impl() {
-        synchronized (mLock) {
-            return mPlaylistParams;
+    public int getShuffleMode_impl() {
+        return mShuffleMode;
+    }
+
+    @Override
+    public void setShuffleMode_impl(int shuffleMode) {
+        final IMediaSession2 binder = getSessionBinderIfAble(
+                COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE);
+        if (binder != null) {
+            try {
+                binder.setShuffleMode(mControllerStub, shuffleMode);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
+    }
+
+    @Override
+    public int getRepeatMode_impl() {
+        return mRepeatMode;
+    }
+
+    @Override
+    public void setRepeatMode_impl(int repeatMode) {
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE);
+        if (binder != null) {
+            try {
+                binder.setRepeatMode(mControllerStub, repeatMode);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
         }
     }
 
@@ -719,19 +756,6 @@
         }
     }
 
-    // TODO(jaewan): Remove (b/74116823)
-    @Override
-    public void setPlaylistParams_impl(PlaylistParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("params shouldn't be null");
-        }
-        /*
-        Bundle args = new Bundle();
-        args.putBundle(MediaSession2Stub.ARGUMENT_KEY_PLAYLIST_PARAMS, params.toBundle());
-        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS, args);
-        */
-    }
-
     @Override
     public int getPlayerState_impl() {
         synchronized (mLock) {
@@ -817,18 +841,6 @@
         });
     }
 
-    void pushPlaylistParamsChanges(final PlaylistParams params) {
-        synchronized (mLock) {
-            mPlaylistParams = params;
-        }
-        mCallbackExecutor.execute(() -> {
-            if (!mInstance.isConnected()) {
-                return;
-            }
-            mCallback.onPlaylistParamsChanged(mInstance, params);
-        });
-    }
-
     void pushPlaybackInfoChanges(final PlaybackInfo info) {
         synchronized (mLock) {
             mPlaybackInfo = info;
@@ -855,7 +867,7 @@
         });
     }
 
-    public void pushPlaylistMetadataChanges(MediaMetadata2 metadata) {
+    void pushPlaylistMetadataChanges(MediaMetadata2 metadata) {
         synchronized (mLock) {
             mPlaylistMetadata = metadata;
         }
@@ -868,6 +880,32 @@
         });
     }
 
+    void pushShuffleModeChanges(int shuffleMode) {
+        synchronized (mLock) {
+            mShuffleMode = shuffleMode;
+        }
+        mCallbackExecutor.execute(() -> {
+            if (!mInstance.isConnected()) {
+                return;
+            }
+            // TODO(jaewan): Fix public API not to take playlistAgent.
+            mCallback.onShuffleModeChanged(mInstance, null, shuffleMode);
+        });
+    }
+
+    void pushRepeatModeChanges(int repeatMode) {
+        synchronized (mLock) {
+            mRepeatMode = repeatMode;
+        }
+        mCallbackExecutor.execute(() -> {
+            if (!mInstance.isConnected()) {
+                return;
+            }
+            // TODO(jaewan): Fix public API not to take playlistAgent.
+            mCallback.onRepeatModeChanged(mInstance, null, repeatMode);
+        });
+    }
+
     // Should be used without a lock to prevent potential deadlock.
     void onConnectedNotLocked(IMediaSession2 sessionBinder,
             final CommandGroup allowedCommands,
@@ -877,7 +915,9 @@
             final float playbackSpeed,
             final long bufferedPositionMs,
             final PlaybackInfo info,
-            final PlaylistParams params, final List<MediaItem2> playlist,
+            final int repeatMode,
+            final int shuffleMode,
+            final List<MediaItem2> playlist,
             final PendingIntent sessionActivity) {
         if (DEBUG) {
             Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
@@ -907,7 +947,8 @@
                 mPlaybackSpeed = playbackSpeed;
                 mBufferedPositionMs = bufferedPositionMs;
                 mPlaybackInfo = info;
-                mPlaylistParams = params;
+                mRepeatMode = repeatMode;
+                mShuffleMode = shuffleMode;
                 mPlaylist = playlist;
                 mSessionActivity = sessionActivity;
                 mSessionBinder = sessionBinder;
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
index 99bdfce..cfc8d1e 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
@@ -24,7 +24,6 @@
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
-import android.media.MediaSession2.PlaylistParams;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.text.TextUtils;
@@ -169,7 +168,7 @@
     }
 
     @Override
-    public void onPlaylistParamsChanged(Bundle paramsBundle) throws RuntimeException {
+    public void onRepeatModeChanged(int repeatMode) {
         final MediaController2Impl controller;
         try {
             controller = getController();
@@ -177,12 +176,7 @@
             Log.w(TAG, "Don't fail silently here. Highly likely a bug");
             return;
         }
-        PlaylistParams params = PlaylistParams.fromBundle(controller.getContext(), paramsBundle);
-        if (params == null) {
-            Log.w(TAG, "onPlaylistParamsChanged(): Ignoring null playlistParams");
-            return;
-        }
-        controller.pushPlaylistParamsChanges(params);
+        controller.pushRepeatModeChanges(repeatMode);
     }
 
     @Override
@@ -208,9 +202,21 @@
     }
 
     @Override
+    public void onShuffleModeChanged(int shuffleMode) {
+        final MediaController2Impl controller;
+        try {
+            controller = getController();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        controller.pushShuffleModeChanges(shuffleMode);
+    }
+
+    @Override
     public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
             int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed,
-            long bufferedPositionMs, Bundle playbackInfo, Bundle playlistParams,
+            long bufferedPositionMs, Bundle playbackInfo, int shuffleMode, int repeatMode,
             List<Bundle> itemBundleList, PendingIntent sessionActivity) {
         final MediaController2Impl controller = mController.get();
         if (controller == null) {
@@ -233,8 +239,7 @@
         controller.onConnectedNotLocked(sessionBinder,
                 CommandGroup.fromBundle(context, commandGroup),
                 playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs,
-                PlaybackInfoImpl.fromBundle(context, playbackInfo),
-                PlaylistParams.fromBundle(context, playlistParams),
+                PlaybackInfoImpl.fromBundle(context, playbackInfo), repeatMode, shuffleMode,
                 itemList, sessionActivity);
     }
 
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 20ae4c5..800a9c3 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -47,9 +47,6 @@
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.PlaylistParams;
-import android.media.MediaSession2.PlaylistParams.RepeatMode;
-import android.media.MediaSession2.PlaylistParams.ShuffleMode;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
 import android.media.SessionToken2;
@@ -254,6 +251,7 @@
             mSessionStub.notifyPlaybackInfoChanged(info);
             notifyPlayerUpdatedNotLocked(oldPlayer);
         }
+        // TODO(jaewan): Repeat the same thing for the playlist agent.
     }
 
     private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
@@ -430,41 +428,6 @@
         mSessionStub.notifyCustomLayoutNotLocked(controller, layout);
     }
 
-    @Override
-    public void setPlaylistParams_impl(PlaylistParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("params shouldn't be null");
-        }
-        ensureCallingThread();
-        // TODO: Uncomment or remove
-        /*
-        final MediaPlayerBase player = mPlayer;
-        if (player != null) {
-            // TODO implement
-            //player.setPlaylistParams(params);
-            mSessionStub.notifyPlaylistParamsChanged(params);
-        }
-        */
-    }
-
-    @Override
-    public PlaylistParams getPlaylistParams_impl() {
-        // TODO: Uncomment or remove
-        /*
-        final MediaPlayerBase player = mPlayer;
-        if (player != null) {
-            // TODO(jaewan): Is it safe to be called on any thread?
-            //               Otherwise MediaSession2 should cache parameter of setPlaylistParams.
-            // TODO implement
-            //return player.getPlaylistParams();
-            return null;
-        } else if (DEBUG) {
-            Log.d(TAG, "API calls after the close()", new IllegalStateException());
-        }
-        */
-        return null;
-    }
-
     //////////////////////////////////////////////////////////////////////////////////////
     // TODO(jaewan): Implement follows
     //////////////////////////////////////////////////////////////////////////////////////
@@ -598,6 +561,48 @@
     }
 
     @Override
+    public int getRepeatMode_impl() {
+        final MediaPlaylistAgent agent = mPlaylistAgent;
+        if (agent != null) {
+            return agent.getRepeatMode();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
+        return MediaPlaylistAgent.REPEAT_MODE_NONE;
+    }
+
+    @Override
+    public void setRepeatMode_impl(int repeatMode) {
+        final MediaPlaylistAgent agent = mPlaylistAgent;
+        if (agent != null) {
+            agent.setRepeatMode(repeatMode);
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
+    }
+
+    @Override
+    public int getShuffleMode_impl() {
+        final MediaPlaylistAgent agent = mPlaylistAgent;
+        if (agent != null) {
+            return agent.getShuffleMode();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
+        return MediaPlaylistAgent.SHUFFLE_MODE_NONE;
+    }
+
+    @Override
+    public void setShuffleMode_impl(int shuffleMode) {
+        final MediaPlaylistAgent agent = mPlaylistAgent;
+        if (agent != null) {
+            agent.setShuffleMode(shuffleMode);
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
+    }
+
+    @Override
     public void prepare_impl() {
         ensureCallingThread();
         final MediaPlayerBase player = mPlayer;
@@ -681,7 +686,7 @@
     private void notifyPlaylistChangedOnExecutor(MediaPlaylistAgent playlistAgent,
             List<MediaItem2> list, MediaMetadata2 metadata) {
         if (playlistAgent != mPlaylistAgent) {
-            // Ignore calls from the old agent. Ignore.
+            // Ignore calls from the old agent.
             return;
         }
         mCallback.onPlaylistChanged(mInstance, playlistAgent, list, metadata);
@@ -691,13 +696,33 @@
     private void notifyPlaylistMetadataChangedOnExecutor(MediaPlaylistAgent playlistAgent,
             MediaMetadata2 metadata) {
         if (playlistAgent != mPlaylistAgent) {
-            // Ignore calls from the old agent. Ignore.
+            // Ignore calls from the old agent.
             return;
         }
         mCallback.onPlaylistMetadataChanged(mInstance, playlistAgent, metadata);
         mSessionStub.notifyPlaylistMetadataChangedNotLocked(metadata);
     }
 
+    private void notifyRepeatModeChangedOnExecutor(MediaPlaylistAgent playlistAgent,
+            int repeatMode) {
+        if (playlistAgent != mPlaylistAgent) {
+            // Ignore calls from the old agent.
+            return;
+        }
+        mCallback.onRepeatModeChanged(mInstance, playlistAgent, repeatMode);
+        mSessionStub.notifyRepeatModeChangedNotLocked(repeatMode);
+    }
+
+    private void notifyShuffleModeChangedOnExecutor(MediaPlaylistAgent playlistAgent,
+            int shuffleMode) {
+        if (playlistAgent != mPlaylistAgent) {
+            // Ignore calls from the old agent.
+            return;
+        }
+        mCallback.onShuffleModeChanged(mInstance, playlistAgent, shuffleMode);
+        mSessionStub.notifyShuffleModeChangedNotLocked(shuffleMode);
+    }
+
     private void notifyPlayerUpdatedNotLocked(MediaPlayerBase oldPlayer) {
         final MediaPlayerBase player = mPlayer;
         // TODO(jaewan): (Can be post-P) Find better way for player.getPlayerState() //
@@ -892,15 +917,21 @@
         }
 
         @Override
-        public void onShuffleModeChanged(MediaPlaylistAgent playlistAgent, int shuffleMode) {
-            super.onShuffleModeChanged(playlistAgent, shuffleMode);
-            // TODO(jaewan): Handle this (b/74118768)
+        public void onRepeatModeChanged(MediaPlaylistAgent playlistAgent, int repeatMode) {
+            final MediaSession2Impl session = mSession.get();
+            if (session == null) {
+                return;
+            }
+            session.notifyRepeatModeChangedOnExecutor(playlistAgent, repeatMode);
         }
 
         @Override
-        public void onRepeatModeChanged(MediaPlaylistAgent playlistAgent, int repeatMode) {
-            super.onRepeatModeChanged(playlistAgent, repeatMode);
-            // TODO(jaewan): Handle this (b/74118768)
+        public void onShuffleModeChanged(MediaPlaylistAgent playlistAgent, int shuffleMode) {
+            final MediaSession2Impl session = mSession.get();
+            if (session == null) {
+                return;
+            }
+            session.notifyShuffleModeChangedOnExecutor(playlistAgent, shuffleMode);
         }
     }
 
@@ -1245,76 +1276,6 @@
         }
     }
 
-    public static class PlaylistParamsImpl implements PlaylistParamsProvider {
-        /**
-         * Keys used for converting a PlaylistParams object to a bundle object and vice versa.
-         */
-        private static final String KEY_REPEAT_MODE =
-                "android.media.session2.playlistparams2.repeat_mode";
-        private static final String KEY_SHUFFLE_MODE =
-                "android.media.session2.playlistparams2.shuffle_mode";
-        private static final String KEY_MEDIA_METADATA2_BUNDLE =
-                "android.media.session2.playlistparams2.metadata2_bundle";
-
-        private Context mContext;
-        private PlaylistParams mInstance;
-        private @RepeatMode int mRepeatMode;
-        private @ShuffleMode int mShuffleMode;
-        private MediaMetadata2 mPlaylistMetadata;
-
-        public PlaylistParamsImpl(Context context, PlaylistParams instance,
-                @RepeatMode int repeatMode, @ShuffleMode int shuffleMode,
-                MediaMetadata2 playlistMetadata) {
-            // TODO(jaewan): Sanity check
-            mContext = context;
-            mInstance = instance;
-            mRepeatMode = repeatMode;
-            mShuffleMode = shuffleMode;
-            mPlaylistMetadata = playlistMetadata;
-        }
-
-        public @RepeatMode int getRepeatMode_impl() {
-            return mRepeatMode;
-        }
-
-        public @ShuffleMode int getShuffleMode_impl() {
-            return mShuffleMode;
-        }
-
-        public MediaMetadata2 getPlaylistMetadata_impl() {
-            return mPlaylistMetadata;
-        }
-
-        @Override
-        public Bundle toBundle_impl() {
-            Bundle bundle = new Bundle();
-            bundle.putInt(KEY_REPEAT_MODE, mRepeatMode);
-            bundle.putInt(KEY_SHUFFLE_MODE, mShuffleMode);
-            if (mPlaylistMetadata != null) {
-                bundle.putBundle(KEY_MEDIA_METADATA2_BUNDLE, mPlaylistMetadata.toBundle());
-            }
-            return bundle;
-        }
-
-        public static PlaylistParams fromBundle(Context context, Bundle bundle) {
-            if (bundle == null) {
-                return null;
-            }
-            if (!bundle.containsKey(KEY_REPEAT_MODE) || !bundle.containsKey(KEY_SHUFFLE_MODE)) {
-                return null;
-            }
-
-            Bundle metadataBundle = bundle.getBundle(KEY_MEDIA_METADATA2_BUNDLE);
-            MediaMetadata2 metadata = metadataBundle == null
-                    ? null : MediaMetadata2.fromBundle(context, metadataBundle);
-
-            return new PlaylistParams(context,
-                    bundle.getInt(KEY_REPEAT_MODE),
-                    bundle.getInt(KEY_SHUFFLE_MODE),
-                    metadata);
-        }
-    }
-
     public static class CommandButtonImpl implements CommandButtonProvider {
         private static final String KEY_COMMAND
                 = "android.media.media_session2.command_button.command";
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 4ec0da9..6bab471 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -22,13 +22,11 @@
 import android.media.MediaItem2;
 import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaMetadata2;
-import android.media.MediaPlayerBase;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.PlaylistParams;
 import android.media.Rating2;
 import android.media.VolumeProvider2;
 import android.net.Uri;
@@ -396,8 +394,8 @@
                 final long bufferedPositionMs = session.getInstance().getBufferedPosition();
                 final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl)
                         session.getPlaybackInfo().getProvider()).toBundle();
-                final PlaylistParams params = session.getInstance().getPlaylistParams();
-                final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
+                final int repeatMode = session.getInstance().getRepeatMode();
+                final int shuffleMode = session.getInstance().getShuffleMode();
                 final PendingIntent sessionActivity = session.getSessionActivity();
                 final List<MediaItem2> playlist =
                         allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST)
@@ -427,8 +425,8 @@
                 try {
                     caller.onConnected(MediaSession2Stub.this, allowedCommands.toBundle(),
                             playerState, positionEventTimeMs, positionMs, playbackSpeed,
-                            bufferedPositionMs, playbackInfoBundle, paramsBundle, playlistBundle,
-                            sessionActivity);
+                            bufferedPositionMs, playbackInfoBundle, repeatMode, shuffleMode,
+                            playlistBundle, sessionActivity);
                 } catch (RemoteException e) {
                     // Controller may be died prematurely.
                     // TODO(jaewan): Handle here.
@@ -507,14 +505,6 @@
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
                     session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION));
                     break;
-                    // TODO(jaewan): Remove (b/74116823)
-                    /*
-                case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
-                    session.getInstance().setPlaylistParams(
-                            PlaylistParams.fromBundle(session.getContext(),
-                                    args.getBundle(ARGUMENT_KEY_PLAYLIST_PARAMS)));
-                    break;
-                    */
                 default:
                     // TODO(jaewan): Resend unknown (new) commands through the custom command.
             }
@@ -741,6 +731,21 @@
                 });
     }
 
+    @Override
+    public void setRepeatMode(IMediaController2 caller, int repeatMode) {
+        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE,
+                (session, controller) -> {
+                    session.getInstance().setRepeatMode(repeatMode);
+                });
+    }
+
+    @Override
+    public void setShuffleMode(IMediaController2 caller, int shuffleMode) {
+        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE,
+                (session, controller) -> {
+                    session.getInstance().setShuffleMode(shuffleMode);
+                });
+    }
 
     //////////////////////////////////////////////////////////////////////////////////////////////
     // AIDL methods for LibrarySession overrides
@@ -985,6 +990,18 @@
                 });
     }
 
+    public void notifyRepeatModeChangedNotLocked(int repeatMode) {
+        notifyAll((unused, iController) -> {
+            iController.onRepeatModeChanged(repeatMode);
+        });
+    }
+
+    public void notifyShuffleModeChangedNotLocked(int shuffleMode) {
+        notifyAll((unused, iController) -> {
+            iController.onShuffleModeChanged(shuffleMode);
+        });
+    }
+
     public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) {
         final Bundle playbackInfoBundle =
                 ((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle();
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 7f225de..395e675 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -35,7 +35,6 @@
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
@@ -52,7 +51,6 @@
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
 import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
-import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
 import android.media.update.MediaSessionService2Provider;
 import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
 import android.media.update.SessionToken2Provider;
@@ -76,7 +74,6 @@
 import com.android.media.MediaMetadata2Impl;
 import com.android.media.MediaPlaylistAgentImpl;
 import com.android.media.MediaSession2Impl;
-import com.android.media.MediaSession2Impl.PlaylistParamsImpl;
 import com.android.media.MediaSessionService2Impl;
 import com.android.media.Rating2Impl;
 import com.android.media.SessionToken2Impl;
@@ -141,19 +138,6 @@
     }
 
     @Override
-    public PlaylistParamsProvider createMediaSession2PlaylistParams(Context context,
-            PlaylistParams playlistParams, int repeatMode, int shuffleMode,
-            MediaMetadata2 playlistMetadata) {
-        return new PlaylistParamsImpl(context, playlistParams, repeatMode, shuffleMode,
-                playlistMetadata);
-    }
-
-    @Override
-    public PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle) {
-        return PlaylistParamsImpl.fromBundle(context, bundle);
-    }
-
-    @Override
     public BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
             MediaSession2.CommandButton.Builder instance) {
         return new MediaSession2Impl.CommandButtonImpl.BuilderImpl(context, instance);