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

This commit is contained in:
Jin Seok Park
2020-04-17 08:51:13 +00:00
committed by Android (Google) Code Review
3 changed files with 372 additions and 87 deletions

View File

@@ -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.
*
* <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
* 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<Integer, Integer> 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.
* <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.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<Integer, Integer> 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.
* <p>
* 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);
}
}

View File

@@ -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,