Prevent press&hold for non-repeatable keycode.
For non-repeat keys like power or mute key Hdmi-Cec
doesn't support press&hold feature. This change sends
user control pressed immediataly if the given keycode
is non-repeatable key.
Change-Id: Id571e67d94125a8c6a3553135b8e714b721405f3
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 7080a56..c0c8424 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -175,15 +175,21 @@
private final int mAndroidKeycode;
private final int mCecKeycode;
private final int mParam;
+ private final boolean mIsRepeatable;
- private KeycodeEntry(int androidKeycode, int cecKeycode, int param) {
- this.mAndroidKeycode = androidKeycode;
- this.mCecKeycode = cecKeycode;
- this.mParam = param;
+ private KeycodeEntry(int androidKeycode, int cecKeycode, int param, boolean isRepeatable) {
+ mAndroidKeycode = androidKeycode;
+ mCecKeycode = cecKeycode;
+ mParam = param;
+ mIsRepeatable = isRepeatable;
}
private KeycodeEntry(int androidKeycode, int cecKeycode) {
- this(androidKeycode, cecKeycode, NO_PARAM);
+ this(androidKeycode, cecKeycode, NO_PARAM, true);
+ }
+
+ private KeycodeEntry(int androidKeycode, int cecKeycode, boolean isRepeatable) {
+ this(androidKeycode, cecKeycode, NO_PARAM, isRepeatable);
}
private byte[] toCecKeycodeIfMatched(int androidKeycode) {
@@ -210,6 +216,14 @@
return UNSUPPORTED_KEYCODE;
}
}
+
+ private Boolean isRepeatableIfMatched(int androidKeycode) {
+ if (mAndroidKeycode == androidKeycode) {
+ return mIsRepeatable;
+ } else {
+ return null;
+ }
+ }
}
// Keycode entry container for all mappings.
@@ -221,19 +235,26 @@
new KeycodeEntry(KeyEvent.KEYCODE_DPAD_LEFT, CEC_KEYCODE_LEFT),
new KeycodeEntry(KeyEvent.KEYCODE_DPAD_RIGHT, CEC_KEYCODE_RIGHT),
// No Android keycode defined for CEC_KEYCODE_RIGHT_UP
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RIGHT_UP),
// No Android keycode defined for CEC_KEYCODE_RIGHT_DOWN
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RIGHT_DOWN),
// No Android keycode defined for CEC_KEYCODE_LEFT_UP
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_UP),
// No Android keycode defined for CEC_KEYCODE_LEFT_DOWN
- new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU),
- new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU),
- new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_CONTENTS_MENU),
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_DOWN),
+ new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU, false),
+ new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU, false),
+ new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_CONTENTS_MENU, false),
// No Android keycode defined for CEC_KEYCODE_FAVORITE_MENU
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_FAVORITE_MENU),
new KeycodeEntry(KeyEvent.KEYCODE_BACK, CEC_KEYCODE_EXIT),
// RESERVED
new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_TOP_MENU, CEC_KEYCODE_MEDIA_TOP_MENU),
// No Android keycode defined for CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU),
// RESERVED
// No Android keycode defined for CEC_KEYCODE_NUMBER_ENTRY_MODE
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_NUMBER_ENTRY_MODE),
new KeycodeEntry(KeyEvent.KEYCODE_11, CEC_KEYCODE_NUMBER_11),
new KeycodeEntry(KeyEvent.KEYCODE_12, CEC_KEYCODE_NUMBER_12),
new KeycodeEntry(KeyEvent.KEYCODE_0, CEC_KEYCODE_NUMBER_0_OR_NUMBER_10),
@@ -251,20 +272,23 @@
new KeycodeEntry(KeyEvent.KEYCODE_CLEAR, CEC_KEYCODE_CLEAR),
// RESERVED
// No Android keycode defined for CEC_KEYCODE_NEXT_FAVORITE
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_NEXT_FAVORITE),
new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP),
new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN),
new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL),
// No Android keycode defined for CEC_KEYCODE_SOUND_SELECT
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SOUND_SELECT),
new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT),
new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION),
// No Android keycode defined for CEC_KEYCODE_HELP
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_HELP),
new KeycodeEntry(KeyEvent.KEYCODE_PAGE_UP, CEC_KEYCODE_PAGE_UP),
new KeycodeEntry(KeyEvent.KEYCODE_PAGE_DOWN, CEC_KEYCODE_PAGE_DOWN),
// RESERVED
- new KeycodeEntry(KeyEvent.KEYCODE_POWER, CEC_KEYCODE_POWER),
+ new KeycodeEntry(KeyEvent.KEYCODE_POWER, CEC_KEYCODE_POWER, false),
new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_UP, CEC_KEYCODE_VOLUME_UP),
new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_DOWN, CEC_KEYCODE_VOLUME_DOWN),
- new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_MUTE, CEC_KEYCODE_MUTE),
+ new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_MUTE, CEC_KEYCODE_MUTE, false),
new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PLAY, CEC_KEYCODE_PLAY),
new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_STOP, CEC_KEYCODE_STOP),
new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PAUSE, CEC_KEYCODE_PAUSE),
@@ -275,33 +299,57 @@
new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_NEXT, CEC_KEYCODE_FORWARD),
new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PREVIOUS, CEC_KEYCODE_BACKWARD),
// No Android keycode defined for CEC_KEYCODE_STOP_RECORD
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_STOP_RECORD),
// No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_RECORD),
// No Android keycode defined for CEC_KEYCODE_RESERVED
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RESERVED),
// No Android keycode defined for CEC_KEYCODE_ANGLE
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_ANGLE),
// No Android keycode defined for CEC_KEYCODE_SUB_PICTURE
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SUB_PICTURE),
// No Android keycode defined for CEC_KEYCODE_VIDEO_ON_DEMAND
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_VIDEO_ON_DEMAND),
new KeycodeEntry(KeyEvent.KEYCODE_GUIDE, CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE),
// No Android keycode defined for CEC_KEYCODE_TIMER_PROGRAMMING
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_TIMER_PROGRAMMING),
// No Android keycode defined for CEC_KEYCODE_INITIAL_CONFIGURATION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_INITIAL_CONFIGURATION),
// No Android keycode defined for CEC_KEYCODE_SELECT_BROADCAST_TYPE
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_BROADCAST_TYPE),
// No Android keycode defined for CEC_KEYCODE_SELECT_SOUND_PRESENTATION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_SOUND_PRESENTATION),
// RESERVED
// The following deterministic key definitions do not need key mapping
// since they are supposed to be generated programmatically only.
// No Android keycode defined for CEC_KEYCODE_PLAY_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PLAY_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_PAUSE_PLAY_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_PLAY_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_RECORD_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RECORD_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_RECORD_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_STOP_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_STOP_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_MUTE_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_MUTE_FUNCTION, false),
// No Android keycode defined for CEC_KEYCODE_RESTORE_VOLUME_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RESTORE_VOLUME_FUNCTION, false),
// No Android keycode defined for CEC_KEYCODE_TUNE_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_TUNE_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_SELECT_MEDIA_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_MEDIA_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_POWER_TOGGLE_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_TOGGLE_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_POWER_OFF_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_OFF_FUNCTION),
// No Android keycode defined for CEC_KEYCODE_POWER_ON_FUNCTION
+ new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_ON_FUNCTION, false),
// RESERVED
new KeycodeEntry(KeyEvent.KEYCODE_PROG_BLUE, CEC_KEYCODE_F1_BLUE),
new KeycodeEntry(KeyEvent.KEYCODE_PROG_RED, CEC_KEYCODE_F2_RED),
@@ -346,4 +394,20 @@
}
return UNSUPPORTED_KEYCODE;
}
+
+ /**
+ * Whether the given {@code androidKeycode} is repeatable key or not.
+ *
+ * @param androidKeycode keycode of android
+ * @return false if the given {@code androidKeycode} is not supported key code
+ */
+ static boolean isRepeatableKey(int androidKeycode) {
+ for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) {
+ Boolean isRepeatable = KEYCODE_ENTRIES[i].isRepeatableIfMatched(androidKeycode);
+ if (isRepeatable != null) {
+ return isRepeatable;
+ }
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 5d81251..a525cda 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -44,24 +44,30 @@
private final int mTargetAddress;
// The key code of the last key press event the action is passed via processKeyEvent.
- private int mLastKeyCode;
+ private int mLastKeycode;
/**
* Constructor.
*
* @param source {@link HdmiCecLocalDevice} instance
* @param targetAddress logical address of the device to send the keys to
- * @param keyCode remote control key code as defined in {@link KeyEvent}
+ * @param keycode remote control key code as defined in {@link KeyEvent}
*/
- SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) {
+ SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keycode) {
super(source);
mTargetAddress = targetAddress;
- mLastKeyCode = keyCode;
+ mLastKeycode = keycode;
}
@Override
public boolean start() {
- sendKeyDown(mLastKeyCode);
+ sendKeyDown(mLastKeycode);
+ // finish action for non-repeatable key.
+ if (!HdmiCecKeycode.isRepeatableKey(mLastKeycode)) {
+ sendKeyUp();
+ finish();
+ return true;
+ }
mState = STATE_PROCESSING_KEYCODE;
addTimer(mState, IRT_MS);
return true;
@@ -70,10 +76,10 @@
/**
* Called when a key event should be handled for the action.
*
- * @param keyCode key code of {@link KeyEvent} object
+ * @param keycode key code of {@link KeyEvent} object
* @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN}
*/
- void processKeyEvent(int keyCode, boolean isPressed) {
+ void processKeyEvent(int keycode, boolean isPressed) {
if (mState != STATE_PROCESSING_KEYCODE) {
Slog.w(TAG, "Not in a valid state");
return;
@@ -84,27 +90,32 @@
// Key release event indicates that the action shall be finished. Send UCR
// command and terminate the action. Other release events are ignored.
if (isPressed) {
- if (keyCode != mLastKeyCode) {
+ if (keycode != mLastKeycode) {
+ if (!HdmiCecKeycode.isRepeatableKey(keycode)) {
+ sendKeyUp();
+ finish();
+ return;
+ }
mActionTimer.clearTimerMessage();
- sendKeyDown(keyCode);
+ sendKeyDown(keycode);
addTimer(mState, IRT_MS);
- mLastKeyCode = keyCode;
+ mLastKeycode = keycode;
}
} else {
- if (keyCode == mLastKeyCode) {
+ if (keycode == mLastKeycode) {
sendKeyUp();
finish();
}
}
}
- private void sendKeyDown(int keyCode) {
- byte[] keyCodeAndParam = getCecKeyCodeAndParam(keyCode);
- if (keyCodeAndParam == null) {
+ private void sendKeyDown(int keycode) {
+ byte[] keycodeAndParam = getCecKeycodeAndParam(keycode);
+ if (keycodeAndParam == null) {
return;
}
sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
- mTargetAddress, keyCodeAndParam));
+ mTargetAddress, keycodeAndParam));
}
private void sendKeyUp() {
@@ -128,7 +139,7 @@
Slog.w(TAG, "Not in a valid state");
return;
}
- sendKeyDown(mLastKeyCode);
+ sendKeyDown(mLastKeycode);
addTimer(mState, IRT_MS);
}
@@ -137,7 +148,7 @@
// Broadcast' with the parameter 'cable', for instance, shall have its counterpart such as
// KeyEvent.KEYCODE_TV_BROADCAST_CABLE.
// The return byte array contains both UI command (keycode) and optional parameter.
- private byte[] getCecKeyCodeAndParam(int keyCode) {
- return HdmiCecKeycode.androidKeyToCecKey(keyCode);
+ private byte[] getCecKeycodeAndParam(int keycode) {
+ return HdmiCecKeycode.androidKeyToCecKey(keycode);
}
}