Media router API updates.

Added a MediaRouteDialogFactory to make it easier for applications
to customize the dialog fragments such as adding custom views
into the media route controller dialog.

Added an API to disable volume control in the media route
controller dialog.

Added a method that the application can override to change how
routes are filtered in the chooser dialog.

Changed the remote playback protocol to isolate the queuing
feature and @hide it.  This required a few semantic changes
to the protocol so that the non-queuing subset is able to
stand on its own and be coherent.  Most of these changes are
simple renames and incremental changes of existing concepts
but the documentation had to be substantially updated to keep
it clear.

The protocol changes are roughly as follows.

- QUEUE_ID renamed to SESSION_ID.  Sessions are created
implicitly by ACTION_PLAY just like queues but the concept is
somewhat more generic.  The session is documented conceptually
as having a queue that can only contain at most one item which
makes it easier to explain certain behaviors.  However, we
now deemphasize the concept of the queue in the documentation and
focus more on the session.  Since the word "queue" no longer
appears in the API, we could go either way in the future and choose
to expand on or abandon the queue concept just by changing
the documentation.

- ACTION_STOP renamed to ACTION_REMOVE and now @hide.  The action
removes one specific item from the queue.

- ACTION_CLEAR_QUEUE renamed to ACTION_STOP.  Now documented to
have the side-effect of both clearing and unpausing the queue
like hitting a reset switch on the session.  The choice of name
creates good symmetry with ACTION_PLAY in the single item case.

- ACTION_ENQUEUE (@hide) added to represent the previous
queuing behavior so that we can continue to experiment with it.
Support for this action might be optional in the future.

- ACTION_PLAY is documented in a way that makes it equivalent
to ACTION_STOP followed by ACTION_ENQUEUE.  This way we enforce
the constraint that the queue can have at most one item in it
while still leaving the door open to exposing more features later.

- ACTION_PAUSE_QUEUE renamed to ACTION_PAUSE.  We still refer to
it as conceptually pausing a queue but this now fits in better with
the one at a time nature of ACTION_PLAY.

- ACTION_RESUME_QUEUE renamed to ACTION_RESUME.

- PLAYBACK_STATE_QUEUED renamed to PLAYBACK_STATE_PENDING.

- PLAYBACK_STATE_STOPPED renamed to PLAYBACK_STATE_FINISHED to make
it clear that this state means the media item finished playing
normally.

- PLAYBACK_STATE_CANCELED now only means that the media item was
canceled by the app by way of ACTION_PLAY or ACTION_STOP
(or ACTION_REMOVE) which caused the item to be removed from the
queue before it had a chance to finish normally.

- PLAYBACK_STATE_INVALIDATED added to specifically refer to the
case where another application takes control of the route and
stomps on the session and its media items.  The documentation now
consistently uses the word invalidate rather than cancel when
referring to sessions or media items that have been involuntarily
aborted by an external cause.

- @hide HTTP request headers, response headers and status code.

Bug: 9743462
Change-Id: I3bdb4cd8947112ab409983a74fa4bc062465149a
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
index 7dd36b5..2fd2488 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
@@ -130,6 +130,7 @@
     private final MediaRouterCallback mCallback;
 
     private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
+    private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
     private MediaRouteButton mButton;
 
     /**
@@ -189,6 +190,36 @@
     }
 
     /**
+     * Gets the media route dialog factory to use when showing the route chooser
+     * or controller dialog.
+     *
+     * @return The dialog factory, never null.
+     */
+    public MediaRouteDialogFactory getDialogFactory() {
+        return mDialogFactory;
+    }
+
+    /**
+     * Sets the media route dialog factory to use when showing the route chooser
+     * or controller dialog.
+     *
+     * @param factory The dialog factory, must not be null.
+     */
+    public void setDialogFactory(MediaRouteDialogFactory factory) {
+        if (factory == null) {
+            throw new IllegalArgumentException("factory must not be null");
+        }
+
+        if (mDialogFactory != factory) {
+            mDialogFactory = factory;
+
+            if (mButton != null) {
+                mButton.setDialogFactory(factory);
+            }
+        }
+    }
+
+    /**
      * Gets the associated media route button, or null if it has not yet been created.
      */
     public MediaRouteButton getMediaRouteButton() {
@@ -197,25 +228,16 @@
 
     /**
      * Called when the media route button is being created.
+     * <p>
+     * Subclasses may override this method to customize the button.
+     * </p>
      */
-    @SuppressWarnings("deprecation")
     public MediaRouteButton onCreateMediaRouteButton() {
-        if (mButton != null) {
-            Log.e(TAG, "onCreateMediaRouteButton: This ActionProvider is already associated "
-                    + "with a menu item. Don't reuse MediaRouteActionProvider instances!  "
-                    + "Abandoning the old button...");
-        }
-
-        mButton = new MediaRouteButton(getContext());
-        mButton.setCheatSheetEnabled(true);
-        mButton.setRouteSelector(mSelector);
-        mButton.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.FILL_PARENT));
-        return mButton;
+        return new MediaRouteButton(getContext());
     }
 
     @Override
+    @SuppressWarnings("deprecation")
     public View onCreateActionView() {
         if (mButton != null) {
             Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
@@ -224,6 +246,12 @@
         }
 
         mButton = onCreateMediaRouteButton();
+        mButton.setCheatSheetEnabled(true);
+        mButton.setRouteSelector(mSelector);
+        mButton.setDialogFactory(mDialogFactory);
+        mButton.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.FILL_PARENT));
         return mButton;
     }
 
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
index ead8a85..f30e7bb 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
@@ -87,6 +87,7 @@
     private final MediaRouterCallback mCallback;
 
     private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
+    private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
 
     private boolean mAttachedToWindow;
 
@@ -173,6 +174,30 @@
     }
 
     /**
+     * Gets the media route dialog factory to use when showing the route chooser
+     * or controller dialog.
+     *
+     * @return The dialog factory, never null.
+     */
+    public MediaRouteDialogFactory getDialogFactory() {
+        return mDialogFactory;
+    }
+
+    /**
+     * Sets the media route dialog factory to use when showing the route chooser
+     * or controller dialog.
+     *
+     * @param factory The dialog factory, must not be null.
+     */
+    public void setDialogFactory(MediaRouteDialogFactory factory) {
+        if (factory == null) {
+            throw new IllegalArgumentException("factory must not be null");
+        }
+
+        mDialogFactory = factory;
+    }
+
+    /**
      * Show the route chooser or controller dialog.
      * <p>
      * If the default route is selected or if the currently selected route does
@@ -181,9 +206,8 @@
      * a choice to disconnect from the route or perform other control actions
      * such as setting the route's volume.
      * </p><p>
-     * The application can customize the dialogs by overriding
-     * {@link #onCreateChooserDialogFragment()} or {@link #onCreateControllerDialogFragment()}
-     * as appropriate.
+     * The application can customize the dialogs by calling {@link #setDialogFactory}
+     * to provide a customized dialog factory.
      * </p>
      *
      * @return True if the dialog was actually shown.
@@ -207,7 +231,8 @@
                 Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
                 return false;
             }
-            MediaRouteChooserDialogFragment f = onCreateChooserDialogFragment();
+            MediaRouteChooserDialogFragment f =
+                    mDialogFactory.onCreateChooserDialogFragment();
             f.setRouteSelector(mSelector);
             f.show(fm, CHOOSER_FRAGMENT_TAG);
         } else {
@@ -215,36 +240,13 @@
                 Log.w(TAG, "showDialog(): Route controller dialog already showing!");
                 return false;
             }
-            MediaRouteControllerDialogFragment f = onCreateControllerDialogFragment();
+            MediaRouteControllerDialogFragment f =
+                    mDialogFactory.onCreateControllerDialogFragment();
             f.show(fm, CONTROLLER_FRAGMENT_TAG);
         }
         return true;
     }
 
-    /**
-     * Called when the chooser dialog is being opened and it is time to create the fragment.
-     * <p>
-     * Subclasses may override this method to create a customized fragment.
-     * </p>
-     *
-     * @return The media route chooser dialog fragment, must not be null.
-     */
-    public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() {
-        return new MediaRouteChooserDialogFragment();
-    }
-
-    /**
-     * Called when the controller dialog is being opened and it is time to create the fragment.
-     * <p>
-     * Subclasses may override this method to create a customized fragment.
-     * </p>
-     *
-     * @return The media route controller dialog fragment, must not be null.
-     */
-    public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
-        return new MediaRouteControllerDialogFragment();
-    }
-
     private FragmentManager getFragmentManager() {
         Activity activity = getActivity();
         if (activity instanceof FragmentActivity) {
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 077a232..91a4c08 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -97,6 +97,21 @@
         }
     }
 
+    /**
+     * Returns true if the route should be included in the list.
+     * <p>
+     * The default implementation returns true for non-default routes that
+     * match the selector.  Subclasses can override this method to filter routes
+     * differently.
+     * </p>
+     *
+     * @param route The route to consider, never null.
+     * @return True if the route should be included in the chooser dialog.
+     */
+    public boolean onFilterRoute(MediaRouter.RouteInfo route) {
+        return !route.isDefault() && route.matchesSelector(mSelector);
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -135,7 +150,10 @@
         super.onDetachedFromWindow();
     }
 
-    private void refreshRoutes() {
+    /**
+     * Refreshes the list of routes that are shown in the chooser dialog.
+     */
+    public void refreshRoutes() {
         if (mAttachedToWindow) {
             mAdapter.update();
         }
@@ -156,7 +174,7 @@
             final int count = routes.size();
             for (int i = 0; i < count; i++) {
                 MediaRouter.RouteInfo route = routes.get(i);
-                if (!route.isDefault() && route.matchesSelector(mSelector)) {
+                if (onFilterRoute(route)) {
                     add(route);
                 }
             }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java
index efb7b3e..3b5239a 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java
@@ -17,6 +17,7 @@
 package android.support.v7.app;
 
 import android.app.Dialog;
+import android.content.Context;
 import android.os.Bundle;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.media.MediaRouteSelector;
@@ -24,8 +25,8 @@
 /**
  * Media route chooser dialog fragment.
  * <p>
- * Creates a {@link MediaRouteChooserDialog}.  The application may subclass this
- * dialog fragment to customize the dialog.
+ * Creates a {@link MediaRouteChooserDialog}.  The application may subclass
+ * this dialog fragment to customize the media route chooser dialog.
  * </p>
  */
 public class MediaRouteChooserDialogFragment extends DialogFragment {
@@ -33,6 +34,12 @@
 
     private MediaRouteSelector mSelector;
 
+    /**
+     * Creates a media route chooser dialog fragment.
+     * <p>
+     * All subclasses of this class must also possess a default constructor.
+     * </p>
+     */
     public MediaRouteChooserDialogFragment() {
         setCancelable(true);
     }
@@ -88,9 +95,20 @@
         }
     }
 
+    /**
+     * Called when the chooser dialog is being created.
+     * <p>
+     * Subclasses may override this method to customize the dialog.
+     * </p>
+     */
+    public MediaRouteChooserDialog onCreateChooserDialog(
+            Context context, Bundle savedInstanceState) {
+        return new MediaRouteChooserDialog(context);
+    }
+
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        MediaRouteChooserDialog dialog = new MediaRouteChooserDialog(getActivity());
+        MediaRouteChooserDialog dialog = onCreateChooserDialog(getActivity(), savedInstanceState);
         dialog.setRouteSelector(getRouteSelector());
         return dialog;
     }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index d852b92..8f18972 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -28,7 +28,6 @@
 import android.view.Window;
 import android.widget.Button;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.SeekBar;
 
@@ -48,10 +47,12 @@
     private final MediaRouterCallback mCallback;
     private final MediaRouter.RouteInfo mRoute;
 
+    private boolean mCreated;
     private Drawable mMediaRouteConnectingDrawable;
     private Drawable mMediaRouteOnDrawable;
     private Drawable mCurrentIconDrawable;
 
+    private boolean mVolumeControlEnabled = true;
     private LinearLayout mVolumeLayout;
     private SeekBar mVolumeSlider;
     private boolean mVolumeSliderTouched;
@@ -101,6 +102,30 @@
         return mControlView;
     }
 
+    /**
+     * Sets whether to enable the volume slider and volume control using the volume keys
+     * when the route supports it.
+     * <p>
+     * The default value is true.
+     * </p>
+     */
+    public void setVolumeControlEnabled(boolean enable) {
+        if (mVolumeControlEnabled != enable) {
+            mVolumeControlEnabled = enable;
+            if (mCreated) {
+                updateVolume();
+            }
+        }
+    }
+
+    /**
+     * Returns whether to enable the volume slider and volume control using the volume keys
+     * when the route supports it.
+     */
+    public boolean isVolumeControlEnabled() {
+        return mVolumeControlEnabled;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -142,6 +167,7 @@
             }
         });
 
+        mCreated = true;
         if (update()) {
             mControlView = onCreateMediaControlView(savedInstanceState);
             if (mControlView != null) {
@@ -171,7 +197,7 @@
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
+        if (isVolumeControlAvailable()) {
             if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                 mRoute.requestUpdateVolume(-1);
                 return true;
@@ -185,7 +211,7 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
+        if (isVolumeControlAvailable()) {
             if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                     || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
                 return true;
@@ -235,7 +261,7 @@
 
     private void updateVolume() {
         if (!mVolumeSliderTouched) {
-            if (mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
+            if (isVolumeControlAvailable()) {
                 mVolumeLayout.setVisibility(View.VISIBLE);
                 mVolumeSlider.setMax(mRoute.getVolumeMax());
                 mVolumeSlider.setProgress(mRoute.getVolume());
@@ -245,6 +271,11 @@
         }
     }
 
+    private boolean isVolumeControlAvailable() {
+        return mVolumeControlEnabled && mRoute.getVolumeHandling() ==
+                MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
+    }
+
     private final class MediaRouterCallback extends MediaRouter.Callback {
         @Override
         public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java
index a6dc205..beb9832 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java
@@ -17,23 +17,41 @@
 package android.support.v7.app;
 
 import android.app.Dialog;
+import android.content.Context;
 import android.os.Bundle;
 import android.support.v4.app.DialogFragment;
 
 /**
  * Media route controller dialog fragment.
  * <p>
- * Creates a {@link MediaRouteControllerDialog}.  The application may subclass this
- * dialog fragment to customize the dialog.
+ * Creates a {@link MediaRouteControllerDialog}.  The application may subclass
+ * this dialog fragment to customize the media route controller dialog.
  * </p>
  */
 public class MediaRouteControllerDialogFragment extends DialogFragment {
+    /**
+     * Creates a media route controller dialog fragment.
+     * <p>
+     * All subclasses of this class must also possess a default constructor.
+     * </p>
+     */
     public MediaRouteControllerDialogFragment() {
         setCancelable(true);
     }
 
+    /**
+     * Called when the controller dialog is being created.
+     * <p>
+     * Subclasses may override this method to customize the dialog.
+     * </p>
+     */
+    public MediaRouteControllerDialog onCreateControllerDialog(
+            Context context, Bundle savedInstanceState) {
+        return new MediaRouteControllerDialog(context);
+    }
+
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        return new MediaRouteControllerDialog(getActivity());
+        return onCreateControllerDialog(getActivity(), savedInstanceState);
     }
 }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteDialogFactory.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteDialogFactory.java
new file mode 100644
index 0000000..834b50d
--- /dev/null
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteDialogFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 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.support.v7.app;
+
+/**
+ * The media route dialog factory is responsible for creating the media route
+ * chooser and controller dialogs as needed.
+ * <p>
+ * The application can customize the dialogs by providing a subclass of the
+ * dialog factory to the {@link MediaRouteButton} using the
+ * {@link MediaRouteButton#setDialogFactory setDialogFactory} method.
+ * </p>
+ */
+public class MediaRouteDialogFactory {
+    private static final MediaRouteDialogFactory sDefault = new MediaRouteDialogFactory();
+
+    /**
+     * Creates a default media route dialog factory.
+     */
+    public MediaRouteDialogFactory() {
+    }
+
+    /**
+     * Gets the default factory instance.
+     *
+     * @return The default media route dialog factory, never null.
+     */
+    public static MediaRouteDialogFactory getDefault() {
+        return sDefault;
+    }
+
+    /**
+     * Called when the chooser dialog is being opened and it is time to create the fragment.
+     * <p>
+     * Subclasses may override this method to create a customized fragment.
+     * </p>
+     *
+     * @return The media route chooser dialog fragment, must not be null.
+     */
+    public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() {
+        return new MediaRouteChooserDialogFragment();
+    }
+
+    /**
+     * Called when the controller dialog is being opened and it is time to create the fragment.
+     * <p>
+     * Subclasses may override this method to create a customized fragment.
+     * </p>
+     *
+     * @return The media route controller dialog fragment, must not be null.
+     */
+    public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
+        return new MediaRouteControllerDialogFragment();
+    }
+}
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaControlIntent.java b/v7/mediarouter/src/android/support/v7/media/MediaControlIntent.java
index b7e3dc0..19f72ff 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaControlIntent.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaControlIntent.java
@@ -77,8 +77,8 @@
  * or send broadcasts.  The difference is that media control intents are directed to
  * routes rather than activity or broadcast receiver components.
  * </p><p>
- * Each media route control intent specifies an action, a category and some number of parameters.
- * Applications send media control requests to routes using the
+ * Each media route control intent specifies an action, a category and some number of parameters
+ * that are supplied as extras.  Applications send media control requests to routes using the
  * {@link MediaRouter.RouteInfo#sendControlRequest MediaRouter.RouteInfo.sendControlRequest}
  * method and receive results via a callback.
  * </p><p>
@@ -107,66 +107,123 @@
  * Remote playback routes present media remotely by playing content from a Uri.
  * These routes destinations take responsibility for fetching and rendering content
  * on their own.  Applications do not render the content themselves; instead, applications
- * send control requests to initiate playback, pause, resume, or manipulate queues of
- * media items and receive status updates when the state of each item changes.
- * This allows applications to queue several items to play one after another and
- * provide feedback to the user as playback progresses.
+ * send control requests to initiate play, pause, resume, or stop media items and receive
+ * status updates as they change state.
  * </p>
  *
- * <h4>Actions</h4>
+ * <h4>Sessions</h4>
  * <p>
- * The following actions are defined:
- * </p><ul>
- * <li>{@link #ACTION_PLAY Play}: Starts playing or enqueues content specified by a given Uri
- * and returns a new media item id to describe the request.  Implicitly creates a new
- * queue of media items if none was specified.
- * <li>{@link #ACTION_CANCEL Cancel}: Cancels playback of a media item and removes it
- * from the queue of items to be played.
- * <li>{@link #ACTION_SEEK Seek}: Sets the content playback position of a media item.
- * <li>{@link #ACTION_GET_STATUS Get status}: Gets the status of a media item including
- * the item's current playback position and progress.
- * <li>{@link #ACTION_PAUSE_QUEUE Pause queue}: Pauses a queue of media items.
- * <li>{@link #ACTION_RESUME_QUEUE Resume queue}: Resumes a queue of media items.
- * <li>{@link #ACTION_CLEAR_QUEUE Clear queue}: Cancels and removes all items from a
- * media queue.
- * </ul>
+ * Each remote media playback action is conducted within the scope of a session.
+ * Sessions are used to prevent applications from accidentally interfering with one
+ * another because at most one session can be valid at a time.
+ * </p><p>
+ * Some actions have the side-effect of creating new sessions.  If an application
+ * performs an action that has the effect of creating a new session then
+ * the previous session is invalidated and any ongoing media playback is stopped
+ * before the requested action is performed.  Any attempt to use an invalidated
+ * session will result in an error.  (Protocol implementations are encouraged to
+ * aggressively discard any information associated with invalidated sessions.)
+ * </p><p>
+ * A session is implicitly created when an application issues a
+ * {@link #ACTION_PLAY play action} without specifying an existing session id as a parameter.
+ * When this happens, any previous session is invalidated and the play action
+ * returns a new session id that may be used to control the session with subsequent
+ * actions such as pause, resume, or stop.
+ * </p>
  *
  * <h4>Media items</h4>
  * <p>
  * Each successful {@link #ACTION_PLAY play action} returns a unique media item id that
  * an application can use to monitor and control playback.  The media item id may be passed
- * to other actions such as {@link #ACTION_CANCEL cancel}, {@link #ACTION_SEEK seek}
- * or {@link #ACTION_GET_STATUS get status}.  It will also appear as a parameter in
+ * to other actions such as {@link #ACTION_SEEK seek} or
+ * {@link #ACTION_GET_STATUS get status}.  It will also appear as a parameter in
  * status update broadcasts to identify the associated playback request.
+ * </p><p>
+ * Each media item is scoped to the session in which it was created.  Therefore media item
+ * ids are only ever used together with session ids.  Media item ids are meaningless
+ * on their own.  When the session is invalidated, all of its media items are also
+ * invalidated.
  * </p>
- * 
- * <h4>Queues</h4>
+ *
+ * <h4>The playback queue</h4>
  * <p>
- * Each successful {@link #ACTION_PLAY play action} has the effect of adding a new media
- * item to a queue of media items to be played.  Queues are created implicitly as part
- * of issuing playback requests and are identified by unique queue ids.
+ * Each session has its own playback queue that consists of the media items that
+ * are pending, playing, buffering or paused.  Items are added to the queue when
+ * a playback request is issued.  Items are removed from the queue when they are no
+ * longer eligible for playback (enter terminal states).
  * </p><p>
- * There is at most one valid queue in existence at any given time for a given route.
- * If an application sends a request that has the effect of creating a new queue then
- * the previously valid queue is cleared and all of its items are canceled before the
- * new queue is created.  In this way, one application can determine when another
- * application has taken control of a route because its own items will all be canceled
- * as soon as the other application begins playing something else.
+ * As described in the {@link MediaItemStatus} class, media items initially
+ * start in a pending state, transition to the playing (or buffering or paused) state
+ * during playback, and end in a finished, canceled, invalidated or error state.
+ * Once the current item enters a terminal state, playback proceeds on to the
+ * next item.
  * </p><p>
- * Queues are intended to hold a small number of items to help media routes optimize
- * the playback experience.  As each item in the queue completes playback, the next item
- * in the queue should begin playing immediately without delay.
+ * In the current version of the protocol, the queue may contain at most one item.
+ * Each play action has the effect of clearing the queue and resetting its state
+ * before the next item is played.
+ * </p>
+ *
+ * <h4>Impact of pause, resume, stop and play actions on the playback queue</h4>
+ * <p>
+ * The pause, resume and stop actions affect the session's whole queue.  Pause causes
+ * the playback queue to be suspended no matter which item is currently playing.
+ * Resume reverses the effects of pause.  Stop clears the queue and also resets
+ * the pause flag just like resume.
  * </p><p>
- * It is usually sufficient for an application to enqueue no more than a few items at a time
- * to ensure continuous playback.  Typically the application will start by enqueuing two
- * media items at once: one item to play now and one item to play next.  When the first
- * item finishes, the second item will begin playing immediately.  The application will
- * receive one status update broadcast indicating that the first item finished playing
- * and another status update broadcast indicating that the second item has started playing.
- * Upon receipt of such broadcasts, the application may choose to enqueue another media
- * item to play once the second one finishes.
+ * As described earlier, the play action has the effect of clearing the queue
+ * and completely resetting its state (like the stop action) then enqueuing a
+ * new media item to be played immediately.  Play is therefore equivalent
+ * to stop followed by an action to enqueue an item.
  * </p><p>
- * Media route providers are required to support queues of at least 3 items.
+ * The play action is also special in that it can be used to create new sessions.
+ * An application with simple needs may find that it only needs to use play
+ * (and occasionally stop) to control playback.
+ * </p>
+ *
+ * <h4>Resolving conflicts between applications</h4>
+ * <p>
+ * When an application has a valid session, it is essentially in control of remote playback
+ * on the route.  No other application can view or modify the remote playback state
+ * of that applicaton's session without knowing its id.
+ * </p><p>
+ * However, other applications can perform actions that have the effect of stopping
+ * playback and invalidating the current session.  When this occurs, the former application
+ * will be informed that it has lost control by way of individual media item status
+ * update broadcasts that indicate that its queued media items have become
+ * {@link MediaItemStatus#PLAYBACK_STATE_INVALIDATED invalidated}.  This broadcast
+ * implies that playback was terminated abnormally by an external cause.
+ * </p><p>
+ * Applications should handle conflicts conservatively to allow other applications to
+ * smoothly assume control over the route.  When a conflict occurs, the currently playing
+ * application should release its session and allow the new application to use the
+ * route until such time as the user intervenes to take over the route again and begin
+ * a new playback session.
+ * </p>
+ *
+ * <h4>Actions</h4>
+ * <p>
+ * The following remote playback actions are defined:
+ * </p><ul>
+ * <li>{@link #ACTION_PLAY Play}: Starts playing content specified by a given Uri
+ * and returns a new media item id to describe the request.  Implicitly creates a new
+ * session if no session id was specified as a parameter.
+ * <li>{@link #ACTION_SEEK Seek}: Sets the content playback position of a specific media item.
+ * <li>{@link #ACTION_GET_STATUS Get status}: Gets the status of a media item
+ * including the item's current playback position and progress.
+ * <li>{@link #ACTION_PAUSE Pause}: Pauses playback of the queue.
+ * <li>{@link #ACTION_RESUME Resume}: Resumes playback of the queue.
+ * <li>{@link #ACTION_STOP Stop}: Stops playback, clears the queue, and resets the
+ * pause state.
+ * </ul>
+ *
+ * <h4>Implementation note</h4>
+ * <p>
+ * Implementations of the remote playback protocol must implement <em>all</em> of the
+ * documented actions, parameters and results.  Note that the documentation is written from
+ * the perspective of a client of the protocol.  In particular, whenever a parameter
+ * is described as being "optional", it is only from the perspective of the client.
+ * Compliant media route provider implementations of this protocol must support all
+ * of the features described herein.
  * </p>
  */
 public final class MediaControlIntent {
@@ -221,75 +278,88 @@
     public static final String CATEGORY_REMOTE_PLAYBACK =
             "android.media.intent.category.REMOTE_PLAYBACK";
 
+    /* Remote playback actions that affect individual items. */
+
     /**
-     * Media control action: Play media item.
+     * Remote playback media control action: Play media item.
      * <p>
      * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
      * media control.
      * </p><p>
      * This action causes a remote playback route to start playing content with
      * the {@link Uri} specified in the {@link Intent}'s {@link Intent#getData() data uri}.
-     * The action returns a media item id which can be used to control playback
-     * using other remote playback actions.
+     * The action returns a media session id and media item id which can be used
+     * to control playback using other remote playback actions.
      * </p><p>
-     * Once initiated, playback of the specified content will be queued and managed
-     * independently by the destination.  The application will receive status updates
-     * as the content is played.
+     * Once initiated, playback of the specified content will be managed independently
+     * by the destination.  The application will receive status updates as the state
+     * of the media item changes.
      * </p><p>
      * If the data uri specifies an HTTP or HTTPS scheme, then the destination is
      * responsible for following HTTP redirects to a reasonable depth of at least 3
      * levels as might typically be handled by a web browser.  If an HTTP error
      * occurs, then the destination should send a {@link MediaItemStatus status update}
      * back to the client indicating the {@link MediaItemStatus#PLAYBACK_STATE_ERROR error}
-     * {@link MediaItemStatus#getPlaybackState() playback state}
-     * and include the {@link MediaItemStatus#EXTRA_HTTP_STATUS_CODE HTTP status code}
-     * and {@link MediaItemStatus#EXTRA_HTTP_RESPONSE_HEADERS response headers}.
+     * {@link MediaItemStatus#getPlaybackState() playback state}.
      * </p>
      *
-     * <h3>Queuing</h3>
+     * <h3>One item at a time</h3>
      * <p>
-     * This request has the effect of implicitly creating a media queue whenever the
-     * application does not specify the {@link #EXTRA_QUEUE_ID} parameter.  Because there
-     * can only be one valid queue at a time, creating a new queue has the side-effect
-     * of invalidating any existing queues and canceling all of their items before
-     * enqueuing the new playback request media item onto the newly created queue.
+     * Each successful play action <em>replaces</em> the previous play action.
+     * If an item is already playing, then it is canceled, the session's playback queue
+     * is cleared and the new item begins playing immediately (regardless of
+     * whether the previously playing item had been paused).
      * </p><p>
-     * If the application specifies an invalid queue id, then the request has no effect
-     * and an error is returned.  The application may then ask that a new queue be
-     * created (and the current one invalidated) by issuing a new playback request without
-     * a queue id parameter.  However, it should only do this at the user's request
-     * (say, by the user explicitly clicking a play button) since another application may
-     * be trying to take control of the route.
+     * Play is therefore equivalent to {@link #ACTION_STOP stop} followed by an action
+     * to enqueue a new media item to be played immediately.
+     * </p>
+     *
+     * <h3>Sessions</h3>
+     * <p>
+     * This request has the effect of implicitly creating a media session whenever the
+     * application does not specify the {@link #EXTRA_SESSION_ID session id} parameter.
+     * Because there can only be at most one valid session at a time, creating a new session
+     * has the side-effect of invalidating any existing sessions and their media items,
+     * then handling the playback request with a new session.
      * </p><p>
-     * For more information on queuing, please refer to the class documentation.
+     * If the application specifies an invalid session id, then an error is returned.
+     * When this happens, the application should assume that its session
+     * is no longer valid.  To obtain a new session, the application may try again
+     * and omit the session id parameter.  However, the application should
+     * only retry requests due to an explicit action performed by the user,
+     * such as the user clicking on a "play" button in the UI, since another
+     * application may be trying to take control of the route and the former
+     * application should try to stay out of its way.
+     * </p><p>
+     * For more information on sessions, queues and media items, please refer to the
+     * class documentation.
      * </p>
      *
      * <h3>Request parameters</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(optional)</i>: specifies the queue id of the queue
-     * to which the new playback request should be appended.  If omitted, a new queue
-     * is created.
-     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <i>(optional)</i>: specifies the initial
+     * <li>{@link #EXTRA_SESSION_ID} <em>(optional)</em>: Specifies the session id of the
+     * session to which the playback request belongs.  If omitted, a new session
+     * is created implicitly.
+     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <em>(optional)</em>: Specifies the initial
      * content playback position as a long integer number of milliseconds from
      * the beginning of the content.
-     * <li>{@link #EXTRA_ITEM_METADATA} <i>(optional)</i>: specifies metadata associated
+     * <li>{@link #EXTRA_ITEM_METADATA} <em>(optional)</em>: Specifies metadata associated
      * with the content such as the title of a song.
-     * <li>{@link #EXTRA_ITEM_HTTP_HEADERS} <i>(optional)</i>: specifies HTTP headers to
-     * supply to the server when fetching the content.
-     * <li>{@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER} <i>(optional)</i>: specifies a
+     * <li>{@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER} <em>(optional)</em>: Specifies a
      * {@link PendingIntent} for a broadcast receiver that will receive status updates
      * about the media item.
      * </ul>
      *
      * <h3>Result data</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the queue id of the queue
-     * to which the new media item was appended.  This will be a new queue in
-     * the case where no queue id was supplied as a parameter.
-     * <li>{@link #EXTRA_ITEM_ID} <i>(required)</i>: specifies an opaque string identifier
-     * to use to refer to the media item in subsequent requests such as {@link #ACTION_CANCEL}.
-     * <li>{@link #EXTRA_ITEM_STATUS} <i>(required)</i>: specifies the initial status of
-     * the item that has been enqueued.
+     * <li>{@link #EXTRA_SESSION_ID} <em>(always returned)</em>: Specifies the session id of the
+     * session that was affected by the request.  This will be a new session in
+     * the case where no session id was supplied as a parameter.
+     * <li>{@link #EXTRA_ITEM_ID} <em>(always returned)</em>: Specifies an opaque string identifier
+     * to use to refer to the media item in subsequent requests such as
+     * {@link #ACTION_GET_STATUS}.
+     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the initial status of
+     * the new media item.
      * </ul>
      *
      * <h3>Status updates</h3>
@@ -304,6 +374,13 @@
      * Refer to {@link MediaItemStatus} for details.
      * </p>
      *
+     * <h3>Errors</h3>
+     * <p>
+     * This action returns an error if a session id was provided but is unknown or
+     * no longer valid, if the item Uri or content type is not supported, or if
+     * any other arguments are invalid.
+     * </p>
+     *
      * <h3>Example</h3>
      * <pre>
      * MediaRouter mediaRouter = MediaRouter.getInstance(context);
@@ -315,8 +392,8 @@
      *     MediaRouter.ControlRequestCallback callback = new MediaRouter.ControlRequestCallback() {
      *         public void onResult(Bundle data) {
      *             // The request succeeded.
-     *             // Playback may be controlled using the returned queue and item id.
-     *             String queueId = data.getString(MediaControlIntent.EXTRA_QUEUE_ID);
+     *             // Playback may be controlled using the returned session and item id.
+     *             String sessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID);
      *             String itemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID);
      *             MediaItemStatus status = MediaItemStatus.fromBundle(data.getBundle(
      *                     MediaControlIntent.EXTRA_ITEM_STATUS));
@@ -332,90 +409,91 @@
      *
      * @see MediaRouter.RouteInfo#sendControlRequest
      * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_CANCEL
      * @see #ACTION_SEEK
      * @see #ACTION_GET_STATUS
+     * @see #ACTION_PAUSE
+     * @see #ACTION_RESUME
+     * @see #ACTION_STOP
      */
     public static final String ACTION_PLAY = "android.media.intent.action.PLAY";
 
     /**
-     * Media control action: Cancel media item playback.
+     * Remote playback media control action: Enqueue media item.
      * <p>
      * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
      * media control.
      * </p><p>
-     * This action causes a remote playback route to cancel playback of the
-     * specified media item and remove it from the queue.
+     * This action works just like {@link #ACTION_PLAY play} except that it does
+     * not clear the queue or reset the pause state when it enqueues the
+     * new media item into the session's playback queue.  This action only
+     * enqueues a media item with no other side-effects on the queue.
      * </p><p>
-     * This action has no effect if the media item's status is
-     * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED} or
-     * {@link MediaItemStatus#PLAYBACK_STATE_ERROR}.
-     * Otherwise the media item's status is set to
-     * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED}, playback of this media item is
-     * stopped if it had been playing and the item is removed from the queue (skipped).
+     * If the queue is currently empty and then the item will play immediately
+     * (assuming the queue is not paused).  Otherwise, the item will play
+     * after all earlier items in the queue have finished or been removed.
      * </p><p>
-     * A status update is sent to the status update receiver indicating the new status
-     * of the item.
+     * The enqueue action can be used to create new sessions just like play.
+     * Its parameters and results are also the same.  Only the queuing behavior
+     * is different.
      * </p>
      *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the queue id of the queue
-     * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} (required): specifies the media item id of media item
-     * to cancel.
-     * </ul>
+     * @see #ACTION_PLAY
      *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_ITEM_STATUS} <i>(required)</i>: specifies the new status of the item.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
+     * @hide Pending API review.
      */
-    public static final String ACTION_CANCEL = "android.media.intent.action.CANCEL";
+    public static final String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
 
     /**
-     * Media control action: Seek media item to a new playback position.
+     * Remote playback media control action: Seek media item to a new playback position.
      * <p>
      * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
      * media control.
      * </p><p>
-     * This action causes a remote playback route to modify the current playback
-     * position of the specified media item.
+     * This action causes a remote playback route to modify the current playback position
+     * of the specified media item.
      * </p><p>
      * This action only affects the playback position of the media item; not its playback state.
-     * If the item is paused, then seeking sets the position but the item remains paused.
-     * Likewise if the item is playing, then seeking will cause playback to jump to the
-     * new position and continue playing from that point.  If the item has not yet started
-     * playing, then the new playback position is be remembered and used as the item's
-     * initial content position when playback eventually begins.
+     * If the playback queue is paused, then seeking sets the position but the item
+     * remains paused.  Likewise if the item is playing, then seeking will cause playback
+     * to jump to the new position and continue playing from that point.  If the item has
+     * not yet started playing, then the new playback position is remembered by the
+     * queue and used as the item's initial content position when playback eventually begins.
+     * </p><p>
+     * If successful, the media item's playback position is changed.
      * </p>
      *
      * <h3>Request parameters</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the queue id of the queue
+     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
      * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <i>(required)</i>: specifies the media item id of
+     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
      * the media item to seek.
-     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <i>(required)</i>: specifies the new
+     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <em>(required)</em>: Specifies the new
      * content position for playback as a long integer number of milliseconds from
      * the beginning of the content.
      * </ul>
      *
      * <h3>Result data</h3>
      * <ul>
-     * <li>{@link #EXTRA_ITEM_STATUS} <i>(required)</i>: specifies the new status of the item.
+     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the new status of
+     * the media item.
      * </ul>
      *
+     * <h3>Errors</h3>
+     * <p>
+     * This action returns an error if the session id or media item id are unknown
+     * or no longer valid, if the content position is invalid, or if the media item
+     * is in a terminal state.
+     * </p>
+     *
      * @see MediaRouter.RouteInfo#sendControlRequest
      * @see #CATEGORY_REMOTE_PLAYBACK
      */
     public static final String ACTION_SEEK = "android.media.intent.action.SEEK";
 
     /**
-     * Media control action: Get media item playback status and progress information.
+     * Remote playback media control action: Get media item playback status
+     * and progress information.
      * <p>
      * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
      * media control.
@@ -426,17 +504,24 @@
      *
      * <h3>Request parameters</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the queue id of the queue
+     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
      * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <i>(required)</i>: specifies the media item id of
+     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
      * the media item to query.
      * </ul>
      *
      * <h3>Result data</h3>
      * <ul>
-     * <li>{@link #EXTRA_ITEM_STATUS} <i>(required)</i>: specifies the current status of the item.
+     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the current status of
+     * the media item.
      * </ul>
      *
+     * <h3>Errors</h3>
+     * <p>
+     * This action returns an error if the session id or media item id are unknown
+     * or no longer valid.
+     * </p>
+     *
      * @see MediaRouter.RouteInfo#sendControlRequest
      * @see #CATEGORY_REMOTE_PLAYBACK
      * @see #EXTRA_ITEM_STATUS_UPDATE_RECEIVER
@@ -444,112 +529,176 @@
     public static final String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
 
     /**
-     * Media control action: Pause media queue playback.
+     * Remote playback media control action: Remove media item from session's queue.
      * <p>
      * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
      * media control.
      * </p><p>
-     * This action causes playback on the specified media queue to be paused.
+     * This action asks a remote playback route to remove the specified media item
+     * from the session's playback queue.  If the current item is removed, then
+     * playback will proceed to the next media item (assuming the queue has not been
+     * paused).
+     * </p><p>
+     * This action does not affect the pause state of the queue.  If the queue was paused
+     * then it remains paused (even if it is now empty) until a resume, stop or play
+     * action is issued that causes the pause state to be cleared.
      * </p>
      *
      * <h3>Request parameters</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the queue id of the queue
-     * to be paused.
+     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
+     * to which the media item belongs.
+     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
+     * the media item to remove.
      * </ul>
      *
      * <h3>Result data</h3>
      * <ul>
-     * <li><i>None</i>
+     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the new status of
+     * the media item.
      * </ul>
      *
+     * <h3>Errors</h3>
+     * <p>
+     * This action returns an error if the session id or media item id are unknown
+     * or no longer valid, or if the media item is in a terminal state (and therefore
+     * no longer in the queue).
+     * </p>
+     *
      * @see MediaRouter.RouteInfo#sendControlRequest
      * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_RESUME_QUEUE
+     *
+     * @hide Pending API review.
      */
-    public static final String ACTION_PAUSE_QUEUE = "android.media.intent.action.PAUSE_QUEUE";
+    public static final String ACTION_REMOVE = "android.media.intent.action.REMOVE";
+
+    /* Remote playback actions that affect the whole playback queue. */
 
     /**
-     * Media control action: Resume media queue playback (unpause).
+     * Remote playback media control action: Pause media playback.
      * <p>
      * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
      * media control.
      * </p><p>
-     * This action causes playback on the specified media queue to be resumed.
-     * Reverses the effects of {@link #ACTION_PAUSE_QUEUE}.
+     * This action causes the playback queue of the specified session to be paused.
      * </p>
      *
      * <h3>Request parameters</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the queue id of the queue
-     * to be resumed.
+     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
+     * whose playback queue is to be paused.
      * </ul>
      *
      * <h3>Result data</h3>
      * <ul>
-     * <li><i>None</i>
+     * <li><em>None</em>
      * </ul>
      *
+     * <h3>Errors</h3>
+     * <p>
+     * This action returns an error if the session id is unknown or no longer valid.
+     * </p>
+     *
      * @see MediaRouter.RouteInfo#sendControlRequest
      * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_PAUSE_QUEUE
+     * @see #ACTION_RESUME
      */
-    public static final String ACTION_RESUME_QUEUE = "android.media.intent.action.RESUME_QUEUE";
+    public static final String ACTION_PAUSE = "android.media.intent.action.PAUSE";
 
     /**
-     * Media control action: Clear media queue.
+     * Remote playback media control action: Resume media playback (unpause).
      * <p>
      * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
      * media control.
      * </p><p>
-     * This action causes all media items in the specified media queue to be canceled
-     * and removed.  The queue is left in an empty state.
+     * This action causes the playback queue of the specified session to be resumed.
+     * Reverses the effects of {@link #ACTION_PAUSE}.
      * </p>
      *
      * <h3>Request parameters</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the queue id of the queue
-     * to be cleared.
+     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
+     * whose playback queue is to be resumed.
      * </ul>
      *
      * <h3>Result data</h3>
      * <ul>
-     * <li><i>None</i>
+     * <li><em>None</em>
      * </ul>
      *
+     * <h3>Errors</h3>
+     * <p>
+     * This action returns an error if the session id is unknown or no longer valid.
+     * </p>
+     *
+     * @see MediaRouter.RouteInfo#sendControlRequest
+     * @see #CATEGORY_REMOTE_PLAYBACK
+     * @see #ACTION_PAUSE
+     */
+    public static final String ACTION_RESUME = "android.media.intent.action.RESUME";
+
+    /**
+     * Remote playback media control action: Stop media playback (clear queue and unpause).
+     * <p>
+     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
+     * media control.
+     * </p><p>
+     * This action causes a remote playback route to stop playback, cancel and remove
+     * all media items from the session's media item queue and, reset the queue's
+     * pause state.
+     * </p><p>
+     * If successful, the status of all media items in the queue is set to
+     * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED canceled} and a status update is sent
+     * to the appropriate status update receivers indicating the new status of each item.
+     * </p>
+     *
+     * <h3>Request parameters</h3>
+     * <ul>
+     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
+     * the session whose playback queue is to be stopped (cleared and unpaused).
+     * </ul>
+     *
+     * <h3>Result data</h3>
+     * <ul>
+     * <li><em>None</em>
+     * </ul>
+     *
+     * <h3>Errors</h3>
+     * <p>
+     * This action returns an error if the session id is unknown or no longer valid.
+     * </p>
+     *
      * @see MediaRouter.RouteInfo#sendControlRequest
      * @see #CATEGORY_REMOTE_PLAYBACK
      */
-    public static final String ACTION_CLEAR_QUEUE = "android.media.intent.action.CLEAR_QUEUE";
+    public static final String ACTION_STOP = "android.media.intent.action.STOP";
 
     /**
-     * Bundle extra: Media queue id.
+     * Bundle extra: Media session id.
      * <p>
-     * An opaque unique identifier returned as a result from {@link #ACTION_PLAY} that
-     * represents the queue of media items to which an item was appended.  Subsequent
-     * playback requests may specify the same queue id to enqueue addition items onto
-     * the same queue.
+     * An opaque unique identifier that identifies the remote playback media session.
+     * The session id is returned by {@link #ACTION_PLAY} along with the media item id
+     * of the requested content.
      * </p><p>
-     * Used with various actions to specify the id of the media queue to be controlled.
+     * Used with various actions to specify the id of the media session to be controlled.
      * </p><p>
      * Included in broadcast intents sent to
      * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER status update receivers} to identify
-     * the queue to which the item in question belongs.
+     * the session to which the item in question belongs.
      * </p><p>
      * The value is a unique string value generated by the media route provider
-     * to represent one particular media queue.
+     * to represent one particular media session.
      * </p>
      *
      * @see #ACTION_PLAY
-     * @see #ACTION_CANCEL
      * @see #ACTION_SEEK
      * @see #ACTION_GET_STATUS
-     * @see #ACTION_PAUSE_QUEUE
-     * @see #ACTION_RESUME_QUEUE
-     * @see #ACTION_CLEAR_QUEUE
+     * @see #ACTION_PAUSE
+     * @see #ACTION_RESUME
+     * @see #ACTION_STOP
      */
-    public static final String EXTRA_QUEUE_ID =
-            "android.media.intent.extra.QUEUE_ID";
+    public static final String EXTRA_SESSION_ID =
+            "android.media.intent.extra.SESSION_ID";
 
     /**
      * Bundle extra: Media item id.
@@ -568,7 +717,6 @@
      * </p>
      *
      * @see #ACTION_PLAY
-     * @see #ACTION_CANCEL
      * @see #ACTION_SEEK
      * @see #ACTION_GET_STATUS
      */
@@ -579,8 +727,8 @@
      * Bundle extra: Media item status.
      * <p>
      * Returned as a result from media item actions such as {@link #ACTION_PLAY},
-     * {@link #ACTION_SEEK}, {@link #ACTION_CANCEL} and {@link #ACTION_GET_STATUS}
-     * to describe the status of the relevant media item.
+     * a{@link #ACTION_SEEK}, and {@link #ACTION_GET_STATUS}
+     * to describe the status of the specified media item.
      * </p><p>
      * Included in broadcast intents sent to
      * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER status update receivers} to provide
@@ -592,7 +740,6 @@
      * </p>
      *
      * @see #ACTION_PLAY
-     * @see #ACTION_CANCEL
      * @see #ACTION_SEEK
      * @see #ACTION_GET_STATUS
      */
@@ -644,6 +791,8 @@
      * </p>
      *
      * @see #ACTION_PLAY
+     *
+     * @hide Pending API review.
      */
     public static final String EXTRA_ITEM_HTTP_HEADERS =
             "android.media.intent.extra.HTTP_HEADERS";
@@ -660,18 +809,19 @@
      * to which the item belongs, the item itself and the item's updated status.
      * </p><p>
      * The same pending intent and broadcast receiver may be shared by any number of
-     * media items since the broadcast intent includes the media queue id and media item id.
+     * media items since the broadcast intent includes the media session id
+     * and media item id.
      * </p><p>
      * The value is a {@link PendingIntent}.
      * </p>
      *
      * <h3>Broadcast extras</h3>
      * <ul>
-     * <li>{@link #EXTRA_QUEUE_ID} <i>(required)</i>: specifies the media queue id of the
-     * queue to which the item in question belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <i>(required)</i>: specifies the media item id of the
+     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
+     * the session to which the item in question belongs.
+     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of the
      * media item in question.
-     * <li>{@link #EXTRA_ITEM_STATUS} <i>(required)</i>: specifies the status of the
+     * <li>{@link #EXTRA_ITEM_STATUS} <em>(required)</em>: Specifies the status of the
      * item as a bundle that can be decoded into a {@link MediaItemStatus} object.
      * </ul>
      *
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaItemStatus.java b/v7/mediarouter/src/android/support/v7/media/MediaItemStatus.java
index 72f4cb0..615efe9 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaItemStatus.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaItemStatus.java
@@ -25,9 +25,10 @@
  * Describes the playback status of a media item.
  * <p>
  * As a media item is played, it transitions through a sequence of states including:
- * {@link #PLAYBACK_STATE_QUEUED queued}, {@link #PLAYBACK_STATE_BUFFERING buffering},
+ * {@link #PLAYBACK_STATE_PENDING pending}, {@link #PLAYBACK_STATE_BUFFERING buffering},
  * {@link #PLAYBACK_STATE_PLAYING playing}, {@link #PLAYBACK_STATE_PAUSED paused},
- * {@link #PLAYBACK_STATE_STOPPED stopped}, {@link #PLAYBACK_STATE_CANCELED canceled},
+ * {@link #PLAYBACK_STATE_FINISHED finished}, {@link #PLAYBACK_STATE_CANCELED canceled},
+ * {@link #PLAYBACK_STATE_INVALIDATED invalidated}, and
  * {@link #PLAYBACK_STATE_ERROR error}.  Refer to the documentation of each state
  * for an explanation of its meaning.
  * </p><p>
@@ -40,7 +41,7 @@
  * as the {@link MediaControlIntent#EXTRA_ITEM_STATUS_UPDATE_RECEIVER status update receiver}
  * for a given {@link MediaControlIntent#ACTION_PLAY playback request}.  Note that
  * the status update receiver will only be invoked for major status changes such as a
- * transition from playing to stopped.
+ * transition from playing to finished.
  * </p><p class="note">
  * The status update receiver will not be invoked for minor progress updates such as
  * changes to playback position or duration.  If the application wants to monitor
@@ -63,12 +64,12 @@
     private final Bundle mBundle;
 
     /**
-     * Playback state: Queued.
+     * Playback state: Pending.
      * <p>
-     * Indicates that the media item is in the queue to be played eventually.
+     * Indicates that the media item has not yet started playback but will be played eventually.
      * </p>
      */
-    public static final int PLAYBACK_STATE_QUEUED = 0;
+    public static final int PLAYBACK_STATE_PENDING = 0;
 
     /**
      * Playback state: Playing.
@@ -81,15 +82,8 @@
     /**
      * Playback state: Paused.
      * <p>
-     * Indicates that playback of the media item has been paused because the
-     * queue was paused.  Playback can be resumed playback by sending
-     * {@link MediaControlIntent#ACTION_RESUME_QUEUE} to resume playback of the queue.
-     * </p><p>
-     * Only the media item at the head of the queue enters the paused state when the
-     * queue is paused because that is the media item that would otherwise have been
-     * {@link #PLAYBACK_STATE_PLAYING playing}; other media items in the queue remain
-     * in the {@link #PLAYBACK_STATE_QUEUED queued} state until the head item
-     * finishes playing or is removed from the queue.
+     * Indicates that playback of the media item has been paused.  Playback can be
+     * resumed using the {@link MediaControlIntent#ACTION_RESUME resume} action.
      * </p>
      */
     public static final int PLAYBACK_STATE_PAUSED = 2;
@@ -98,41 +92,58 @@
      * Playback state: Buffering or seeking to a new position.
      * <p>
      * Indicates that the media item has been temporarily interrupted
-     * to fetch more content.  Playback will resume automatically
+     * to fetch more content.  Playback will continue automatically
      * when enough content has been buffered.
      * </p>
      */
     public static final int PLAYBACK_STATE_BUFFERING = 3;
 
     /**
-     * Playback state: Stopped.
+     * Playback state: Finished.
      * <p>
-     * Indicates that the media item has been stopped permanently either because
-     * it reached the end of the content or because the user ended playback.
+     * Indicates that the media item played to the end of the content and finished normally.
      * </p><p>
-     * A stopped media item cannot be resumed.  To play the content again, the application
-     * must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
-     * a new playback request and obtain a new media item id from that request.
+     * A finished media item cannot be resumed.  To play the content again, the application
+     * must send a new {@link MediaControlIntent#ACTION_PLAY play} action.
      * </p>
      */
-    public static final int PLAYBACK_STATE_STOPPED = 4;
+    public static final int PLAYBACK_STATE_FINISHED = 4;
 
     /**
      * Playback state: Canceled.
      * <p>
-     * Indicates that the media item was canceled permanently.  This may
-     * happen because the media item was removed from the queue, the queue was
-     * cleared by the application, or the queue was invalidated by another playback
-     * request that resulted in the creation of a new queue.
+     * Indicates that the media item was explicitly removed from the queue by the
+     * application.  Items may be canceled and removed from the queue using the
+     * {@link MediaControlIntent#ACTION_STOP stop} action or by issuing
+     * another {@link MediaControlIntent#ACTION_PLAY play} action that has the
+     * side-effect of clearing the queue.
      * </p><p>
-     * A canceled media item cannot be resumed.  To play the content again, the application
-     * must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
-     * a new playback request and obtain a new media item id from that request.
+     * A canceled media item cannot be resumed.  To play the content again, the
+     * application must send a new {@link MediaControlIntent#ACTION_PLAY play} action.
      * </p>
      */
     public static final int PLAYBACK_STATE_CANCELED = 5;
 
     /**
+     * Playback state: Invalidated.
+     * <p>
+     * Indicates that the media item was invalidated permanently and involuntarily.
+     * This state is used to indicate that the media item was invalidated and removed
+     * from the queue because the session to which it belongs was invalidated
+     * (typically by another application taking control of the route).
+     * </p><p>
+     * When invalidation occurs, the application should generally wait for the user
+     * to perform an explicit action, such as clicking on a play button in the UI,
+     * before creating a new media session to avoid unnecessarily interrupting
+     * another application that may have just started using the route.
+     * </p><p>
+     * An invalidated media item cannot be resumed.  To play the content again, the application
+     * must send a new {@link MediaControlIntent#ACTION_PLAY play} action.
+     * </p>
+     */
+    public static final int PLAYBACK_STATE_INVALIDATED = 6;
+
+    /**
      * Playback state: Playback halted or aborted due to an error.
      * <p>
      * Examples of errors are no network connectivity when attempting to retrieve content
@@ -140,11 +151,10 @@
      * content.
      * </p><p>
      * A media item in the error state cannot be resumed.  To play the content again,
-     * the application must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
-     * a new playback request and obtain a new media item id from that request.
+     * the application must send a new {@link MediaControlIntent#ACTION_PLAY play} action.
      * </p>
      */
-    public static final int PLAYBACK_STATE_ERROR = 6;
+    public static final int PLAYBACK_STATE_ERROR = 7;
 
     /**
      * Integer extra: HTTP status code.
@@ -158,6 +168,8 @@
      * The value is an integer HTTP status code, such as 401 (Unauthorized),
      * 404 (Not Found), or 500 (Server Error), or 0 if none.
      * </p>
+     *
+     * @hide Pending API review.
      */
     public static final String EXTRA_HTTP_STATUS_CODE =
             "android.media.status.extra.HTTP_STATUS_CODE";
@@ -173,6 +185,8 @@
      * The value is a {@link android.os.Bundle} of string based key-value pairs
      * that describe the HTTP response headers.
      * </p>
+     *
+     * @hide Pending API review.
      */
     public static final String EXTRA_HTTP_RESPONSE_HEADERS =
             "android.media.status.extra.HTTP_RESPONSE_HEADERS";
@@ -194,13 +208,14 @@
     /**
      * Gets the playback state of the media item.
      *
-     * @return The playback state.  One of {@link #PLAYBACK_STATE_QUEUED},
-     * {@link #PLAYBACK_STATE_PLAYING},
-     * {@link #PLAYBACK_STATE_PAUSED}, {@link #PLAYBACK_STATE_BUFFERING},
-     * {@link #PLAYBACK_STATE_CANCELED}, or {@link #PLAYBACK_STATE_ERROR}.
+     * @return The playback state.  One of {@link #PLAYBACK_STATE_PENDING},
+     * {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
+     * {@link #PLAYBACK_STATE_BUFFERING}, {@link #PLAYBACK_STATE_FINISHED},
+     * {@link #PLAYBACK_STATE_CANCELED}, {@link #PLAYBACK_STATE_INVALIDATED},
+     * or {@link #PLAYBACK_STATE_ERROR}.
      */
     public int getPlaybackState() {
-        return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_CANCELED);
+        return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_ERROR);
     }
 
     /**
@@ -239,7 +254,7 @@
         result.append("timestamp=");
         TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
         result.append(" ms ago");
-        result.append(", playbackState=").append(getPlaybackState());
+        result.append(", playbackState=").append(playbackStateToString(getPlaybackState()));
         result.append(", contentPosition=").append(getContentPosition());
         result.append(", contentDuration=").append(getContentDuration());
         result.append(", extras=").append(getExtras());
@@ -247,6 +262,28 @@
         return result.toString();
     }
 
+    private static String playbackStateToString(int playbackState) {
+        switch (playbackState) {
+            case PLAYBACK_STATE_PENDING:
+                return "pending";
+            case PLAYBACK_STATE_BUFFERING:
+                return "buffering";
+            case PLAYBACK_STATE_PLAYING:
+                return "playing";
+            case PLAYBACK_STATE_PAUSED:
+                return "paused";
+            case PLAYBACK_STATE_FINISHED:
+                return "finished";
+            case PLAYBACK_STATE_CANCELED:
+                return "canceled";
+            case PLAYBACK_STATE_INVALIDATED:
+                return "invalidated";
+            case PLAYBACK_STATE_ERROR:
+                return "error";
+        }
+        return Integer.toString(playbackState);
+    }
+
     /**
      * Converts this object to a bundle for serialization.
      *