Merge "Add javadoc to explain ApnSetting conflict." into pi-dev
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e455948..8dbb9f0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8610,6 +8610,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     @UserProvisioningState
     public int getUserProvisioningState() {
         throwIfParentInstance("getUserProvisioningState");
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 17002aa..591f33f 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -25,9 +25,7 @@
 import android.content.Context;
 import android.media.MediaPlaylistAgent.RepeatMode;
 import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.ErrorCode;
 import android.media.session.MediaSessionManager;
@@ -56,7 +54,7 @@
  * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
  * available only if the session service allows this controller by
  * {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)} for the service.
- * Wait {@link ControllerCallback#onConnected(MediaController2, CommandGroup)} or
+ * Wait {@link ControllerCallback#onConnected(MediaController2, SessionCommandGroup2)} or
  * {@link ControllerCallback#onDisconnected(MediaController2)} for the result.
  * <p>
  * A controller can be created through token from {@link MediaSessionManager} if you hold the
@@ -83,7 +81,7 @@
          * @param allowedCommands commands that's allowed by the session.
          */
         public void onConnected(@NonNull MediaController2 controller,
-                @NonNull CommandGroup allowedCommands) { }
+                @NonNull SessionCommandGroup2 allowedCommands) { }
 
         /**
          * Called when the session refuses the controller or the controller is disconnected from
@@ -102,7 +100,8 @@
          * Called when the session set the custom layout through the
          * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
          * <p>
-         * Can be called before {@link #onConnected(MediaController2, CommandGroup)} is called.
+         * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)} is
+         * called.
          *
          * @param controller the controller for this event
          * @param layout
@@ -126,7 +125,7 @@
          * @param commands newly allowed commands
          */
         public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
-                @NonNull CommandGroup commands) { }
+                @NonNull SessionCommandGroup2 commands) { }
 
         /**
          * Called when the session sent a custom command.
@@ -137,7 +136,7 @@
          * @param receiver
          */
         public void onCustomCommand(@NonNull MediaController2 controller,
-                @NonNull Command command, @Nullable Bundle args,
+                @NonNull SessionCommand2 command, @Nullable Bundle args,
                 @Nullable ResultReceiver receiver) { }
 
         /**
@@ -149,16 +148,6 @@
         public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
 
         /**
-         * Called when the player's position is changed
-         *
-         * @param controller the controller for this event
-         * @param eventTimeMs timestamp when the position information is sent from the session
-         * @param positionMs position in millis
-         */
-        public void onPositionChanged(@NonNull MediaController2 controller,
-                long eventTimeMs, long positionMs) { }
-
-        /**
          * Called when playback speed is changed.
          *
          * @param controller the controller for this event
@@ -180,6 +169,14 @@
                 @NonNull MediaItem2 item, @MediaPlayerBase.BuffState int state) { }
 
         /**
+         * Called to indicate that seeking is completed.
+         *
+         * @param controller the controller for this event.
+         * @param position the previous seeking request.
+         */
+        public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
+
+        /**
          * Called when a error from
          *
          * @param controller the controller for this event
@@ -197,7 +194,6 @@
          *
          * @param controller the controller for this event
          * @param item new item
-         * @see #onPositionChanged(MediaController2, long, long)
          * @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
          */
         // TODO(jaewan): Use this (b/74316764)
@@ -423,16 +419,14 @@
     }
 
     /**
-     * Start fast forwarding. If playback is already fast forwarding this
-     * may increase the rate.
+     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
      */
     public void fastForward() {
         mProvider.fastForward_impl();
     }
 
     /**
-     * Start rewinding. If playback is already rewinding this may increase
-     * the rate.
+     * Rewinds playback. If playback is already rewinding this may increase the rate.
      */
     public void rewind() {
         mProvider.rewind_impl();
@@ -689,7 +683,7 @@
      * @param args optional argument
      * @param cb optional result receiver
      */
-    public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args,
+    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
             @Nullable ResultReceiver cb) {
         mProvider.sendCustomCommand_impl(command, args, cb);
     }
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 5c08f19..a426552 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -130,33 +130,6 @@
      */
     public abstract void seekTo(long pos);
 
-    /**
-     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
-     * <p>
-     * Default implementation sets the playback speed to the 2.0f
-     * @see #setPlaybackSpeed(float)
-     * @hide
-     */
-    // TODO(jaewan): Unhide (b/74724709)
-    public void fastForward() {
-        setPlaybackSpeed(2.0f);
-    }
-
-    /**
-     * Rewinds playback. If playback is already rewinding this may increase the rate.
-     * <p>
-     * Default implementation sets the playback speed to the -1.0f if
-     * {@link #isReversePlaybackSupported()} returns {@code true}.
-     * @see #setPlaybackSpeed(float)
-     * @hide
-     */
-    // TODO(jaewan): Unhide (b/74724709)
-    public void rewind() {
-        if (isReversePlaybackSupported()) {
-            setPlaybackSpeed(-1.0f);
-        }
-    }
-
     public static final long UNKNOWN_TIME = -1;
 
     /**
@@ -340,10 +313,19 @@
 
         /**
          * Called to indicate that the playback speed has changed.
-         * @param mpb the player that is buffering
+         * @param mpb the player that has changed the playback speed.
          * @param speed the new playback speed.
          */
         public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
+
+        /**
+         * Called to indicate that {@link #seekTo(long)} is completed.
+         *
+         * @param mpb the player that has completed seeking.
+         * @param position the previous seeking request.
+         * @see #seekTo(long)
+         */
+        public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
     }
 
 }
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 1959d41..2b3c2b4 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -33,8 +33,6 @@
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
 import android.media.update.MediaSession2Provider.CommandButtonProvider;
-import android.media.update.MediaSession2Provider.CommandGroupProvider;
-import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
 import android.media.update.ProviderCreator;
 import android.net.Uri;
@@ -45,7 +43,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -85,255 +82,6 @@
     private final MediaSession2Provider mProvider;
 
     /**
-     * Command code for the custom command which can be defined by string action in the
-     * {@link Command}.
-     */
-    public static final int COMMAND_CODE_CUSTOM = 0;
-
-    /**
-     * Command code for {@link MediaController2#play()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
-
-    /**
-     * Command code for {@link MediaController2#pause()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
-
-    /**
-     * Command code for {@link MediaController2#stop()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
-
-    /**
-     * Command code for {@link MediaController2#skipToNextItem()}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the {@link SessionCallback#onCommandRequest(
-     * MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
-
-    /**
-     * Command code for {@link MediaController2#skipToPreviousItem()}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the {@link SessionCallback#onCommandRequest(
-     * MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
-
-    /**
-     * Command code for {@link MediaController2#prepare()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
-
-    /**
-     * 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
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7;
-
-    /**
-     * Command code for {@link MediaController2#rewind()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_REWIND = 8;
-
-    /**
-     * 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(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
-
-    /**
-     * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
-     * <p>
-     * Command would set the device volume or send to the volume provider directly if the session
-     * doesn't reject the request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_SET_VOLUME = 10;
-
-    /**
-     * Command code for both {@link MediaController2#adjustVolume(int, int)}.
-     * <p>
-     * Command would adjust the device volume or send to the volume provider directly if the session
-     * doesn't reject the request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYBACK_ADJUST_VOLUME = 11;
-
-    /**
-     * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
-
-    /**
-     * Command code for {@link MediaController2#setShuffleMode(int)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
-
-    /**
-     * Command code for {@link MediaController2#setRepeatMode(int)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
-
-    /**
-     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
-
-    /**
-     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
-
-    /**
-     * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
-     * information to the controller.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
-
-    /**
-     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
-     * metadata information to the controller.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
-
-    /**
-     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
-     * <p>
-     * Command would be sent directly to the playlist agent if the session doesn't reject the
-     * request through the
-     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
-     */
-    public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
-
-    /**
-     * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
-
-    /**
-     * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
-
-    /**
-     * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
-
-    /**
-     * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
-     */
-    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
-
-    /**
-     * Command code for {@link MediaController2#setRating(String, Rating2)}.
-     * @hide
-     */
-    public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
-
-    /**
-     * Command code for {@link android.media.MediaLibraryService2.MediaLibrarySession} specific
-     * functions. With or without this, a {@link MediaSession2} that isn't
-     * {@link android.media.MediaLibraryService2.MediaLibrarySession} would automatically reject
-     * the calls.
-     *
-     * @see android.media.MediaLibraryService2.MediaLibrarySession
-     * @see MediaBrowser2
-     * @hide
-     */
-    // TODO(jaewan): Remove
-    public static final int COMMAND_CODE_BROWSER = 29;
-
-    // TODO(jaewan): Add javadoc
-    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
-    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
-    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
-    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
-    public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
-    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
-    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
-
-    /**
      * @hide
      */
     @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
@@ -436,155 +184,6 @@
     }
 
     /**
-     * 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.
-     * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
-     * {@link #getCustomCommand()} shouldn't be {@code null}.
-     */
-    public static final class Command {
-        private final CommandProvider mProvider;
-
-        public Command(int commandCode) {
-            mProvider = ApiLoader.getProvider().createMediaSession2Command(
-                    this, commandCode, null, null);
-        }
-
-        public Command(@NonNull String action, @Nullable Bundle extras) {
-            if (action == null) {
-                throw new IllegalArgumentException("action shouldn't be null");
-            }
-            mProvider = ApiLoader.getProvider().createMediaSession2Command(
-                    this, COMMAND_CODE_CUSTOM, action, extras);
-        }
-
-        /**
-         * @hide
-         */
-        public CommandProvider getProvider() {
-            return mProvider;
-        }
-
-        public int getCommandCode() {
-            return mProvider.getCommandCode_impl();
-        }
-
-        public @Nullable String getCustomCommand() {
-            return mProvider.getCustomCommand_impl();
-        }
-
-        public @Nullable Bundle getExtras() {
-            return mProvider.getExtras_impl();
-        }
-
-        /**
-         * @return a new Bundle instance from the Command
-         * @hide
-         */
-        public Bundle toBundle() {
-            return mProvider.toBundle_impl();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof Command)) {
-                return false;
-            }
-            return mProvider.equals_impl(((Command) obj).mProvider);
-        }
-
-        @Override
-        public int hashCode() {
-            return mProvider.hashCode_impl();
-        }
-
-        /**
-         * @return a new Command instance from the Bundle
-         * @hide
-         */
-        public static Command fromBundle(@NonNull Bundle command) {
-            return ApiLoader.getProvider().fromBundle_MediaSession2Command(command);
-        }
-    }
-
-    /**
-     * Represent set of {@link Command}.
-     */
-    public static final class CommandGroup {
-        private final CommandGroupProvider mProvider;
-
-        public CommandGroup() {
-            mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, null);
-        }
-
-        public CommandGroup(@Nullable CommandGroup others) {
-            mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, others);
-        }
-
-        /**
-         * @hide
-         */
-        public CommandGroup(@NonNull CommandGroupProvider provider) {
-            mProvider = provider;
-        }
-
-        public void addCommand(@NonNull Command command) {
-            mProvider.addCommand_impl(command);
-        }
-
-        public void addCommand(int commandCode) {
-            // TODO(jaewna): Implement
-        }
-
-        public void addAllPredefinedCommands() {
-            mProvider.addAllPredefinedCommands_impl();
-        }
-
-        public void removeCommand(@NonNull Command command) {
-            mProvider.removeCommand_impl(command);
-        }
-
-        public void removeCommand(int commandCode) {
-            // TODO(jaewan): Implement.
-        }
-
-        public boolean hasCommand(@NonNull Command command) {
-            return mProvider.hasCommand_impl(command);
-        }
-
-        public boolean hasCommand(int code) {
-            return mProvider.hasCommand_impl(code);
-        }
-
-        public @NonNull Set<Command> getCommands() {
-            return mProvider.getCommands_impl();
-        }
-
-        /**
-         * @hide
-         */
-        public @NonNull CommandGroupProvider getProvider() {
-            return mProvider;
-        }
-
-        /**
-         * @return new bundle from the CommandGroup
-         * @hide
-         */
-        public @NonNull Bundle toBundle() {
-            return mProvider.toBundle_impl();
-        }
-
-        /**
-         * @return new instance of CommandGroup from the bundle
-         * @hide
-         */
-        public static @Nullable CommandGroup fromBundle(Bundle commands) {
-            return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(commands);
-        }
-    }
-
-    /**
      * Callback to be called for all incoming commands from {@link MediaController2}s.
      * <p>
      * If it's not set, the session will accept all controllers and all incoming commands by
@@ -604,9 +203,9 @@
          * @param controller controller information.
          * @return allowed commands. Can be {@code null} to reject connection.
          */
-        public @Nullable CommandGroup onConnect(@NonNull MediaSession2 session,
+        public @Nullable SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller) {
-            CommandGroup commands = new CommandGroup();
+            SessionCommandGroup2 commands = new SessionCommandGroup2();
             commands.addAllPredefinedCommands();
             return commands;
         }
@@ -628,23 +227,23 @@
          * @param controller controller information.
          * @param command a command. This method will be called for every single command.
          * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
-         * @see #COMMAND_CODE_PLAYBACK_PLAY
-         * @see #COMMAND_CODE_PLAYBACK_PAUSE
-         * @see #COMMAND_CODE_PLAYBACK_STOP
-         * @see #COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
-         * @see #COMMAND_CODE_PLAYBACK_PREPARE
-         * @see #COMMAND_CODE_PLAYBACK_FAST_FORWARD
-         * @see #COMMAND_CODE_PLAYBACK_REWIND
-         * @see #COMMAND_CODE_PLAYBACK_SEEK_TO
-         * @see #COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_ADD_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_REMOVE_ITEM
-         * @see #COMMAND_CODE_PLAYLIST_GET_LIST
-         * @see #COMMAND_CODE_PLAYBACK_SET_VOLUME
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PLAY
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PAUSE
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_STOP
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PREPARE
+         * @see SessionCommand2#COMMAND_CODE_SESSION_FAST_FORWARD
+         * @see SessionCommand2#COMMAND_CODE_SESSION_REWIND
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_SEEK_TO
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REMOVE_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST
+         * @see SessionCommand2#COMMAND_CODE_SET_VOLUME
          */
         public boolean onCommandRequest(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Command command) {
+                @NonNull ControllerInfo controller, @NonNull SessionCommand2 command) {
             return true;
         }
 
@@ -667,7 +266,7 @@
 
         /**
          * Called when a controller sent a custom command through
-         * {@link MediaController2#sendCustomCommand(Command, Bundle, ResultReceiver)}.
+         * {@link MediaController2#sendCustomCommand(SessionCommand2, Bundle, ResultReceiver)}.
          *
          * @param session the session for this event
          * @param controller controller information
@@ -676,7 +275,7 @@
          * @param cb optional result receiver
          */
         public void onCustomCommand(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Command customCommand,
+                @NonNull ControllerInfo controller, @NonNull SessionCommand2 customCommand,
                 @Nullable Bundle args, @Nullable ResultReceiver cb) { }
 
         /**
@@ -687,7 +286,7 @@
          * @param controller controller information
          * @param mediaId media id
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
          */
         public void onPlayFromMediaId(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -704,7 +303,7 @@
          * @param controller controller information
          * @param query query string. Can be empty to indicate any suggested media
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
          */
         public void onPlayFromSearch(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String query,
@@ -718,7 +317,7 @@
          * @param controller controller information
          * @param uri uri
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PLAY_FROM_URI
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_URI
          */
         public void onPlayFromUri(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Uri uri,
@@ -742,7 +341,7 @@
          * @param controller controller information
          * @param mediaId media id to prepare
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
          */
         public void onPrepareFromMediaId(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -766,7 +365,7 @@
          * @param controller controller information
          * @param query query string. Can be empty to indicate any suggested media
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
          */
         public void onPrepareFromSearch(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull String query,
@@ -790,12 +389,26 @@
          * @param controller controller information
          * @param uri uri
          * @param extras optional extra bundle
-         * @see #COMMAND_CODE_SESSION_PREPARE_FROM_URI
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_URI
          */
         public void onPrepareFromUri(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
 
         /**
+         * Called when a controller called {@link MediaController2#fastForward()}
+         *
+         * @param session the session for this event
+         */
+        public void onFastForward(@NonNull MediaSession2 session) { }
+
+        /**
+         * Called when a controller called {@link MediaController2#rewind()}
+         *
+         * @param session the session for this event
+         */
+        public void onRewind(@NonNull MediaSession2 session) { }
+
+        /**
          * Called when the player's current playing item is changed
          * <p>
          * When it's called, you should invalidate previous playback information and wait for later
@@ -850,6 +463,17 @@
                 @NonNull MediaPlayerBase player, float speed) { }
 
         /**
+         * Called to indicate that {@link #seekTo(long)} is completed.
+         *
+         * @param session the session for this event.
+         * @param mpb the player that has completed seeking.
+         * @param position the previous seeking request.
+         * @see #seekTo(long)
+         */
+        public void onSeekCompleted(@NonNull MediaSession2 session, @NonNull MediaPlayerBase mpb,
+                long position) { }
+
+        /**
          * Called when a playlist is changed from the {@link MediaPlaylistAgent}.
          * <p>
          * This is called when the underlying agent has called
@@ -1135,7 +759,7 @@
     }
 
     /**
-     * Button for a {@link Command} that will be shown by the controller.
+     * Button for a {@link SessionCommand2} that will be shown by the controller.
      * <p>
      * It's up to the controller's decision to respect or ignore this customization request.
      */
@@ -1155,7 +779,8 @@
          *
          * @return command or {@code null}
          */
-        public @Nullable Command getCommand() {
+        public @Nullable
+        SessionCommand2 getCommand() {
             return mProvider.getCommand_impl();
         }
 
@@ -1214,7 +839,7 @@
                 mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(this);
             }
 
-            public @NonNull Builder setCommand(@Nullable Command command) {
+            public @NonNull Builder setCommand(@Nullable SessionCommand2 command) {
                 return mProvider.setCommand_impl(command);
             }
 
@@ -1351,7 +976,8 @@
      *      expanded row:   layout[5] layout[6] layout[7] layout[8] layout[9]
      *      main row:       layout[3] layout[1] layout[0] layout[2] layout[4]
      * <p>
-     * This API can be called in the {@link SessionCallback#onConnect(MediaSession2, ControllerInfo)}.
+     * This API can be called in the {@link SessionCallback#onConnect(
+     * MediaSession2, ControllerInfo)}.
      *
      * @param controller controller to specify layout.
      * @param layout ordered list of layout.
@@ -1368,7 +994,7 @@
      * @param commands new allowed commands
      */
     public void setAllowedCommands(@NonNull ControllerInfo controller,
-            @NonNull CommandGroup commands) {
+            @NonNull SessionCommandGroup2 commands) {
         mProvider.setAllowedCommands_impl(controller, commands);
     }
 
@@ -1378,7 +1004,7 @@
      * @param command a command
      * @param args optional argument
      */
-    public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args) {
+    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args) {
         mProvider.sendCustomCommand_impl(command, args);
     }
 
@@ -1389,8 +1015,9 @@
      * @param args optional argument
      * @param receiver result receiver for the session
      */
-    public void sendCustomCommand(@NonNull ControllerInfo controller, @NonNull Command command,
-            @Nullable Bundle args, @Nullable ResultReceiver receiver) {
+    public void sendCustomCommand(@NonNull ControllerInfo controller,
+            @NonNull SessionCommand2 command, @Nullable Bundle args,
+            @Nullable ResultReceiver receiver) {
         // Equivalent to the MediaController.sendCustomCommand(Action action, ResultReceiver r);
         mProvider.sendCustomCommand_impl(controller, command, args, receiver);
     }
@@ -1436,20 +1063,6 @@
     }
 
     /**
-     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
-     */
-    public void fastForward() {
-        mProvider.fastForward_impl();
-    }
-
-    /**
-     * Rewinds playback. If playback is already rewinding this may increase the rate.
-     */
-    public void rewind() {
-        mProvider.rewind_impl();
-    }
-
-    /**
      * Move to a new location in the media stream.
      *
      * @param pos Position to move to, in milliseconds.
@@ -1550,7 +1163,8 @@
      *      <li>{@link MediaItem2} specified by {@link #setPlaylist(List, MediaMetadata2)} doesn't
      *          have {@link DataSourceDesc}</li>
      *      <li>{@link MediaController2#addPlaylistItem(int, MediaItem2)} is called and accepted
-     *          by {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
+     *          by {@link SessionCallback#onCommandRequest(
+     *          MediaSession2, ControllerInfo, SessionCommand2)}.
      *          In that case, an item would be added automatically without the data source.</li>
      * </ul>
      * <p>
@@ -1562,9 +1176,9 @@
      * @param helper a data source missing helper.
      * @throws IllegalStateException when the helper is set when the playlist agent is set
      * @see #setPlaylist(List, MediaMetadata2)
-     * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)
-     * @see #COMMAND_CODE_PLAYLIST_ADD_ITEM
-     * @see #COMMAND_CODE_PLAYLIST_REPLACE_ITEM
+     * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)
+     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REPLACE_ITEM
      */
     public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
         mProvider.setOnDataSourceMissingHelper_impl(helper);
diff --git a/media/java/android/media/SessionCommand2.java b/media/java/android/media/SessionCommand2.java
new file mode 100644
index 0000000..fe86a3a
--- /dev/null
+++ b/media/java/android/media/SessionCommand2.java
@@ -0,0 +1,336 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * @hide
+ * 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.
+ * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+ * {@link #getCustomCommand()} shouldn't be {@code null}.
+ */
+public final class SessionCommand2 {
+    /**
+     * Command code for the custom command which can be defined by string action in the
+     * {@link SessionCommand2}.
+     */
+    public static final int COMMAND_CODE_CUSTOM = 0;
+
+    /**
+     * Command code for {@link MediaController2#play()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
+
+    /**
+     * Command code for {@link MediaController2#pause()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+
+    /**
+     * Command code for {@link MediaController2#stop()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+
+    /**
+     * Command code for {@link MediaController2#skipToNextItem()}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
+
+    /**
+     * Command code for {@link MediaController2#skipToPreviousItem()}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
+
+    /**
+     * Command code for {@link MediaController2#prepare()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
+
+    /**
+     * Command code for {@link MediaController2#fastForward()}.
+     */
+    public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 7;
+
+    /**
+     * Command code for {@link MediaController2#rewind()}.
+     */
+    public static final int COMMAND_CODE_SESSION_REWIND = 8;
+
+    /**
+     * 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(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
+
+    /**
+     * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
+     * <p>
+     * Command would set the device volume or send to the volume provider directly if the session
+     * doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_SET_VOLUME = 10;
+
+    /**
+     * Command code for both {@link MediaController2#adjustVolume(int, int)}.
+     * <p>
+     * Command would adjust the device volume or send to the volume provider directly if the session
+     * doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_ADJUST_VOLUME = 11;
+
+    /**
+     * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
+
+    /**
+     * Command code for {@link MediaController2#setShuffleMode(int)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
+
+    /**
+     * Command code for {@link MediaController2#setRepeatMode(int)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
+
+    /**
+     * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
+     * information to the controller.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
+
+    /**
+     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
+     * metadata information to the controller.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
+
+    /**
+     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
+
+    /**
+     * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
+
+    /**
+     * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
+
+    /**
+     * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
+
+    /**
+     * Command code for {@link MediaController2#setRating(String, Rating2)}.
+     */
+    public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
+
+    // TODO(jaewan): Add javadoc
+    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
+    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
+    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
+    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
+    public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
+    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
+    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
+
+    // TODO(jaewan): Rename and move provider
+    private final MediaSession2Provider.CommandProvider mProvider;
+
+    public SessionCommand2(int commandCode) {
+        mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                this, commandCode, null, null);
+    }
+
+    public SessionCommand2(@NonNull String action, @Nullable Bundle extras) {
+        if (action == null) {
+            throw new IllegalArgumentException("action shouldn't be null");
+        }
+        mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                this, COMMAND_CODE_CUSTOM, action, extras);
+    }
+
+    /**
+     * @hide
+     */
+    public MediaSession2Provider.CommandProvider getProvider() {
+        return mProvider;
+    }
+
+    public int getCommandCode() {
+        return mProvider.getCommandCode_impl();
+    }
+
+    public @Nullable String getCustomCommand() {
+        return mProvider.getCustomCommand_impl();
+    }
+
+    public @Nullable Bundle getExtras() {
+        return mProvider.getExtras_impl();
+    }
+
+    /**
+     * @return a new Bundle instance from the Command
+     * @hide
+     */
+    public Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SessionCommand2)) {
+            return false;
+        }
+        return mProvider.equals_impl(((SessionCommand2) obj).mProvider);
+    }
+
+    @Override
+    public int hashCode() {
+        return mProvider.hashCode_impl();
+    }
+
+    /**
+     * @return a new Command instance from the Bundle
+     * @hide
+     */
+    public static SessionCommand2 fromBundle(@NonNull Bundle command) {
+        return ApiLoader.getProvider().fromBundle_MediaSession2Command(command);
+    }
+}
diff --git a/media/java/android/media/SessionCommandGroup2.java b/media/java/android/media/SessionCommandGroup2.java
new file mode 100644
index 0000000..399765e
--- /dev/null
+++ b/media/java/android/media/SessionCommandGroup2.java
@@ -0,0 +1,106 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.os.Bundle;
+
+import java.util.Set;
+
+/**
+ * @hide
+ * Represent set of {@link SessionCommand2}.
+ */
+public final class SessionCommandGroup2 {
+    // TODO(jaewan): Rename and move provider
+    private final MediaSession2Provider.CommandGroupProvider mProvider;
+
+    public SessionCommandGroup2() {
+        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, null);
+    }
+
+    public SessionCommandGroup2(@Nullable SessionCommandGroup2 others) {
+        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, others);
+    }
+
+    /**
+     * @hide
+     */
+    public SessionCommandGroup2(@NonNull MediaSession2Provider.CommandGroupProvider provider) {
+        mProvider = provider;
+    }
+
+    public void addCommand(@NonNull SessionCommand2 command) {
+        mProvider.addCommand_impl(command);
+    }
+
+    public void addCommand(int commandCode) {
+        // TODO(jaewna): Implement
+    }
+
+    public void addAllPredefinedCommands() {
+        mProvider.addAllPredefinedCommands_impl();
+    }
+
+    public void removeCommand(@NonNull SessionCommand2 command) {
+        mProvider.removeCommand_impl(command);
+    }
+
+    public void removeCommand(int commandCode) {
+        // TODO(jaewan): Implement.
+    }
+
+    public boolean hasCommand(@NonNull SessionCommand2 command) {
+        return mProvider.hasCommand_impl(command);
+    }
+
+    public boolean hasCommand(int code) {
+        return mProvider.hasCommand_impl(code);
+    }
+
+    public @NonNull
+    Set<SessionCommand2> getCommands() {
+        return mProvider.getCommands_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public @NonNull MediaSession2Provider.CommandGroupProvider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * @return new bundle from the CommandGroup
+     * @hide
+     */
+    public @NonNull Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    /**
+     * @return new instance of CommandGroup from the bundle
+     * @hide
+     */
+    public static @Nullable SessionCommandGroup2 fromBundle(Bundle commands) {
+        return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(commands);
+    }
+}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 213897d..7234f7b 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -21,7 +21,7 @@
 import android.media.MediaController2.PlaybackInfo;
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
-import android.media.MediaSession2.Command;
+import android.media.SessionCommand2;
 import android.media.Rating2;
 import android.media.SessionToken2;
 import android.net.Uri;
@@ -52,9 +52,11 @@
     void playFromSearch_impl(String query, Bundle extras);
     void playFromUri_impl(Uri uri, Bundle extras);
     void playFromMediaId_impl(String mediaId, Bundle extras);
+    void fastForward_impl();
+    void rewind_impl();
 
     void setRating_impl(String mediaId, Rating2 rating);
-    void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb);
+    void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb);
     List<MediaItem2> getPlaylist_impl();
     void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
     MediaMetadata2 getPlaylistMetadata_impl();
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 5a1db3e..4751348 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -23,10 +23,10 @@
 import android.media.MediaPlayerBase;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
-import android.media.MediaSession2.Command;
+import android.media.SessionCommand2;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandButton.Builder;
-import android.media.MediaSession2.CommandGroup;
+import android.media.SessionCommandGroup2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.media.MediaSession2.SessionCallback;
@@ -55,10 +55,10 @@
     List<ControllerInfo> getConnectedControllers_impl();
     void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
     void setAudioFocusRequest_impl(AudioFocusRequest afr);
-    void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands);
-    void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
+    void setAllowedCommands_impl(ControllerInfo controller, SessionCommandGroup2 commands);
+    void sendCustomCommand_impl(ControllerInfo controller, SessionCommand2 command, Bundle args,
             ResultReceiver receiver);
-    void sendCustomCommand_impl(Command command, Bundle args);
+    void sendCustomCommand_impl(SessionCommand2 command, Bundle args);
     void addPlaylistItem_impl(int index, MediaItem2 item);
     void removePlaylistItem_impl(MediaItem2 item);
     void replacePlaylistItem_impl(int index, MediaItem2 item);
@@ -72,6 +72,7 @@
     void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
     void clearOnDataSourceMissingHelper_impl();
 
+    // TODO(jaewan): Rename and move provider
     interface CommandProvider {
         int getCommandCode_impl();
         String getCustomCommand_impl();
@@ -82,25 +83,26 @@
         int hashCode_impl();
     }
 
+    // TODO(jaewan): Rename and move provider
     interface CommandGroupProvider {
-        void addCommand_impl(Command command);
+        void addCommand_impl(SessionCommand2 command);
         void addAllPredefinedCommands_impl();
-        void removeCommand_impl(Command command);
-        boolean hasCommand_impl(Command command);
+        void removeCommand_impl(SessionCommand2 command);
+        boolean hasCommand_impl(SessionCommand2 command);
         boolean hasCommand_impl(int code);
-        Set<Command> getCommands_impl();
+        Set<SessionCommand2> getCommands_impl();
         Bundle toBundle_impl();
     }
 
     interface CommandButtonProvider {
-        Command getCommand_impl();
+        SessionCommand2 getCommand_impl();
         int getIconResId_impl();
         String getDisplayName_impl();
         Bundle getExtras_impl();
         boolean isEnabled_impl();
 
         interface BuilderProvider {
-            Builder setCommand_impl(Command command);
+            Builder setCommand_impl(SessionCommand2 command);
             Builder setIconResId_impl(int resId);
             Builder setDisplayName_impl(String displayName);
             Builder setEnabled_impl(boolean enabled);
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index f1290f4..8687b80 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -35,6 +35,8 @@
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
 import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
 import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
@@ -67,12 +69,12 @@
             ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
 
-    CommandProvider createMediaSession2Command(MediaSession2.Command instance,
+    CommandProvider createMediaSession2Command(SessionCommand2 instance,
             int commandCode, String action, Bundle extra);
-    MediaSession2.Command fromBundle_MediaSession2Command(Bundle bundle);
-    CommandGroupProvider createMediaSession2CommandGroup(
-            MediaSession2.CommandGroup instance, MediaSession2.CommandGroup others);
-    MediaSession2.CommandGroup fromBundle_MediaSession2CommandGroup(Bundle bundle);
+    SessionCommand2 fromBundle_MediaSession2Command(Bundle bundle);
+    CommandGroupProvider createMediaSession2CommandGroup(SessionCommandGroup2 instance,
+            SessionCommandGroup2 others);
+    SessionCommandGroup2 fromBundle_MediaSession2CommandGroup(Bundle bundle);
     ControllerInfoProvider createMediaSession2ControllerInfo(Context context,
             MediaSession2.ControllerInfo instance, int uid, int pid,
             String packageName, IInterface callback);
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
index 03944d2..d89a88a 100644
--- a/media/java/android/media/update/TransportControlProvider.java
+++ b/media/java/android/media/update/TransportControlProvider.java
@@ -29,8 +29,6 @@
     void skipToNextItem_impl();
 
     void prepare_impl();
-    void fastForward_impl();
-    void rewind_impl();
     void seekTo_impl(long pos);
     void skipToPlaylistItem_impl(MediaItem2 item);
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index f0bcb81..e0487ea 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -382,10 +382,26 @@
         Preconditions.checkNotNull(secretTypes, "secretTypes is null");
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
-        long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
-        if (updatedRows > 0) {
-            mDatabase.setShouldCreateSnapshot(userId, uid, true);
+
+        int[] currentSecretTypes = mDatabase.getRecoverySecretTypes(userId, uid);
+        if (Arrays.equals(secretTypes, currentSecretTypes)) {
+            Log.v(TAG, "Not updating secret types - same as old value.");
+            return;
         }
+
+        long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
+        if (updatedRows < 1) {
+            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR,
+                    "Database error trying to set secret types.");
+        }
+
+        if (currentSecretTypes.length == 0) {
+            Log.i(TAG, "Initialized secret types.");
+            return;
+        }
+
+        Log.i(TAG, "Updated secret types. Snapshot pending.");
+        mDatabase.setShouldCreateSnapshot(userId, uid, true);
     }
 
     /**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 56c9807..4944ba8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7640,6 +7640,7 @@
         if (!mHasFeature) {
             return DevicePolicyManager.STATE_USER_UNMANAGED;
         }
+        enforceManageUsers();
         int userHandle = mInjector.userHandleGetCallingUserId();
         return getUserProvisioningState(userHandle);
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 43490d3..a64efb7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2387,12 +2387,12 @@
     }
 
     public void testGetUserProvisioningState_defaultResult() {
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
         assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
     }
 
     public void testSetUserProvisioningState_permission() throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2407,6 +2407,7 @@
 
     public void testSetUserProvisioningState_noManagement() {
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
         assertExpectException(IllegalStateException.class,
                 /* messageRegex= */ "change provisioning state unless a .* owner is set",
                 () -> dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
@@ -2417,7 +2418,6 @@
     public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                 DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
@@ -2428,7 +2428,6 @@
             throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                 DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
@@ -2438,7 +2437,6 @@
     public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
                 DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2447,7 +2445,6 @@
     public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser()
             throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
@@ -2457,7 +2454,6 @@
     public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
             throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
@@ -2466,7 +2462,6 @@
 
     public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2474,7 +2469,6 @@
 
     public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         assertExpectException(IllegalStateException.class,
                 /* messageRegex= */ "Cannot move to user provisioning state",
@@ -2486,7 +2480,6 @@
     public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
             throws Exception {
         setupProfileOwner();
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         assertExpectException(IllegalStateException.class,
                 /* messageRegex= */ "Cannot move to user provisioning state",
@@ -2496,6 +2489,9 @@
     }
 
     private void exerciseUserProvisioningTransitions(int userId, int... states) {
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
+
         assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
         for (int state : states) {
             dpm.setUserProvisioningState(state, userId);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b7ce59d..06b94cb 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -879,7 +879,7 @@
     }
 
     @Test
-    public void setRecoverySecretTypes() throws Exception {
+    public void setRecoverySecretTypes_updatesSecretTypes() throws Exception {
         int[] types1 = new int[]{11, 2000};
         int[] types2 = new int[]{1, 2, 3};
         int[] types3 = new int[]{};
@@ -898,6 +898,41 @@
     }
 
     @Test
+    public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        int[] secretTypes = new int[] { 101 };
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+    }
+
+    @Test
+    public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue()
+            throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        int[] secretTypes = new int[] { 101 };
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+    }
+
+    @Test
+    public void setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 101 });
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 });
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+    }
+
+    @Test
     public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
         try {
             mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);