Bug 5045498 RemoteControlClient interface

Define a RemoteControlClient interface that applications must
 implement and register to be displayed on the lockscreen.

Change-Id: I67276ae653f203e76727432231f1d76535c31942
diff --git a/Android.mk b/Android.mk
index 752a5f8..a0b80e4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -183,7 +183,7 @@
 	media/java/android/media/IAudioFocusDispatcher.aidl \
 	media/java/android/media/IMediaScannerListener.aidl \
 	media/java/android/media/IMediaScannerService.aidl \
-	media/java/android/media/IRemoteControlClient.aidl \
+	media/java/android/media/IRemoteControlClientDispatcher.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index bf47314..2593065 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -104,6 +104,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/nfc)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SystemUI_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R/com/android/systemui/R.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7a92b35..f9efd3c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.graphics.Bitmap;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -1715,29 +1716,103 @@
     }
 
     /**
+     * Acts as a proxy between AudioService and the RemoteControlClient
+     */
+    private IRemoteControlClientDispatcher mRcClientDispatcher =
+            new IRemoteControlClientDispatcher.Stub() {
+
+        public String getMetadataStringForClient(String clientName, int field) {
+            RemoteControlClient realClient;
+            synchronized(mRcClientMap) {
+                realClient = mRcClientMap.get(clientName);
+            }
+            if (realClient != null) {
+                return realClient.getMetadataString(field);
+            } else {
+                return null;
+            }
+        }
+
+        public int getPlaybackStateForClient(String clientName) {
+            RemoteControlClient realClient;
+            synchronized(mRcClientMap) {
+                realClient = mRcClientMap.get(clientName);
+            }
+            if (realClient != null) {
+                return realClient.getPlaybackState();
+            } else {
+                return 0;
+            }
+        }
+
+        public int getTransportControlFlagsForClient(String clientName) {
+            RemoteControlClient realClient;
+            synchronized(mRcClientMap) {
+                realClient = mRcClientMap.get(clientName);
+            }
+            if (realClient != null) {
+                return realClient.getTransportControlFlags();
+            } else {
+                return 0;
+            }
+        }
+
+        public Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight) {
+            RemoteControlClient realClient;
+            synchronized(mRcClientMap) {
+                realClient = mRcClientMap.get(clientName);
+            }
+            if (realClient != null) {
+                return realClient.getAlbumArt(maxWidth, maxHeight);
+            } else {
+                return null;
+            }
+        }
+    };
+
+    private HashMap<String, RemoteControlClient> mRcClientMap =
+            new HashMap<String, RemoteControlClient>();
+
+    private String getIdForRcClient(RemoteControlClient client) {
+        // client is guaranteed to be non-null
+        return client.toString();
+    }
+
+    /**
      * @hide
-     * Registers the remote control client for providing information to display on the remotes.
+     * Registers the remote control client for providing information to display on the remote
+     * controls.
      * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
      *      that will receive the media button intent, and associated with the remote control
      *      client. This method has no effect if
      *      {@link #registerMediaButtonEventReceiver(ComponentName)} hasn't been called
      *      with the same eventReceiver, or if
      *      {@link #unregisterMediaButtonEventReceiver(ComponentName)} has been called.
-     * @param rcClient the client associated with the event receiver, responsible for providing
-     *      the information to display on the remote control.
+     * @param rcClient the remote control client associated with the event receiver, responsible
+     *      for providing the information to display on the remote control.
      */
     public void registerRemoteControlClient(ComponentName eventReceiver,
-            IRemoteControlClient rcClient) {
-        if (eventReceiver == null) {
+            RemoteControlClient rcClient) {
+        if ((eventReceiver == null) || (rcClient == null)) {
             return;
         }
+        String clientKey = getIdForRcClient(rcClient);
+        synchronized(mRcClientMap) {
+            if (mRcClientMap.containsKey(clientKey)) {
+                return;
+            }
+            mRcClientMap.put(clientKey, rcClient);
+        }
         IAudioService service = getService();
         try {
-            service.registerRemoteControlClient(eventReceiver, rcClient,
+            service.registerRemoteControlClient(eventReceiver, mRcClientDispatcher, clientKey,
                     // used to match media button event receiver and audio focus
                     mContext.getPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
+            synchronized(mRcClientMap) {
+                mRcClientMap.remove(clientKey);
+            }
         }
     }
 
@@ -1748,17 +1823,28 @@
      * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
      *      that receives the media button intent, and associated with the remote control
      *      client.
-     * @see #registerRemoteControlClient(ComponentName)
-
+     * @param rcClient the remote control client to unregister
+     * @see #registerRemoteControlClient(ComponentName, RemoteControlClient)
      */
-    public void unregisterRemoteControlClient(ComponentName eventReceiver) {
-        if (eventReceiver == null) {
+    public void unregisterRemoteControlClient(ComponentName eventReceiver,
+            RemoteControlClient rcClient) {
+        if ((eventReceiver == null) || (rcClient == null)) {
             return;
         }
         IAudioService service = getService();
         try {
-            // unregistering a IRemoteControlClient is equivalent to setting it to null
-            service.registerRemoteControlClient(eventReceiver, null, mContext.getPackageName());
+            // remove locally
+            boolean unregister = true;
+            synchronized(mRcClientMap) {
+                if (mRcClientMap.remove(getIdForRcClient(rcClient)) == null) {
+                    unregister = false;
+                }
+            }
+            if (unregister) {
+                // unregistering a RemoteControlClient is equivalent to setting it to null
+                service.registerRemoteControlClient(eventReceiver, null, null,
+                        mContext.getPackageName());
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
         }
@@ -1767,17 +1853,17 @@
     /**
      * @hide
      * Returns the current remote control client.
-     * @param rcClientId the counter value that matches the extra
-     *     {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT} in the
+     * @param rcClientId the generation counter that matches the extra
+     *     {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the
      *     {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event
-     * @return the current IRemoteControlClient from which information to display on the remote
+     * @return the current RemoteControlClient from which information to display on the remote
      *     control can be retrieved, or null if rcClientId doesn't match the current generation
      *     counter.
      */
-    public IRemoteControlClient getRemoteControlClient(int rcClientId) {
+    public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) {
         IAudioService service = getService();
         try {
-            return service.getRemoteControlClient(rcClientId);
+            return service.getRemoteControlClientDispatcher(rcClientId);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in getRemoteControlClient "+e);
             return null;
@@ -1786,160 +1872,6 @@
 
     /**
      * @hide
-     * Definitions of constants to be used in {@link android.media.IRemoteControlClient}.
-     */
-    public final class RemoteControlParameters {
-        /**
-         * Playback state of an IRemoteControlClient which is stopped.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_STOPPED            = 1;
-        /**
-         * Playback state of an IRemoteControlClient which is paused.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_PAUSED             = 2;
-        /**
-         * Playback state of an IRemoteControlClient which is playing media.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_PLAYING            = 3;
-        /**
-         * Playback state of an IRemoteControlClient which is fast forwarding in the media
-         *    it is currently playing.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_FAST_FORWARDING    = 4;
-        /**
-         * Playback state of an IRemoteControlClient which is fast rewinding in the media
-         *    it is currently playing.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_REWINDING          = 5;
-        /**
-         * Playback state of an IRemoteControlClient which is skipping to the next
-         *    logical chapter (such as a song in a playlist) in the media it is currently playing.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_SKIPPING_FORWARDS  = 6;
-        /**
-         * Playback state of an IRemoteControlClient which is skipping back to the previous
-         *    logical chapter (such as a song in a playlist) in the media it is currently playing.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7;
-        /**
-         * Playback state of an IRemoteControlClient which is buffering data to play before it can
-         *    start or resume playback.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_BUFFERING          = 8;
-        /**
-         * Playback state of an IRemoteControlClient which cannot perform any playback related
-         *    operation because of an internal error. Examples of such situations are no network
-         *    connectivity when attempting to stream data from a server, or expired user credentials
-         *    when trying to play subscription-based content.
-         *
-         * @see android.media.IRemoteControlClient#getPlaybackState()
-         */
-        public final static int PLAYSTATE_ERROR              = 9;
-
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "previous" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS
-         */
-        public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "rewing" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND
-         */
-        public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "play" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY
-         */
-        public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "play/pause" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE
-         */
-        public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "pause" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE
-         */
-        public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "stop" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_STOP
-         */
-        public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "fast forward" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD
-         */
-        public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
-        /**
-         * Flag indicating an IRemoteControlClient makes use of the "next" media key.
-         *
-         * @see android.media.IRemoteControlClient#getTransportControlFlags()
-         * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT
-         */
-        public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
-
-        /**
-         * Flag used to signal that the metadata exposed by the IRemoteControlClient has changed.
-         *
-         * @see #notifyRemoteControlInformationChanged(ComponentName, int)
-         */
-        public final static int FLAG_INFORMATION_CHANGED_METADATA = 1 << 0;
-        /**
-         * Flag used to signal that the transport control buttons supported by the
-         * IRemoteControlClient have changed.
-         * This can for instance happen when playback is at the end of a playlist, and the "next"
-         * operation is not supported anymore.
-         *
-         * @see #notifyRemoteControlInformationChanged(ComponentName, int)
-         */
-        public final static int FLAG_INFORMATION_CHANGED_KEY_MEDIA = 1 << 1;
-        /**
-         * Flag used to signal that the playback state of the IRemoteControlClient has changed.
-         *
-         * @see #notifyRemoteControlInformationChanged(ComponentName, int)
-         */
-        public final static int FLAG_INFORMATION_CHANGED_PLAYSTATE = 1 << 2;
-        /**
-         * Flag used to signal that the album art for the IRemoteControlClient has changed.
-         *
-         * @see #notifyRemoteControlInformationChanged(ComponentName, int)
-         */
-        public final static int FLAG_INFORMATION_CHANGED_ALBUM_ART = 1 << 3;
-    }
-
-    /**
-     * @hide
      * Broadcast intent action indicating that the displays on the remote controls
      * should be updated because a new remote control client is now active. If there is no
      * {@link #EXTRA_REMOTE_CONTROL_CLIENT}, the remote control display should be cleared
@@ -1952,16 +1884,27 @@
 
     /**
      * @hide
-     * The IRemoteControlClient monotonically increasing generation counter.
+     * The IRemoteControlClientDispatcher monotonically increasing generation counter.
      *
      * @see #REMOTE_CONTROL_CLIENT_CHANGED_ACTION
      */
-    public static final String EXTRA_REMOTE_CONTROL_CLIENT =
-            "android.media.EXTRA_REMOTE_CONTROL_CLIENT";
+    public static final String EXTRA_REMOTE_CONTROL_CLIENT_GENERATION =
+            "android.media.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION";
 
     /**
      * @hide
-     * The media button event receiver associated with the IRemoteControlClient.
+     * The name of the RemoteControlClient.
+     * This String is passed as the client name when calling methods from the
+     * IRemoteControlClientDispatcher interface.
+     *
+     * @see #REMOTE_CONTROL_CLIENT_CHANGED_ACTION
+     */
+    public static final String EXTRA_REMOTE_CONTROL_CLIENT_NAME =
+            "android.media.EXTRA_REMOTE_CONTROL_CLIENT_NAME";
+
+    /**
+     * @hide
+     * The media button event receiver associated with the RemoteControlClient.
      * The {@link android.content.ComponentName} value of the event receiver can be retrieved with
      * {@link android.content.ComponentName#unflattenFromString(String)}
      *
@@ -1992,10 +1935,10 @@
      * @param infoFlag the type of information that has changed since this method was last called,
      *      or the event receiver was registered. Use one or multiple of the following flags to
      *      describe what changed:
-     *      {@link RemoteControlParameters#FLAG_INFORMATION_CHANGED_METADATA},
-     *      {@link RemoteControlParameters#FLAG_INFORMATION_CHANGED_KEY_MEDIA},
-     *      {@link RemoteControlParameters#FLAG_INFORMATION_CHANGED_PLAYSTATE},
-     *      {@link RemoteControlParameters#FLAG_INFORMATION_CHANGED_ALBUM_ART}.
+     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_METADATA},
+     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_KEY_MEDIA},
+     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_PLAYSTATE},
+     *      {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_ALBUM_ART}.
      */
     public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) {
         IAudioService service = getService();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index ff2e66b..92829ca 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -34,6 +34,7 @@
 import android.database.ContentObserver;
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
+import android.media.IRemoteControlClientDispatcher;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -2165,7 +2166,7 @@
                     // TODO remove log before release
                     Log.i(TAG, "Clear remote control display");
                     Intent clearIntent = new Intent(AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
-                    // no extra means no IRemoteControlClient, which is a request to clear
+                    // no extra means no IRemoteControlClientDispatcher, which is a request to clear
                     clearIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     mContext.sendBroadcast(clearIntent);
                     break;
@@ -2185,7 +2186,8 @@
                             Log.i(TAG, "Display/update remote control ");
                             Intent rcClientIntent = new Intent(
                                     AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
-                            rcClientIntent.putExtra(AudioManager.EXTRA_REMOTE_CONTROL_CLIENT,
+                            rcClientIntent.putExtra(
+                                    AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION,
                                     mCurrentRcClientGen);
                             rcClientIntent.putExtra(
                                     AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED,
@@ -2193,6 +2195,9 @@
                             rcClientIntent.putExtra(
                                     AudioManager.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER,
                                     rcse.mReceiverComponent.flattenToString());
+                            rcClientIntent.putExtra(
+                                    AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_NAME,
+                                    rcse.mRcClientName);
                             rcClientIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                             mContext.sendBroadcast(rcClientIntent);
                         }
@@ -2888,14 +2893,14 @@
      * This object may be null.
      * Access protected by mCurrentRcLock.
      */
-    private IRemoteControlClient mCurrentRcClient = null;
+    private IRemoteControlClientDispatcher mCurrentRcClient = null;
 
     private final static int RC_INFO_NONE = 0;
     private final static int RC_INFO_ALL =
-        AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_ALBUM_ART |
-        AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_KEY_MEDIA |
-        AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_METADATA |
-        AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_PLAYSTATE;
+        RemoteControlClient.FLAG_INFORMATION_CHANGED_ALBUM_ART |
+        RemoteControlClient.FLAG_INFORMATION_CHANGED_KEY_MEDIA |
+        RemoteControlClient.FLAG_INFORMATION_CHANGED_METADATA |
+        RemoteControlClient.FLAG_INFORMATION_CHANGED_PLAYSTATE;
 
     /**
      * A monotonically increasing generation counter for mCurrentRcClient.
@@ -2907,13 +2912,13 @@
     /**
      * Returns the current remote control client.
      * @param rcClientId the counter value that matches the extra
-     *     {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT} in the
+     *     {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the
      *     {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event
-     * @return the current IRemoteControlClient from which information to display on the remote
-     *     control can be retrieved, or null if rcClientId doesn't match the current generation
-     *     counter.
+     * @return the current IRemoteControlClientDispatcher from which information to display on the
+     *     remote control can be retrieved, or null if rcClientId doesn't match the current
+     *     generation counter.
      */
-    public IRemoteControlClient getRemoteControlClient(int rcClientId) {
+    public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) {
         synchronized(mCurrentRcLock) {
             if (rcClientId == mCurrentRcClientGen) {
                 return mCurrentRcClient;
@@ -2940,7 +2945,7 @@
             Log.w(TAG, "  RemoteControlClient died");
             // remote control client died, make sure the displays don't use it anymore
             //  by setting its remote control client to null
-            registerRemoteControlClient(mRcEventReceiver, null, null/*ignored*/);
+            registerRemoteControlClient(mRcEventReceiver, null, null, null/*ignored*/);
         }
 
         public IBinder getBinder() {
@@ -2952,10 +2957,11 @@
         /** the target for the ACTION_MEDIA_BUTTON events */
         public ComponentName mReceiverComponent;// always non null
         public String mCallingPackageName;
+        public String mRcClientName;
         public int mCallingUid;
 
         /** provides access to the information to display on the remote control */
-        public IRemoteControlClient mRcClient;
+        public IRemoteControlClientDispatcher mRcClient;
         public RcClientDeathHandler mRcClientDeathHandler;
 
         public RemoteControlStackEntry(ComponentName r) {
@@ -3210,7 +3216,7 @@
 
     /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
     public void registerRemoteControlClient(ComponentName eventReceiver,
-            IRemoteControlClient rcClient, String callingPackageName) {
+            IRemoteControlClientDispatcher rcClient, String clientName, String callingPackageName) {
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
                 // store the new display information
@@ -3226,8 +3232,10 @@
                         // save the new remote control client
                         rcse.mRcClient = rcClient;
                         rcse.mCallingPackageName = callingPackageName;
+                        rcse.mRcClientName = clientName;
                         rcse.mCallingUid = Binder.getCallingUid();
                         if (rcClient == null) {
+                            rcse.mRcClientDeathHandler = null;
                             break;
                         }
                         // monitor the new client's death
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c259aa3..7f9ced9 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -18,9 +18,7 @@
 
 import android.content.ComponentName;
 import android.media.IAudioFocusDispatcher;
-import android.media.IRemoteControlClient;
-import android.net.Uri;
-import android.os.Bundle;
+import android.media.IRemoteControlClientDispatcher;
 
 /**
  * {@hide}
@@ -91,9 +89,10 @@
     void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
 
     void registerRemoteControlClient(in ComponentName eventReceiver,
-           in IRemoteControlClient rcClient, in String callingPackageName);
+           in IRemoteControlClientDispatcher rcClient, in String clientName,
+           in String callingPackageName);
 
-    IRemoteControlClient getRemoteControlClient(in int rcClientId);
+    IRemoteControlClientDispatcher getRemoteControlClientDispatcher(in int rcClientId);
 
     void notifyRemoteControlInformationChanged(in ComponentName eventReceiver, int infoFlag);
 
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClientDispatcher.aidl
similarity index 88%
rename from media/java/android/media/IRemoteControlClient.aidl
rename to media/java/android/media/IRemoteControlClientDispatcher.aidl
index 76d178c..98142cc 100644
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ b/media/java/android/media/IRemoteControlClientDispatcher.aidl
@@ -20,13 +20,11 @@
 
 /**
  * @hide
- * Interface for an object that exposes information meant to be consumed by remote controls
- * capable of displaying metadata, album art and media transport control buttons.
- * Such a remote control client object is associated with a media button event receiver
- * when registered through
- * {@link AudioManager#registerRemoteControlClient(ComponentName, IRemoteControlClient)}.
+ * Interface registered by AudioManager to dispatch remote control information requests
+ * to the RemoteControlClient implementation. This is used by AudioService.
+ * {@see AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}.
  */
-interface IRemoteControlClient
+interface IRemoteControlClientDispatcher
 {
     /**
      * Called by a remote control to retrieve a String of information to display.
@@ -49,7 +47,7 @@
      * @return null if the requested field is not supported, or the String matching the
      *       metadata field.
      */
-    String getMetadataString(int field);
+    String getMetadataStringForClient(String clientName, int field);
 
     /**
      * Called by a remote control to retrieve the current playback state.
@@ -64,7 +62,7 @@
      *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_BUFFERING},
      *       {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_ERROR}.
      */
-    int getPlaybackState();
+    int getPlaybackStateForClient(String clientName);
 
     /**
      * Called by a remote control to retrieve the flags for the media transport control buttons
@@ -78,7 +76,7 @@
      *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_FAST_FORWARD},
      *      {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_NEXT}
      */
-    int getTransportControlFlags();
+    int getTransportControlFlagsForClient(String clientName);
 
     /**
      * Called by a remote control to retrieve the album art picture at the requested size.
@@ -90,5 +88,5 @@
      * @return the bitmap for the album art, or null if there isn't any.
      * @see android.graphics.Bitmap
      */
-    Bitmap getAlbumArt(int maxWidth, int maxHeight);
+    Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight);
 }
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
new file mode 100644
index 0000000..c384636
--- /dev/null
+++ b/media/java/android/media/RemoteControlClient.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2011 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.content.ComponentName;
+import android.graphics.Bitmap;
+
+/**
+ * @hide
+ * Interface for an object that exposes information meant to be consumed by remote controls
+ * capable of displaying metadata, album art and media transport control buttons.
+ * Such a remote control client object is associated with a media button event receiver
+ * when registered through
+ * {@link AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}.
+ */
+public interface RemoteControlClient
+{
+    /**
+     * Playback state of a RemoteControlClient which is stopped.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_STOPPED            = 1;
+    /**
+     * Playback state of a RemoteControlClient which is paused.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_PAUSED             = 2;
+    /**
+     * Playback state of a RemoteControlClient which is playing media.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_PLAYING            = 3;
+    /**
+     * Playback state of a RemoteControlClient which is fast forwarding in the media
+     *    it is currently playing.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_FAST_FORWARDING    = 4;
+    /**
+     * Playback state of a RemoteControlClient which is fast rewinding in the media
+     *    it is currently playing.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_REWINDING          = 5;
+    /**
+     * Playback state of a RemoteControlClient which is skipping to the next
+     *    logical chapter (such as a song in a playlist) in the media it is currently playing.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_SKIPPING_FORWARDS  = 6;
+    /**
+     * Playback state of a RemoteControlClient which is skipping back to the previous
+     *    logical chapter (such as a song in a playlist) in the media it is currently playing.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7;
+    /**
+     * Playback state of a RemoteControlClient which is buffering data to play before it can
+     *    start or resume playback.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_BUFFERING          = 8;
+    /**
+     * Playback state of a RemoteControlClient which cannot perform any playback related
+     *    operation because of an internal error. Examples of such situations are no network
+     *    connectivity when attempting to stream data from a server, or expired user credentials
+     *    when trying to play subscription-based content.
+     *
+     * @see android.media.RemoteControlClient#getPlaybackState()
+     */
+    public final static int PLAYSTATE_ERROR              = 9;
+
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "previous" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS
+     */
+    public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "rewing" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND
+     */
+    public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "play" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY
+     */
+    public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "play/pause" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE
+     */
+    public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "pause" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE
+     */
+    public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "stop" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_STOP
+     */
+    public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "fast forward" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD
+     */
+    public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
+    /**
+     * Flag indicating a RemoteControlClient makes use of the "next" media key.
+     *
+     * @see android.media.RemoteControlClient#getTransportControlFlags()
+     * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT
+     */
+    public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
+
+    /**
+     * Flag used to signal that the metadata exposed by the RemoteControlClient has changed.
+     *
+     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
+     */
+    public final static int FLAG_INFORMATION_CHANGED_METADATA = 1 << 0;
+    /**
+     * Flag used to signal that the transport control buttons supported by the
+     * RemoteControlClient have changed.
+     * This can for instance happen when playback is at the end of a playlist, and the "next"
+     * operation is not supported anymore.
+     *
+     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
+     */
+    public final static int FLAG_INFORMATION_CHANGED_KEY_MEDIA = 1 << 1;
+    /**
+     * Flag used to signal that the playback state of the RemoteControlClient has changed.
+     *
+     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
+     */
+    public final static int FLAG_INFORMATION_CHANGED_PLAYSTATE = 1 << 2;
+    /**
+     * Flag used to signal that the album art for the RemoteControlClient has changed.
+     *
+     * @see #notifyRemoteControlInformationChanged(ComponentName, int)
+     */
+    public final static int FLAG_INFORMATION_CHANGED_ALBUM_ART = 1 << 3;
+
+    /**
+     * Called by a remote control to retrieve a String of information to display.
+     * @param field the identifier for a metadata field to retrieve. Valid values are
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
+     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+     * @return null if the requested field is not supported, or the String matching the
+     *       metadata field.
+     */
+    String getMetadataString(int field);
+
+    /**
+     * Called by a remote control to retrieve the current playback state.
+     * @return one of the following values:
+     *       {@link #PLAYSTATE_STOPPED},
+     *       {@link #PLAYSTATE_PAUSED},
+     *       {@link #PLAYSTATE_PLAYING},
+     *       {@link #PLAYSTATE_FAST_FORWARDING},
+     *       {@link #PLAYSTATE_REWINDING},
+     *       {@link #PLAYSTATE_SKIPPING_FORWARDS},
+     *       {@link #PLAYSTATE_SKIPPING_BACKWARDS},
+     *       {@link #PLAYSTATE_BUFFERING},
+     *       {@link #PLAYSTATE_ERROR}.
+     */
+    int getPlaybackState();
+
+    /**
+     * Called by a remote control to retrieve the flags for the media transport control buttons
+     * that this client supports.
+     * @see {@link #FLAG_KEY_MEDIA_PREVIOUS},
+     *      {@link #FLAG_KEY_MEDIA_REWIND},
+     *      {@link #FLAG_KEY_MEDIA_PLAY},
+     *      {@link #FLAG_KEY_MEDIA_PLAY_PAUSE},
+     *      {@link #FLAG_KEY_MEDIA_PAUSE},
+     *      {@link #FLAG_KEY_MEDIA_STOP},
+     *      {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
+     *      {@link #FLAG_KEY_MEDIA_NEXT}
+     */
+    int getTransportControlFlags();
+
+    /**
+     * Called by a remote control to retrieve the album art picture at the requested size.
+     * Note that returning a bitmap smaller than the maximum requested dimension is accepted
+     * and it will be scaled as needed, but exceeding the maximum dimensions may produce
+     * unspecified results, such as the image being cropped or simply not being displayed.
+     * @param maxWidth the maximum width of the requested bitmap expressed in pixels.
+     * @param maxHeight the maximum height of the requested bitmap expressed in pixels.
+     * @return the bitmap for the album art, or null if there isn't any.
+     * @see android.graphics.Bitmap
+     */
+    Bitmap getAlbumArt(int maxWidth, int maxHeight);
+}