Merge "Bug 5300223 RemoteControlClient uses PendingIntent for media button events" into ics-factoryrom
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index 29ad15b..f9c2cce 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -21,6 +21,8 @@
 import com.android.internal.widget.LockScreenWidgetCallback;
 import com.android.internal.widget.LockScreenWidgetInterface;
 
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -68,7 +70,7 @@
     private int mClientGeneration;
     private Metadata mMetadata = new Metadata();
     private boolean mAttached;
-    private ComponentName mClientName;
+    private PendingIntent mClientIntent;
     private int mTransportControlFlags;
     private int mPlayState;
     private AudioManager mAudioManager;
@@ -116,7 +118,7 @@
                     }
                 }
                 mClientGeneration = msg.arg1;
-                mClientName = (ComponentName) msg.obj;
+                mClientIntent = (PendingIntent) msg.obj;
                 break;
 
             }
@@ -174,12 +176,12 @@
             }
         }
 
-        public void setCurrentClientId(int clientGeneration, ComponentName clientEventReceiver,
+        public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
                 boolean clearing) throws RemoteException {
             Handler handler = mLocalHandler.get();
             if (handler != null) {
                 handler.obtainMessage(MSG_SET_GENERATION_ID,
-                    clientGeneration, (clearing ? 1 : 0), clientEventReceiver).sendToTarget();
+                    clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
             }
         }
     };
@@ -365,16 +367,27 @@
     }
 
     private void sendMediaButtonClick(int keyCode) {
-        // TODO: target to specific player based on mClientName
+        // use the registered PendingIntent that will be processed by the registered
+        //    media button event receiver, which is the component of mClientIntent
         KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
         Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
         intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-        getContext().sendOrderedBroadcast(intent, null);
+        try {
+            mClientIntent.send(getContext(), 0, intent);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Error sending intent for media button down: "+e);
+            e.printStackTrace();
+        }
 
         keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
         intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
         intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-        getContext().sendOrderedBroadcast(intent, null);
+        try {
+            mClientIntent.send(getContext(), 0, intent);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Error sending intent for media button up: "+e);
+            e.printStackTrace();
+        }
     }
 
     public void setCallback(LockScreenWidgetCallback callback) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index cd8bb1d..a0881a7 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -18,8 +18,10 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.os.Binder;
@@ -1684,17 +1686,42 @@
      * Register a component to be the sole receiver of MEDIA_BUTTON intents.
      * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
      *      that will receive the media button intent. This broadcast receiver must be declared
-     *      in the application manifest.
+     *      in the application manifest. The package of the component must match that of
+     *      the context you're registering from.
      */
     public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
         if (eventReceiver == null) {
             return;
         }
+        if (!eventReceiver.getPackageName().equals(mContext.getPackageName())) {
+            Log.e(TAG, "registerMediaButtonEventReceiver() error: " +
+                    "receiver and context package names don't match");
+            return;
+        }
+        // construct a PendingIntent for the media button and register it
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        //     the associated intent will be handled by the component being registered
+        mediaButtonIntent.setComponent(eventReceiver);
+        PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+        registerMediaButtonIntent(pi, eventReceiver);
+    }
+
+    /**
+     * @hide
+     * no-op if (pi == null) or (eventReceiver == null)
+     */
+    public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
+        if ((pi == null) || (eventReceiver == null)) {
+            Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
+            return;
+        }
         IAudioService service = getService();
         try {
-            service.registerMediaButtonEventReceiver(eventReceiver);
+            // pi != null
+            service.registerMediaButtonIntent(pi, eventReceiver);
         } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in registerMediaButtonEventReceiver"+e);
+            Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
         }
     }
 
@@ -1707,14 +1734,26 @@
         if (eventReceiver == null) {
             return;
         }
-        IAudioService service = getService();
-        try {
-            service.unregisterMediaButtonEventReceiver(eventReceiver);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiver"+e);
-        }
+        // construct a PendingIntent for the media button and unregister it
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        //     the associated intent will be handled by the component being registered
+        mediaButtonIntent.setComponent(eventReceiver);
+        PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+        unregisterMediaButtonIntent(pi, eventReceiver);
     }
 
+    /**
+     * @hide
+     */
+    public void unregisterMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
+        IAudioService service = getService();
+        try {
+            service.unregisterMediaButtonIntent(pi, eventReceiver);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
+        }
+    }
 
     /**
      * Registers the remote control client for providing information to display on the remote
@@ -1724,14 +1763,13 @@
      * @see RemoteControlClient
      */
     public void registerRemoteControlClient(RemoteControlClient rcClient) {
-        if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
+        if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
             return;
         }
         IAudioService service = getService();
         try {
-            service.registerRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
+            service.registerRemoteControlClient(rcClient.getRcMediaIntent(),   /* mediaIntent   */
                     rcClient.getIRemoteControlClient(),                        /* rcClient      */
-                    rcClient.toString(),                                       /* clientName    */
                     // used to match media button event receiver and audio focus
                     mContext.getPackageName());                                /* packageName   */
         } catch (RemoteException e) {
@@ -1746,13 +1784,13 @@
      * @see #registerRemoteControlClient(RemoteControlClient)
      */
     public void unregisterRemoteControlClient(RemoteControlClient rcClient) {
-        if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
+        if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
             return;
         }
         IAudioService service = getService();
         try {
-            service.unregisterRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
-                    rcClient.getIRemoteControlClient());                         /* rcClient      */
+            service.unregisterRemoteControlClient(rcClient.getRcMediaIntent(), /* mediaIntent   */
+                    rcClient.getIRemoteControlClient());                       /* rcClient      */
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index db27cfd..8895c9e 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -18,6 +18,8 @@
 
 import android.app.ActivityManagerNative;
 import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
@@ -35,6 +37,7 @@
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
@@ -2084,7 +2087,7 @@
             }
         }
 
-        private void persistMediaButtonReceiver(ComponentName receiver) {
+        private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
             Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
                     receiver == null ? "" : receiver.flattenToString());
         }
@@ -2201,7 +2204,7 @@
                     break;
 
                 case MSG_PERSIST_MEDIABUTTONRECEIVER:
-                    persistMediaButtonReceiver( (ComponentName) msg.obj );
+                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
                     break;
 
                 case MSG_RCDISPLAY_CLEAR:
@@ -2894,14 +2897,22 @@
                 }
                 synchronized(mRCStack) {
                     if (!mRCStack.empty()) {
-                        // create a new intent specifically aimed at the current registered listener
+                        // create a new intent to fill in the extras of the registered PendingIntent
                         Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-                        targetedIntent.putExtras(intent.getExtras());
-                        targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
-                        // trap the current broadcast
-                        abortBroadcast();
-                        //Log.v(TAG, " Sending intent" + targetedIntent);
-                        context.sendBroadcast(targetedIntent, null);
+                        Bundle extras = intent.getExtras();
+                        if (extras != null) {
+                            targetedIntent.putExtras(extras);
+                            // trap the current broadcast
+                            abortBroadcast();
+                            //Log.v(TAG, " Sending intent" + targetedIntent);
+                            // send the intent that was registered by the client
+                            try {
+                                mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
+                            } catch (CanceledException e) {
+                                Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
+                                e.printStackTrace();
+                            }
+                        }
                     }
                 }
             }
@@ -2936,18 +2947,18 @@
      */
     private class RcClientDeathHandler implements IBinder.DeathRecipient {
         private IBinder mCb; // To be notified of client's death
-        private ComponentName mRcEventReceiver;
+        private PendingIntent mMediaIntent;
 
-        RcClientDeathHandler(IBinder cb, ComponentName eventReceiver) {
+        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
             mCb = cb;
-            mRcEventReceiver = eventReceiver;
+            mMediaIntent = pi;
         }
 
         public void binderDied() {
             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, null/*ignored*/);
+            registerRemoteControlClient(mMediaIntent, null, null/*ignored*/);
         }
 
         public IBinder getBinder() {
@@ -2956,18 +2967,29 @@
     }
 
     private static class RemoteControlStackEntry {
-        /** the target for the ACTION_MEDIA_BUTTON events */
-        public ComponentName mReceiverComponent;// always non null
+        /**
+         * The target for the ACTION_MEDIA_BUTTON events.
+         * Always non null.
+         */
+        public PendingIntent mMediaIntent;
+        /**
+         * The registered media button event receiver.
+         * Always non null.
+         */
+        public ComponentName mReceiverComponent;
         public String mCallingPackageName;
-        public String mRcClientName;
         public int mCallingUid;
-
-        /** provides access to the information to display on the remote control */
+        /**
+         * Provides access to the information to display on the remote control.
+         * May be null (when a media button event receiver is registered,
+         *     but no remote control client has been registered) */
         public IRemoteControlClient mRcClient;
         public RcClientDeathHandler mRcClientDeathHandler;
 
-        public RemoteControlStackEntry(ComponentName r) {
-            mReceiverComponent = r;
+        /** precondition: mediaIntent != null, eventReceiver != null */
+        public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
+            mMediaIntent = mediaIntent;
+            mReceiverComponent = eventReceiver;
             mCallingUid = -1;
             mRcClient = null;
         }
@@ -3003,7 +3025,8 @@
             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
             while(stackIterator.hasNext()) {
                 RemoteControlStackEntry rcse = stackIterator.next();
-                pw.println("     receiver: " + rcse.mReceiverComponent +
+                pw.println("  pi: " + rcse.mMediaIntent +
+                        "  -- ercvr: " + rcse.mReceiverComponent +
                         "  -- client: " + rcse.mRcClient +
                         "  -- uid: " + rcse.mCallingUid);
             }
@@ -3035,7 +3058,6 @@
                     mAudioHandler.sendMessage(
                             mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
                                     null));
-                    return;
                 } else if (oldTop != mRCStack.peek()) {
                     // the top of the stack has changed, save it in the system settings
                     // by posting a message to persist it
@@ -3049,25 +3071,32 @@
 
     /**
      * Helper function:
-     * Restore remote control receiver from the system settings
+     * Restore remote control receiver from the system settings.
      */
     private void restoreMediaButtonReceiver() {
         String receiverName = Settings.System.getString(mContentResolver,
                 Settings.System.MEDIA_BUTTON_RECEIVER);
         if ((null != receiverName) && !receiverName.isEmpty()) {
-            ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName);
-            registerMediaButtonEventReceiver(receiverComponentName);
+            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
+            // construct a PendingIntent targeted to the restored component name
+            // for the media button and register it
+            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+            //     the associated intent will be handled by the component being registered
+            mediaButtonIntent.setComponent(eventReceiver);
+            PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+            registerMediaButtonIntent(pi, eventReceiver);
         }
-        // upon restoring (e.g. after boot), do we want to refresh all remotes?
     }
 
     /**
      * Helper function:
-     * Set the new remote control receiver at the top of the RC focus stack
+     * Set the new remote control receiver at the top of the RC focus stack.
+     * precondition: mediaIntent != null, target != null
      */
-    private void pushMediaButtonReceiver(ComponentName newReceiver) {
+    private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
         // already at top of stack?
-        if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
+        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
             return;
         }
         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
@@ -3075,31 +3104,32 @@
         boolean wasInsideStack = false;
         while(stackIterator.hasNext()) {
             rcse = (RemoteControlStackEntry)stackIterator.next();
-            if(rcse.mReceiverComponent.equals(newReceiver)) {
+            if(rcse.mMediaIntent.equals(mediaIntent)) {
                 wasInsideStack = true;
                 stackIterator.remove();
                 break;
             }
         }
         if (!wasInsideStack) {
-            rcse = new RemoteControlStackEntry(newReceiver);
+            rcse = new RemoteControlStackEntry(mediaIntent, target);
         }
         mRCStack.push(rcse);
 
         // post message to persist the default media button receiver
         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
-                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, newReceiver/*obj*/) );
+                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
     }
 
     /**
      * Helper function:
-     * Remove the remote control receiver from the RC focus stack
+     * Remove the remote control receiver from the RC focus stack.
+     * precondition: pi != null
      */
-    private void removeMediaButtonReceiver(ComponentName newReceiver) {
+    private void removeMediaButtonReceiver(PendingIntent pi) {
         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
         while(stackIterator.hasNext()) {
             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
-            if(rcse.mReceiverComponent.equals(newReceiver)) {
+            if(rcse.mMediaIntent.equals(pi)) {
                 stackIterator.remove();
                 break;
             }
@@ -3110,8 +3140,8 @@
      * Helper function:
      * Called synchronized on mRCStack
      */
-    private boolean isCurrentRcController(ComponentName eventReceiver) {
-        if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(eventReceiver)) {
+    private boolean isCurrentRcController(PendingIntent pi) {
+        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
             return true;
         }
         return false;
@@ -3124,12 +3154,12 @@
      * Update the remote control displays with the new "focused" client generation
      */
     private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
-            ComponentName newClientEventReceiver, boolean clearing) {
+            PendingIntent newMediaIntent, boolean clearing) {
         // NOTE: Only one IRemoteControlDisplay supported in this implementation
         if (mRcDisplay != null) {
             try {
                 mRcDisplay.setCurrentClientId(
-                        newClientGeneration, newClientEventReceiver, clearing);
+                        newClientGeneration, newMediaIntent, clearing);
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
                 // if we had a display before, stop monitoring its death
@@ -3167,10 +3197,9 @@
      *    where the display should be cleared.
      */
     private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
-            ComponentName newClientEventReceiver, boolean clearing) {
+            PendingIntent newMediaIntent, boolean clearing) {
         // send the new valid client generation ID to all displays
-        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newClientEventReceiver,
-                clearing);
+        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
         // send the new valid client generation ID to all clients
         setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
     }
@@ -3186,7 +3215,7 @@
                 mCurrentRcClientGen++;
                 // synchronously update the displays and clients with the new client generation
                 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
-                        null /*event receiver*/, true /*clearing*/);
+                        null /*newMediaIntent*/, true /*clearing*/);
             }
         }
     }
@@ -3204,7 +3233,7 @@
                     // synchronously update the displays and clients with
                     //      the new client generation
                     setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
-                            rcse.mReceiverComponent /*event receiver*/,
+                            rcse.mMediaIntent /*newMediaIntent*/,
                             false /*clearing*/);
 
                     // tell the current client that it needs to send info
@@ -3301,27 +3330,34 @@
         updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
     }
 
-    /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
-    public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
-        Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);
+    /** 
+     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
+     * precondition: mediaIntent != null, target != null
+     */
+    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
+        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
 
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
-                pushMediaButtonReceiver(eventReceiver);
+                pushMediaButtonReceiver(mediaIntent, eventReceiver);
                 // new RC client, assume every type of information shall be queried
                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
         }
     }
 
-    /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
-    public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
-        Log.i(TAG, "  Remote Control   unregisterMediaButtonEventReceiver() for " + eventReceiver);
+    /** 
+     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
+     * precondition: mediaIntent != null, eventReceiver != null
+     */
+    public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
+    {
+        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
 
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
-                boolean topOfStackWillChange = isCurrentRcController(eventReceiver);
-                removeMediaButtonReceiver(eventReceiver);
+                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
+                removeMediaButtonReceiver(mediaIntent);
                 if (topOfStackWillChange) {
                     // current RC client will change, assume every type of info needs to be queried
                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
@@ -3331,8 +3367,8 @@
     }
 
     /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
-    public void registerRemoteControlClient(ComponentName eventReceiver,
-            IRemoteControlClient rcClient, String clientName, String callingPackageName) {
+    public void registerRemoteControlClient(PendingIntent mediaIntent,
+            IRemoteControlClient rcClient, String callingPackageName) {
         if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
@@ -3340,7 +3376,7 @@
                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
                 while(stackIterator.hasNext()) {
                     RemoteControlStackEntry rcse = stackIterator.next();
-                    if(rcse.mReceiverComponent.equals(eventReceiver)) {
+                    if(rcse.mMediaIntent.equals(mediaIntent)) {
                         // already had a remote control client?
                         if (rcse.mRcClientDeathHandler != null) {
                             // stop monitoring the old client's death
@@ -3357,7 +3393,6 @@
                             }
                         }
                         rcse.mCallingPackageName = callingPackageName;
-                        rcse.mRcClientName = clientName;
                         rcse.mCallingUid = Binder.getCallingUid();
                         if (rcClient == null) {
                             rcse.mRcClientDeathHandler = null;
@@ -3366,7 +3401,7 @@
                         // monitor the new client's death
                         IBinder b = rcClient.asBinder();
                         RcClientDeathHandler rcdh =
-                                new RcClientDeathHandler(b, rcse.mReceiverComponent);
+                                new RcClientDeathHandler(b, rcse.mMediaIntent);
                         try {
                             b.linkToDeath(rcdh, 0);
                         } catch (RemoteException e) {
@@ -3380,7 +3415,7 @@
                 }
                 // if the eventReceiver is at the top of the stack
                 // then check for potential refresh of the remote controls
-                if (isCurrentRcController(eventReceiver)) {
+                if (isCurrentRcController(mediaIntent)) {
                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                 }
             }
@@ -3388,24 +3423,23 @@
     }
 
     /**
-     * see AudioManager.unregisterRemoteControlClient(ComponentName eventReceiver, ...)
+     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
      * rcClient is guaranteed non-null
      */
-    public void unregisterRemoteControlClient(ComponentName eventReceiver,
+    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
             IRemoteControlClient rcClient) {
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
                 while(stackIterator.hasNext()) {
                     RemoteControlStackEntry rcse = stackIterator.next();
-                    if ((rcse.mReceiverComponent.equals(eventReceiver))
+                    if ((rcse.mMediaIntent.equals(mediaIntent))
                             && rcClient.equals(rcse.mRcClient)) {
                         // we found the IRemoteControlClient to unregister
                         // stop monitoring its death
                         rcse.unlinkToRcClientDeath();
                         // reset the client-related fields
                         rcse.mRcClient = null;
-                        rcse.mRcClientName = null;
                         rcse.mRcClientDeathHandler = null;
                         rcse.mCallingPackageName = null;
                     }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7bf9814..5294d36 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.media.IAudioFocusDispatcher;
 import android.media.IRemoteControlClient;
@@ -85,13 +86,12 @@
     
     void unregisterAudioFocusClient(String clientId);
 
-    void registerMediaButtonEventReceiver(in ComponentName eventReceiver);
+    oneway void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
+    oneway void unregisterMediaButtonIntent(in PendingIntent pi,  in ComponentName c);
 
-    void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
-
-    oneway void registerRemoteControlClient(in ComponentName eventReceiver,
-           in IRemoteControlClient rcClient, in String clientName, in String callingPackageName);
-    oneway void unregisterRemoteControlClient(in ComponentName eventReceiver,
+    oneway void registerRemoteControlClient(in PendingIntent mediaIntent,
+           in IRemoteControlClient rcClient, in String callingPackageName);
+    oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
            in IRemoteControlClient rcClient);
 
     oneway void   registerRemoteControlDisplay(in IRemoteControlDisplay rcd);
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
index fd50b7e..e15b07c 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.graphics.Bitmap;
 import android.os.Bundle;
@@ -31,14 +32,12 @@
     /**
      * Sets the generation counter of the current client that is displayed on the remote control.
      * @param clientGeneration the new RemoteControlClient generation
-     * @param clientEventReceiver the media button event receiver associated with the client.
-     *    May be null, which implies there is no registered media button event receiver. This
-     *    parameter is supplied as an optimization so a display can directly target media button
-     *    events to the client.
+     * @param clientMediaIntent the PendingIntent associated with the client.
+     *    May be null, which implies there is no registered media button event receiver.
      * @param clearing true if the new client generation value maps to a remote control update
      *    where the display should be cleared.
      */
-    void setCurrentClientId(int clientGeneration, in ComponentName clientEventReceiver,
+    void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent,
             boolean clearing);
 
     void setPlaybackState(int generationId, int state);
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index cdebba0..5dea87f 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -34,6 +34,7 @@
 import java.lang.IllegalArgumentException;
 
 /**
+ * TODO javadoc update for ComponentName - PendingIntent change
  * RemoteControlClient enables exposing information meant to be consumed by remote controls
  * capable of displaying metadata, artwork and media transport control buttons.
  * A remote control client object is associated with a media button event receiver. This
@@ -205,50 +206,6 @@
     public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3;
 
     /**
-     * @hide
-     * TODO remove after modifying known (internal) media apps using this API
-     * Class constructor.
-     * @param mediaButtonEventReceiver The receiver for the media button events. It needs to have
-     *     been registered with {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)}
-     *     before this new RemoteControlClient can itself be registered with
-     *     {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
-     * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
-     * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
-     */
-    public RemoteControlClient(ComponentName mediaButtonEventReceiver) {
-        mRcEventReceiver = mediaButtonEventReceiver;
-
-        Looper looper;
-        if ((looper = Looper.myLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else if ((looper = Looper.getMainLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else {
-            mEventHandler = null;
-            Log.e(TAG, "RemoteControlClient() couldn't find main application thread");
-        }
-    }
-
-    /**
-     * @hide
-     * TODO remove after modifying known (internal) media apps using this API
-     * Class constructor for a remote control client whose internal event handling
-     * happens on a user-provided Looper.
-     * @param mediaButtonEventReceiver The receiver for the media button events. It needs to have
-     *     been registered with {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)}
-     *     before this new RemoteControlClient can itself be registered with
-     *     {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
-     * @param looper The Looper running the event loop.
-     * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
-     * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
-     */
-    public RemoteControlClient(ComponentName mediaButtonEventReceiver, Looper looper) {
-        mRcEventReceiver = mediaButtonEventReceiver;
-
-        mEventHandler = new EventHandler(this, looper);
-    }
-
-    /**
      * Class constructor.
      * @param mediaButtonIntent The intent that will be sent for the media button events sent
      *     by remote controls.
@@ -262,8 +219,7 @@
      * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
      */
     public RemoteControlClient(PendingIntent mediaButtonIntent) {
-        // TODO implement using PendingIntent instead of ComponentName
-        mRcEventReceiver = null;
+        mRcMediaIntent = mediaButtonIntent;
 
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
@@ -292,8 +248,7 @@
      * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
      */
     public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) {
-        // TODO implement using PendingIntent instead of ComponentName
-        mRcEventReceiver = null;
+        mRcMediaIntent = mediaButtonIntent;
 
         mEventHandler = new EventHandler(this, looper);
     }
@@ -614,9 +569,10 @@
     private int mInternalClientGenId = -2;
 
     /**
-     * The media button event receiver associated with this remote control client
+     * The media button intent description associated with this remote control client
+     * (can / should include target component for intent handling)
      */
-    private final ComponentName mRcEventReceiver;
+    private final PendingIntent mRcMediaIntent;
 
     /**
      * The remote control display to which this client will send information.
@@ -626,10 +582,10 @@
 
     /**
      * @hide
-     * Accessor to media button event receiver
+     * Accessor to media button intent description (includes target component)
      */
-    public ComponentName getRcEventReceiver() {
-        return mRcEventReceiver;
+    public PendingIntent getRcMediaIntent() {
+        return mRcMediaIntent;
     }
     /**
      * @hide