diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java index e0efd8a759722..0b9697840a1fb 100644 --- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java +++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java @@ -16,24 +16,64 @@ package com.android.server.media; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.session.ISessionManager; import android.media.session.MediaSession; import android.os.Binder; import android.view.KeyEvent; +import android.view.ViewConfiguration; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Map; /** * Provides a way to customize behavior for media key events. - * + *

+ * In order to override the implementation of the single/double/triple click or long press, + * {@link #setOverriddenKeyEvents(int, int)} should be called for each key code with the + * overridden {@link KeyEventType} bit value set, and the corresponding method, + * {@link #onSingleClick(KeyEvent)}, {@link #onDoubleClick(KeyEvent)}, + * {@link #onTripleClick(KeyEvent)}, {@link #onLongPress(KeyEvent)} should be implemented. + *

* Note: When instantiating this class, {@link MediaSessionService} will only use the constructor * without any parameters. */ +// TODO: Change API names from using "click" to "tap" +// TODO: Move this class to apex/media/ public abstract class MediaKeyDispatcher { + @IntDef(flag = true, value = { + KEY_EVENT_SINGLE_CLICK, + KEY_EVENT_DOUBLE_CLICK, + KEY_EVENT_TRIPLE_CLICK, + KEY_EVENT_LONG_PRESS + }) + @Retention(RetentionPolicy.SOURCE) + @interface KeyEventType {} + static final int KEY_EVENT_SINGLE_CLICK = 1 << 0; + static final int KEY_EVENT_DOUBLE_CLICK = 1 << 1; + static final int KEY_EVENT_TRIPLE_CLICK = 1 << 2; + static final int KEY_EVENT_LONG_PRESS = 1 << 3; + + private Map mOverriddenKeyEvents; + public MediaKeyDispatcher() { // Constructor used for reflection + mOverriddenKeyEvents = new HashMap<>(); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY, 0); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PAUSE, 0); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MUTE, 0); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_HEADSETHOOK, 0); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_STOP, 0); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_NEXT, 0); + mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, 0); } + // TODO: Move this method into SessionPolicyProvider.java for better readability. /** * Implement this to customize the logic for which MediaSession should consume which key event. * @@ -49,4 +89,137 @@ public abstract class MediaKeyDispatcher { boolean asSystemService) { return null; } + + /** + * Gets the map of key code -> {@link KeyEventType} that have been overridden. + *

+ * The list of valid key codes are the following: + *

+ * @see {@link KeyEvent#isMediaSessionKey(int)} + */ + @KeyEventType Map getOverriddenKeyEvents() { + return mOverriddenKeyEvents; + } + + static boolean isSingleClickOverridden(@KeyEventType int overriddenKeyEvents) { + return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_SINGLE_CLICK) != 0; + } + + static boolean isDoubleClickOverridden(@KeyEventType int overriddenKeyEvents) { + return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_DOUBLE_CLICK) != 0; + } + + static boolean isTripleClickOverridden(@KeyEventType int overriddenKeyEvents) { + return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_TRIPLE_CLICK) != 0; + } + + static boolean isLongPressOverridden(@KeyEventType int overriddenKeyEvents) { + return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_LONG_PRESS) != 0; + } + + /** + * Sets the value of the given key event type flagged with overridden {@link KeyEventType} to + * the given key code. If called multiple times for the same key code, will be overwritten to + * the most recently called {@link KeyEventType} value. + *

+ * The list of valid key codes are the following: + *

+ * @see {@link KeyEvent#isMediaSessionKey(int)} + * @param keyCode + */ + void setOverriddenKeyEvents(int keyCode, @KeyEventType int keyEventType) { + mOverriddenKeyEvents.put(keyCode, keyEventType); + } + + /** + * Customized implementation for single click event. Will be run if + * {@link #KEY_EVENT_SINGLE_CLICK} flag is on for the corresponding key code from + * {@link #getOverriddenKeyEvents()}. + * + * It is considered a single click if only one {@link KeyEvent} with the same + * {@link KeyEvent#getKeyCode()} is dispatched within + * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds. Change the + * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval. + * + * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent. + * + * @param keyEvent + */ + void onSingleClick(KeyEvent keyEvent) { + } + + /** + * Customized implementation for double click event. Will be run if + * {@link #KEY_EVENT_DOUBLE_CLICK} flag is on for the corresponding key code from + * {@link #getOverriddenKeyEvents()}. + * + * It is considered a double click if two {@link KeyEvent}s with the same + * {@link KeyEvent#getKeyCode()} are dispatched within + * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the + * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval. + * + * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent. + * + * @param keyEvent + */ + void onDoubleClick(KeyEvent keyEvent) { + } + + /** + * Customized implementation for triple click event. Will be run if + * {@link #KEY_EVENT_TRIPLE_CLICK} flag is on for the corresponding key code from + * {@link #getOverriddenKeyEvents()}. + * + * It is considered a triple click if three {@link KeyEvent}s with the same + * {@link KeyEvent#getKeyCode()} are dispatched within + * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the + * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval. + * + * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent. + * + * @param keyEvent + */ + void onTripleClick(KeyEvent keyEvent) { + } + + /** + * Customized implementation for long press event. Will be run if + * {@link #KEY_EVENT_LONG_PRESS} flag is on for the corresponding key code from + * {@link #getOverriddenKeyEvents()}. + * + * It is considered a long press if an {@link KeyEvent#ACTION_DOWN} key event is followed by + * another {@link KeyEvent#ACTION_DOWN} key event with {@link KeyEvent#FLAG_LONG_PRESS} + * enabled, and an {@link KeyEvent#getRepeatCount()} that is equal to 1. + * + * Note: This will be called for the following key events: + * + * + * @param keyEvent + */ + void onLongPress(KeyEvent keyEvent) { + } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 5757b1a9697b6..e5867e7939484 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -86,10 +86,10 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; /** * System implementation of MediaSessionManager @@ -103,12 +103,14 @@ public class MediaSessionService extends SystemService implements Monitor { private static final int WAKELOCK_TIMEOUT = 5000; private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; private static final int SESSION_CREATION_LIMIT_PER_UID = 100; + private static final int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() + + /* Buffer for delayed delivery of key event */ 50; + private static final int MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout(); private final Context mContext; private final SessionManagerImpl mSessionManagerImpl; private final MessageHandler mHandler = new MessageHandler(); private final PowerManager.WakeLock mMediaEventWakeLock; - private final int mLongPressTimeout; private final INotificationManager mNotificationManager; private final Object mLock = new Object(); // Keeps the full user id for each user. @@ -142,8 +144,7 @@ public class MediaSessionService extends SystemService implements Monitor { private SessionPolicyProvider mCustomSessionPolicyProvider; private MediaKeyDispatcher mCustomMediaKeyDispatcher; - private Method mGetSessionForKeyEventMethod; - private Method mGetSessionPoliciesMethod; + private Map mOverriddenKeyEventsMap; public MediaSessionService(Context context) { super(context); @@ -151,7 +152,6 @@ public class MediaSessionService extends SystemService implements Monitor { mSessionManagerImpl = new SessionManagerImpl(); PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); - mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); mNotificationManager = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); } @@ -184,9 +184,10 @@ public class MediaSessionService extends SystemService implements Monitor { mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); + updateUser(); + instantiateCustomProvider(null); instantiateCustomDispatcher(null); - updateUser(); } private boolean isGlobalPriorityActiveLocked() { @@ -570,13 +571,9 @@ public class MediaSessionService extends SystemService implements Monitor { String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) { synchronized (mLock) { int policies = 0; - if (mCustomSessionPolicyProvider != null && mGetSessionPoliciesMethod != null) { - try { - policies = (int) mGetSessionPoliciesMethod.invoke( - mCustomSessionPolicyProvider, callerUid, callerPackageName); - } catch (InvocationTargetException | IllegalAccessException e) { - Log.w(TAG, "Encountered problem while using reflection", e); - } + if (mCustomSessionPolicyProvider != null) { + policies = mCustomSessionPolicyProvider.getSessionPoliciesForApplication( + callerUid, callerPackageName); } FullUserRecord user = getFullUserRecordLocked(userId); @@ -762,44 +759,46 @@ public class MediaSessionService extends SystemService implements Monitor { } private void instantiateCustomDispatcher(String nameFromTesting) { - mCustomMediaKeyDispatcher = null; - mGetSessionForKeyEventMethod = null; + synchronized (mLock) { + mCustomMediaKeyDispatcher = null; + mOverriddenKeyEventsMap = null; - String customDispatcherClassName = (nameFromTesting == null) - ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher) - : nameFromTesting; - try { - if (!TextUtils.isEmpty(customDispatcherClassName)) { - Class customDispatcherClass = Class.forName(customDispatcherClassName); - Constructor constructor = customDispatcherClass.getDeclaredConstructor(); - mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance(); - mGetSessionForKeyEventMethod = customDispatcherClass.getDeclaredMethod( - "getSessionForKeyEvent", KeyEvent.class, int.class, boolean.class); + String customDispatcherClassName = (nameFromTesting == null) + ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher) + : nameFromTesting; + try { + if (!TextUtils.isEmpty(customDispatcherClassName)) { + Class customDispatcherClass = Class.forName(customDispatcherClassName); + Constructor constructor = customDispatcherClass.getDeclaredConstructor(); + mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance(); + mOverriddenKeyEventsMap = mCustomMediaKeyDispatcher.getOverriddenKeyEvents(); + } + } catch (ClassNotFoundException | InstantiationException | InvocationTargetException + | IllegalAccessException | NoSuchMethodException e) { + mCustomMediaKeyDispatcher = null; + Log.w(TAG, "Encountered problem while using reflection", e); } - } catch (ClassNotFoundException | InstantiationException | InvocationTargetException - | IllegalAccessException | NoSuchMethodException e) { - Log.w(TAG, "Encountered problem while using reflection", e); } } private void instantiateCustomProvider(String nameFromTesting) { - mCustomSessionPolicyProvider = null; - mGetSessionPoliciesMethod = null; + synchronized (mLock) { + mCustomSessionPolicyProvider = null; - String customProviderClassName = (nameFromTesting == null) - ? mContext.getResources().getString(R.string.config_customSessionPolicyProvider) - : nameFromTesting; - try { - if (!TextUtils.isEmpty(customProviderClassName)) { - Class customProviderClass = Class.forName(customProviderClassName); - Constructor constructor = customProviderClass.getDeclaredConstructor(); - mCustomSessionPolicyProvider = (SessionPolicyProvider) constructor.newInstance(); - mGetSessionPoliciesMethod = customProviderClass.getDeclaredMethod( - "getSessionPoliciesForApplication", int.class, String.class); + String customProviderClassName = (nameFromTesting == null) + ? mContext.getResources().getString(R.string.config_customSessionPolicyProvider) + : nameFromTesting; + try { + if (!TextUtils.isEmpty(customProviderClassName)) { + Class customProviderClass = Class.forName(customProviderClassName); + Constructor constructor = customProviderClass.getDeclaredConstructor(); + mCustomSessionPolicyProvider = + (SessionPolicyProvider) constructor.newInstance(); + } + } catch (ClassNotFoundException | InstantiationException | InvocationTargetException + | IllegalAccessException | NoSuchMethodException e) { + Log.w(TAG, "Encountered problem while using reflection", e); } - } catch (ClassNotFoundException | InstantiationException | InvocationTargetException - | IllegalAccessException | NoSuchMethodException e) { - Log.w(TAG, "Encountered problem while using reflection", e); } } @@ -1098,8 +1097,9 @@ public class MediaSessionService extends SystemService implements Monitor { "android.media.AudioService.WAKELOCK_ACQUIRED"; private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number - private boolean mVoiceButtonDown = false; - private boolean mVoiceButtonHandled = false; + private KeyEvent mPendingFirstDownKeyEvent = null; + private boolean mIsLongPressing = false; + private Runnable mLongPressTimeoutRunnable = null; @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, @@ -1362,12 +1362,12 @@ public class MediaSessionService extends SystemService implements Monitor { } } } - if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) { - handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, - needWakeLock); - } else { + if (isGlobalPriorityActive) { dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, needWakeLock); + } else { + handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, + needWakeLock); } } } finally { @@ -1641,7 +1641,7 @@ public class MediaSessionService extends SystemService implements Monitor { } /** - * Dispaches volume key events. This is called when the foreground activity didn't handled + * Dispatches volume key events. This is called when the foreground activity didn't handle * the incoming volume key event. *

* Handles the dispatching of the volume button events to one of the @@ -1662,7 +1662,7 @@ public class MediaSessionService extends SystemService implements Monitor { * {@link KeyEvent#KEYCODE_VOLUME_DOWN}, * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}. * @param stream stream type to adjust volume. - * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume. + * @param musicOnly true if both UI and haptic feedback aren't needed when adjusting volume. * @see #dispatchVolumeKeyEventToSessionAsSystemService */ @Override @@ -1707,7 +1707,7 @@ public class MediaSessionService extends SystemService implements Monitor { mHandler.obtainMessage( MessageHandler.MSG_VOLUME_INITIAL_DOWN, mCurrentFullUserRecord.mFullUserId, 0), - mLongPressTimeout); + LONG_PRESS_TIMEOUT); } if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); @@ -2112,31 +2112,147 @@ public class MediaSessionService extends SystemService implements Monitor { } } - private void handleVoiceKeyEventLocked(String packageName, int pid, int uid, + // A long press is determined by: + // 1) A KeyEvent with KeyEvent.ACTION_DOWN and repeat count of 0, followed by + // 2) A KeyEvent with KeyEvent.ACTION_DOWN and repeat count of 1 and FLAG_LONG_PRESS within + // ViewConfiguration.getLongPressTimeout(). + // TODO: Add description about what a click is determined by. + private void handleKeyEventLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { - int action = keyEvent.getAction(); - boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; - if (action == KeyEvent.ACTION_DOWN) { - if (keyEvent.getRepeatCount() == 0) { - mVoiceButtonDown = true; - mVoiceButtonHandled = false; - } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) { - mVoiceButtonHandled = true; - startVoiceInput(needWakeLock); + if (keyEvent.isCanceled()) { + return; + } + + int overriddenKeyEvents = (mCustomMediaKeyDispatcher == null) ? 0 + : mCustomMediaKeyDispatcher.getOverriddenKeyEvents().get(keyEvent.getKeyCode()); + cancelPendingIfNeeded(keyEvent); + if (!needPending(keyEvent, overriddenKeyEvents)) { + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, + needWakeLock); + return; + } + + if (isFirstDownKeyEvent(keyEvent)) { + mPendingFirstDownKeyEvent = keyEvent; + mIsLongPressing = false; + return; + } + + if (isFirstLongPressKeyEvent(keyEvent)) { + mIsLongPressing = true; + } + if (mIsLongPressing) { + handleLongPressLocked(keyEvent, needWakeLock, overriddenKeyEvents); + } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) { + mPendingFirstDownKeyEvent = null; + // TODO: Replace this with code to determine whether + // single/double/triple click and run custom implementations, + // if they exist. + dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService, + keyEvent, needWakeLock); + } + } + + private void cancelPendingIfNeeded(KeyEvent keyEvent) { + if (mPendingFirstDownKeyEvent == null) { + return; + } + if (isFirstDownKeyEvent(keyEvent)) { + if (mLongPressTimeoutRunnable != null) { + mHandler.removeCallbacks(mLongPressTimeoutRunnable); + mLongPressTimeoutRunnable.run(); + } else { + resetLongPressTracking(); } - } else if (action == KeyEvent.ACTION_UP) { - if (mVoiceButtonDown) { - mVoiceButtonDown = false; - if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { - // Resend the down then send this event through - KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); - dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, - downEvent, needWakeLock); - dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, - keyEvent, needWakeLock); - } + return; + } + if (mPendingFirstDownKeyEvent.getDownTime() == keyEvent.getDownTime() + && mPendingFirstDownKeyEvent.getKeyCode() == keyEvent.getKeyCode() + && keyEvent.getAction() == KeyEvent.ACTION_DOWN + && keyEvent.getRepeatCount() > 1 && !mIsLongPressing) { + resetLongPressTracking(); + } + } + + private boolean needPending(KeyEvent keyEvent, int overriddenKeyEvents) { + if (!isFirstDownKeyEvent(keyEvent)) { + if (mPendingFirstDownKeyEvent == null) { + return false; + } else if (mPendingFirstDownKeyEvent.getDownTime() != keyEvent.getDownTime() + || mPendingFirstDownKeyEvent.getKeyCode() != keyEvent.getKeyCode()) { + return false; } } + if (overriddenKeyEvents == 0 && !isVoiceKey(keyEvent.getKeyCode())) { + return false; + } + return true; + } + + private void handleLongPressLocked(KeyEvent keyEvent, boolean needWakeLock, + int overriddenKeyEvents) { + if (mCustomMediaKeyDispatcher != null + && mCustomMediaKeyDispatcher.isLongPressOverridden(overriddenKeyEvents)) { + mCustomMediaKeyDispatcher.onLongPress(keyEvent); + + if (mLongPressTimeoutRunnable != null) { + mHandler.removeCallbacks(mLongPressTimeoutRunnable); + } + if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + if (mLongPressTimeoutRunnable == null) { + mLongPressTimeoutRunnable = createLongPressTimeoutRunnable(keyEvent); + } + mHandler.postDelayed(mLongPressTimeoutRunnable, LONG_PRESS_TIMEOUT); + } else { + resetLongPressTracking(); + } + } else if (isFirstLongPressKeyEvent(keyEvent) && isVoiceKey(keyEvent.getKeyCode())) { + // Default implementation + startVoiceInput(needWakeLock); + resetLongPressTracking(); + } + } + + private Runnable createLongPressTimeoutRunnable(KeyEvent keyEvent) { + return new Runnable() { + @Override + public void run() { + if (mCustomMediaKeyDispatcher != null) { + mCustomMediaKeyDispatcher.onLongPress(createCanceledKeyEvent(keyEvent)); + } + resetLongPressTracking(); + } + }; + } + + private void resetLongPressTracking() { + mPendingFirstDownKeyEvent = null; + mIsLongPressing = false; + mLongPressTimeoutRunnable = null; + } + + private KeyEvent createCanceledKeyEvent(KeyEvent keyEvent) { + KeyEvent upEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP); + return KeyEvent.changeTimeRepeat(upEvent, System.currentTimeMillis(), 0, + KeyEvent.FLAG_CANCELED); + } + + private boolean isFirstLongPressKeyEvent(KeyEvent keyEvent) { + return ((keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) + && keyEvent.getRepeatCount() == 1; + } + + private boolean isFirstDownKeyEvent(KeyEvent keyEvent) { + return keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.getRepeatCount() == 0; + } + + private void dispatchDownAndUpKeyEventsLocked(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { + KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + downEvent, needWakeLock); + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + keyEvent, needWakeLock); } private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, @@ -2149,15 +2265,11 @@ public class MediaSessionService extends SystemService implements Monitor { MediaSessionRecord session = null; // Retrieve custom session for key event if it exists. - if (mCustomMediaKeyDispatcher != null && mGetSessionForKeyEventMethod != null) { - try { - Object tokenObject = mGetSessionForKeyEventMethod.invoke( - mCustomMediaKeyDispatcher, keyEvent, uid, asSystemService); - if (tokenObject != null) { - session = getMediaSessionRecordLocked((MediaSession.Token) tokenObject); - } - } catch (InvocationTargetException | IllegalAccessException e) { - Log.w(TAG, "Encountered problem while using reflection", e); + if (mCustomMediaKeyDispatcher != null) { + MediaSession.Token token = mCustomMediaKeyDispatcher.getSessionForKeyEvent( + keyEvent, uid, asSystemService); + if (token != null) { + session = getMediaSessionRecordLocked(token); } } @@ -2312,12 +2424,11 @@ public class MediaSessionService extends SystemService implements Monitor { mHandled = true; mHandler.removeCallbacks(this); synchronized (mLock) { - if (!isGlobalPriorityActiveLocked() - && isVoiceKey(mKeyEvent.getKeyCode())) { - handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, + if (isGlobalPriorityActiveLocked()) { + dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, mKeyEvent, mNeedWakeLock); } else { - dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, + handleKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, mKeyEvent, mNeedWakeLock); } } diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/SessionPolicyProvider.java index 40a3d2d66b1bf..5f02a075344e9 100644 --- a/services/core/java/com/android/server/media/SessionPolicyProvider.java +++ b/services/core/java/com/android/server/media/SessionPolicyProvider.java @@ -29,6 +29,7 @@ import java.lang.annotation.RetentionPolicy; * Note: When instantiating this class, {@link MediaSessionService} will only use the constructor * without any parameters. */ +// TODO: Move this class to apex/media/ public abstract class SessionPolicyProvider { @IntDef(value = { SESSION_POLICY_IGNORE_BUTTON_RECEIVER,