Merge "Move media key processing to sessions"
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1dcfcb8..3a3f76d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -428,7 +428,6 @@
     public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE;
 
     private static IAudioService sService;
-    private MediaSessionLegacyHelper mSessionHelper;
 
     /**
      * @hide
@@ -439,9 +438,6 @@
                 com.android.internal.R.bool.config_useMasterVolume);
         mUseVolumeKeySounds = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useVolumeKeySounds);
-        if (USE_SESSIONS) {
-            mSessionHelper = MediaSessionLegacyHelper.getHelper(context);
-        }
     }
 
     private static IAudioService getService()
@@ -478,11 +474,16 @@
      *     or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
      */
     public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
-        IAudioService service = getService();
-        try {
-            service.dispatchMediaKeyEvent(keyEvent);
-        } catch (RemoteException e) {
-            Log.e(TAG, "dispatchMediaKeyEvent threw exception ", e);
+        if (USE_SESSIONS) {
+            MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+            helper.sendMediaButtonEvent(keyEvent, false);
+        } else {
+            IAudioService service = getService();
+            try {
+                service.dispatchMediaKeyEvent(keyEvent);
+            } catch (RemoteException e) {
+                Log.e(TAG, "dispatchMediaKeyEvent threw exception ", e);
+            }
         }
     }
 
@@ -2178,7 +2179,8 @@
             Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
         }
         if (USE_SESSIONS) {
-            mSessionHelper.addMediaButtonListener(pi, mContext);
+            MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+            helper.addMediaButtonListener(pi, mContext);
         }
     }
 
@@ -2254,7 +2256,8 @@
             Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
         }
         if (USE_SESSIONS) {
-            mSessionHelper.removeMediaButtonListener(pi);
+            MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+            helper.removeMediaButtonListener(pi);
         }
     }
 
@@ -2281,7 +2284,7 @@
             Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
         }
         if (USE_SESSIONS) {
-            rcClient.registerWithSession(mSessionHelper);
+            rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
         }
     }
 
@@ -2303,7 +2306,7 @@
             Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
         }
         if (USE_SESSIONS) {
-            rcClient.unregisterWithSession(mSessionHelper);
+            rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext));
         }
     }
 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 724022b..bb8cfa6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -46,6 +46,7 @@
 import android.hardware.usb.UsbManager;
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
+import android.media.session.MediaSessionLegacyHelper;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Environment;
@@ -108,6 +109,10 @@
     /** Debug volumes */
     protected static final boolean DEBUG_VOL = false;
 
+    /** Reroute calls to media session apis */
+    private static final boolean USE_SESSIONS = true;
+    private static final boolean DEBUG_SESSIONS = true;
+
     /** How long to delay before persisting a change in volume/ringer mode. */
     private static final int PERSIST_DELAY = 500;
 
@@ -3472,7 +3477,7 @@
                 if (volume < 0) {
                     volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
                 } else {
-                    volFloat = (float) volume / 1000.0f;
+                    volFloat = volume / 1000.0f;
                 }
 
                 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
@@ -3554,7 +3559,7 @@
                     }
                     Settings.System.putFloatForUser(mContentResolver,
                                                     Settings.System.VOLUME_MASTER,
-                                                    (float)msg.arg1 / (float)1000.0,
+                                                    msg.arg1 / (float)1000.0,
                                                     UserHandle.USER_CURRENT);
                     break;
 
@@ -4325,11 +4330,27 @@
     }
 
     public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
-        mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+        if (USE_SESSIONS) {
+            if (DEBUG_SESSIONS) {
+                int pid = getCallingPid();
+                Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
+            }
+            MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
+        } else {
+            mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+        }
     }
 
     public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
-        mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+        if (USE_SESSIONS) {
+            if (DEBUG_SESSIONS) {
+                int pid = getCallingPid();
+                Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
+            }
+            MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
+        } else {
+            mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+        }
     }
 
     //==========================================================================================
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 1552513..7b0412e 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -28,7 +28,7 @@
  */
 oneway interface ISessionCallback {
     void onCommand(String command, in Bundle extras, in ResultReceiver cb);
-    void onMediaButton(in Intent mediaButtonIntent);
+    void onMediaButton(in Intent mediaButtonIntent, in ResultReceiver cb);
     void onRequestRouteChange(in RouteInfo route);
     void onRouteConnected(in RouteInfo route, in RouteOptions options);
     void onRouteDisconnected(in RouteInfo route, int reason);
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index e341647..38b92932 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -19,6 +19,7 @@
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.os.Bundle;
+import android.view.KeyEvent;
 
 /**
  * Interface to the MediaSessionManagerService
@@ -27,4 +28,5 @@
 interface ISessionManager {
     ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
     List<IBinder> getSessions(in ComponentName compName, int userId);
+    void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index c07229d..e1eae09 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -35,6 +35,7 @@
  */
 public class MediaSessionLegacyHelper {
     private static final String TAG = "MediaSessionHelper";
+    private static final boolean DEBUG = true;
 
     private static final Object sLock = new Object();
     private static MediaSessionLegacyHelper sInstance;
@@ -52,6 +53,9 @@
     }
 
     public static MediaSessionLegacyHelper getHelper(Context context) {
+        if (DEBUG) {
+            Log.d(TAG, "Attempting to get helper with context " + context);
+        }
         synchronized (sLock) {
             if (sInstance == null) {
                 sInstance = new MediaSessionLegacyHelper(context);
@@ -65,12 +69,25 @@
         return holder == null ? null : holder.mSession;
     }
 
-    public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
+    public void sendMediaButtonEvent(KeyEvent keyEvent, boolean needWakeLock) {
+        mSessionManager.dispatchMediaKeyEvent(keyEvent, needWakeLock);
+        if (DEBUG) {
+            Log.d(TAG, "dispatched media key " + keyEvent);
+        }
+    }
 
+    public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
+        if (pi == null) {
+            Log.w(TAG, "Pending intent was null, can't add rcc listener.");
+            return;
+        }
         SessionHolder holder = getHolder(pi, true);
         TransportPerformer performer = holder.mSession.getTransportPerformer();
         if (holder.mRccListener != null) {
             if (holder.mRccListener == listener) {
+                if (DEBUG) {
+                    Log.d(TAG, "addRccListener listener already added.");
+                }
                 // This is already the registered listener, ignore
                 return;
             }
@@ -82,9 +99,15 @@
         holder.mFlags |= Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
         holder.mSession.setFlags(holder.mFlags);
         holder.update();
+        if (DEBUG) {
+            Log.d(TAG, "Added rcc listener for " + pi + ".");
+        }
     }
 
     public void removeRccListener(PendingIntent pi) {
+        if (pi == null) {
+            return;
+        }
         SessionHolder holder = getHolder(pi, false);
         if (holder != null && holder.mRccListener != null) {
             holder.mSession.getTransportPerformer().removeListener(holder.mRccListener);
@@ -92,30 +115,56 @@
             holder.mFlags &= ~Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
             holder.mSession.setFlags(holder.mFlags);
             holder.update();
+            if (DEBUG) {
+                Log.d(TAG, "Removed rcc listener for " + pi + ".");
+            }
         }
     }
 
     public void addMediaButtonListener(PendingIntent pi,
             Context context) {
+        if (pi == null) {
+            Log.w(TAG, "Pending intent was null, can't addMediaButtonListener.");
+            return;
+        }
         SessionHolder holder = getHolder(pi, true);
         if (holder.mMediaButtonListener != null) {
-            // Already have this listener registered
+            // Already have this listener registered, but update it anyway as
+            // the extras may have changed.
+            if (DEBUG) {
+                Log.d(TAG, "addMediaButtonListener already added " + pi);
+            }
             return;
         }
         holder.mMediaButtonListener = new MediaButtonListener(pi, context);
         holder.mFlags |= Session.FLAG_HANDLES_MEDIA_BUTTONS;
         holder.mSession.setFlags(holder.mFlags);
         holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
+
+        holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
+        holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
+        if (DEBUG) {
+            Log.d(TAG, "addMediaButtonListener added " + pi);
+        }
     }
 
     public void removeMediaButtonListener(PendingIntent pi) {
+        if (pi == null) {
+            return;
+        }
         SessionHolder holder = getHolder(pi, false);
         if (holder != null && holder.mMediaButtonListener != null) {
             holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener);
             holder.mFlags &= ~Session.FLAG_HANDLES_MEDIA_BUTTONS;
             holder.mSession.setFlags(holder.mFlags);
             holder.mMediaButtonListener = null;
+
+            holder.mSession.removeCallback(holder.mMediaButtonReceiver);
+            holder.mMediaButtonReceiver = null;
             holder.update();
+            if (DEBUG) {
+                Log.d(TAG, "removeMediaButtonListener removed " + pi);
+            }
         }
     }
 
@@ -130,7 +179,32 @@
         return holder;
     }
 
-    public static class MediaButtonListener extends TransportPerformer.Listener {
+    private static void sendKeyEvent(PendingIntent pi, Context context, Intent intent) {
+        try {
+            pi.send(context, 0, intent);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Error sending media key down event:", e);
+            // Don't bother sending up if down failed
+            return;
+        }
+    }
+
+    private static final class MediaButtonReceiver extends Session.Callback {
+        private final PendingIntent mPendingIntent;
+        private final Context mContext;
+
+        public MediaButtonReceiver(PendingIntent pi, Context context) {
+            mPendingIntent = pi;
+            mContext = context;
+        }
+
+        @Override
+        public void onMediaButton(Intent mediaButtonIntent) {
+            MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, mediaButtonIntent);
+        }
+    }
+
+    private static final class MediaButtonListener extends TransportPerformer.Listener {
         private final PendingIntent mPendingIntent;
         private final Context mContext;
 
@@ -179,20 +253,14 @@
             Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
 
             intent.putExtra(Intent.EXTRA_KEY_EVENT, ke);
-            try {
-                mPendingIntent.send(mContext, 0, intent);
-            } catch (CanceledException e) {
-                Log.e(TAG, "Error sending media key down event:", e);
-                // Don't bother sending up if down failed
-                return;
-            }
+            MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, intent);
 
             ke = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
             intent.putExtra(Intent.EXTRA_KEY_EVENT, ke);
-            try {
-                mPendingIntent.send(mContext, 0, intent);
-            } catch (CanceledException e) {
-                Log.e(TAG, "Error sending media key up event:", e);
+            MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, intent);
+
+            if (DEBUG) {
+                Log.d(TAG, "Sent " + keyCode + " to pending intent " + mPendingIntent);
             }
         }
     }
@@ -201,6 +269,7 @@
         public final Session mSession;
         public final PendingIntent mPi;
         public MediaButtonListener mMediaButtonListener;
+        public MediaButtonReceiver mMediaButtonReceiver;
         public TransportPerformer.Listener mRccListener;
         public int mFlags;
 
@@ -213,10 +282,6 @@
             if (mMediaButtonListener == null && mRccListener == null) {
                 mSession.release();
                 mSessions.remove(mPi);
-            } else if (mMediaButtonListener != null && mRccListener != null) {
-                // TODO set session to active
-            } else {
-                // TODO set session to inactive
             }
         }
     }
diff --git a/media/java/android/media/session/Session.java b/media/java/android/media/session/Session.java
index 2ffced6..e439772 100644
--- a/media/java/android/media/session/Session.java
+++ b/media/java/android/media/session/Session.java
@@ -114,6 +114,13 @@
      */
     public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
 
+    /**
+     * Status code indicating the call was handled.
+     *
+     * @hide
+     */
+    public static final int RESULT_SUCCESS = 0;
+
     private static final int MSG_MEDIA_BUTTON = 1;
     private static final int MSG_COMMAND = 2;
     private static final int MSG_ROUTE_CHANGE = 3;
@@ -544,11 +551,15 @@
         }
 
         @Override
-        public void onMediaButton(Intent mediaButtonIntent) throws RemoteException {
+        public void onMediaButton(Intent mediaButtonIntent, ResultReceiver cb)
+                throws RemoteException {
             Session session = mMediaSession.get();
             if (session != null) {
                 session.postMediaButton(mediaButtonIntent);
             }
+            if (cb != null) {
+                cb.send(RESULT_SUCCESS, null);
+            }
         }
 
         @Override
diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/SessionManager.java
index 1eb3b7a..1838132 100644
--- a/media/java/android/media/session/SessionManager.java
+++ b/media/java/android/media/session/SessionManager.java
@@ -25,6 +25,7 @@
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.util.Log;
+import android.view.KeyEvent;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -138,4 +139,30 @@
         }
         return controllers;
     }
+
+    /**
+     * Send a media key event. The receiver will be selected automatically.
+     *
+     * @param keyEvent The KeyEvent to send.
+     * @hide
+     */
+    public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
+        dispatchMediaKeyEvent(keyEvent, false);
+    }
+
+    /**
+     * Send a media key event. The receiver will be selected automatically.
+     *
+     * @param keyEvent The KeyEvent to send
+     * @param needWakeLock true if a wake lock should be held while sending the
+     *            key
+     * @hide
+     */
+    public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+        try {
+            mService.dispatchMediaKeyEvent(keyEvent, needWakeLock);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to send key event.", e);
+        }
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 466c8ed..7c89d23 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -46,6 +46,7 @@
 import android.media.IAudioService;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
+import android.media.session.MediaSessionLegacyHelper;
 import android.os.Bundle;
 import android.os.FactoryTest;
 import android.os.Handler;
@@ -137,6 +138,9 @@
     static final boolean ENABLE_CAR_DOCK_HOME_CAPTURE = true;
     static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;
 
+    // Whether to use the new Session APIs
+    static final boolean USE_SESSIONS = true;
+
     static final int LONG_PRESS_POWER_NOTHING = 0;
     static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
     static final int LONG_PRESS_POWER_SHUT_OFF = 2;
@@ -1325,11 +1329,11 @@
                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
         }
     }
-    
+
     void readLidState() {
         mLidState = mWindowManagerFuncs.getLidState();
     }
-    
+
     private boolean isHidden(int accessibilityMode) {
         switch (accessibilityMode) {
             case 1:
@@ -1676,16 +1680,16 @@
 
     /**
      * Preflight adding a window to the system.
-     * 
+     *
      * Currently enforces that three window types are singletons:
      * <ul>
      * <li>STATUS_BAR_TYPE</li>
      * <li>KEYGUARD_TYPE</li>
      * </ul>
-     * 
+     *
      * @param win The window to be added
      * @param attrs Information about the window to be added
-     * 
+     *
      * @return If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons,
      * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
      */
@@ -1758,7 +1762,7 @@
     }
 
     static final boolean PRINT_ANIM = false;
-    
+
     /** {@inheritDoc} */
     @Override
     public int selectAnimationLw(WindowState win, int transit) {
@@ -2337,7 +2341,7 @@
         Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {
-            // TODO: This only stops the factory-installed search manager.  
+            // TODO: This only stops the factory-installed search manager.
             // Need to formalize an API to handle others
             SearchManager searchManager = getSearchManager();
             if (searchManager != null) {
@@ -3032,7 +3036,7 @@
 
             if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
                     == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() 
+                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                             + "): IN_SCREEN, INSET_DECOR");
                 // This is the case for a normal activity window: we want it
                 // to cover all of the screen space, and it can take care of
@@ -3316,7 +3320,7 @@
 
         if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
                 + ": sim=#" + Integer.toHexString(sim)
-                + " attach=" + attached + " type=" + attrs.type 
+                + " attach=" + attached + " type=" + attrs.type
                 + String.format(" flags=0x%08x", fl)
                 + " pf=" + pf.toShortString() + " df=" + df.toShortString()
                 + " of=" + of.toShortString()
@@ -3365,7 +3369,7 @@
         mForceStatusBarFromKeyguard = false;
         mForcingShowNavBar = false;
         mForcingShowNavBarLayer = -1;
-        
+
         mHideLockScreen = false;
         mAllowLockscreenWhenOn = false;
         mDismissKeyguard = DISMISS_KEYGUARD_NONE;
@@ -4209,12 +4213,16 @@
 
     void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) {
         if (ActivityManagerNative.isSystemReady()) {
-            IAudioService audioService = getAudioService();
-            if (audioService != null) {
-                try {
-                    audioService.dispatchMediaKeyEventUnderWakelock(event);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
+            if (USE_SESSIONS) {
+                MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true);
+            } else {
+                IAudioService audioService = getAudioService();
+                if (audioService != null) {
+                    try {
+                        audioService.dispatchMediaKeyEventUnderWakelock(event);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
+                    }
                 }
             }
         }
@@ -4440,7 +4448,7 @@
     }
 
     public void dismissKeyguardLw() {
-        if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) { 
+        if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
             mHandler.post(new Runnable() {
                 public void run() {
                     if (mKeyguardDelegate.isDismissable()) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9677577..f6be1ff 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -365,6 +365,10 @@
         return mSessionCb.mCb;
     }
 
+    public void sendMediaButton(KeyEvent ke, ResultReceiver cb) {
+        mSessionCb.sendMediaButton(ke, cb);
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + mTag + " " + this);
 
@@ -645,11 +649,11 @@
             mCb = cb;
         }
 
-        public void sendMediaButton(KeyEvent keyEvent) {
+        public void sendMediaButton(KeyEvent keyEvent, ResultReceiver cb) {
             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
             mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
             try {
-                mCb.onMediaButton(mediaButtonIntent);
+                mCb.onMediaButton(mediaButtonIntent, cb);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
             }
@@ -785,7 +789,7 @@
 
         @Override
         public void sendMediaButton(KeyEvent mediaButtonIntent) {
-            mSessionCb.sendMediaButton(mediaButtonIntent);
+            mSessionCb.sendMediaButton(mediaButtonIntent, null);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 78f3b5f..a2ca2a1 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -17,9 +17,12 @@
 package com.android.server.media;
 
 import android.Manifest;
+import android.app.Activity;
 import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.media.routeprovider.RouteRequest;
 import android.media.session.ISession;
@@ -29,14 +32,18 @@
 import android.media.session.RouteOptions;
 import android.media.session.Session;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.KeyEvent;
 
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
@@ -64,6 +71,7 @@
     // = new ArrayList<MediaRouteProviderProxy>();
     private final Object mLock = new Object();
     private final Handler mHandler = new Handler();
+    private final PowerManager.WakeLock mMediaEventWakeLock;
 
     private MediaSessionRecord mPrioritySession;
     private int mCurrentUserId = -1;
@@ -79,6 +87,8 @@
         super(context);
         mSessionManagerImpl = new SessionManagerImpl();
         mPriorityStack = new MediaSessionStack();
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
     }
 
     @Override
@@ -377,7 +387,7 @@
 
     /*
      * When a session is created the following things need to happen.
-     * 1. It's callback binder needs a link to death
+     * 1. Its callback binder needs a link to death
      * 2. It needs to be added to all sessions.
      * 3. It needs to be added to the priority stack.
      * 4. It needs to be added to the relevant user record.
@@ -585,9 +595,10 @@
     }
 
     class SessionManagerImpl extends ISessionManager.Stub {
-        // TODO add createSessionAsUser, pass user-id to
-        // ActivityManagerNative.handleIncomingUser and stash result for use
-        // when starting services on that session's behalf.
+        private static final String EXTRA_WAKELOCK_ACQUIRED =
+                "android.media.AudioService.WAKELOCK_ACQUIRED";
+        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
+
         @Override
         public ISession createSession(String packageName, ISessionCallback cb, String tag,
                 int userId) throws RemoteException {
@@ -644,6 +655,59 @@
             }
         }
 
+        /**
+         * Handles the dispatching of the media button events to one of the
+         * registered listeners, or if there was none, broadcast an
+         * ACTION_MEDIA_BUTTON intent to the rest of the system.
+         *
+         * @param keyEvent a non-null KeyEvent whose key code is one of the
+         *            supported media buttons
+         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
+         *            while this key event is dispatched.
+         */
+        @Override
+        public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+            if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
+                return;
+            }
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                if (needWakeLock) {
+                    mMediaEventWakeLock.acquire();
+                }
+                synchronized (mLock) {
+                    MediaSessionRecord mbSession = mPriorityStack
+                            .getDefaultMediaButtonSession(mCurrentUserId);
+                    if (mbSession != null) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Sending media key to " + mbSession.getSessionInfo());
+                        }
+                        mbSession.sendMediaButton(keyEvent,
+                                needWakeLock ? mKeyEventDoneReceiver : null);
+                    } else {
+                        if (DEBUG) {
+                            Log.d(TAG, "Sending media key ordered broadcast");
+                        }
+                        // Fallback to legacy behavior
+                        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+                        if (needWakeLock) {
+                            keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
+                                    WAKELOCK_RELEASE_ON_FINISHED);
+                        }
+                        getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+                                null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         @Override
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
@@ -678,6 +742,36 @@
                 }
             }
         }
+
+        ResultReceiver mKeyEventDoneReceiver = new ResultReceiver(mHandler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                synchronized (mLock) {
+                    if (mMediaEventWakeLock.isHeld()) {
+                        mMediaEventWakeLock.release();
+                    }
+                }
+            }
+        };
+
+        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent == null) {
+                    return;
+                }
+                Bundle extras = intent.getExtras();
+                if (extras == null) {
+                    return;
+                }
+                synchronized (mLock) {
+                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
+                            && mMediaEventWakeLock.isHeld()) {
+                        mMediaEventWakeLock.release();
+                    }
+                }
+            }
+        };
     }
 
 }