Merge "Merge "Frameworks: Annotate JUnit4 test with @Test" am: 3a12454f9a am: af8f4b192d am: b4654687ef"
diff --git a/api/current.txt b/api/current.txt
index 573e635..fff502a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22842,6 +22842,27 @@
     field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
   }
 
+  public class MediaBrowser2 extends android.media.MediaController2 {
+    ctor public MediaBrowser2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaBrowser2.BrowserCallback);
+    method public void getChildren(java.lang.String, int, int, android.os.Bundle);
+    method public void getItem(java.lang.String);
+    method public void getLibraryRoot(android.os.Bundle);
+    method public void getSearchResult(java.lang.String, int, int, android.os.Bundle);
+    method public void search(java.lang.String, android.os.Bundle);
+    method public void subscribe(java.lang.String, android.os.Bundle);
+    method public void unsubscribe(java.lang.String);
+  }
+
+  public static class MediaBrowser2.BrowserCallback extends android.media.MediaController2.ControllerCallback {
+    ctor public MediaBrowser2.BrowserCallback();
+    method public void onChildrenChanged(java.lang.String, int, android.os.Bundle);
+    method public void onGetChildrenDone(java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
+    method public void onGetItemDone(java.lang.String, android.media.MediaItem2);
+    method public void onGetLibraryRootDone(android.os.Bundle, java.lang.String, android.os.Bundle);
+    method public void onGetSearchResultDone(java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
+    method public void onSearchResultChanged(java.lang.String, int, android.os.Bundle);
+  }
+
   public final class MediaCas implements java.lang.AutoCloseable {
     ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
     method public void close();
@@ -23318,6 +23339,73 @@
     field public static final int REGULAR_CODECS = 0; // 0x0
   }
 
+  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.MediaController2.PlaybackInfo getPlaybackInfo();
+    method public float getPlaybackSpeed();
+    method public int getPlayerState();
+    method public java.util.List<android.media.MediaItem2> getPlaylist();
+    method public android.media.MediaSession2.PlaylistParams getPlaylistParams();
+    method public long getPosition();
+    method public android.app.PendingIntent getSessionActivity();
+    method public android.media.SessionToken2 getSessionToken();
+    method public boolean isConnected();
+    method public void pause();
+    method public void play();
+    method public void playFromMediaId(java.lang.String, android.os.Bundle);
+    method public void playFromSearch(java.lang.String, android.os.Bundle);
+    method public void playFromUri(android.net.Uri, android.os.Bundle);
+    method public void prepare();
+    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 removePlaylistItem(android.media.MediaItem2);
+    method public void rewind();
+    method public void seekTo(long);
+    method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+    method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams);
+    method public void setRating(java.lang.String, android.media.Rating2);
+    method public void setVolumeTo(int, int);
+    method public void skipToNext();
+    method public void skipToPlaylistItem(android.media.MediaItem2);
+    method public void skipToPrevious();
+    method public void stop();
+  }
+
+  public static abstract class MediaController2.ControllerCallback {
+    ctor public MediaController2.ControllerCallback();
+    method public void onAllowedCommandsChanged(android.media.MediaSession2.CommandGroup);
+    method public void onBufferedPositionChanged(long);
+    method public void onConnected(android.media.MediaSession2.CommandGroup);
+    method public void onCurrentPlaylistItemChanged(android.media.MediaItem2);
+    method public void onCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+    method public void onCustomLayoutChanged(java.util.List<android.media.MediaSession2.CommandButton>);
+    method public void onDisconnected();
+    method public void onError(int, int);
+    method public void onPlaybackInfoChanged(android.media.MediaController2.PlaybackInfo);
+    method public void onPlaybackSpeedChanged(float);
+    method public void onPlayerStateChanged(int);
+    method public void onPlaylistChanged(java.util.List<android.media.MediaItem2>);
+    method public void onPlaylistParamsChanged(android.media.MediaSession2.PlaylistParams);
+    method public void onPositionUpdated(long, long);
+  }
+
+  public static final class MediaController2.PlaybackInfo {
+    method public android.media.AudioAttributes getAudioAttributes();
+    method public int getControlType();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
   public final class MediaCrypto {
     ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
     method protected void finalize();
@@ -23713,6 +23801,69 @@
     field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
   }
 
+  public class MediaItem2 {
+    method public static android.media.MediaItem2 fromBundle(android.content.Context, android.os.Bundle);
+    method public android.media.DataSourceDesc getDataSourceDesc();
+    method public int getFlags();
+    method public java.lang.String getMediaId();
+    method public android.media.MediaMetadata2 getMetadata();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void setMetadata(android.media.MediaMetadata2);
+    method public android.os.Bundle toBundle();
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public static final class MediaItem2.Builder {
+    ctor public MediaItem2.Builder(android.content.Context, int);
+    method public android.media.MediaItem2 build();
+    method public android.media.MediaItem2.Builder setDataSourceDesc(android.media.DataSourceDesc);
+    method public android.media.MediaItem2.Builder setMediaId(java.lang.String);
+    method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata2);
+  }
+
+  public abstract class MediaLibraryService2 extends android.media.MediaSessionService2 {
+    ctor public MediaLibraryService2();
+    method public abstract android.media.MediaLibraryService2.MediaLibrarySession onCreateSession(java.lang.String);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
+  }
+
+  public static final class MediaLibraryService2.LibraryRoot {
+    ctor public MediaLibraryService2.LibraryRoot(android.content.Context, java.lang.String, android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
+  }
+
+  public static final class MediaLibraryService2.MediaLibrarySession extends android.media.MediaSession2 {
+    method public void notifyChildrenChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
+    method public void notifyChildrenChanged(java.lang.String, int, android.os.Bundle);
+    method public void notifySearchResultChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
+  }
+
+  public static final class MediaLibraryService2.MediaLibrarySession.Builder {
+    ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, android.media.MediaPlayerBase, java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
+    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 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);
+  }
+
+  public static class MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback extends android.media.MediaSession2.SessionCallback {
+    ctor public MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback(android.content.Context);
+    method public java.util.List<android.media.MediaItem2> onGetChildren(android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
+    method public android.media.MediaItem2 onGetItem(android.media.MediaSession2.ControllerInfo, java.lang.String);
+    method public android.media.MediaLibraryService2.LibraryRoot onGetLibraryRoot(android.media.MediaSession2.ControllerInfo, android.os.Bundle);
+    method public java.util.List<android.media.MediaItem2> onGetSearchResult(android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
+    method public void onSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+    method public void onSubscribe(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+    method public void onUnsubscribe(android.media.MediaSession2.ControllerInfo, java.lang.String);
+  }
+
   public final class MediaMetadata implements android.os.Parcelable {
     method public boolean containsKey(java.lang.String);
     method public int describeContents();
@@ -23768,6 +23919,79 @@
     method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
   }
 
+  public final class MediaMetadata2 {
+    method public boolean containsKey(java.lang.String);
+    method public static android.media.MediaMetadata2 fromBundle(android.content.Context, android.os.Bundle);
+    method public android.graphics.Bitmap getBitmap(java.lang.String);
+    method public android.os.Bundle getExtras();
+    method public float getFloat(java.lang.String);
+    method public long getLong(java.lang.String);
+    method public java.lang.String getMediaId();
+    method public android.media.Rating2 getRating(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.CharSequence getText(java.lang.String);
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+    method public android.os.Bundle toBundle();
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final java.lang.String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
+    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final java.lang.String METADATA_KEY_RADIO_CALLSIGN = "android.media.metadata.RADIO_CALLSIGN";
+    field public static final java.lang.String METADATA_KEY_RADIO_FREQUENCY = "android.media.metadata.RADIO_FREQUENCY";
+    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaMetadata2.Builder {
+    ctor public MediaMetadata2.Builder(android.content.Context);
+    ctor public MediaMetadata2.Builder(android.content.Context, android.media.MediaMetadata2);
+    method public android.media.MediaMetadata2 build();
+    method public android.media.MediaMetadata2.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+    method public android.media.MediaMetadata2.Builder putFloat(java.lang.String, float);
+    method public android.media.MediaMetadata2.Builder putLong(java.lang.String, long);
+    method public android.media.MediaMetadata2.Builder putRating(java.lang.String, android.media.Rating2);
+    method public android.media.MediaMetadata2.Builder putString(java.lang.String, java.lang.String);
+    method public android.media.MediaMetadata2.Builder putText(java.lang.String, java.lang.CharSequence);
+    method public android.media.MediaMetadata2.Builder setExtras(android.os.Bundle);
+  }
+
   public abstract deprecated class MediaMetadataEditor {
     method public synchronized void addEditableKey(int);
     method public abstract void apply();
@@ -24239,6 +24463,19 @@
     field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
   }
 
+  public abstract class MediaPlayerBase implements java.lang.AutoCloseable {
+    ctor public MediaPlayerBase();
+    method public abstract android.media.AudioAttributes getAudioAttributes();
+    method public abstract int getPlayerState();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void setAudioAttributes(android.media.AudioAttributes);
+    field public static final int STATE_ERROR = 0; // 0x0
+    field public static final int STATE_IDLE = 0; // 0x0
+    field public static final int STATE_PAUSED = 0; // 0x0
+    field public static final int STATE_PLAYING = 0; // 0x0
+  }
+
   public class MediaRecorder implements android.media.AudioRouting {
     ctor public MediaRecorder();
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -24515,6 +24752,172 @@
     method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
   }
 
+  public class MediaSession2 implements java.lang.AutoCloseable {
+    method public void addPlaylistItem(int, android.media.MediaItem2);
+    method public void close();
+    method public void editPlaylistItem(android.media.MediaItem2);
+    method public void fastForward();
+    method public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
+    method public android.media.MediaItem2 getCurrentPlaylistItem();
+    method public android.media.MediaPlayerBase getPlayer();
+    method public java.util.List<android.media.MediaItem2> getPlaylist();
+    method public android.media.MediaSession2.PlaylistParams getPlaylistParams();
+    method public android.media.SessionToken2 getToken();
+    method public void notifyError(int, int);
+    method public void pause();
+    method public void play();
+    method public void prepare();
+    method public void removePlaylistItem(android.media.MediaItem2);
+    method public void rewind();
+    method public void seekTo(long);
+    method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle);
+    method public void sendCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+    method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup);
+    method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List<android.media.MediaSession2.CommandButton>);
+    method public void setPlayer(android.media.MediaPlayerBase);
+    method public void setPlayer(android.media.MediaPlayerBase, android.media.VolumeProvider2);
+    method public void setPlaylist(java.util.List<android.media.MediaItem2>);
+    method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams);
+    method public void skipToNext();
+    method public void skipToPlaylistItem(android.media.MediaItem2);
+    method public void skipToPrevious();
+    method public void stop();
+    field public static final int COMMAND_CODE_BROWSER = 22; // 0x16
+    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+    field public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7; // 0x7
+    field public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2; // 0x2
+    field public static final int COMMAND_CODE_PLAYBACK_PLAY = 1; // 0x1
+    field public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6; // 0x6
+    field public static final int COMMAND_CODE_PLAYBACK_REWIND = 8; // 0x8
+    field public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9; // 0x9
+    field public static final int COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM = 10; // 0xa
+    field public static final int COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS = 11; // 0xb
+    field public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4; // 0x4
+    field public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5; // 0x5
+    field public static final int COMMAND_CODE_PLAYBACK_STOP = 3; // 0x3
+    field public static final int COMMAND_CODE_PLAYLIST_ADD = 12; // 0xc
+    field public static final int COMMAND_CODE_PLAYLIST_GET = 14; // 0xe
+    field public static final int COMMAND_CODE_PLAYLIST_REMOVE = 13; // 0xd
+    field public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 16; // 0x10
+    field public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 18; // 0x12
+    field public static final int COMMAND_CODE_PLAY_FROM_URI = 17; // 0x11
+    field public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 19; // 0x13
+    field public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 21; // 0x15
+    field public static final int COMMAND_CODE_PREPARE_FROM_URI = 20; // 0x14
+    field public static final int COMMAND_CODE_SET_VOLUME = 15; // 0xf
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SETUP_REQUIRED = 12; // 0xc
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+  }
+
+  public static final class MediaSession2.Builder {
+    ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase);
+    method public android.media.MediaSession2 build();
+    method public android.media.MediaSession2.Builder setId(java.lang.String);
+    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);
+  }
+
+  public static final class MediaSession2.Command {
+    ctor public MediaSession2.Command(android.content.Context, int);
+    ctor public MediaSession2.Command(android.content.Context, java.lang.String, android.os.Bundle);
+    method public int getCommandCode();
+    method public java.lang.String getCustomCommand();
+    method public android.os.Bundle getExtra();
+  }
+
+  public static final class MediaSession2.CommandButton {
+    method public android.media.MediaSession2.Command getCommand();
+    method public java.lang.String getDisplayName();
+    method public android.os.Bundle getExtra();
+    method public int getIconResId();
+    method public boolean isEnabled();
+  }
+
+  public static final class MediaSession2.CommandButton.Builder {
+    ctor public MediaSession2.CommandButton.Builder(android.content.Context);
+    method public android.media.MediaSession2.CommandButton build();
+    method public android.media.MediaSession2.CommandButton.Builder setCommand(android.media.MediaSession2.Command);
+    method public android.media.MediaSession2.CommandButton.Builder setDisplayName(java.lang.String);
+    method public android.media.MediaSession2.CommandButton.Builder setEnabled(boolean);
+    method public android.media.MediaSession2.CommandButton.Builder setExtra(android.os.Bundle);
+    method public android.media.MediaSession2.CommandButton.Builder setIconResId(int);
+  }
+
+  public static final class MediaSession2.CommandGroup {
+    ctor public MediaSession2.CommandGroup(android.content.Context);
+    ctor public MediaSession2.CommandGroup(android.content.Context, android.media.MediaSession2.CommandGroup);
+    method public void addAllPredefinedCommands();
+    method public void addCommand(android.media.MediaSession2.Command);
+    method public boolean hasCommand(android.media.MediaSession2.Command);
+    method public boolean hasCommand(int);
+    method public void removeCommand(android.media.MediaSession2.Command);
+  }
+
+  public static final class MediaSession2.ControllerInfo {
+    method public java.lang.String getPackageName();
+    method public int getUid();
+    method public boolean isTrusted();
+  }
+
+  public static final class MediaSession2.PlaylistParams {
+    ctor public MediaSession2.PlaylistParams(android.content.Context, int, int, android.media.MediaMetadata2);
+    method public static android.media.MediaSession2.PlaylistParams fromBundle(android.content.Context, android.os.Bundle);
+    method public android.media.MediaMetadata2 getPlaylistMetadata();
+    method public int getRepeatMode();
+    method public int getShuffleMode();
+    method public android.os.Bundle toBundle();
+    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
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+  }
+
+  public static abstract class MediaSession2.SessionCallback {
+    ctor public MediaSession2.SessionCallback(android.content.Context);
+    method public boolean onCommandRequest(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command);
+    method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2.ControllerInfo);
+    method public void onCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
+    method public void onDisconnected(android.media.MediaSession2.ControllerInfo);
+    method public void onPlayFromMediaId(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+    method public void onPlayFromSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
+    method public void onPrepareFromMediaId(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+    method public void onPrepareFromSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
+    method public void onPrepareFromUri(android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
+    method public void onSetRating(android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2);
+  }
+
+  public abstract class MediaSessionService2 extends android.app.Service {
+    ctor public MediaSessionService2();
+    method public final android.media.MediaSession2 getSession();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.media.MediaSession2 onCreateSession(java.lang.String);
+    method public android.media.MediaSessionService2.MediaNotification onUpdateNotification();
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaSessionService2";
+    field public static final java.lang.String SERVICE_META_DATA = "android.media.session";
+  }
+
+  public static class MediaSessionService2.MediaNotification {
+    ctor public MediaSessionService2.MediaNotification(android.content.Context, int, android.app.Notification);
+    method public android.app.Notification getNotification();
+    method public int getNotificationId();
+  }
+
   public final class MediaSync {
     ctor public MediaSync();
     method public android.view.Surface createInputSurface();
@@ -24645,6 +25048,29 @@
     field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
   }
 
+  public final class Rating2 {
+    method public static android.media.Rating2 fromBundle(android.content.Context, android.os.Bundle);
+    method public float getPercentRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.media.Rating2 newHeartRating(android.content.Context, boolean);
+    method public static android.media.Rating2 newPercentageRating(android.content.Context, float);
+    method public static android.media.Rating2 newStarRating(android.content.Context, int, float);
+    method public static android.media.Rating2 newThumbRating(android.content.Context, boolean);
+    method public static android.media.Rating2 newUnratedRating(android.content.Context, int);
+    method public android.os.Bundle toBundle();
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
   public deprecated class RemoteControlClient {
     ctor public RemoteControlClient(android.app.PendingIntent);
     ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
@@ -24780,6 +25206,22 @@
     field public static final int URI_COLUMN_INDEX = 2; // 0x2
   }
 
+  public final class SessionToken2 {
+    ctor public SessionToken2(android.content.Context, java.lang.String, java.lang.String);
+    method public static android.media.SessionToken2 fromBundle(android.content.Context, android.os.Bundle);
+    method public java.lang.String getId();
+    method public java.lang.String getPackageName();
+    method public int getType();
+    method public int getUid();
+    method public android.os.Bundle toBundle();
+    field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
+    field public static final int TYPE_SESSION = 0; // 0x0
+    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+  }
+
+  public static abstract class SessionToken2.TokenType implements java.lang.annotation.Annotation {
+  }
+
   public class SoundPool {
     ctor public deprecated SoundPool(int, int, int);
     method public final void autoPause();
@@ -24983,6 +25425,19 @@
     field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
   }
 
+  public abstract class VolumeProvider2 {
+    ctor public VolumeProvider2(android.content.Context, int, int, int);
+    method public final int getControlType();
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
   public final class VolumeShaper implements java.lang.AutoCloseable {
     method public void apply(android.media.VolumeShaper.Operation);
     method public void close();
@@ -25729,14 +26184,23 @@
   public final class MediaSessionManager {
     method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
     method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
+    method public void addOnSessionTokensChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
+    method public java.util.List<android.media.SessionToken2> getActiveSessionTokens();
     method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
+    method public java.util.List<android.media.SessionToken2> getAllSessionTokens();
+    method public java.util.List<android.media.SessionToken2> getSessionServiceTokens();
     method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
+    method public void removeOnSessionTokensChangedListener(android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
   }
 
   public static abstract interface MediaSessionManager.OnActiveSessionsChangedListener {
     method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
   }
 
+  public static abstract interface MediaSessionManager.OnSessionTokensChangedListener {
+    method public abstract void onSessionTokensChanged(java.util.List<android.media.SessionToken2>);
+  }
+
   public final class PlaybackState implements android.os.Parcelable {
     method public int describeContents();
     method public long getActions();
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 340be46..a7ae3234 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -23,7 +23,7 @@
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
+import android.media.MediaPlayerBase;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
@@ -277,7 +277,7 @@
      * @hide
      */
     public void setRouteAttributes(@NonNull List<String> routeCategories,
-            @Nullable MediaPlayerInterface player) {
+            @Nullable MediaPlayerBase player) {
         mProvider.setRouteAttributes_impl(routeCategories, player);
     }
 
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index 3f9a4ef..dea38a8 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -31,7 +31,6 @@
 
 /**
  * Browses media content offered by a {@link MediaLibraryService2}.
- * @hide
  */
 public class MediaBrowser2 extends MediaController2 {
     // Equals to the ((MediaBrowser2Provider) getProvider())
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 6682e08..0114240 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -19,13 +19,13 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.ErrorCode;
 import android.media.MediaSession2.PlaylistParams;
 import android.media.session.MediaSessionManager;
 import android.media.update.ApiLoader;
@@ -64,9 +64,8 @@
  * <p>
  * @see MediaSession2
  * @see MediaSessionService2
- * @hide
  */
-public class MediaController2 implements AutoCloseable {
+public class MediaController2 implements AutoCloseable, MediaPlaylistController {
     /**
      * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
      * active if and only if it has set a player.
@@ -126,19 +125,69 @@
 
         /**
          * Called when the playlist is changed.
+         * <p>
+         * When it's called, you should invalidate previous playback information such as position,
+         * player state, current item, etc.
          *
          * @param playlist A new playlist set by the session.
          */
+        // TODO(jaewan): Enhance doc
         public void onPlaylistChanged(@NonNull List<MediaItem2> playlist) { }
 
         /**
          * Called when the playback state is changed.
          *
          * @param state latest playback state
+         * @hide
          */
+        // TODo(jaewan): Remove
         public void onPlaybackStateChanged(@NonNull PlaybackState2 state) { }
 
         /**
+         * Called when the player state is changed.
+         *
+         * @param state
+         */
+        public void onPlayerStateChanged(int state) { }
+
+        /**
+         * Called when the player's position is changed
+         *
+         * @param updateTimeMs timestamp when the position information is sent from the session
+         * @param positionMs position in millis
+         */
+        public void onPositionUpdated(long updateTimeMs, long positionMs) { }
+
+        /**
+         * Called when playback speed is changed.
+         *
+         * @param speed speed
+         */
+        public void onPlaybackSpeedChanged(float speed) { }
+
+        /**
+         * Called when the player's buffering position
+         *
+         * @param positionMs buffering position in millis
+         */
+        public void onBufferedPositionChanged(long positionMs) { }
+
+        /**
+         * Called when a error from
+         *
+         * @param errorCode error code
+         * @param extra extra information
+         */
+        public void onError(@ErrorCode int errorCode, int extra) { }
+
+        /**
+         * Called when the player's current playing item is changed
+         *
+         * @param item new item
+         */
+        public void onCurrentPlaylistItemChanged(MediaItem2 item) { }
+
+        /**
          * Called when the playlist parameters are changed.
          *
          * @param params The new play list parameters.
@@ -166,7 +215,6 @@
         /**
          * @hide
          */
-        @SystemApi
         public PlaybackInfo(PlaybackInfoProvider provider) {
             mProvider = provider;
         }
@@ -174,7 +222,6 @@
         /**
          * @hide
          */
-        @SystemApi
         public PlaybackInfoProvider getProvider() {
             return mProvider;
         }
@@ -281,7 +328,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public MediaController2Provider getProvider() {
         return mProvider;
     }
@@ -325,7 +371,7 @@
      * 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
      * start of the playback. Once the preparation is done, the session will change its playback
-     * state to {@link PlaybackState2#STATE_PAUSED}. Afterwards, {@link #play} can be called to
+     * state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards, {@link #play} can be called to
      * start playback.
      */
     public void prepare() {
@@ -360,12 +406,13 @@
     /**
      * Sets the index of current DataSourceDesc in the play list to be played.
      *
-     * @param index the index of DataSourceDesc in the play list you want to play
+     * @param item the index of DataSourceDesc in the play list you want to play
      * @throws IllegalArgumentException if the play list is null
      * @throws NullPointerException if index is outside play list range
      */
-    public void setCurrentPlaylistItem(int index) {
-        mProvider.setCurrentPlaylistItem_impl(index);
+    @Override
+    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
+        mProvider.skipToPlaylistItem_impl(item);
     }
 
     /**
@@ -375,7 +422,7 @@
      * @param params A {@link PlaylistParams} object to set.
      * @throws IllegalArgumentException if given {@param param} is null.
      */
-    public void setPlaylistParams(PlaylistParams params) {
+    public void setPlaylistParams(@NonNull PlaylistParams params) {
         mProvider.setPlaylistParams_impl(params);
     }
 
@@ -428,12 +475,11 @@
         mProvider.playFromUri_impl(uri, extras);
     }
 
-
     /**
      * Request that the player prepare playback for a specific media id. In other words, other
      * sessions can continue to play during the preparation of this session. This method can be
      * used to speed up the start of the playback. Once the preparation is done, the session
-     * will change its playback state to {@link PlaybackState2#STATE_PAUSED}. Afterwards,
+     * will change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
      * {@link #play} can be called to start playback. If the preparation is not needed,
      * {@link #playFromMediaId} can be directly called without this method.
      *
@@ -450,7 +496,7 @@
      * query should be treated as a request to prepare any music. In other words, other sessions
      * can continue to play during the preparation of this session. This method can be used to
      * speed up the start of the playback. Once the preparation is done, the session will
-     * change its playback state to {@link PlaybackState2#STATE_PAUSED}. Afterwards,
+     * change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
      * {@link #play} can be called to start playback. If the preparation is not needed,
      * {@link #playFromSearch} can be directly called without this method.
      *
@@ -466,7 +512,7 @@
      * Request that the player prepare playback for a specific {@link Uri}. In other words,
      * other sessions can continue to play during the preparation of this session. This method
      * can be used to speed up the start of the playback. Once the preparation is done, the
-     * session will change its playback state to {@link PlaybackState2#STATE_PAUSED}. Afterwards,
+     * session will change its playback state to {@link MediaPlayerBase#STATE_PAUSED}. Afterwards,
      * {@link #play} can be called to start playback. If the preparation is not needed,
      * {@link #playFromUri} can be directly called without this method.
      *
@@ -537,12 +583,65 @@
      * playback state.
      *
      * @return a playback state. Can be {@code null}
+     * @hide
      */
     public @Nullable PlaybackState2 getPlaybackState() {
         return mProvider.getPlaybackState_impl();
     }
 
     /**
+     * Get the lastly cached player state from {@link ControllerCallback#onPlayerStateChanged(int)}.
+     *
+     * @return player state
+     */
+    public int getPlayerState() {
+        return mProvider.getPlayerState_impl();
+    }
+
+    /**
+     * Get the lastly cached position from {@link ControllerCallback#onPositionUpdated(long, long)}.
+     * <p>
+     * This returns the calculated value of the position, based on the difference between the
+     * update time and current time.
+     *
+     * @return position
+     */
+    public long getPosition() {
+        return mProvider.getPosition_impl();
+    }
+
+    /**
+     * Get the lastly cached playback speed from
+     * {@link ControllerCallback#onPlaybackSpeedChanged(float)}.
+     *
+     * @return speed
+     */
+    public float getPlaybackSpeed() {
+        return mProvider.getPlaybackSpeed_impl();
+    }
+
+    /**
+     * Get the lastly cached buffered position from
+     * {@link ControllerCallback#onBufferedPositionChanged(long)}.
+     *
+     * @return buffering position in millis
+     */
+    public long getBufferedPosition() {
+        return mProvider.getBufferedPosition_impl();
+    }
+
+    /**
+     * Get the lastly cached current item from
+     * {@link ControllerCallback#onCurrentPlaylistItemChanged(MediaItem2)}.
+     *
+     * @return index of the current item
+     */
+    @Override
+    public MediaItem2 getCurrentPlaylistItem() {
+        return mProvider.getCurrentPlaylistItem_impl();
+    }
+
+    /**
      * Get the current playback info for this session.
      *
      * @return The current playback info or null.
@@ -584,6 +683,7 @@
      *
      * @return playlist. Can be {@code null} if the controller doesn't have enough permission.
      */
+    @Override
     public @Nullable List<MediaItem2> getPlaylist() {
         return mProvider.getPlaylist_impl();
     }
@@ -603,13 +703,11 @@
      * If index is same as the current index of the playlist, current playback
      * will be stopped and playback moves to next source in the list.
      *
-     * @return the removed DataSourceDesc at index in the play list
      * @throws IllegalArgumentException if the play list is null
      * @throws IndexOutOfBoundsException if index is outside play list range
      */
-    // TODO(jaewan): Remove with index was previously rejected by council (b/36524925)
-    // TODO(jaewan): Should we also add movePlaylistItem from index to index?
-    public void removePlaylistItem(MediaItem2 item) {
+    @Override
+    public void removePlaylistItem(@NonNull MediaItem2 item) {
         mProvider.removePlaylistItem_impl(item);
     }
 
@@ -620,12 +718,12 @@
      * If index is less than or equal to the current index of the play list,
      * the current index of the play list will be incremented correspondingly.
      *
-     * @param index the index you want to add dsd to the play list
-     * @param item the media item you want to add to the play list
+     * @param index the index you want to add
+     * @param item the media item you want to add
      * @throws IndexOutOfBoundsException if index is outside play list range
-     * @throws NullPointerException if dsd is null
      */
-    public void addPlaylistItem(int index, MediaItem2 item) {
+    @Override
+    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.addPlaylistItem_impl(index, item);
     }
 }
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 667aac1..b7b75e4 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.MediaItem2Provider;
@@ -35,7 +34,6 @@
  * When it's sent to a controller or browser, it's anonymized and data descriptor wouldn't be sent.
  * <p>
  * This object isn't a thread safe.
- * @hide
  */
 public class MediaItem2 {
     /** @hide */
@@ -64,7 +62,9 @@
      * @param mediaId id of this item. It must be unique whithin this app
      * @param metadata metadata with the media id.
      * @param flags The flags for this item.
+     * @hide
      */
+    // TODO(jaewan): Remove this
     public MediaItem2(@NonNull Context context, @NonNull String mediaId,
             @NonNull DataSourceDesc dsd, @Nullable MediaMetadata2 metadata,
             @Flags int flags) {
@@ -76,7 +76,6 @@
      * Create a new media item
      * @hide
      */
-    @SystemApi
     public MediaItem2(MediaItem2Provider provider) {
         mProvider = provider;
     }
@@ -156,4 +155,87 @@
     public @Nullable DataSourceDesc getDataSourceDesc() {
         return mProvider.getDataSourceDesc_impl();
     }
+
+    /**
+     * Build {@link MediaItem2}
+     */
+    // TODO(jaewan): Move it to updatable
+    public static final class Builder {
+        private Context mContext;
+        private @Flags int mFlags;
+        private String mMediaId;
+        private MediaMetadata2 mMetadata;
+        private DataSourceDesc mDataSourceDesc;
+
+        /**
+         * Constructor for {@link Builder}
+         *
+         * @param context
+         * @param flags
+         */
+        public Builder(@NonNull Context context, @Flags int flags) {
+            mContext = context;
+            mFlags = flags;
+        }
+
+        /**
+         * Set the media id of this instance. {@code null} for unset.
+         * <p>
+         * Media id is used to identify a media contents between session and controller.
+         * <p>
+         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
+         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
+         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
+         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
+         *
+         * @param mediaId media id
+         * @return this instance for chaining
+         */
+        public Builder setMediaId(@Nullable String mediaId) {
+            mMediaId = mediaId;
+            return this;
+        }
+
+        /**
+         * Set the metadata of this instance. {@code null} for unset.
+         * <p>
+         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
+         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
+         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
+         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
+         *
+         * @param metadata metadata
+         * @return this instance for chaining
+         */
+        public Builder setMetadata(@Nullable MediaMetadata2 metadata) {
+            mMetadata = metadata;
+            return this;
+        }
+
+        /**
+         * 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(@Nullable DataSourceDesc dataSourceDesc) {
+            mDataSourceDesc = dataSourceDesc;
+            return this;
+        }
+
+        /**
+         * Build {@link MediaItem2}.
+         *
+         * @return a new {@link MediaItem2}.
+         */
+        public MediaItem2 build() {
+            String id = (mMetadata != null)
+                    ? mMetadata.getString(MediaMetadata2.METADATA_KEY_MEDIA_ID) : null;
+            if (id == null) {
+                //  TODO(jaewan): Double check if its sufficient (e.g. Use UUID instead?)
+                id = (mMediaId != null) ? mMediaId : toString();
+            }
+            return new MediaItem2(mContext, id, mDataSourceDesc, mMetadata, mFlags);
+        }
+    }
 }
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 11b745a..768d044 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -19,10 +19,10 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.media.MediaSession2.BuilderBase;
+import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
 import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
@@ -40,7 +40,7 @@
  * and ask the application to start playing it. They may also be used to control content that
  * is already playing by way of a {@link MediaSession2}.
  * <p>
- * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * When extending this class, also add the following to your {@code AndroidManifest.xml}.
  * <pre>
  * &lt;service android:name="component_name_of_your_implementation" &gt;
  *   &lt;intent-filter&gt;
@@ -48,11 +48,12 @@
  *   &lt;/intent-filter&gt;
  * &lt;/service&gt;</pre>
  * <p>
- * A {@link MediaLibraryService2} is extension of {@link MediaSessionService2}. IDs shouldn't
+ * The {@link MediaLibraryService2} class derives from {@link MediaSessionService2}. IDs shouldn't
  * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
  * default, an empty string will be used for ID of the service. If you want to specify an ID,
  * declare metadata in the manifest as follows.
- * @hide
+ *
+ * @see MediaSessionService2
  */
 public abstract class MediaLibraryService2 extends MediaSessionService2 {
     /**
@@ -63,15 +64,178 @@
 
     /**
      * Session for the {@link MediaLibraryService2}. Build this object with
-     * {@link MediaLibrarySessionBuilder} and return in {@link #onCreateSession(String)}.
+     * {@link Builder} and return in {@link #onCreateSession(String)}.
      */
-    public static class MediaLibrarySession extends MediaSession2 {
+    public static final class MediaLibrarySession extends MediaSession2 {
         private final MediaLibrarySessionProvider mProvider;
 
         /**
+         * Callback for the {@link MediaLibrarySession}.
+         */
+        public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
+            public MediaLibrarySessionCallback(Context context) {
+                super(context);
+            }
+
+            /**
+             * Called to get the root information for browsing by a particular client.
+             * <p>
+             * The implementation should verify that the client package has permission
+             * to access browse media information before returning the root id; it
+             * should return null if the client is not allowed to access this
+             * information.
+             *
+             * @param controllerInfo information of the controller requesting access to browse media.
+             * @param rootHints An optional bundle of service-specific arguments to send
+             * to the media library service when connecting and retrieving the
+             * root id for browsing, or null if none. The contents of this
+             * bundle may affect the information returned when browsing.
+             * @return The {@link LibraryRoot} for accessing this app's content or null.
+             * @see LibraryRoot#EXTRA_RECENT
+             * @see LibraryRoot#EXTRA_OFFLINE
+             * @see LibraryRoot#EXTRA_SUGGESTED
+             */
+            public @Nullable LibraryRoot onGetLibraryRoot(@NonNull ControllerInfo controllerInfo,
+                    @Nullable Bundle rootHints) {
+                return null;
+            }
+
+            /**
+             * Called to get an item. Return result here for the browser.
+             * <p>
+             * Return {@code null} for no result or error.
+             *
+             * @param mediaId item id to get media item.
+             * @return a media item. {@code null} for no result or error.
+             */
+            public @Nullable MediaItem2 onGetItem(@NonNull ControllerInfo controllerInfo,
+                    @NonNull String mediaId) {
+                return null;
+            }
+
+            /**
+             * Called to get children of given parent id. Return the children here for the browser.
+             * <p>
+             * Return an empty list for no children, and return {@code null} for the error.
+             *
+             * @param parentId parent id to get children
+             * @param page number of page
+             * @param pageSize size of the page
+             * @param extras extra bundle
+             * @return list of children. Can be {@code null}.
+             */
+            public @Nullable List<MediaItem2> onGetChildren(@NonNull ControllerInfo controller,
+                    @NonNull String parentId, int page, int pageSize, @Nullable Bundle extras) {
+                return null;
+            }
+
+            /**
+             * Called when a controller subscribes to the parent.
+             * <p>
+             * It's your responsibility to keep subscriptions by your own and call
+             * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)}
+             * when the parent is changed.
+             *
+             * @param controller controller
+             * @param parentId parent id
+             * @param extras extra bundle
+             */
+            public void onSubscribe(@NonNull ControllerInfo controller, @NonNull String parentId,
+                    @Nullable Bundle extras) {
+            }
+
+            /**
+             * Called when a controller unsubscribes to the parent.
+             *
+             * @param controller controller
+             * @param parentId parent id
+             */
+            public void onUnsubscribe(@NonNull ControllerInfo controller,
+                    @NonNull String parentId) {
+            }
+
+            /**
+             * Called when a controller requests search.
+             *
+             * @param query The search query sent from the media browser. It contains keywords
+             *              separated by space.
+             * @param extras The bundle of service-specific arguments sent from the media browser.
+             */
+            public void onSearch(@NonNull ControllerInfo controllerInfo, @NonNull String query,
+                    @Nullable Bundle extras) {
+            }
+
+            /**
+             * Called to get the search result. Return search result here for the browser which has
+             * requested search previously.
+             * <p>
+             * Return an empty list for no search result, and return {@code null} for the error.
+             *
+             * @param controllerInfo Information of the controller requesting the search result.
+             * @param query The search query which was previously sent through
+             *              {@link #onSearch(ControllerInfo, String, Bundle)} call.
+             * @param page page number. Starts from {@code 1}.
+             * @param pageSize page size. Should be greater or equal to {@code 1}.
+             * @param extras The bundle of service-specific arguments sent from the media browser.
+             * @return search result. {@code null} for error.
+             */
+            public @Nullable List<MediaItem2> onGetSearchResult(
+                    @NonNull ControllerInfo controllerInfo, @NonNull String query, int page,
+                    int pageSize, @Nullable Bundle extras) {
+                return null;
+            }
+        }
+
+        /**
+         * Builder for {@link MediaLibrarySession}.
+         */
+        // Override all methods just to show them with the type instead of generics in Javadoc.
+        // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
+        public static final class Builder extends BuilderBase<MediaLibrarySession, Builder,
+                MediaLibrarySessionCallback> {
+            // Builder requires MediaLibraryService2 instead of Context just to ensure that the
+            // builder can be only instantiated within the MediaLibraryService2.
+            // Ideally it's better to make it inner class of service to enforce, it violates API
+            // guideline that Builders should be the inner class of the building target.
+            public Builder(@NonNull MediaLibraryService2 service,
+                    @NonNull MediaPlayerBase player,
+                    @NonNull @CallbackExecutor Executor callbackExecutor,
+                    @NonNull MediaLibrarySessionCallback callback) {
+                super((instance) -> ApiLoader.getProvider(service)
+                        .createMediaLibraryService2Builder(service, (Builder) instance, player,
+                                callbackExecutor, callback));
+            }
+
+            @Override
+            public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+                return super.setVolumeProvider(volumeProvider);
+            }
+
+            @Override
+            public Builder setSessionActivity(@Nullable PendingIntent pi) {
+                return super.setSessionActivity(pi);
+            }
+
+            @Override
+            public Builder setId(String id) {
+                return super.setId(id);
+            }
+
+            @Override
+            public Builder setSessionCallback(@NonNull Executor executor,
+                    @NonNull MediaLibrarySessionCallback callback) {
+                return super.setSessionCallback(executor, callback);
+            }
+
+            @Override
+            public MediaLibrarySession build() {
+                return super.build();
+            }
+        }
+
+        /**
          * @hide
          */
-        @SystemApi
         public MediaLibrarySession(MediaLibrarySessionProvider provider) {
             super(provider);
             mProvider = provider;
@@ -124,166 +288,6 @@
         }
     }
 
-    /**
-     * Callback for the {@link MediaLibrarySession}.
-     */
-    public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
-
-        public MediaLibrarySessionCallback(Context context) {
-            super(context);
-        }
-
-        /**
-         * Called to get the root information for browsing by a particular client.
-         * <p>
-         * The implementation should verify that the client package has permission
-         * to access browse media information before returning the root id; it
-         * should return null if the client is not allowed to access this
-         * information.
-         *
-         * @param controllerInfo information of the controller requesting access to browse media.
-         * @param rootHints An optional bundle of service-specific arguments to send
-         * to the media library service when connecting and retrieving the
-         * root id for browsing, or null if none. The contents of this
-         * bundle may affect the information returned when browsing.
-         * @return The {@link LibraryRoot} for accessing this app's content or null.
-         * @see LibraryRoot#EXTRA_RECENT
-         * @see LibraryRoot#EXTRA_OFFLINE
-         * @see LibraryRoot#EXTRA_SUGGESTED
-         */
-        public @Nullable LibraryRoot onGetLibraryRoot(@NonNull ControllerInfo controllerInfo,
-                @Nullable Bundle rootHints) {
-            return null;
-        }
-
-        /**
-         * Called to get an item. Return result here for the browser.
-         * <p>
-         * Return {@code null} for no result or error.
-         *
-         * @param mediaId item id to get media item.
-         * @return a media item. {@code null} for no result or error.
-         */
-        public @Nullable MediaItem2 onGetItem(@NonNull ControllerInfo controllerInfo,
-                @NonNull String mediaId) {
-            return null;
-        }
-
-        /**
-         * Called to get children of given parent id. Return the children here for the browser.
-         * <p>
-         * Return an empty list for no children, and return {@code null} for the error.
-         *
-         * @param parentId parent id to get children
-         * @param page number of page
-         * @param pageSize size of the page
-         * @param extras extra bundle
-         * @return list of children. Can be {@code null}.
-         */
-        public @Nullable List<MediaItem2> onGetChildren(@NonNull ControllerInfo controller,
-                @NonNull String parentId, int page, int pageSize, @Nullable Bundle extras) {
-            return null;
-        }
-
-        /**
-         * Called when a controller subscribes to the parent.
-         * <p>
-         * It's your responsibility to keep subscriptions by your own and call
-         * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)}
-         * when the parent is changed.
-         *
-         * @param controller controller
-         * @param parentId parent id
-         * @param extras extra bundle
-         */
-        public void onSubscribe(@NonNull ControllerInfo controller, @NonNull String parentId,
-                @Nullable Bundle extras) {
-        }
-
-        /**
-         * Called when a controller unsubscribes to the parent.
-         *
-         * @param controller controller
-         * @param parentId parent id
-         */
-        public void onUnsubscribe(@NonNull ControllerInfo controller, @NonNull String parentId) {
-        }
-
-        /**
-         * Called when a controller requests search.
-         *
-         * @param query The search query sent from the media browser. It contains keywords separated
-         *              by space.
-         * @param extras The bundle of service-specific arguments sent from the media browser.
-         */
-        public void onSearch(@NonNull ControllerInfo controllerInfo, @NonNull String query,
-                @Nullable Bundle extras) {
-        }
-
-        /**
-         * Called to get the search result. Return search result here for the browser which has
-         * requested search previously.
-         * <p>
-         * Return an empty list for no search result, and return {@code null} for the error.
-         *
-         * @param controllerInfo Information of the controller requesting the search result.
-         * @param query The search query which was previously sent through
-         *              {@link #onSearch(ControllerInfo, String, Bundle)} call.
-         * @param page page number. Starts from {@code 1}.
-         * @param pageSize page size. Should be greater or equal to {@code 1}.
-         * @param extras The bundle of service-specific arguments sent from the media browser.
-         * @return search result. {@code null} for error.
-         */
-        public @Nullable List<MediaItem2> onGetSearchResult(@NonNull ControllerInfo controllerInfo,
-                @NonNull String query, int page, int pageSize, @Nullable Bundle extras) {
-            return null;
-        }
-    }
-
-    /**
-     * Builder for {@link MediaLibrarySession}.
-     */
-    // Override all methods just to show them with the type instead of generics in Javadoc.
-    // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
-    public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySession,
-            MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
-        public MediaLibrarySessionBuilder(
-                @NonNull Context context, @NonNull MediaPlayerInterface player,
-                @NonNull @CallbackExecutor Executor callbackExecutor,
-                @NonNull MediaLibrarySessionCallback callback) {
-            super((instance) -> ApiLoader.getProvider(context).createMediaLibraryService2Builder(
-                    context, (MediaLibrarySessionBuilder) instance, player, callbackExecutor,
-                    callback));
-        }
-
-        @Override
-        public MediaLibrarySessionBuilder setVolumeProvider(
-                @Nullable VolumeProvider2 volumeProvider) {
-            return super.setVolumeProvider(volumeProvider);
-        }
-
-        @Override
-        public MediaLibrarySessionBuilder setSessionActivity(@Nullable PendingIntent pi) {
-            return super.setSessionActivity(pi);
-        }
-
-        @Override
-        public MediaLibrarySessionBuilder setId(String id) {
-            return super.setId(id);
-        }
-
-        @Override
-        public MediaLibrarySessionBuilder setSessionCallback(
-                @NonNull Executor executor, @NonNull MediaLibrarySessionCallback callback) {
-            return super.setSessionCallback(executor, callback);
-        }
-
-        @Override
-        public MediaLibrarySession build() {
-            return super.build();
-        }
-    }
-
     @Override
     MediaSessionService2Provider createProvider() {
         return ApiLoader.getProvider(this).createMediaLibraryService2(this);
@@ -302,7 +306,7 @@
      *
      * @param sessionId session id written in the AndroidManifest.xml.
      * @return a new library session
-     * @see MediaLibrarySessionBuilder
+     * @see Builder
      * @see #getSession()
      * @throws RuntimeException if returned session is invalid
      */
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 1f856bc..b363831 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.media.update.ApiLoader;
@@ -33,15 +32,13 @@
 
 /**
  * Contains metadata about an item, such as the title, artist, etc.
- *
- * @hide
  */
+// New version of MediaMetadata with following changes
+//   - Don't implement Parcelable for updatable support.
+//   - Also support MediaDescription features. MediaDescription is deprecated instead because
+//     it was insufficient for controller to display media contents.
+// TODO(jaewan): Add @see for APIs from MediaDescription
 public final class MediaMetadata2 {
-    // New version of MediaMetadata that no longer implements Parcelable but added from/toBundle()
-    // for updatable.
-    // MediaDescription is deprecated because it was insufficient for controller to display media
-    // contents. Added getExtra() here to support all the features from the MediaDescription.
-
     /**
      * The title of the media.
      */
@@ -389,7 +386,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public MediaMetadata2(MediaMetadata2Provider provider) {
         mProvider = provider;
     }
@@ -568,7 +564,6 @@
         /**
          * @hide
          */
-        @SystemApi
         public Builder(@NonNull MediaMetadata2Provider.BuilderProvider provider) {
             mProvider = provider;
         }
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
new file mode 100644
index 0000000..3181362
--- /dev/null
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -0,0 +1,247 @@
+/*
+ * 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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Base class for all media players that want media session.
+ */
+public abstract class MediaPlayerBase implements AutoCloseable {
+    /**
+     * @hide
+     */
+    @IntDef({STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_ERROR})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
+    /**
+     * State when the player is idle, and needs configuration to start playback.
+     */
+    public static final int STATE_IDLE = 0;
+
+    /**
+     * State when the player's playback is paused
+     */
+    public static final int STATE_PAUSED = 0;
+
+    /**
+     * State when the player's playback is ongoing
+     */
+    public static final int STATE_PLAYING = 0;
+
+    /**
+     * State when the player is in error state and cannot be recovered self.
+     */
+    public static final int STATE_ERROR = 0;
+
+    /**
+     * Unspecified media player error.
+     * @hide
+     */
+    public static final int MEDIA_ERROR_UNKNOWN = MediaPlayer2.MEDIA_ERROR_UNKNOWN;
+
+    /**
+     * The video is streamed and its container is not valid for progressive
+     * playback i.e the video's index (e.g moov atom) is not at the start of the
+     * file.
+     * @hide
+     */
+    public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK =
+            MediaPlayer2.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
+
+    /**
+     * File or network related operation errors.
+     * @hide
+     */
+    public static final int MEDIA_ERROR_IO = MediaPlayer2.MEDIA_ERROR_IO;
+
+    /**
+     * Bitstream is not conforming to the related coding standard or file spec.
+     * @hide
+     */
+    public static final int MEDIA_ERROR_MALFORMED = MediaPlayer2.MEDIA_ERROR_MALFORMED;
+
+    /**
+     * Bitstream is conforming to the related coding standard or file spec, but
+     * the media framework does not support the feature.
+     * @hide
+     */
+    public static final int MEDIA_ERROR_UNSUPPORTED = MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
+
+    /**
+     * Some operation takes too long to complete, usually more than 3-5 seconds.
+     * @hide
+     */
+    public static final int MEDIA_ERROR_TIMED_OUT = MediaPlayer2.MEDIA_ERROR_TIMED_OUT;
+
+    /**
+     * Callbacks to listens to the changes in {@link PlaybackState2} and error.
+     * @hide
+     */
+    public static abstract class EventCallback {
+        /**
+         * Called when {@link PlaybackState2} for this player is changed.
+         */
+        public void onPlaybackStateChanged(PlaybackState2 state) { }
+
+        /**
+         * Called to indicate an error.
+         *
+         * @param mediaId optional mediaId to indicate error
+         * @param what what
+         * @param extra
+         */
+        public void onError(@Nullable String mediaId, int what, int extra) { }
+    }
+
+    // Transport controls that session will send command directly to this player.
+    /**
+     * Start or resumes playback
+     */
+    public abstract void play();
+
+    /**
+     * @hide
+     */
+    public abstract void prepare();
+
+    /**
+     * Pause playback
+     */
+    public abstract void pause();
+
+    /**
+     * @hide
+     */
+    public abstract void stop();
+
+    /**
+     * @hide
+     */
+    public abstract void skipToPrevious();
+
+    /**
+     * @hide
+     */
+    public abstract void skipToNext();
+
+    /**
+     * @hide
+     */
+    public abstract void seekTo(long pos);
+
+    /**
+     * @hide
+     */
+    public abstract void fastForward();
+
+    /**
+     * @hide
+     */
+    public abstract void rewind();
+
+    /**
+     * @hide
+     */
+    public abstract PlaybackState2 getPlaybackState();
+
+    /**
+     * Return player state.
+     *
+     * @return player state
+     * @see #STATE_IDLE
+     * @see #STATE_PLAYING
+     * @see #STATE_PAUSED
+     * @see #STATE_ERROR
+     */
+    public abstract @State int getPlayerState();
+
+    /**
+     * Sets the {@link AudioAttributes} to be used during the playback of the media.
+     *
+     * @param attributes non-null <code>AudioAttributes</code>.
+     */
+    public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
+
+    /**
+     * Returns AudioAttributes that media player has.
+     */
+    public abstract @Nullable AudioAttributes getAudioAttributes();
+
+    /**
+     * @hide
+     */
+    public abstract void addPlaylistItem(int index, MediaItem2 item);
+
+    /**
+     * @hide
+     */
+    public abstract void removePlaylistItem(MediaItem2 item);
+
+    /**
+     * @hide
+     */
+    public abstract void setPlaylist(List<MediaItem2> playlist);
+
+    /**
+     * @hide
+     */
+    public abstract List<MediaItem2> getPlaylist();
+
+    /**
+     * @hide
+     */
+    public abstract void setCurrentPlaylistItem(MediaItem2 item);
+
+    /**
+     * @hide
+     */
+    public abstract void setPlaylistParams(PlaylistParams params);
+
+    /**
+     * @hide
+     */
+    public abstract PlaylistParams getPlaylistParams();
+
+    /**
+     * Register a {@link EventCallback}.
+     *
+     * @param executor a callback executor
+     * @param callback a EventCallback
+     * @hide
+     */
+    public abstract void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull EventCallback callback);
+
+    /**
+     * Unregister previously registered {@link EventCallback}.
+     *
+     * @param callback a EventCallback
+     * @hide
+     */
+    public abstract void unregisterEventCallback(@NonNull EventCallback callback);
+}
diff --git a/media/java/android/media/MediaPlayerInterface.java b/media/java/android/media/MediaPlayerInterface.java
deleted file mode 100644
index b81c3d6..0000000
--- a/media/java/android/media/MediaPlayerInterface.java
+++ /dev/null
@@ -1,136 +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.NonNull;
-import android.annotation.Nullable;
-import android.media.MediaSession2.PlaylistParams;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Base interfaces for all media players that want media session.
- * @hide
- */
-public interface MediaPlayerInterface {
-    /**
-     * Unspecified media player error.
-     */
-    int MEDIA_ERROR_UNKNOWN = MediaPlayer2.MEDIA_ERROR_UNKNOWN;
-
-    /**
-     * The video is streamed and its container is not valid for progressive
-     * playback i.e the video's index (e.g moov atom) is not at the start of the
-     * file.
-     */
-    int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK =
-            MediaPlayer2.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
-
-    /**
-     * File or network related operation errors.
-     */
-    int MEDIA_ERROR_IO = MediaPlayer2.MEDIA_ERROR_IO;
-
-    /**
-     * Bitstream is not conforming to the related coding standard or file spec.
-     */
-    int MEDIA_ERROR_MALFORMED = MediaPlayer2.MEDIA_ERROR_MALFORMED;
-
-    /**
-     * Bitstream is conforming to the related coding standard or file spec, but
-     * the media framework does not support the feature.
-     */
-    int MEDIA_ERROR_UNSUPPORTED = MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
-    /**
-     * Some operation takes too long to complete, usually more than 3-5 seconds.
-     */
-    int MEDIA_ERROR_TIMED_OUT = MediaPlayer2.MEDIA_ERROR_TIMED_OUT;
-
-    /**
-     * Callbacks to listens to the changes in {@link PlaybackState2} and error.
-     */
-    interface EventCallback {
-        /**
-         * Called when {@link PlaybackState2} for this player is changed.
-         */
-        default void onPlaybackStateChanged(PlaybackState2 state) { }
-
-        /**
-         * Called to indicate an error.
-         *
-         * @param mediaId optional mediaId to indicate error
-         * @param what what
-         * @param extra
-         */
-        default void onError(@Nullable String mediaId, int what, int extra) { }
-    }
-
-    // Transport controls that session will send command directly to this player.
-    void play();
-    void prepare();
-    void pause();
-    void stop();
-    void skipToPrevious();
-    void skipToNext();
-    void seekTo(long pos);
-    void fastForward();
-    void rewind();
-
-    PlaybackState2 getPlaybackState();
-
-    /**
-     * Sets the {@link AudioAttributes} to be used during the playback of the media.
-     *
-     * @param attributes non-null <code>AudioAttributes</code>.
-     */
-    void setAudioAttributes(@NonNull AudioAttributes attributes);
-
-    /**
-     * Returns AudioAttributes that media player has.
-     */
-    @Nullable
-    AudioAttributes getAudioAttributes();
-
-    void addPlaylistItem(int index, MediaItem2 item);
-    void removePlaylistItem(MediaItem2 item);
-
-    void setPlaylist(List<MediaItem2> playlist);
-    List<MediaItem2> getPlaylist();
-
-    void setCurrentPlaylistItem(int index);
-    void setPlaylistParams(PlaylistParams params);
-    PlaylistParams getPlaylistParams();
-
-    /**
-     * Register a {@link EventCallback}.
-     *
-     * @param executor a callback executor
-     * @param callback a EventCallback
-     */
-    void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull EventCallback callback);
-
-    /**
-     * Unregister previously registered {@link EventCallback}.
-     *
-     * @param callback a EventCallback
-     */
-    void unregisterEventCallback(@NonNull EventCallback callback);
-}
diff --git a/media/java/android/media/MediaPlaylistController.java b/media/java/android/media/MediaPlaylistController.java
new file mode 100644
index 0000000..916c12a
--- /dev/null
+++ b/media/java/android/media/MediaPlaylistController.java
@@ -0,0 +1,41 @@
+/*
+ * 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.NonNull;
+
+import java.util.List;
+
+/**
+ * Controller interfaces for playlist management for both {@link MediaSession2} and
+ * {@link MediaController2} that related with metadata. This ensures that two classes share the same
+ * interface.
+ * <p>
+ * This class only includes methods that involves {@link MediaItem2}. Because other APIs are
+ * considered as the part of {@link MediaPlayerBase} (e.g. set/getPlaylistParams()}. Note that
+ * setPlaylist() isn't added on purpose because it's considered as session specific.
+ *
+ * @hide
+ */
+public interface MediaPlaylistController {
+    // TODO(jaewan): is Index correct here?
+    void addPlaylistItem(int index, @NonNull MediaItem2 item);
+    void removePlaylistItem(@NonNull MediaItem2 item);
+    MediaItem2 getCurrentPlaylistItem();
+    void skipToPlaylistItem(@NonNull MediaItem2 item);
+    List<MediaItem2> getPlaylist();
+}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 63e4e65..54b1f0e 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -20,11 +20,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.media.MediaPlayerInterface.EventCallback;
+import android.media.MediaPlayerBase.EventCallback;
 import android.media.session.MediaSession;
 import android.media.session.MediaSession.Callback;
 import android.media.session.PlaybackState;
@@ -70,7 +69,7 @@
  * <p>
  * When a session receive transport control commands, the session sends the commands directly to
  * the the underlying media player set by {@link Builder} or
- * {@link #setPlayer(MediaPlayerInterface)}.
+ * {@link #setPlayer(MediaPlayerBase)}.
  * <p>
  * When an app is finished performing playback it must call {@link #close()} to clean up the session
  * and notify any controllers.
@@ -78,9 +77,8 @@
  * {@link MediaSession2} objects should be used on the thread on the looper.
  *
  * @see MediaSessionService2
- * @hide
  */
-public class MediaSession2 implements AutoCloseable {
+public class MediaSession2 implements AutoCloseable, MediaPlaylistController {
     private final MediaSession2Provider mProvider;
 
     // TODO(jaewan): Should we define IntDef? Currently we don't have to allow subclass to add more.
@@ -143,7 +141,7 @@
     public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
 
     /**
-     * Command code for {@link MediaController2#fastForward()} ()}.
+     * Command code for {@link MediaController2#fastForward()}.
      * <p>
      * This is transport control command. Command would be sent directly to the player if the
      * session doesn't reject the request through the
@@ -160,14 +158,14 @@
     public static final int COMMAND_CODE_PLAYBACK_REWIND = 8;
 
     /**
-     * Command code for {@link MediaController2#seekTo(long)} ()}.
+     * Command code for {@link MediaController2#seekTo(long)}.
      * <p>
      * Command would be sent directly to the player if the session doesn't reject the request
      * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
      */
     public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
     /**
-     * Command code for {@link MediaController2#setCurrentPlaylistItem(int)} ()}.
+     * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
      * <p>
      * Command would be sent directly to the player if the session doesn't reject the request
      * through the {@link SessionCallback#onCommandRequest(ControllerInfo, Command)}.
@@ -222,7 +220,7 @@
     public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 16;
 
     /**
-     * Command code for {@link MediaController2#playFromUri(String, Bundle)}.
+     * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
      */
     public static final int COMMAND_CODE_PLAY_FROM_URI = 17;
 
@@ -256,6 +254,84 @@
     public static final int COMMAND_CODE_BROWSER = 22;
 
     /**
+     * @hide
+     */
+    @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
+            ERROR_CODE_AUTHENTICATION_EXPIRED, ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
+            ERROR_CODE_CONCURRENT_STREAM_LIMIT, ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
+            ERROR_CODE_NOT_AVAILABLE_IN_REGION, ERROR_CODE_CONTENT_ALREADY_PLAYING,
+            ERROR_CODE_SKIP_LIMIT_REACHED, ERROR_CODE_ACTION_ABORTED, ERROR_CODE_END_OF_QUEUE,
+            ERROR_CODE_SETUP_REQUIRED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorCode {}
+
+    /**
+     * This is the default error code and indicates that none of the other error codes applies.
+     */
+    public static final int ERROR_CODE_UNKNOWN_ERROR = 0;
+
+    /**
+     * Error code when the application state is invalid to fulfill the request.
+     */
+    public static final int ERROR_CODE_APP_ERROR = 1;
+
+    /**
+     * Error code when the request is not supported by the application.
+     */
+    public static final int ERROR_CODE_NOT_SUPPORTED = 2;
+
+    /**
+     * Error code when the request cannot be performed because authentication has expired.
+     */
+    public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3;
+
+    /**
+     * Error code when a premium account is required for the request to succeed.
+     */
+    public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4;
+
+    /**
+     * Error code when too many concurrent streams are detected.
+     */
+    public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5;
+
+    /**
+     * Error code when the content is blocked due to parental controls.
+     */
+    public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6;
+
+    /**
+     * Error code when the content is blocked due to being regionally unavailable.
+     */
+    public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7;
+
+    /**
+     * Error code when the requested content is already playing.
+     */
+    public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8;
+
+    /**
+     * Error code when the application cannot skip any more songs because skip limit is reached.
+     */
+    public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9;
+
+    /**
+     * Error code when the action is interrupted due to some external event.
+     */
+    public static final int ERROR_CODE_ACTION_ABORTED = 10;
+
+    /**
+     * Error code when the playback navigation (previous, next) is not possible because the queue
+     * was exhausted.
+     */
+    public static final int ERROR_CODE_END_OF_QUEUE = 11;
+
+    /**
+     * Error code when the session needs user's manual intervention.
+     */
+    public static final int ERROR_CODE_SETUP_REQUIRED = 12;
+
+    /**
      * 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.
@@ -324,7 +400,7 @@
     /**
      * Represent set of {@link Command}.
      */
-    public static class CommandGroup {
+    public static final class CommandGroup {
         private final CommandGroupProvider mProvider;
 
         public CommandGroup(Context context) {
@@ -360,7 +436,6 @@
         /**
          * @hide
          */
-        @SystemApi
         public CommandGroupProvider getProvider() {
             return mProvider;
         }
@@ -390,7 +465,7 @@
      * default.
      */
     // TODO(jaewan): Can we move this inside of the updatable for default implementation.
-    public static class SessionCallback {
+    public static abstract class SessionCallback {
         private final Context mContext;
 
         public SessionCallback(Context context) {
@@ -587,7 +662,7 @@
     /**
      * Base builder class for MediaSession2 and its subclass. Any change in this class should be
      * also applied to the subclasses {@link MediaSession2.Builder} and
-     * {@link MediaLibraryService2.MediaLibrarySessionBuilder}.
+     * {@link MediaLibraryService2.MediaLibrarySession.Builder}.
      * <p>
      * APIs here should be package private, but should have documentations for developers.
      * Otherwise, javadoc will generate documentation with the generic types such as follows.
@@ -684,7 +759,7 @@
     // Override all methods just to show them with the type instead of generics in Javadoc.
     // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
     public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
-        public Builder(Context context, @NonNull MediaPlayerInterface player) {
+        public Builder(Context context, @NonNull MediaPlayerBase player) {
             super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
                     context, (Builder) instance, player));
         }
@@ -725,7 +800,6 @@
         /**
          * @hide
          */
-        // TODO(jaewan): SystemApi
         // TODO(jaewan): Also accept componentName to check notificaiton listener.
         public ControllerInfo(Context context, int uid, int pid, String packageName,
                 IInterface callback) {
@@ -762,7 +836,6 @@
         /**
          * @hide
          */
-        @SystemApi
         public ControllerInfoProvider getProvider() {
             return mProvider;
         }
@@ -794,13 +867,12 @@
      * <p>
      * It's up to the controller's decision to respect or ignore this customization request.
      */
-    public static class CommandButton {
+    public static final class CommandButton {
         private final CommandButtonProvider mProvider;
 
         /**
          * @hide
          */
-        @SystemApi
         public CommandButton(CommandButtonProvider provider) {
             mProvider = provider;
         }
@@ -856,7 +928,6 @@
         /**
          * @hide
          */
-        @SystemApi
         public CommandButtonProvider getProvider() {
             return mProvider;
         }
@@ -864,7 +935,7 @@
         /**
          * Builder for {@link CommandButton}.
          */
-        public static class Builder {
+        public static final class Builder {
             private final CommandButtonProvider.BuilderProvider mProvider;
 
             public Builder(@NonNull Context context) {
@@ -1038,7 +1109,6 @@
      *       framework had to add heuristics to figure out if an app is
      * @hide
      */
-    @SystemApi
     public MediaSession2(MediaSession2Provider provider) {
         super();
         mProvider = provider;
@@ -1047,39 +1117,34 @@
     /**
      * @hide
      */
-    @SystemApi
     public MediaSession2Provider getProvider() {
         return mProvider;
     }
 
     /**
-     * Set the underlying {@link MediaPlayerInterface} for this session to dispatch incoming event
+     * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event
      * to. Events from the {@link MediaController2} will be sent directly to the underlying
      * player on the {@link Handler} where the session is created on.
      * <p>
-     * If the new player is successfully set,
-     * {@link EventCallback#onPlaybackStateChanged(PlaybackState2)} will be called to tell the
-     * current playback state of the new player.
-     * <p>
      * For the remote playback case which you want to handle volume by yourself, use
-     * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}.
+     * {@link #setPlayer(MediaPlayerBase, VolumeProvider2)}.
      *
-     * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
+     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
      * @throws IllegalArgumentException if the player is {@code null}.
      */
-    public void setPlayer(@NonNull MediaPlayerInterface player) {
+    public void setPlayer(@NonNull MediaPlayerBase player) {
         mProvider.setPlayer_impl(player);
     }
 
     /**
-     * Set the underlying {@link MediaPlayerInterface} with the volume provider for remote playback.
+     * Set the underlying {@link MediaPlayerBase} with the volume provider for remote playback.
      *
-     * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
+     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
      * @param volumeProvider a volume provider
-     * @see #setPlayer(MediaPlayerInterface)
+     * @see #setPlayer(MediaPlayerBase)
      * @see Builder#setVolumeProvider(VolumeProvider2)
      */
-    public void setPlayer(@NonNull MediaPlayerInterface player,
+    public void setPlayer(@NonNull MediaPlayerBase player,
             @NonNull VolumeProvider2 volumeProvider) {
         mProvider.setPlayer_impl(player, volumeProvider);
     }
@@ -1093,7 +1158,7 @@
      * @return player
      */
     public @Nullable
-    MediaPlayerInterface getPlayer() {
+    MediaPlayerBase getPlayer() {
         return mProvider.getPlayer_impl();
     }
 
@@ -1123,7 +1188,9 @@
      * @param focusGain the type of audio focus gain that will be requested, or
      *                  {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during
      *                  playback.
+     * @hide
      */
+    // TODO(jaewan): Revisit
     public void setAudioFocusRequest(int focusGain) {
         mProvider.setAudioFocusRequest_impl(focusGain);
     }
@@ -1164,14 +1231,6 @@
     }
 
     /**
-     * Notify changes in metadata of previously set playlist. Controller will get the whole set of
-     * playlist again.
-     */
-    public void notifyMetadataChanged() {
-        mProvider.notifyMetadataChanged_impl();
-    }
-
-    /**
      * Send custom command to all connected controllers.
      *
      * @param command a command
@@ -1264,14 +1323,14 @@
     }
 
     /**
-     * Sets the index of current DataSourceDesc in the play list to be played.
+     * Skip to the item in the play list.
      *
-     * @param index the index of DataSourceDesc in the play list you want to play
+     * @param item item in the play list you want to play
      * @throws IllegalArgumentException if the play list is null
      * @throws NullPointerException if index is outside play list range
      */
-    public void setCurrentPlaylistItem(int index) {
-        mProvider.setCurrentPlaylistItem_impl(index);
+    public void skipToPlaylistItem(MediaItem2 item) {
+        mProvider.skipToPlaylistItem_impl(item);
     }
 
     /**
@@ -1289,7 +1348,7 @@
     }
 
     /**
-     * Sets a list of {@link MediaItem2} as the current play list.
+     * Set a list of {@link MediaItem2} as the current play list.
      *
      * @param playlist A list of {@link MediaItem2} objects to set as a play list.
      * @throws IllegalArgumentException if given {@param playlist} is null.
@@ -1299,13 +1358,68 @@
     }
 
     /**
-     * Returns the playlist which is lastly set.
+     * Remove the media item at index in the play list.
+     * <p>
+     * If index is same as the current index of the playlist, current playback
+     * will be stopped and playback moves to next source in the list.
+     *
+     * @throws IllegalArgumentException if the play list is null
      */
+    // TODO(jaewan): Remove with index was previously rejected by council (b/36524925)
+    // TODO(jaewan): Should we also add movePlaylistItem from index to index?
+    public void removePlaylistItem(MediaItem2 item) {
+        mProvider.removePlaylistItem_impl(item);
+    }
+
+    /**
+     * Add the media item to the play list at position index.
+     * <p>
+     * This will not change the currently playing media item.
+     * If index is less than or equal to the current index of the play list,
+     * the current index of the play list will be incremented correspondingly.
+     *
+     * @param index the index you want to add
+     * @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);
+    }
+
+    /**
+     * Edit the media item to the play list at position index. This is expected to be called when
+     * the metadata information is updated.
+     * <p>
+     * This will not change the currently playing media item.
+     *
+     * @param item the media item you want to add to the play list
+     */
+    public void editPlaylistItem(@NonNull MediaItem2 item) {
+        mProvider.editPlaylistItem_impl(item);
+    }
+
+    /**
+     * Return the playlist which is lastly set.
+     *
+     * @return playlist
+     */
+    @Override
     public List<MediaItem2> getPlaylist() {
         return mProvider.getPlaylist_impl();
     }
 
     /**
+     * Return currently playing media item.
+     *
+     * @return currently playing media item
+     */
+    @Override
+    public MediaItem2 getCurrentPlaylistItem() {
+        return mProvider.getCurrentPlaylistItem_impl();
+    }
+
+    /**
      * Sets the {@link PlaylistParams} for the current play list. Repeat/shuffle mode and metadata
      * for the list can be set by calling this method.
      *
@@ -1324,16 +1438,28 @@
         return mProvider.getPlaylistParams_impl();
     }
 
-    /*
+    /**
+     * Notify errors to the connected controllers
+     *
+     * @param errorCode error code
+     * @param extra extra
+     */
+    public void notifyError(@ErrorCode int errorCode, int extra) {
+        mProvider.notifyError_impl(errorCode, extra);
+    }
+
+    /**
      * Register {@link EventCallback} to listen changes in the underlying
-     * {@link MediaPlayerInterface}, regardless of the change in the underlying player.
+     * {@link MediaPlayerBase}, regardless of the change in the underlying player.
      * <p>
      * Registered callbacks will be also called when the underlying player is changed.
      *
      * @param executor a callback Executor
      * @param callback a EventCallback
      * @throws IllegalArgumentException if executor or callback is {@code null}.
+     * @hide
      */
+    // TODO(jaewan): Unhide or remove
     public void registerPlayerEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull EventCallback callback) {
         mProvider.registerPlayerEventCallback_impl(executor, callback);
@@ -1344,7 +1470,9 @@
      *
      * @param callback the callback to be removed
      * @throws IllegalArgumentException if the callback is {@code null}.
+     * @hide
      */
+    // TODO(jaewan): Unhide or remove
     public void unregisterPlayerEventCallback(@NonNull EventCallback callback) {
         mProvider.unregisterPlayerEventCallback_impl(callback);
     }
@@ -1353,6 +1481,7 @@
      * Return the {@link PlaybackState2} from the player.
      *
      * @return playback state
+     * @hide
      */
     public PlaybackState2 getPlaybackState() {
         return mProvider.getPlaybackState_impl();
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 0b5dddf..56e8e5d 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -90,7 +90,7 @@
  * rejected, the controller will unbind. If it's accepted, the controller will be available to use
  * and keep binding.
  * <p>
- * When playback is started for this session service, {@link #onUpdateNotification(PlaybackState2)}
+ * When playback is started for this session service, {@link #onUpdateNotification()}
  * is called and service would become a foreground service. It's needed to keep playback after the
  * controller is destroyed. The session service becomes background service when the playback is
  * stopped.
@@ -100,7 +100,6 @@
  * Any app can bind to the session service with controller, but the controller can be used only if
  * the session service accepted the connection request through
  * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}.
- * @hide
  */
 public abstract class MediaSessionService2 extends Service {
     private final MediaSessionService2Provider mProvider;
@@ -158,17 +157,16 @@
     public @NonNull abstract MediaSession2 onCreateSession(String sessionId);
 
     /**
-     * Called when the playback state of this session is changed, and notification needs update.
-     * Override this method to show your own notification UI.
+     * Called when the playback state of this session is changed so notification needs update.
+     * Override this method to show or cancel your own notification UI.
      * <p>
      * With the notification returned here, the service become foreground service when the playback
      * is started. It becomes background service after the playback is stopped.
      *
-     * @param state playback state
      * @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
      */
-    public MediaNotification onUpdateNotification(PlaybackState2 state) {
-        return mProvider.onUpdateNotification_impl(state);
+    public MediaNotification onUpdateNotification() {
+        return mProvider.onUpdateNotification_impl();
     }
 
     /**
@@ -201,9 +199,9 @@
     }
 
     /**
-     * Returned by {@link #onUpdateNotification(PlaybackState2)} for making session service
-     * foreground service to keep playback running in the background. It's highly recommended to
-     * show media style notification here.
+     * Returned by {@link #onUpdateNotification()} for making session service forground service
+     * to keep playback running in the background. It's highly recommended to show media style
+     * notification here.
      */
     public static class MediaNotification {
         private final MediaNotificationProvider mProvider;
diff --git a/media/java/android/media/PlaybackState2.java b/media/java/android/media/PlaybackState2.java
index a95b8f2..7afb579 100644
--- a/media/java/android/media/PlaybackState2.java
+++ b/media/java/android/media/PlaybackState2.java
@@ -28,11 +28,12 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Playback state for a {@link MediaPlayerInterface}, to be shared between {@link MediaSession2} and
+ * Playback state for a {@link MediaPlayerBase}, to be shared between {@link MediaSession2} and
  * {@link MediaController2}. This includes a playback state {@link #STATE_PLAYING},
  * the current playback position and extra.
  * @hide
  */
+// TODO(jaewan): Remove this.
 public final class PlaybackState2 {
     // Similar to the PlaybackState with following changes
     //    - Not implement Parcelable and added from/toBundle()
@@ -214,4 +215,4 @@
             @Nullable Bundle bundle) {
         return ApiLoader.getProvider(context).fromBundle_PlaybackState2(context, bundle);
     }
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
index 4f77ecd..e5b05fb 100644
--- a/media/java/android/media/Rating2.java
+++ b/media/java/android/media/Rating2.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.Rating2Provider;
@@ -36,12 +35,10 @@
  * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
  * be defined as "unrated"), both of which are defined when the rating instance is constructed
  * through one of the factory methods.
- * @hide
  */
+// New version of Rating with following change
+//   - Don't implement Parcelable for updatable support.
 public final class Rating2 {
-    // Mostly same as the android.media.Rating, but it's no longer implements Parcelable for
-    // updatable support.
-
     /**
      * @hide
      */
@@ -100,7 +97,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public Rating2(@NonNull Rating2Provider provider) {
         mProvider = provider;
     }
@@ -113,7 +109,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public Rating2Provider getProvider() {
         return mProvider;
     }
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
index 2c2090c..fdfa43a 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.session.MediaSessionManager;
 import android.media.update.ApiLoader;
@@ -36,8 +35,11 @@
  * {@link MediaController2} to communicate with the session.
  * <p>
  * It can be also obtained by {@link MediaSessionManager}.
- * @hide
  */
+// New version of MediaSession.Token for following reasons
+//   - Stop implementing Parcelable for updatable support
+//   - Represent session and library service (formerly browser service) in one class.
+//     Previously MediaSession.Token was for session and ComponentName was for service.
 public final class SessionToken2 {
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
@@ -86,7 +88,6 @@
      * Constructor for the token.
      * @hide
      */
-    @SystemApi
     public SessionToken2(@NonNull SessionToken2Provider provider) {
         mProvider = provider;
     }
@@ -106,7 +107,9 @@
         return mProvider.toString_impl();
     }
 
-    @SystemApi
+    /**
+     * @hide
+     */
     public SessionToken2Provider getProvider() {
         return mProvider;
     }
@@ -147,7 +150,7 @@
      * @return
      */
     public static SessionToken2 fromBundle(@NonNull Context context, @NonNull Bundle bundle) {
-        return ApiLoader.getProvider(context).SessionToken2_fromBundle(context, bundle);
+        return ApiLoader.getProvider(context).fromBundle_SessionToken2(context, bundle);
     }
 
     /**
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 53ba466..8e1cfbf 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.VolumeProvider2Provider;
@@ -32,12 +31,11 @@
  * {@link #setCurrentVolume(int)} each time the volume being provided changes.
  * <p>
  * You can set a volume provider on a session by calling
- * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider2)}.
- *
- * @hide
+ * {@link MediaSession2#setPlayer(MediaPlayerBase, VolumeProvider2)}.
  */
+// New version of VolumeProvider with following changes
+//   - Don't implement Parcelable for updatable support.
 public abstract class VolumeProvider2 {
-
     /**
      * @hide
      */
@@ -85,7 +83,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public VolumeProvider2Provider getProvider() {
         return mProvider;
     }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index e7fcc7f..d079b7a 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -343,7 +343,6 @@
      * Called when a {@link MediaSession2} is created.
      * @hide
      */
-    // TODO(jaewan): System API
     public boolean createSession2(@NonNull SessionToken2 token) {
         if (token == null) {
             return false;
@@ -356,10 +355,10 @@
         return false;
     }
 
-    /** Called when a {@link MediaSession2} is destroyed.
+    /**
+     * Called when a {@link MediaSession2} is destroyed.
      * @hide
      */
-    // TODO(jaewan): System API
     public void destroySession2(@NonNull SessionToken2 token) {
         if (token == null) {
             return;
@@ -381,9 +380,7 @@
      * using the {@link NotificationListenerService} APIs.
      *
      * @return list of tokens
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public List<SessionToken2> getActiveSessionTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
@@ -404,9 +401,7 @@
      * using the {@link NotificationListenerService} APIs.
      *
      * @return list of tokens
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public List<SessionToken2> getSessionServiceTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
@@ -429,9 +424,7 @@
      * @return list of tokens
      * @see #getActiveSessionTokens
      * @see #getSessionServiceTokens
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public List<SessionToken2> getAllSessionTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
@@ -452,9 +445,7 @@
      *
      * @param executor executor to run this command
      * @param listener The listener to add.
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public void addOnSessionTokensChangedListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull OnSessionTokensChangedListener listener) {
         addOnSessionTokensChangedListener(UserHandle.myUserId(), executor, listener);
@@ -501,9 +492,7 @@
      * Stop receiving session token updates on the specified listener.
      *
      * @param listener The listener to remove.
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public void removeOnSessionTokensChangedListener(
             @NonNull OnSessionTokensChangedListener listener) {
         if (listener == null) {
@@ -660,9 +649,7 @@
     /**
      * Listens for changes to the {@link #getAllSessionTokens()}. This can be added
      * using {@link #addOnActiveSessionsChangedListener}.
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public interface OnSessionTokensChangedListener {
         void onSessionTokensChanged(@NonNull List<SessionToken2> tokens);
     }
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 8d9efd5..ca5c16d 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -16,7 +16,6 @@
 
 package android.media.update;
 
-import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.media.AudioAttributes;
 import android.media.MediaController2.PlaybackInfo;
@@ -65,6 +64,11 @@
     PlaylistParams getPlaylistParams_impl();
     void setPlaylistParams_impl(PlaylistParams params);
     PlaybackState2 getPlaybackState_impl();
+    int getPlayerState_impl();
+    long getPosition_impl();
+    float getPlaybackSpeed_impl();
+    long getBufferedPosition_impl();
+    MediaItem2 getCurrentPlaylistItem_impl();
 
     interface PlaybackInfoProvider {
         int getPlaybackType_impl();
diff --git a/media/java/android/media/update/MediaItem2Provider.java b/media/java/android/media/update/MediaItem2Provider.java
index 2970f0e..1d5b414 100644
--- a/media/java/android/media/update/MediaItem2Provider.java
+++ b/media/java/android/media/update/MediaItem2Provider.java
@@ -23,7 +23,6 @@
 /**
  * @hide
  */
-// TODO(jaewan): SystemApi
 public interface MediaItem2Provider {
     Bundle toBundle_impl();
     String toString_impl();
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
index 7489f76..9a0d693 100644
--- a/media/java/android/media/update/MediaLibraryService2Provider.java
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -16,16 +16,12 @@
 
 package android.media.update;
 
-import android.annotation.SystemApi;
-import android.media.MediaLibraryService2.MediaLibrarySession;
-import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
 import android.media.MediaSession2.ControllerInfo;
 import android.os.Bundle;
 
 /**
  * @hide
  */
-// TODO: @SystemApi
 public interface MediaLibraryService2Provider extends MediaSessionService2Provider {
     // Nothing new for now
 
diff --git a/media/java/android/media/update/MediaMetadata2Provider.java b/media/java/android/media/update/MediaMetadata2Provider.java
index b6e5c8a..22463e9 100644
--- a/media/java/android/media/update/MediaMetadata2Provider.java
+++ b/media/java/android/media/update/MediaMetadata2Provider.java
@@ -11,7 +11,6 @@
 /**
  * @hide
  */
-// TODO(jaewan): SystemApi
 public interface MediaMetadata2Provider {
     boolean containsKey_impl(String key);
     CharSequence getText_impl(String key);
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index fc1f671..dbd4a0a 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -19,8 +19,8 @@
 import android.app.PendingIntent;
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
-import android.media.MediaPlayerInterface.EventCallback;
+import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.EventCallback;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
@@ -40,27 +40,28 @@
 /**
  * @hide
  */
-// TODO: @SystemApi
 public interface MediaSession2Provider extends TransportControlProvider {
     void close_impl();
-    void setPlayer_impl(MediaPlayerInterface player);
-    void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider);
-    MediaPlayerInterface getPlayer_impl();
+    void setPlayer_impl(MediaPlayerBase player);
+    void setPlayer_impl(MediaPlayerBase player, VolumeProvider2 volumeProvider);
+    MediaPlayerBase getPlayer_impl();
     SessionToken2 getToken_impl();
     List<ControllerInfo> getConnectedControllers_impl();
     void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
     void setAudioFocusRequest_impl(int focusGain);
-
     void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands);
-    void notifyMetadataChanged_impl();
     void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
             ResultReceiver receiver);
     void sendCustomCommand_impl(Command command, Bundle args);
     void setPlaylist_impl(List<MediaItem2> playlist);
+    void addPlaylistItem_impl(int index, MediaItem2 item);
+    void removePlaylistItem_impl(MediaItem2 item);
+    void editPlaylistItem_impl(MediaItem2 item);
     List<MediaItem2> getPlaylist_impl();
+    MediaItem2 getCurrentPlaylistItem_impl();
     void setPlaylistParams_impl(PlaylistParams params);
     PlaylistParams getPlaylistParams_impl();
-
+    void notifyError_impl(int errorCode, int extra);
     void registerPlayerEventCallback_impl(Executor executor, EventCallback callback);
     void unregisterPlayerEventCallback_impl(EventCallback callback);
 
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
index 42e7587..8697e70 100644
--- a/media/java/android/media/update/MediaSessionService2Provider.java
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -16,7 +16,6 @@
 
 package android.media.update;
 
-import android.annotation.SystemApi;
 import android.app.Notification;
 import android.content.Intent;
 import android.media.MediaSession2;
@@ -29,7 +28,7 @@
  */
 public interface MediaSessionService2Provider {
     MediaSession2 getSession_impl();
-    MediaNotification onUpdateNotification_impl(PlaybackState2 state);
+    MediaNotification onUpdateNotification_impl();
 
     // Service
     void onCreate_impl();
diff --git a/media/java/android/media/update/PlaybackState2Provider.java b/media/java/android/media/update/PlaybackState2Provider.java
index 93f769c..66b8fa5 100644
--- a/media/java/android/media/update/PlaybackState2Provider.java
+++ b/media/java/android/media/update/PlaybackState2Provider.java
@@ -21,7 +21,6 @@
 /**
  * @hide
  */
-// TODO(jaewan): @SystemApi
 public interface PlaybackState2Provider {
     String toString_impl();
 
diff --git a/media/java/android/media/update/Rating2Provider.java b/media/java/android/media/update/Rating2Provider.java
index 8966196..28ad273 100644
--- a/media/java/android/media/update/Rating2Provider.java
+++ b/media/java/android/media/update/Rating2Provider.java
@@ -22,7 +22,6 @@
 /**
  * @hide
  */
-// TODO(jaewan): @SystemApi
 public interface Rating2Provider {
     String toString_impl();
     boolean equals_impl(Object obj);
@@ -34,4 +33,4 @@
     boolean isThumbUp_impl();
     float getStarRating_impl();
     float getPercentRating_impl();
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 29a30343..62759eb 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -28,10 +28,9 @@
 import android.media.MediaLibraryService2;
 import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaLibraryService2.MediaLibrarySession;
-import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
-import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
 import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
+import android.media.MediaPlayerBase;
 import android.media.MediaSession2;
 import android.media.MediaSession2.CommandButton.Builder;
 import android.media.MediaSession2.PlaylistParams;
@@ -44,7 +43,7 @@
 import android.media.VolumeProvider2;
 import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
-import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
 import android.media.update.MediaSession2Provider.CommandGroupProvider;
 import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
@@ -86,9 +85,10 @@
             PlaylistParams playlistParams, int repeatMode, int shuffleMode,
             MediaMetadata2 playlistMetadata);
     PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle);
-    BuilderProvider createMediaSession2CommandButtonBuilder(Context context, Builder builder);
+    CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
+            MediaSession2.CommandButton.Builder builder);
     BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
-            Context context, MediaSession2.Builder instance, MediaPlayerInterface player);
+            Context context, MediaSession2.Builder instance, MediaPlayerBase player);
 
     MediaController2Provider createMediaController2(Context context, MediaController2 instance,
             SessionToken2 token, Executor executor, ControllerCallback callback);
@@ -103,14 +103,15 @@
     MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
     BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
         createMediaLibraryService2Builder(
-            Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
-            Executor callbackExecutor, MediaLibrarySessionCallback callback);
+            MediaLibraryService2 service, MediaLibrarySession.Builder instance,
+            MediaPlayerBase player, Executor callbackExecutor,
+            MediaLibrarySessionCallback callback);
     LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
             String rootId, Bundle extras);
 
     SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
             String packageName, String serviceName, int uid);
-    SessionToken2 SessionToken2_fromBundle(Context context, Bundle bundle);
+    SessionToken2 fromBundle_SessionToken2(Context context, Bundle bundle);
 
     MediaItem2Provider createMediaItem2(Context context, MediaItem2 mediaItem2,
             String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags);
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
index 44f82b29..9af8ada 100644
--- a/media/java/android/media/update/TransportControlProvider.java
+++ b/media/java/android/media/update/TransportControlProvider.java
@@ -16,6 +16,7 @@
 
 package android.media.update;
 
+import android.media.MediaItem2;
 import android.media.PlaybackState2;
 
 /**
@@ -32,7 +33,7 @@
     void fastForward_impl();
     void rewind_impl();
     void seekTo_impl(long pos);
-    void setCurrentPlaylistItem_impl(int index);
+    void skipToPlaylistItem_impl(MediaItem2 item);
 
     PlaybackState2 getPlaybackState_impl();
 }
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 4333c96..152ace9 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -19,7 +19,7 @@
 import android.annotation.SystemApi;
 import android.media.AudioAttributes;
 import android.media.MediaMetadata2;
-import android.media.MediaPlayerInterface;
+import android.media.MediaPlayerBase;
 import android.media.session.MediaController;
 import android.media.session.PlaybackState;
 import android.media.session.MediaSession;
@@ -65,7 +65,7 @@
     /**
      * @hide
      */
-    void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player);
+    void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player);
     // TODO: remove setRouteAttributes_impl with MediaSession.Callback once MediaSession2 is ready.
     void setRouteAttributes_impl(List<String> routeCategories, MediaSession.Callback sessionPlayer);
     void setVideoPath_impl(String path);
diff --git a/media/java/android/media/update/VolumeProvider2Provider.java b/media/java/android/media/update/VolumeProvider2Provider.java
index 5657af6..5b5cfd3 100644
--- a/media/java/android/media/update/VolumeProvider2Provider.java
+++ b/media/java/android/media/update/VolumeProvider2Provider.java
@@ -18,7 +18,6 @@
 /**
  * @hide
  */
-// TODO(jaewan): @SystemApi
 public interface VolumeProvider2Provider {
     int getControlType_impl();
     int getMaxVolume_impl();