diff options
10 files changed, 296 insertions, 61 deletions
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 class AudioManager { public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE; private static IAudioService sService; - private MediaSessionLegacyHelper mSessionHelper; /** * @hide @@ -439,9 +438,6 @@ public class AudioManager { 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 @@ public class AudioManager { * 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 @@ public class AudioManager { 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 @@ public class AudioManager { 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 @@ public class AudioManager { Log.e(TAG, "Dead object in registerRemoteControlClient"+e); } if (USE_SESSIONS) { - rcClient.registerWithSession(mSessionHelper); + rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext)); } } @@ -2303,7 +2306,7 @@ public class AudioManager { 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.database.ContentObserver; 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 @@ public class AudioService extends IAudioService.Stub { /** 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 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { } 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 class AudioService extends IAudioService.Stub { } 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 @@ import android.os.ResultReceiver; */ 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..38b9293 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -19,6 +19,7 @@ import android.content.ComponentName; 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 @@ import android.os.Bundle; 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 @@ import android.view.KeyEvent; */ 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 class MediaSessionLegacyHelper { } 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 @@ public class MediaSessionLegacyHelper { 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 @@ public class MediaSessionLegacyHelper { 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 @@ public class MediaSessionLegacyHelper { 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 @@ public class MediaSessionLegacyHelper { 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 @@ public class MediaSessionLegacyHelper { 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 class MediaSessionLegacyHelper { 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 @@ public class MediaSessionLegacyHelper { 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 final class Session { */ 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 @@ public final class Session { } @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.ServiceManager; 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 @@ public final class SessionManager { } 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.AudioSystem; 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { | 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** * 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } static final boolean PRINT_ANIM = false; - + /** {@inheritDoc} */ @Override public int selectAnimationLw(WindowState win, int transit) { @@ -2337,7 +2341,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { mForceStatusBarFromKeyguard = false; mForcingShowNavBar = false; mForcingShowNavBarLayer = -1; - + mHideLockScreen = false; mAllowLockscreenWhenOn = false; mDismissKeyguard = DISMISS_KEYGUARD_NONE; @@ -4209,12 +4213,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 class PhoneWindowManager implements WindowManagerPolicy { } 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @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.RouteInfo; 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 @@ public class MediaSessionService extends SystemService implements Monitor { // = 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { /* * 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 @@ public class MediaSessionService extends SystemService implements Monitor { } 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 @@ public class MediaSessionService extends SystemService implements Monitor { } } + /** + * 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 @@ public class MediaSessionService extends SystemService implements Monitor { } } } + + 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(); + } + } + } + }; } } |