Merge "[Media ML] Support customization for long press implementation" into rvc-dev am: 7101c11243

Change-Id: I641a8036239df9a9895ccfd6befe70e6128645f4
This commit is contained in:
Jin Seok Park
2020-04-17 09:02:53 +00:00
committed by Automerger Merge Worker
3 changed files with 372 additions and 87 deletions

View File

@@ -16,24 +16,64 @@
package com.android.server.media; package com.android.server.media;
import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.media.session.ISessionManager; import android.media.session.ISessionManager;
import android.media.session.MediaSession; import android.media.session.MediaSession;
import android.os.Binder; import android.os.Binder;
import android.view.KeyEvent; 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. * Provides a way to customize behavior for media key events.
* * <p>
* 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.
* <p>
* Note: When instantiating this class, {@link MediaSessionService} will only use the constructor * Note: When instantiating this class, {@link MediaSessionService} will only use the constructor
* without any parameters. * without any parameters.
*/ */
// TODO: Change API names from using "click" to "tap"
// TODO: Move this class to apex/media/
public abstract class MediaKeyDispatcher { 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<Integer, Integer> mOverriddenKeyEvents;
public MediaKeyDispatcher() { public MediaKeyDispatcher() {
// Constructor used for reflection // 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. * Implement this to customize the logic for which MediaSession should consume which key event.
* *
@@ -49,4 +89,137 @@ public abstract class MediaKeyDispatcher {
boolean asSystemService) { boolean asSystemService) {
return null; return null;
} }
/**
* Gets the map of key code -> {@link KeyEventType} that have been overridden.
* <p>
* The list of valid key codes are the following:
* <ul>
* <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE}
* <li> {@link KeyEvent#KEYCODE_MUTE}
* <li> {@link KeyEvent#KEYCODE_HEADSETHOOK}
* <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
* <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
* </ul>
* @see {@link KeyEvent#isMediaSessionKey(int)}
*/
@KeyEventType Map<Integer, Integer> 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.
* <p>
* The list of valid key codes are the following:
* <ul>
* <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE}
* <li> {@link KeyEvent#KEYCODE_MUTE}
* <li> {@link KeyEvent#KEYCODE_HEADSETHOOK}
* <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
* <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
* </ul>
* @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:
* <ul>
* <li>A {@link KeyEvent#ACTION_DOWN} KeyEvent with {@link KeyEvent#FLAG_LONG_PRESS} and
* {@link KeyEvent#getRepeatCount()} equal to 1</li>
* <li>Multiple {@link KeyEvent#ACTION_DOWN} KeyEvents with increasing
* {@link KeyEvent#getRepeatCount()}</li>
* <li>A {@link KeyEvent#ACTION_UP} KeyEvent</li>
* </ul>
*
* @param keyEvent
*/
void onLongPress(KeyEvent keyEvent) {
}
} }

View File

@@ -86,10 +86,10 @@ import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* System implementation of MediaSessionManager * 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 WAKELOCK_TIMEOUT = 5000;
private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
private static final int SESSION_CREATION_LIMIT_PER_UID = 100; 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 Context mContext;
private final SessionManagerImpl mSessionManagerImpl; private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler(); private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock; private final PowerManager.WakeLock mMediaEventWakeLock;
private final int mLongPressTimeout;
private final INotificationManager mNotificationManager; private final INotificationManager mNotificationManager;
private final Object mLock = new Object(); private final Object mLock = new Object();
// Keeps the full user id for each user. // Keeps the full user id for each user.
@@ -142,8 +144,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private SessionPolicyProvider mCustomSessionPolicyProvider; private SessionPolicyProvider mCustomSessionPolicyProvider;
private MediaKeyDispatcher mCustomMediaKeyDispatcher; private MediaKeyDispatcher mCustomMediaKeyDispatcher;
private Method mGetSessionForKeyEventMethod; private Map<Integer, Integer> mOverriddenKeyEventsMap;
private Method mGetSessionPoliciesMethod;
public MediaSessionService(Context context) { public MediaSessionService(Context context) {
super(context); super(context);
@@ -151,7 +152,6 @@ public class MediaSessionService extends SystemService implements Monitor {
mSessionManagerImpl = new SessionManagerImpl(); mSessionManagerImpl = new SessionManagerImpl();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
mNotificationManager = INotificationManager.Stub.asInterface( mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE)); ServiceManager.getService(Context.NOTIFICATION_SERVICE));
} }
@@ -184,9 +184,10 @@ public class MediaSessionService extends SystemService implements Monitor {
mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature( mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK); PackageManager.FEATURE_LEANBACK);
updateUser();
instantiateCustomProvider(null); instantiateCustomProvider(null);
instantiateCustomDispatcher(null); instantiateCustomDispatcher(null);
updateUser();
} }
private boolean isGlobalPriorityActiveLocked() { private boolean isGlobalPriorityActiveLocked() {
@@ -570,13 +571,9 @@ public class MediaSessionService extends SystemService implements Monitor {
String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) { String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
synchronized (mLock) { synchronized (mLock) {
int policies = 0; int policies = 0;
if (mCustomSessionPolicyProvider != null && mGetSessionPoliciesMethod != null) { if (mCustomSessionPolicyProvider != null) {
try { policies = mCustomSessionPolicyProvider.getSessionPoliciesForApplication(
policies = (int) mGetSessionPoliciesMethod.invoke( callerUid, callerPackageName);
mCustomSessionPolicyProvider, callerUid, callerPackageName);
} catch (InvocationTargetException | IllegalAccessException e) {
Log.w(TAG, "Encountered problem while using reflection", e);
}
} }
FullUserRecord user = getFullUserRecordLocked(userId); FullUserRecord user = getFullUserRecordLocked(userId);
@@ -762,8 +759,9 @@ public class MediaSessionService extends SystemService implements Monitor {
} }
private void instantiateCustomDispatcher(String nameFromTesting) { private void instantiateCustomDispatcher(String nameFromTesting) {
synchronized (mLock) {
mCustomMediaKeyDispatcher = null; mCustomMediaKeyDispatcher = null;
mGetSessionForKeyEventMethod = null; mOverriddenKeyEventsMap = null;
String customDispatcherClassName = (nameFromTesting == null) String customDispatcherClassName = (nameFromTesting == null)
? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher) ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher)
@@ -773,18 +771,19 @@ public class MediaSessionService extends SystemService implements Monitor {
Class customDispatcherClass = Class.forName(customDispatcherClassName); Class customDispatcherClass = Class.forName(customDispatcherClassName);
Constructor constructor = customDispatcherClass.getDeclaredConstructor(); Constructor constructor = customDispatcherClass.getDeclaredConstructor();
mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance(); mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance();
mGetSessionForKeyEventMethod = customDispatcherClass.getDeclaredMethod( mOverriddenKeyEventsMap = mCustomMediaKeyDispatcher.getOverriddenKeyEvents();
"getSessionForKeyEvent", KeyEvent.class, int.class, boolean.class);
} }
} catch (ClassNotFoundException | InstantiationException | InvocationTargetException } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
| IllegalAccessException | NoSuchMethodException e) { | IllegalAccessException | NoSuchMethodException e) {
mCustomMediaKeyDispatcher = null;
Log.w(TAG, "Encountered problem while using reflection", e); Log.w(TAG, "Encountered problem while using reflection", e);
} }
} }
}
private void instantiateCustomProvider(String nameFromTesting) { private void instantiateCustomProvider(String nameFromTesting) {
synchronized (mLock) {
mCustomSessionPolicyProvider = null; mCustomSessionPolicyProvider = null;
mGetSessionPoliciesMethod = null;
String customProviderClassName = (nameFromTesting == null) String customProviderClassName = (nameFromTesting == null)
? mContext.getResources().getString(R.string.config_customSessionPolicyProvider) ? mContext.getResources().getString(R.string.config_customSessionPolicyProvider)
@@ -793,15 +792,15 @@ public class MediaSessionService extends SystemService implements Monitor {
if (!TextUtils.isEmpty(customProviderClassName)) { if (!TextUtils.isEmpty(customProviderClassName)) {
Class customProviderClass = Class.forName(customProviderClassName); Class customProviderClass = Class.forName(customProviderClassName);
Constructor constructor = customProviderClass.getDeclaredConstructor(); Constructor constructor = customProviderClass.getDeclaredConstructor();
mCustomSessionPolicyProvider = (SessionPolicyProvider) constructor.newInstance(); mCustomSessionPolicyProvider =
mGetSessionPoliciesMethod = customProviderClass.getDeclaredMethod( (SessionPolicyProvider) constructor.newInstance();
"getSessionPoliciesForApplication", int.class, String.class);
} }
} catch (ClassNotFoundException | InstantiationException | InvocationTargetException } catch (ClassNotFoundException | InstantiationException | InvocationTargetException
| IllegalAccessException | NoSuchMethodException e) { | IllegalAccessException | NoSuchMethodException e) {
Log.w(TAG, "Encountered problem while using reflection", e); Log.w(TAG, "Encountered problem while using reflection", e);
} }
} }
}
/** /**
* Information about a full user and its corresponding managed profiles. * Information about a full user and its corresponding managed profiles.
@@ -1098,8 +1097,9 @@ public class MediaSessionService extends SystemService implements Monitor {
"android.media.AudioService.WAKELOCK_ACQUIRED"; "android.media.AudioService.WAKELOCK_ACQUIRED";
private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
private boolean mVoiceButtonDown = false; private KeyEvent mPendingFirstDownKeyEvent = null;
private boolean mVoiceButtonHandled = false; private boolean mIsLongPressing = false;
private Runnable mLongPressTimeoutRunnable = null;
@Override @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 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())) { if (isGlobalPriorityActive) {
handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
needWakeLock);
} else {
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, needWakeLock); keyEvent, needWakeLock);
} else {
handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
needWakeLock);
} }
} }
} finally { } 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. * the incoming volume key event.
* <p> * <p>
* Handles the dispatching of the volume button events to one of the * 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}, * {@link KeyEvent#KEYCODE_VOLUME_DOWN},
* or {@link KeyEvent#KEYCODE_VOLUME_MUTE}. * or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
* @param stream stream type to adjust volume. * @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 * @see #dispatchVolumeKeyEventToSessionAsSystemService
*/ */
@Override @Override
@@ -1707,7 +1707,7 @@ public class MediaSessionService extends SystemService implements Monitor {
mHandler.obtainMessage( mHandler.obtainMessage(
MessageHandler.MSG_VOLUME_INITIAL_DOWN, MessageHandler.MSG_VOLUME_INITIAL_DOWN,
mCurrentFullUserRecord.mFullUserId, 0), mCurrentFullUserRecord.mFullUserId, 0),
mLongPressTimeout); LONG_PRESS_TIMEOUT);
} }
if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
@@ -2112,32 +2112,148 @@ 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) { boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
int action = keyEvent.getAction(); if (keyEvent.isCanceled()) {
boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; return;
if (action == KeyEvent.ACTION_DOWN) {
if (keyEvent.getRepeatCount() == 0) {
mVoiceButtonDown = true;
mVoiceButtonHandled = false;
} else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
mVoiceButtonHandled = true;
startVoiceInput(needWakeLock);
} }
} else if (action == KeyEvent.ACTION_UP) {
if (mVoiceButtonDown) { int overriddenKeyEvents = (mCustomMediaKeyDispatcher == null) ? 0
mVoiceButtonDown = false; : mCustomMediaKeyDispatcher.getOverriddenKeyEvents().get(keyEvent.getKeyCode());
if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { cancelPendingIfNeeded(keyEvent);
// Resend the down then send this event through 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();
}
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); KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
downEvent, needWakeLock); downEvent, needWakeLock);
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, needWakeLock); keyEvent, needWakeLock);
} }
}
}
}
private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
@@ -2149,15 +2265,11 @@ public class MediaSessionService extends SystemService implements Monitor {
MediaSessionRecord session = null; MediaSessionRecord session = null;
// Retrieve custom session for key event if it exists. // Retrieve custom session for key event if it exists.
if (mCustomMediaKeyDispatcher != null && mGetSessionForKeyEventMethod != null) { if (mCustomMediaKeyDispatcher != null) {
try { MediaSession.Token token = mCustomMediaKeyDispatcher.getSessionForKeyEvent(
Object tokenObject = mGetSessionForKeyEventMethod.invoke( keyEvent, uid, asSystemService);
mCustomMediaKeyDispatcher, keyEvent, uid, asSystemService); if (token != null) {
if (tokenObject != null) { session = getMediaSessionRecordLocked(token);
session = getMediaSessionRecordLocked((MediaSession.Token) tokenObject);
}
} catch (InvocationTargetException | IllegalAccessException e) {
Log.w(TAG, "Encountered problem while using reflection", e);
} }
} }
@@ -2312,12 +2424,11 @@ public class MediaSessionService extends SystemService implements Monitor {
mHandled = true; mHandled = true;
mHandler.removeCallbacks(this); mHandler.removeCallbacks(this);
synchronized (mLock) { synchronized (mLock) {
if (!isGlobalPriorityActiveLocked() if (isGlobalPriorityActiveLocked()) {
&& isVoiceKey(mKeyEvent.getKeyCode())) { dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
mKeyEvent, mNeedWakeLock); mKeyEvent, mNeedWakeLock);
} else { } else {
dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, handleKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
mKeyEvent, mNeedWakeLock); mKeyEvent, mNeedWakeLock);
} }
} }

View File

@@ -29,6 +29,7 @@ import java.lang.annotation.RetentionPolicy;
* Note: When instantiating this class, {@link MediaSessionService} will only use the constructor * Note: When instantiating this class, {@link MediaSessionService} will only use the constructor
* without any parameters. * without any parameters.
*/ */
// TODO: Move this class to apex/media/
public abstract class SessionPolicyProvider { public abstract class SessionPolicyProvider {
@IntDef(value = { @IntDef(value = {
SESSION_POLICY_IGNORE_BUTTON_RECEIVER, SESSION_POLICY_IGNORE_BUTTON_RECEIVER,