Merge changes from topics "mplc", "session2_onDataSourceMissingHelper" into pi-dev

* changes:
  Remove MediaPlaylistController, add MediaPlaylistAgent
  MediaSession2: Add SessionCallback#onCurrentMediaItemChanged()
  MediaSession2: Add onDataSourceMissingHelper
diff --git a/api/current.txt b/api/current.txt
index 1146e42..92f7389 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23350,14 +23350,14 @@
     field public static final int REGULAR_CODECS = 0; // 0x0
   }
 
-  public class MediaController2 implements java.lang.AutoCloseable android.media.MediaPlaylistController {
+  public class MediaController2 implements java.lang.AutoCloseable {
     ctor public MediaController2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
     method public void addPlaylistItem(int, android.media.MediaItem2);
     method public void adjustVolume(int, int);
     method public void close();
     method public void fastForward();
     method public long getBufferedPosition();
-    method public android.media.MediaItem2 getCurrentPlaylistItem();
+    method public android.media.MediaItem2 getCurrentMediaItem();
     method public android.media.MediaController2.PlaybackInfo getPlaybackInfo();
     method public float getPlaybackSpeed();
     method public int getPlayerState();
@@ -23378,7 +23378,6 @@
     method public void prepareFromMediaId(java.lang.String, android.os.Bundle);
     method public void prepareFromSearch(java.lang.String, android.os.Bundle);
     method public void prepareFromUri(android.net.Uri, android.os.Bundle);
-    method public void registerPlaylistControllerCallback(java.util.concurrent.Executor, android.media.MediaPlaylistController.PlaylistEventCallback);
     method public void removePlaylistItem(android.media.MediaItem2);
     method public void replacePlaylistItem(int, android.media.MediaItem2);
     method public void rewind();
@@ -23390,11 +23389,10 @@
     method public void setRepeatMode(int);
     method public void setShuffleMode(int);
     method public void setVolumeTo(int, int);
-    method public void skipToNext();
+    method public void skipToNextItem();
     method public void skipToPlaylistItem(android.media.MediaItem2);
-    method public void skipToPrevious();
+    method public void skipToPreviousItem();
     method public void stop();
-    method public void unregisterPlaylistControllerCallback(android.media.MediaPlaylistController.PlaylistEventCallback);
     method public void updatePlaylistMetadata(android.media.MediaMetadata2);
   }
 
@@ -23403,7 +23401,7 @@
     method public void onAllowedCommandsChanged(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
     method public void onBufferedPositionChanged(android.media.MediaController2, long);
     method public void onConnected(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
-    method public void onCurrentPlaylistItemChanged(android.media.MediaController2, android.media.MediaItem2);
+    method public void onCurrentMediaItemChanged(android.media.MediaController2, android.media.MediaItem2);
     method public void onCustomCommand(android.media.MediaController2, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
     method public void onCustomLayoutChanged(android.media.MediaController2, java.util.List<android.media.MediaSession2.CommandButton>);
     method public void onDisconnected(android.media.MediaController2);
@@ -23411,7 +23409,11 @@
     method public void onPlaybackInfoChanged(android.media.MediaController2, android.media.MediaController2.PlaybackInfo);
     method public void onPlaybackSpeedChanged(android.media.MediaController2, float);
     method public void onPlayerStateChanged(android.media.MediaController2, int);
+    method public void onPlaylistChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
+    method public void onPlaylistMetadataChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
     method public void onPositionChanged(android.media.MediaController2, long, long);
+    method public void onRepeatModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
+    method public void onShuffleModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
   }
 
   public static final class MediaController2.PlaybackInfo {
@@ -23872,7 +23874,7 @@
     method public android.media.MediaLibraryService2.MediaLibrarySession build();
     method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setId(java.lang.String);
     method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlayer(android.media.MediaPlayerBase);
-    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlaylistController(android.media.MediaPlaylistController);
+    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlaylistAgent(android.media.MediaPlaylistAgent);
     method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent);
     method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
     method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setVolumeProvider(android.media.VolumeProvider2);
@@ -24541,22 +24543,28 @@
     method public void onPlayerStateChanged(android.media.MediaPlayerBase, int);
   }
 
-  public abstract interface MediaPlaylistController {
-    method public abstract void addPlaylistItem(int, android.media.MediaItem2);
-    method public abstract android.media.MediaItem2 getCurrentPlaylistItem();
-    method public abstract java.util.List<android.media.MediaItem2> getPlaylist();
-    method public abstract android.media.MediaMetadata2 getPlaylistMetadata();
-    method public abstract int getRepeatMode();
-    method public abstract int getShuffleMode();
-    method public abstract void registerPlaylistControllerCallback(java.util.concurrent.Executor, android.media.MediaPlaylistController.PlaylistEventCallback);
-    method public abstract void removePlaylistItem(android.media.MediaItem2);
-    method public abstract void replacePlaylistItem(int, android.media.MediaItem2);
-    method public abstract void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public abstract void setRepeatMode(int);
-    method public abstract void setShuffleMode(int);
-    method public abstract void skipToPlaylistItem(android.media.MediaItem2);
-    method public abstract void unregisterPlaylistControllerCallback(android.media.MediaPlaylistController.PlaylistEventCallback);
-    method public abstract void updatePlaylistMetadata(android.media.MediaMetadata2);
+  public abstract class MediaPlaylistAgent {
+    ctor public MediaPlaylistAgent(android.content.Context);
+    method public void addPlaylistItem(int, android.media.MediaItem2);
+    method public java.util.List<android.media.MediaItem2> getPlaylist();
+    method public android.media.MediaMetadata2 getPlaylistMetadata();
+    method public int getRepeatMode();
+    method public int getShuffleMode();
+    method public final void notifyPlaylistChanged();
+    method public final void notifyPlaylistMetadataChanged();
+    method public final void notifyRepeatModeChanged();
+    method public final void notifyShuffleModeChanged();
+    method public final void registerPlaylistEventCallback(java.util.concurrent.Executor, android.media.MediaPlaylistAgent.PlaylistEventCallback);
+    method public void removePlaylistItem(android.media.MediaItem2);
+    method public void replacePlaylistItem(int, android.media.MediaItem2);
+    method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
+    method public void setRepeatMode(int);
+    method public void setShuffleMode(int);
+    method public void skipToNextItem();
+    method public void skipToPlaylistItem(android.media.MediaItem2);
+    method public void skipToPreviousItem();
+    method public final void unregisterPlaylistEventCallback(android.media.MediaPlaylistAgent.PlaylistEventCallback);
+    method public void updatePlaylistMetadata(android.media.MediaMetadata2);
     field public static final int REPEAT_MODE_ALL = 2; // 0x2
     field public static final int REPEAT_MODE_GROUP = 3; // 0x3
     field public static final int REPEAT_MODE_NONE = 0; // 0x0
@@ -24566,12 +24574,12 @@
     field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
   }
 
-  public static abstract class MediaPlaylistController.PlaylistEventCallback {
-    ctor public MediaPlaylistController.PlaylistEventCallback();
-    method public void onPlaylistChanged(android.media.MediaPlaylistController, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public void onPlaylistMetadataChanged(android.media.MediaPlaylistController, android.media.MediaMetadata2);
-    method public void onRepeatModeChanged(android.media.MediaPlaylistController, int);
-    method public void onShuffleModeChanged(android.media.MediaPlaylistController, int);
+  public static abstract class MediaPlaylistAgent.PlaylistEventCallback {
+    ctor public MediaPlaylistAgent.PlaylistEventCallback();
+    method public void onPlaylistChanged(android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
+    method public void onPlaylistMetadataChanged(android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
+    method public void onRepeatModeChanged(android.media.MediaPlaylistAgent, int);
+    method public void onShuffleModeChanged(android.media.MediaPlaylistAgent, int);
   }
 
   public class MediaRecorder implements android.media.AudioRouting {
@@ -24850,16 +24858,17 @@
     method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
   }
 
-  public class MediaSession2 implements java.lang.AutoCloseable android.media.MediaPlaylistController {
+  public class MediaSession2 implements java.lang.AutoCloseable {
     method public void addPlaylistItem(int, android.media.MediaItem2);
+    method public void clearOnDataSourceMissingHelper();
     method public void close();
     method public void fastForward();
     method public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
-    method public android.media.MediaItem2 getCurrentPlaylistItem();
-    method public android.media.MediaPlaylistController getMediaPlaylistController();
+    method public android.media.MediaItem2 getCurrentMediaItem();
     method public float getPlaybackSpeed();
     method public android.media.MediaPlayerBase getPlayer();
     method public java.util.List<android.media.MediaItem2> getPlaylist();
+    method public android.media.MediaPlaylistAgent getPlaylistAgent();
     method public android.media.MediaMetadata2 getPlaylistMetadata();
     method public int getRepeatMode();
     method public int getShuffleMode();
@@ -24869,7 +24878,6 @@
     method public void pause();
     method public void play();
     method public void prepare();
-    method public void registerPlaylistControllerCallback(java.util.concurrent.Executor, android.media.MediaPlaylistController.PlaylistEventCallback);
     method public void removePlaylistItem(android.media.MediaItem2);
     method public void replacePlaylistItem(int, android.media.MediaItem2);
     method public void rewind();
@@ -24879,16 +24887,16 @@
     method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup);
     method public void setAudioFocusRequest(android.media.AudioFocusRequest);
     method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List<android.media.MediaSession2.CommandButton>);
+    method public void setOnDataSourceMissingHelper(android.media.MediaSession2.OnDataSourceMissingHelper);
     method public void setPlaybackSpeed(float);
     method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
     method public void setRepeatMode(int);
     method public void setShuffleMode(int);
-    method public void skipToNext();
+    method public void skipToNextItem();
     method public void skipToPlaylistItem(android.media.MediaItem2);
-    method public void skipToPrevious();
+    method public void skipToPreviousItem();
     method public void stop();
-    method public void unregisterPlaylistControllerCallback(android.media.MediaPlaylistController.PlaylistEventCallback);
-    method public void updatePlayer(android.media.MediaPlayerBase, android.media.MediaPlaylistController, android.media.VolumeProvider2);
+    method public void updatePlayer(android.media.MediaPlayerBase, android.media.MediaPlaylistAgent, android.media.VolumeProvider2);
     method public void updatePlaylistMetadata(android.media.MediaMetadata2);
     field public static final int COMMAND_CODE_BROWSER = 28; // 0x1c
     field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
@@ -24939,7 +24947,7 @@
     method public android.media.MediaSession2 build();
     method public android.media.MediaSession2.Builder setId(java.lang.String);
     method public android.media.MediaSession2.Builder setPlayer(android.media.MediaPlayerBase);
-    method public android.media.MediaSession2.Builder setPlaylistController(android.media.MediaPlaylistController);
+    method public android.media.MediaSession2.Builder setPlaylistAgent(android.media.MediaPlaylistAgent);
     method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
     method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
     method public android.media.MediaSession2.Builder setVolumeProvider(android.media.VolumeProvider2);
@@ -24988,11 +24996,16 @@
     method public boolean isTrusted();
   }
 
+  public static abstract interface MediaSession2.OnDataSourceMissingHelper {
+    method public abstract android.media.DataSourceDesc onDataSourceMissing(android.media.MediaSession2, android.media.MediaItem2);
+  }
+
   public static abstract class MediaSession2.SessionCallback {
     ctor public MediaSession2.SessionCallback(android.content.Context);
     method public void onBufferingStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2, int);
     method public boolean onCommandRequest(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command);
     method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
+    method public void onCurrentMediaItemChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
     method public void onCustomCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
     method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
     method public void onMediaPrepared(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
@@ -25000,10 +25013,14 @@
     method public void onPlayFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
     method public void onPlayFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
     method public void onPlayerStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, int);
+    method public void onPlaylistChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
+    method public void onPlaylistMetadataChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
     method public void onPrepareFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
     method public void onPrepareFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
     method public void onPrepareFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
+    method public void onRepeatModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
     method public void onSetRating(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2);
+    method public void onShuffleModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
   }
 
   public abstract class MediaSessionService2 extends android.app.Service {
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 25188fb..a9f72d0 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -21,6 +21,8 @@
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.media.MediaPlaylistAgent.RepeatMode;
+import android.media.MediaPlaylistAgent.ShuffleMode;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
@@ -65,7 +67,7 @@
  * @see MediaSession2
  * @see MediaSessionService2
  */
-public class MediaController2 implements AutoCloseable, MediaPlaylistController {
+public class MediaController2 implements AutoCloseable {
     /**
      * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
      * active if and only if it has set a player.
@@ -146,7 +148,7 @@
          * @param playlist A new playlist set by the session.
          * @see #onPositionChanged(MediaController2, long, long)
          * @see #onBufferedPositionChanged(MediaController2, long)
-         * @see #onCurrentPlaylistItemChanged(MediaController2, MediaItem2)
+         * @see #onCurrentMediaItemChanged(MediaController2, MediaItem2)
          * @hide
          */
         // TODO(jaewan): Remove (b/74174728)
@@ -211,7 +213,7 @@
                 @Nullable Bundle extras) { }
 
         /**
-         * Called when the player's current playing item is changed
+         * Called when the player's currently playing item is changed
          * <p>
          * When it's called, you should invalidate previous playback information and wait for later
          * callbacks.
@@ -220,12 +222,62 @@
          * @param item new item
          * @see #onPositionChanged(MediaController2, long, long)
          * @see #onBufferedPositionChanged(MediaController2, long)
-         * @see #onCurrentPlaylistItemChanged(MediaController2, MediaItem2)
          */
-        public void onCurrentPlaylistItemChanged(@NonNull MediaController2 controller,
+        public void onCurrentMediaItemChanged(@NonNull MediaController2 controller,
                 @NonNull MediaItem2 item) { }
 
         /**
+         * Called when a playlist is changed.
+         *
+         * @param controller the controller for this event
+         * @param mplc playlist controller for this event
+         * @param list new playlist
+         * @param metadata new metadata
+         */
+        public void onPlaylistChanged(@NonNull MediaController2 controller,
+                @NonNull MediaPlaylistAgent mplc, @NonNull List<MediaItem2> list,
+                @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when a playlist metadata is changed.
+         *
+         * @param controller the controller for this event
+         * @param mplc playlist controller for this event
+         * @param metadata new metadata
+         */
+        public void onPlaylistMetadataChanged(@NonNull MediaController2 controller,
+                @NonNull MediaPlaylistAgent mplc, @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when the shuffle mode is changed.
+         *
+         * @param controller the controller for this event
+         * @param mplc playlist controller for this event
+         * @param shuffleMode repeat mode
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+         */
+        public void onShuffleModeChanged(@NonNull MediaController2 controller,
+                @NonNull MediaPlaylistAgent mplc,
+                @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
+
+        /**
+         * Called when the repeat mode is changed.
+         *
+         * @param controller the controller for this event
+         * @param mplc playlist controller for this event
+         * @param repeatMode repeat mode
+         * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+         * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+         */
+        public void onRepeatModeChanged(@NonNull MediaController2 controller,
+                @NonNull MediaPlaylistAgent mplc,
+                @MediaPlaylistAgent.RepeatMode int repeatMode) { }
+
+        /**
          * Called when the playlist parameters are changed.
          *
          * @param controller the controller for this event
@@ -399,14 +451,6 @@
         mProvider.stop_impl();
     }
 
-    public void skipToPrevious() {
-        mProvider.skipToPrevious_impl();
-    }
-
-    public void skipToNext() {
-        mProvider.skipToNext_impl();
-    }
-
     /**
      * Request that the player prepare its playback. In other words, other sessions can continue
      * to play during the preparation of this session. This method can be used to speed up the
@@ -703,56 +747,38 @@
     }
 
     /**
-     * Register {@link MediaPlaylistController.PlaylistEventCallback} to listen changes in the
-     * underlying {@link MediaPlaylistController}, regardless of the change in the controller.
-     * <p>
-     * Registered callbacks will be also called when the controller is changed.
-     *
-     * @param executor a callback Executor
-     * @param callback a PlaylistEventCallback
-     * @throws IllegalArgumentException if executor or callback is {@code null}.
-     */
-    @Override
-    public void registerPlaylistControllerCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull PlaylistEventCallback callback) {
-        // TODO(jaewan): Implement (b/74169681)
-        //mProvider.registerPlaylistControllerCallback_impl(executor, callback);
-    }
-
-    /**
-     * Unregister the previously registered {@link MediaPlaylistController.PlaylistEventCallback}.
-     *
-     * @param callback the callback to be removed
-     * @throws IllegalArgumentException if the callback is {@code null}.
-     */
-    @Override
-    public void unregisterPlaylistControllerCallback(@NonNull PlaylistEventCallback callback) {
-        // TODO(jaewan): Implement (b/74169681)
-        //mProvider.unregisterPlaylistControllerCallback_impl(callback);
-    }
-
-    /**
      * Return playlist from the session.
      *
      * @return playlist. Can be {@code null} if the controller doesn't have enough permission.
      */
-    @Override
     public @Nullable List<MediaItem2> getPlaylist() {
         return mProvider.getPlaylist_impl();
     }
 
-
-    @Override
+    /**
+     * Sets the playlist.
+     *
+     * @param list playlist
+     * @param metadata metadata of the playlist
+     */
     public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
         // TODO(jaewan): Implement (b/74174649)
     }
 
-    @Override
+    /**
+     * Updates the playlist metadata
+     *
+     * @param metadata metadata of the playlist
+     */
     public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
         // TODO(jaewan): Implement (b/74174649)
     }
 
-    @Override
+    /**
+     * Returns the playlist metadata
+     *
+     * @return metadata metadata of the playlist, or null if none is set
+     */
     public @Nullable MediaMetadata2 getPlaylistMetadata() {
         // TODO(jaewan): Implement (b/74174649)
         return null;
@@ -780,7 +806,6 @@
      * @param item the media item you want to add
      * @throws IndexOutOfBoundsException if index is outside play list range
      */
-    @Override
     public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.addPlaylistItem_impl(index, item);
     }
@@ -791,7 +816,6 @@
      * If the item is the currently playing item of the playlist, current playback
      * will be stopped and playback moves to next source in the list.
      */
-    @Override
     public void removePlaylistItem(@NonNull MediaItem2 item) {
         mProvider.removePlaylistItem_impl(item);
     }
@@ -801,19 +825,18 @@
      * @param index the index of the item to replace
      * @param item the new item
      */
-    @Override
     public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.replacePlaylistItem_impl(index, item);
     }
 
     /**
      * Get the lastly cached current item from
-     * {@link ControllerCallback#onCurrentPlaylistItemChanged(MediaController2, MediaItem2)}.
+     * {@link ControllerCallback#onCurrentMediaItemChanged(MediaController2, MediaItem2)}.
      *
      * @return index of the current item
      */
-    @Override
-    public MediaItem2 getCurrentPlaylistItem() {
+    public MediaItem2 getCurrentMediaItem() {
+        // TODO(jaewan): Rename provider API
         return mProvider.getCurrentPlaylistItem_impl();
     }
 
@@ -824,30 +847,35 @@
      * @throws IllegalArgumentException if the play list is null
      * @throws NullPointerException if index is outside play list range
      */
-    @Override
     public void skipToPlaylistItem(@NonNull MediaItem2 item) {
         mProvider.skipToPlaylistItem_impl(item);
     }
 
-    @Override
+    public void skipToPreviousItem() {
+        // TODO(jaewan): fix this
+        mProvider.skipToPrevious_impl();
+    }
+
+    public void skipToNextItem() {
+        // TODO(jaewan): fix this
+        mProvider.skipToNext_impl();
+    }
+
     public @RepeatMode int getRepeatMode() {
         // TODO(jaewan): Implement (b/74118768)
         return 0;
     }
 
-    @Override
-    public void setRepeatMode(int repeatMode) {
+    public void setRepeatMode(@RepeatMode int repeatMode) {
         // TODO(jaewan): Implement (b/74118768)
     }
 
-    @Override
     public @ShuffleMode int getShuffleMode() {
         // TODO(jaewan): Implement (b/74118768)
         return 0;
     }
 
-    @Override
-    public void setShuffleMode(int shuffleMode) {
+    public void setShuffleMode(@ShuffleMode int shuffleMode) {
         // TODO(jaewan): Implement (b/74118768)
     }
 }
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index f6edd0c0..2db1392 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -139,6 +139,13 @@
         return mProvider.getDataSourceDesc_impl();
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        // TODO(jaewan): Override this. MediaItem2 may have auto-generated srcId when the DSD isn't
+        //               set, and it should be compared for the equals.
+        return super.equals(obj);
+    }
+
     /**
      * Build {@link MediaItem2}
      */
@@ -189,12 +196,12 @@
         }
 
         /**
-         * Set the data source descriptor for this instance. Should not be {@code null}.
+         * Set the data source descriptor for this instance. {@code null} for unset.
          *
          * @param dataSourceDesc data source descriptor
          * @return this instance for chaining
          */
-        public Builder setDataSourceDesc(@NonNull DataSourceDesc dataSourceDesc) {
+        public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) {
             return mProvider.setDataSourceDesc_impl(dataSourceDesc);
         }
 
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 2372685..38c5389 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -221,8 +221,8 @@
             }
 
             @Override
-            public Builder setPlaylistController(@NonNull MediaPlaylistController mplc) {
-                return super.setPlaylistController(mplc);
+            public Builder setPlaylistAgent(@NonNull MediaPlaylistAgent mplc) {
+                return super.setPlaylistAgent(mplc);
             }
 
             @Override
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
new file mode 100644
index 0000000..9238103
--- /dev/null
+++ b/media/java/android/media/MediaPlaylistAgent.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * MediaPlaylistAgent is the abstract class an application needs to derive from to pass an object
+ * to a MediaSession2 that will override default playlist handling behaviors. It contains a set of
+ * notify methods to signal MediaSession2 that playlist-related state has changed.
+ * <p>
+ * Playlists are composed of one or multiple {@link MediaItem2} instances, which combine metadata
+ * and data sources (as {@link DataSourceDesc})
+ * Used by {@link MediaSession2} and {@link MediaController2}.
+ */
+// This class only includes methods that contain {@link MediaItem2}.
+public abstract class MediaPlaylistAgent {
+    /**
+     * @hide
+     */
+    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
+            REPEAT_MODE_GROUP})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface RepeatMode {}
+
+    /**
+     * Playback will be stopped at the end of the playing media list.
+     */
+    public static final int REPEAT_MODE_NONE = 0;
+
+    /**
+     * Playback of the current playing media item will be repeated.
+     */
+    public static final int REPEAT_MODE_ONE = 1;
+
+    /**
+     * Playing media list will be repeated.
+     */
+    public static final int REPEAT_MODE_ALL = 2;
+
+    /**
+     * Playback of the playing media group will be repeated.
+     * A group is a logical block of media items which is specified in the section 5.7 of the
+     * Bluetooth AVRCP 1.6. An example of a group is the playlist.
+     */
+    public static final int REPEAT_MODE_GROUP = 3;
+
+    /**
+     * @hide
+     */
+    @IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ShuffleMode {}
+
+    /**
+     * Media list will be played in order.
+     */
+    public static final int SHUFFLE_MODE_NONE = 0;
+
+    /**
+     * Media list will be played in shuffled order.
+     */
+    public static final int SHUFFLE_MODE_ALL = 1;
+
+    /**
+     * Media group will be played in shuffled order.
+     * A group is a logical block of media items which is specified in the section 5.7 of the
+     * Bluetooth AVRCP 1.6. An example of a group is the playlist.
+     */
+    public static final int SHUFFLE_MODE_GROUP = 2;
+
+    private MediaPlayerBase mPlayer;
+
+    /**
+     * A callback class to receive notifications for events on the media player. See
+     * {@link MediaPlaylistAgent#registerPlaylistEventCallback(Executor, PlaylistEventCallback)}
+     * to register this callback.
+     */
+    public static abstract class PlaylistEventCallback {
+        /**
+         * Called when a playlist is changed.
+         *
+         * @param mplc playlist controller for this event
+         * @param list new playlist
+         * @param metadata new metadata
+         */
+        public void onPlaylistChanged(@NonNull MediaPlaylistAgent mplc,
+                @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when a playlist metadata is changed.
+         *
+         * @param mplc playlist controller for this event
+         * @param metadata new metadata
+         */
+        public void onPlaylistMetadataChanged(@NonNull MediaPlaylistAgent mplc,
+                @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when the shuffle mode is changed.
+         *
+         * @param mplc playlist controller for this event
+         * @param shuffleMode repeat mode
+         * @see #SHUFFLE_MODE_NONE
+         * @see #SHUFFLE_MODE_ALL
+         * @see #SHUFFLE_MODE_GROUP
+         */
+        public void onShuffleModeChanged(@NonNull MediaPlaylistAgent mplc,
+                @ShuffleMode int shuffleMode) { }
+
+        /**
+         * Called when the repeat mode is changed.
+         *
+         * @param mplc playlist controller for this event
+         * @param repeatMode repeat mode
+         * @see #REPEAT_MODE_NONE
+         * @see #REPEAT_MODE_ONE
+         * @see #REPEAT_MODE_ALL
+         * @see #REPEAT_MODE_GROUP
+         */
+        public void onRepeatModeChanged(@NonNull MediaPlaylistAgent mplc,
+                @RepeatMode int repeatMode) { }
+    }
+
+    public MediaPlaylistAgent(Context context) {
+        // FYI, Need to have a context in the constructor for making this class be updatable
+        // TODO(jaewan) : implement this
+    }
+
+    /**
+     * Register {@link PlaylistEventCallback} to listen changes in the underlying
+     * {@link MediaPlaylistAgent}, regardless of the change in the controller.
+     *
+     * @param executor a callback Executor
+     * @param callback a PlaylistEventCallback
+     * @throws IllegalArgumentException if executor or callback is {@code null}.
+     */
+    public final void registerPlaylistEventCallback(
+            @NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Unregister the previously registered {@link PlaylistEventCallback}.
+     *
+     * @param callback the callback to be removed
+     * @throws IllegalArgumentException if the callback is {@code null}.
+     */
+    public final void unregisterPlaylistEventCallback(
+            @NonNull PlaylistEventCallback callback) {
+        // TODO(jaewan): implement this
+    }
+
+    public final void notifyPlaylistChanged() {
+        // TODO(jaewan): implement this
+    }
+
+    public final void notifyPlaylistMetadataChanged() {
+        // TODO(jaewan): implement this
+    }
+
+    public final void notifyShuffleModeChanged() {
+        // TODO(jaewan): implement this
+    }
+
+    public final void notifyRepeatModeChanged() {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Returns the playlist
+     *
+     * @return playlist, or null if none is set.
+     */
+    public @Nullable List<MediaItem2> getPlaylist() {
+        // TODO(jaewan): implement this
+        return null;
+    }
+
+    /**
+     * Sets the playlist.
+     *
+     * @param list playlist
+     * @param metadata metadata of the playlist
+     */
+    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Returns the playlist metadata
+     *
+     * @return metadata metadata of the playlist, or null if none is set
+     */
+    public @Nullable MediaMetadata2 getPlaylistMetadata() {
+        // TODO(jaewan): implement this
+        return null;
+    }
+
+    /**
+     * Updates the playlist metadata
+     *
+     * @param metadata metadata of the playlist
+     */
+    public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Adds the media item to the playlist at the index
+     *
+     * @param index index
+     * @param item media item to add
+     */
+    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Removes the media item from the playlist
+     *
+     * @param item media item to remove
+     */
+    public void removePlaylistItem(@NonNull MediaItem2 item) {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Replaces the media item with the .
+     * <p>
+     * This can be used to update metadata of a MediaItem.
+     *
+     * @param index index
+     * @param item
+     */
+    public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Skips to the the media item, and plays from it.
+     *
+     * @param item media item to start playing from
+     */
+    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
+        // TODO(jaewan): implement this
+    }
+
+    public void skipToPreviousItem() {
+        // TODO(jaewan): implement this
+    }
+
+    public void skipToNextItem() {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Get repeat mode
+     *
+     * @return repeat mode
+     * @see #REPEAT_MODE_NONE
+     * @see #REPEAT_MODE_ONE
+     * @see #REPEAT_MODE_ALL
+     * @see #REPEAT_MODE_GROUP
+     */
+    public @RepeatMode int getRepeatMode() {
+        // TODO(jaewan): implement this
+        return REPEAT_MODE_NONE;
+    }
+
+    /**
+     * Set repeat mode
+     *
+     * @param repeatMode repeat mode
+     * @see #REPEAT_MODE_NONE
+     * @see #REPEAT_MODE_ONE
+     * @see #REPEAT_MODE_ALL
+     * @see #REPEAT_MODE_GROUP
+     */
+    public void setRepeatMode(@RepeatMode int repeatMode) {
+        // TODO(jaewan): implement this
+    }
+
+    /**
+     * Get shuffle mode
+     *
+     * @return shuffle mode
+     * @see #SHUFFLE_MODE_NONE
+     * @see #SHUFFLE_MODE_ALL
+     * @see #SHUFFLE_MODE_GROUP
+     */
+    public @ShuffleMode int getShuffleMode() {
+        // TODO(jaewan): implement this
+        return SHUFFLE_MODE_NONE;
+    }
+
+    /**
+     * Set shuffle mode
+     *
+     * @param shuffleMode shuffle mode
+     * @see #SHUFFLE_MODE_NONE
+     * @see #SHUFFLE_MODE_ALL
+     * @see #SHUFFLE_MODE_GROUP
+     */
+    public void setShuffleMode(@ShuffleMode int shuffleMode) {
+        // TODO(jaewan): implement this
+    }
+}
diff --git a/media/java/android/media/MediaPlaylistController.java b/media/java/android/media/MediaPlaylistController.java
deleted file mode 100644
index 1ad5617..0000000
--- a/media/java/android/media/MediaPlaylistController.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.MediaSession2.PlaylistParams;
-import android.media.MediaSession2.PlaylistParams.RepeatMode;
-import android.media.MediaSession2.PlaylistParams.ShuffleMode;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Controller interface for playlist management.
- * Playlists are composed of one or multiple {@link MediaItem2} instances, which combine metadata
- * and data sources (as {@link DataSourceDesc})
- * Used by {@link MediaSession2} and {@link MediaController2}.
- */
- // This class only includes methods that contain {@link MediaItem2}.
- // Note that setPlaylist() isn't added on purpose because it's considered session-specific.
-
-public interface MediaPlaylistController {
-    /**
-     * @hide
-     */
-    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
-            REPEAT_MODE_GROUP})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface RepeatMode {}
-
-    /**
-     * Playback will be stopped at the end of the playing media list.
-     */
-    int REPEAT_MODE_NONE = 0;
-
-    /**
-     * Playback of the current playing media item will be repeated.
-     */
-    int REPEAT_MODE_ONE = 1;
-
-    /**
-     * Playing media list will be repeated.
-     */
-    int REPEAT_MODE_ALL = 2;
-
-    /**
-     * Playback of the playing media group will be repeated.
-     * A group is a logical block of media items which is specified in the section 5.7 of the
-     * Bluetooth AVRCP 1.6. An example of a group is the playlist.
-     */
-    int REPEAT_MODE_GROUP = 3;
-
-    /**
-     * @hide
-     */
-    @IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface ShuffleMode {}
-
-    /**
-     * Media list will be played in order.
-     */
-    int SHUFFLE_MODE_NONE = 0;
-
-    /**
-     * Media list will be played in shuffled order.
-     */
-    int SHUFFLE_MODE_ALL = 1;
-
-    /**
-     * Media group will be played in shuffled order.
-     * A group is a logical block of media items which is specified in the section 5.7 of the
-     * Bluetooth AVRCP 1.6. An example of a group is the playlist.
-     */
-    int SHUFFLE_MODE_GROUP = 2;
-
-    abstract class PlaylistEventCallback {
-        /**
-         * Called when a playlist is changed.
-         *
-         * @param mplc playlist controller for this event
-         * @param list new playlist
-         * @param metadata new metadata
-         */
-        public void onPlaylistChanged(@NonNull MediaPlaylistController mplc,
-                @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when a playlist is changed.
-         *
-         * @param mplc playlist controller for this event
-         * @param metadata new metadata
-         */
-        public void onPlaylistMetadataChanged(@NonNull MediaPlaylistController mplc,
-                @Nullable MediaMetadata2 metadata) { }
-
-        /**
-         * Called when a playlist is changed.
-         *
-         * @param mplc playlist controller for this event
-         * @param shuffleMode repeat mode
-         * @see #SHUFFLE_MODE_NONE
-         * @see #SHUFFLE_MODE_ALL
-         * @see #SHUFFLE_MODE_GROUP
-         */
-        public void onShuffleModeChanged(@NonNull MediaPlaylistController mplc,
-                @ShuffleMode int shuffleMode) { }
-
-        /**
-         * Called when a playlist is changed.
-         *
-         * @param mplc playlist controller for this event
-         * @param repeatMode repeat mode
-         * @see #REPEAT_MODE_NONE
-         * @see #REPEAT_MODE_ONE
-         * @see #REPEAT_MODE_ALL
-         * @see #REPEAT_MODE_GROUP
-         */
-        public void onRepeatModeChanged(@NonNull MediaPlaylistController mplc,
-                @RepeatMode int repeatMode) { }
-    }
-
-    /**
-     * Register {@link PlaylistEventCallback} to listen changes in the underlying
-     * {@link MediaPlaylistController}, regardless of the change in the controller.
-     *
-     * @param executor a callback Executor
-     * @param callback a PlaylistEventCallback
-     * @throws IllegalArgumentException if executor or callback is {@code null}.
-     */
-    void registerPlaylistControllerCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull PlaylistEventCallback callback);
-
-    /**
-     * Unregister the previously registered {@link PlaylistEventCallback}.
-     *
-     * @param callback the callback to be removed
-     * @throws IllegalArgumentException if the callback is {@code null}.
-     */
-    void unregisterPlaylistControllerCallback(@NonNull PlaylistEventCallback callback);
-
-    /**
-     * Returns the playlist
-     *
-     * @return playlist, or null if none is set.
-     */
-    @Nullable List<MediaItem2> getPlaylist();
-
-    /**
-     * Sets the playlist.
-     *
-     * @param list playlist
-     * @param metadata metadata of the playlist
-     */
-    void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata);
-
-    /**
-     * Returns the playlist metadata
-     *
-     * @return metadata metadata of the playlist, or null if none is set
-     */
-    @Nullable MediaMetadata2 getPlaylistMetadata();
-
-    /**
-     * Updates the playlist metadata
-     *
-     * @param metadata metadata of the playlist
-     */
-    void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata);
-
-    /**
-     * Adds the media item to the playlist at the index
-     *
-     * @param index index
-     * @param item media item to add
-     */
-    void addPlaylistItem(int index, @NonNull MediaItem2 item);
-
-    /**
-     * Removes the media item from the playlist
-     *
-     * @param item media item to remove
-     */
-    void removePlaylistItem(@NonNull MediaItem2 item);
-
-    /**
-     * Replaces the media item with the .
-     * <p>
-     * This can be used to update metadata of a MediaItem.
-     *
-     * @param index index
-     * @param item
-     */
-    void replacePlaylistItem(int index, @NonNull MediaItem2 item);
-
-    /**
-     * Returns the current media item.
-     * @return the current media item, or null if none is set, or none available to play.
-     */
-    MediaItem2 getCurrentPlaylistItem();
-
-    /**
-     * Skips to the the media item, and plays from it.
-     *
-     * @param item media item to start playing from
-     */
-    void skipToPlaylistItem(@NonNull MediaItem2 item);
-
-    /**
-     * Get repeat mode
-     *
-     * @return repeat mode
-     * @see #REPEAT_MODE_NONE
-     * @see #REPEAT_MODE_ONE
-     * @see #REPEAT_MODE_ALL
-     * @see #REPEAT_MODE_GROUP
-     */
-    @RepeatMode int getRepeatMode();
-
-    /**
-     * Set repeat mode
-     *
-     * @param repeatMode repeat mode
-     * @see #REPEAT_MODE_NONE
-     * @see #REPEAT_MODE_ONE
-     * @see #REPEAT_MODE_ALL
-     * @see #REPEAT_MODE_GROUP
-     */
-    void setRepeatMode(@RepeatMode int repeatMode);
-
-    /**
-     * Get shuffle mode
-     *
-     * @return shuffle mode
-     * @see #SHUFFLE_MODE_NONE
-     * @see #SHUFFLE_MODE_ALL
-     * @see #SHUFFLE_MODE_GROUP
-     */
-    @ShuffleMode int getShuffleMode();
-
-    /**
-     * Set shuffle mode
-     *
-     * @param shuffleMode shuffle mode
-     * @see #SHUFFLE_MODE_NONE
-     * @see #SHUFFLE_MODE_ALL
-     * @see #SHUFFLE_MODE_GROUP
-     */
-    void setShuffleMode(@ShuffleMode int shuffleMode);
-}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index ddb51c3..4ad197a 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -80,7 +80,7 @@
  *
  * @see MediaSessionService2
  */
-public class MediaSession2 implements AutoCloseable, MediaPlaylistController {
+public class MediaSession2 implements AutoCloseable {
     private final MediaSession2Provider mProvider;
 
     /**
@@ -114,7 +114,7 @@
     public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
 
     /**
-     * Command code for {@link MediaController2#skipToNext()}.
+     * Command code for {@link MediaController2#skipToNextItem()}.
      * <p>
      * Command would be sent directly to the player if the session doesn't reject the request
      * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
@@ -122,7 +122,7 @@
     public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
 
     /**
-     * Command code for {@link MediaController2#skipToPrevious()}.
+     * Command code for {@link MediaController2#skipToPreviousItem()}.
      * <p>
      * Command would be sent directly to the player if the session doesn't reject the request
      * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
@@ -408,6 +408,29 @@
     public static final int ERROR_CODE_SETUP_REQUIRED = 12;
 
     /**
+     * Interface definition of a callback to be invoked when a {@link MediaItem2} in the playlist
+     * didn't have a {@link DataSourceDesc} but it's needed now for preparing or playing it.
+     *
+     * #see #setOnDataSourceMissingHelper
+     */
+    public interface OnDataSourceMissingHelper {
+        /**
+         * Called when a {@link MediaItem2} in the playlist didn't have a {@link DataSourceDesc}
+         * but it's needed now for preparing or playing it.
+         * <p>
+         * Returned data source descriptor will be sent to the player directly to prepare or play
+         * the contents.
+         *
+         * @param session the session for this event
+         * @param item media item from the controller
+         * @return a data source descriptor if the media item. Can be {@code null} if the content
+         *        isn't available.
+         */
+        @Nullable DataSourceDesc onDataSourceMissing(@NonNull MediaSession2 session,
+                @NonNull MediaItem2 item);
+    }
+
+    /**
      * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
      * <p>
      * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
@@ -756,6 +779,19 @@
                 @NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
 
         /**
+         * Called when the player's current playing item is changed
+         * <p>
+         * When it's called, you should invalidate previous playback information and wait for later
+         * callbacks.
+         *
+         * @param session the controller for this event
+         * @param mpb the player for this event
+         * @param item new item
+         */
+        public void onCurrentMediaItemChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlayerBase mpb, @NonNull MediaItem2 item) { }
+
+        /**
          * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
          * referenced by the given data source.
          * @param session the session for this event
@@ -777,6 +813,7 @@
 
         /**
          * Called to report buffering events for a data source.
+         *
          * @param session the session for this event
          * @param mpb the player for this event
          * @param item the media item for which buffering is happening.
@@ -784,7 +821,58 @@
          */
         public void onBufferingStateChanged(@NonNull MediaSession2 session,
                 @NonNull MediaPlayerBase mpb, @NonNull MediaItem2 item, @BuffState int state) { }
-    };
+
+        /**
+         * Called when a playlist is changed.
+         *
+         * @param session the session for this event
+         * @param mplc playlist controller for this event
+         * @param list new playlist
+         * @param metadata new metadata
+         */
+        public void onPlaylistChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent mplc, @NonNull List<MediaItem2> list,
+                @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when a playlist metadata is changed.
+         *
+         * @param session the session for this event
+         * @param mplc playlist controller for this event
+         * @param metadata new metadata
+         */
+        public void onPlaylistMetadataChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent mplc, @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when the shuffle mode is changed.
+         *
+         * @param session the session for this event
+         * @param mplc playlist controller for this event
+         * @param shuffleMode repeat mode
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+         */
+        public void onShuffleModeChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent mplc,
+                @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
+
+        /**
+         * Called when the repeat mode is changed.
+         *
+         * @param session the session for this event
+         * @param mplc playlist controller for this event
+         * @param repeatMode repeat mode
+         * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+         * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+         */
+        public void onRepeatModeChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent mplc,
+                @MediaPlaylistAgent.RepeatMode int repeatMode) { }
+    }
 
     /**
      * Base builder class for MediaSession2 and its subclass. Any change in this class should be
@@ -824,13 +912,14 @@
         }
 
         /**
-         * Set the {@link MediaPlaylistController} for this session to manages playlist of the
+         * Set the {@link MediaPlaylistAgent} for this session to manages playlist of the
          * underlying {@link MediaPlayerBase player}.
          *
-         * @param mplc a {@link MediaPlaylistController} that manages playlist of the
+         * @param mplc a {@link MediaPlaylistAgent} that manages playlist of the
          * {@code player.}
          */
-        U setPlaylistController(@NonNull MediaPlaylistController mplc) {
+        U setPlaylistAgent(@NonNull MediaPlaylistAgent mplc) {
+            // TODO(jaewan): fix this
             mProvider.setPlaylistController_impl(mplc);
             return (U) this;
         }
@@ -918,8 +1007,8 @@
         }
 
         @Override
-        public Builder setPlaylistController(@NonNull MediaPlaylistController mplc) {
-            return super.setPlaylistController(mplc);
+        public Builder setPlaylistAgent(@NonNull MediaPlaylistAgent mplc) {
+            return super.setPlaylistAgent(mplc);
         }
 
         @Override
@@ -1280,13 +1369,13 @@
      * player on the {@link Handler} where the session is created on.
      *
      * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
-     * @param mplc a {@link MediaPlaylistController} that manages playlist of the
+     * @param mplc a {@link MediaPlaylistAgent} that manages playlist of the
      * {@code player}
      * @param volumeProvider The provider that will receive volume button events. If
      * {@code null}, system will adjust the appropriate stream volume for this session's player.
      */
     public void updatePlayer(@NonNull MediaPlayerBase player,
-            @Nullable MediaPlaylistController mplc, @Nullable VolumeProvider2 volumeProvider) {
+            @Nullable MediaPlaylistAgent mplc, @Nullable VolumeProvider2 volumeProvider) {
         mProvider.updatePlayer_impl(player, mplc, volumeProvider);
     }
 
@@ -1298,15 +1387,16 @@
     /**
      * @return player
      */
-    public @Nullable
+    public @NonNull
     MediaPlayerBase getPlayer() {
         return mProvider.getPlayer_impl();
     }
 
     /**
-     * @return playlist controller
+     * @return playlist manager
      */
-    public @Nullable MediaPlaylistController getMediaPlaylistController() {
+    public @Nullable
+    MediaPlaylistAgent getPlaylistAgent() {
         // TODO(jaewan): implement this (b/74090741)
         return null;
     }
@@ -1357,7 +1447,7 @@
      * This API can be called in the {@link SessionCallback#onConnect(MediaSession2, ControllerInfo)}.
      *
      * @param controller controller to specify layout.
-     * @param layout oredered list of layout.
+     * @param layout ordered list of layout.
      */
     public void setCustomLayout(@NonNull ControllerInfo controller,
             @NonNull List<CommandButton> layout) {
@@ -1419,14 +1509,6 @@
         mProvider.stop_impl();
     }
 
-    public void skipToPrevious() {
-        mProvider.skipToPrevious_impl();
-    }
-
-    public void skipToNext() {
-        mProvider.skipToNext_impl();
-    }
-
     /**
      * Request that the player prepare its playback. In other words, other sessions can continue
      * to play during the preparation of this session. This method can be used to speed up the
@@ -1509,7 +1591,7 @@
     }
 
     /**
-     * Register {@link EventCallback} to listen changes in the underlying
+     * Register {@link PlayerEventCallback} to listen changes in the underlying
      * {@link MediaPlayerBase}, regardless of the change in the underlying player.
      * <p>
      * Registered callbacks will be also called when the underlying player is changed.
@@ -1526,7 +1608,7 @@
     }
 
     /**
-     * Unregister the previously registered {@link EventCallback}.
+     * Unregister the previously registered {@link PlayerEventCallback}.
      *
      * @param callback the callback to be removed
      * @throws IllegalArgumentException if the callback is {@code null}.
@@ -1565,32 +1647,43 @@
     }
 
     /**
-     * Register {@link MediaPlaylistController.PlaylistEventCallback} to listen changes in the
-     * underlying {@link MediaPlaylistController}, regardless of the change in the controller.
+     * Sets the data source missing helper. Helper will be used to provide default implementation of
+     * {@link MediaPlaylistAgent} when it isn't set by developer.
      * <p>
-     * Registered callbacks will be also called when the controller is changed.
+     * Default implementation of the {@link MediaPlaylistAgent} will call helper when a
+     * {@link MediaItem2} in the playlist doesn't have a {@link DataSourceDesc}. This may happen
+     * when
+     * <ul>
+     *      <li>{@link MediaItem2} specified by {@link #setPlaylist(List, MediaMetadata2)} doesn't
+     *          have {@link DataSourceDesc}</li>
+     *      <li>{@link MediaController2#addPlaylistItem(int, MediaItem2)} is called and accepted
+     *          by {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
+     *          In that case, an item would be added automatically without the data source.</li>
+     * </ul>
+     * <p>
+     * If it's not set, playback wouldn't happen for the item without data source descriptor.
+     * <p>
+     * The helper will be run on the executor that you've specified by the
+     * {@link Builder#setSessionCallback(Executor, SessionCallback)}.
      *
-     * @param executor a callback Executor
-     * @param callback a PlaylistEventCallback
-     * @throws IllegalArgumentException if executor or callback is {@code null}.
+     * @param helper a data source misisng helper.
+     * @throws IllegalStateException when the helper is set when the playlist controller is set
+     * @see #setPlaylist(List, MediaMetadata2)
+     * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)
+     * @see #COMMAND_CODE_PLAYLIST_ADD_ITEM
+     * @see #COMMAND_CODE_PLAYLIST_REPLACE_ITEM
      */
-    @Override
-    public void registerPlaylistControllerCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull PlaylistEventCallback callback) {
-        // TODO(jaewan): Implement (b/74169681)
-        //mProvider.registerPlaylistControllerCallback_impl(executor, callback);
+    public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
+        // TODO(jaewan): Implement (b/74090741)
     }
 
     /**
-     * Unregister the previously registered {@link MediaPlaylistController.PlaylistEventCallback}.
+     * Clears the data source missing helper.
      *
-     * @param callback the callback to be removed
-     * @throws IllegalArgumentException if the callback is {@code null}.
+     * @see #setOnDataSourceMissingHelper(OnDataSourceMissingHelper)
      */
-    @Override
-    public void unregisterPlaylistControllerCallback(@NonNull PlaylistEventCallback callback) {
-        // TODO(jaewan): Implement (b/74169681)
-        //mProvider.unregisterPlaylistControllerCallback_impl(callback);
+    public void clearOnDataSourceMissingHelper() {
+        // TODO(jaewan): Implement (b/74090741)
     }
 
     /**
@@ -1598,7 +1691,6 @@
      *
      * @return playlist
      */
-    @Override
     public List<MediaItem2> getPlaylist() {
         return mProvider.getPlaylist_impl();
     }
@@ -1615,7 +1707,16 @@
         mProvider.setPlaylist_impl(playlist);
     }
 
-    @Override
+    /**
+     * Set a list of {@link MediaItem2} as the current play list.
+     * <p>
+     * You may specify a {@link MediaItem2} without {@link DataSourceDesc}. However, in that case,
+     * you should set {@link OnDataSourceMissingHelper} for player to prepare.
+     *
+     * @param list A list of {@link MediaItem2} objects to set as a play list.
+     * @throws IllegalArgumentException if given {@param playlist} is null.
+     * @see #setOnDataSourceMissingHelper
+     */
     public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
         // TODO(jaewan): Implement (b/74174649)
     }
@@ -1631,7 +1732,16 @@
         mProvider.skipToPlaylistItem_impl(item);
     }
 
-    @Override
+    public void skipToPreviousItem() {
+        // TODO(jaewan): fix this
+        mProvider.skipToPrevious_impl();
+    }
+
+    public void skipToNextItem() {
+        // TODO(jaewan): fix this
+        mProvider.skipToNext_impl();
+    }
+
     public MediaMetadata2 getPlaylistMetadata() {
         // TODO(jaewan): Implement (b/74174649)
         return null;
@@ -1648,7 +1758,6 @@
      * @param item the media item you want to add
      * @throws IndexOutOfBoundsException if index is outside play list range
      */
-    @Override
     public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.addPlaylistItem_impl(index, item);
     }
@@ -1670,7 +1779,6 @@
      * @param index the index of the item to replace
      * @param item the new item
      */
-    @Override
     public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.replacePlaylistItem_impl(index, item);
     }
@@ -1680,34 +1788,29 @@
      *
      * @return currently playing media item
      */
-    @Override
-    public MediaItem2 getCurrentPlaylistItem() {
+    public MediaItem2 getCurrentMediaItem() {
+        // TODO(jaewan): Rename provider API
         return mProvider.getCurrentPlaylistItem_impl();
     }
 
-    @Override
     public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
         // TODO(jaewan): Implement (b/74174649)
     }
 
-    @Override
     public int getRepeatMode() {
         // TODO(jaewan): Implement (b/74118768)
         return 0;
     }
 
-    @Override
     public void setRepeatMode(int repeatMode) {
         // TODO(jaewan): Implement (b/74118768)
     }
 
-    @Override
     public int getShuffleMode() {
         // TODO(jaewan): Implement (b/74118768)
         return 0;
     }
 
-    @Override
     public void setShuffleMode(int shuffleMode) {
         // TODO(jaewan): Implement (b/74118768)
     }
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 2951752..af6133a 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -16,13 +16,12 @@
 
 package android.media.update;
 
-import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
 import android.media.MediaPlayerBase.PlayerEventCallback;
-import android.media.MediaPlaylistController;
+import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
@@ -44,7 +43,7 @@
  */
 public interface MediaSession2Provider extends TransportControlProvider {
     void close_impl();
-    void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistController mplc,
+    void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent mplc,
             VolumeProvider2 volumeProvider);
     MediaPlayerBase getPlayer_impl();
     VolumeProvider2 getVolumeProvider_impl();
@@ -124,7 +123,7 @@
 
     interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
         void setPlayer_impl(MediaPlayerBase player);
-        void setPlaylistController_impl(MediaPlaylistController mplc);
+        void setPlaylistController_impl(MediaPlaylistAgent mplc);
         void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
         void setSessionActivity_impl(PendingIntent pi);
         void setId_impl(String id);