blob: 40d2583691c6be61457298082d93e11014fa717d [file] [log] [blame]
Jinsuk Kimcd97baf2014-05-02 16:27:32 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.hdmi;
17
Jinsuk Kim8566be32014-10-02 12:48:29 +090018import static com.android.server.hdmi.HdmiConfig.IRT_MS;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090019
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090020import android.util.Slog;
Jungshik Jang79c58a42014-06-16 16:45:36 +090021import android.view.KeyEvent;
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090022
23/**
24 * Feature action that transmits remote control key command (User Control Press/
25 * User Control Release) to CEC bus.
26 *
27 * <p>This action is created when a new key event is passed to CEC service. It optionally
28 * does key repeat (a.k.a. press-and-hold) operation until it receives a key release event.
29 * If another key press event is received before the key in use is released, CEC service
30 * does not create a new action but recycles the current one by updating the key used
31 * for press-and-hold operation.
32 *
33 * <p>Package-private, accessed by {@link HdmiControlService} only.
34 */
Jungshik Jangb509c2e2014-08-07 13:45:01 +090035final class SendKeyAction extends HdmiCecFeatureAction {
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090036 private static final String TAG = "SendKeyAction";
37
Jinsuk Kimc4228a72015-01-23 15:39:00 +090038 // If the first key press lasts this much amount of time without any other key event
39 // coming down, we trigger the press-and-hold operation. Set to the value slightly
40 // shorter than the threshold(500ms) between two successive key press events
41 // as specified in the standard for the operation.
42 private static final int AWAIT_LONGPRESS_MS = 400;
43
Jinsuk Kim8566be32014-10-02 12:48:29 +090044 // Amount of time this action waits for a new release key input event. When timed out,
45 // the action sends out UCR and finishes its lifecycle. Used to deal with missing key release
46 // event, which can lead the device on the receiving end to generating unintended key repeats.
47 private static final int AWAIT_RELEASE_KEY_MS = 1000;
48
Jinsuk Kimc4228a72015-01-23 15:39:00 +090049 // State in which the long press is being checked at the beginning. The state is set in
50 // {@link #start()} and lasts for {@link #AWAIT_LONGPRESS_MS}.
51 private static final int STATE_CHECKING_LONGPRESS = 1;
52
53 // State in which the action is handling incoming keys. Persists throughout the process
54 // till it is set back to {@code STATE_NONE} at the end when a release key event for
55 // the last key is processed.
56 private static final int STATE_PROCESSING_KEYCODE = 2;
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090057
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090058 // Logical address of the device to which the UCP/UCP commands are sent.
59 private final int mTargetAddress;
60
61 // The key code of the last key press event the action is passed via processKeyEvent.
Jungshik Jang250cda52014-07-02 17:32:22 +090062 private int mLastKeycode;
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090063
Jinsuk Kim8566be32014-10-02 12:48:29 +090064 // The time stamp when the last CEC key command was sent. Used to determine the press-and-hold
65 // operation.
66 private long mLastSendKeyTime;
67
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090068 /**
69 * Constructor.
70 *
Jungshik Jang79c58a42014-06-16 16:45:36 +090071 * @param source {@link HdmiCecLocalDevice} instance
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090072 * @param targetAddress logical address of the device to send the keys to
Jungshik Jang250cda52014-07-02 17:32:22 +090073 * @param keycode remote control key code as defined in {@link KeyEvent}
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090074 */
Jungshik Jang250cda52014-07-02 17:32:22 +090075 SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keycode) {
Jungshik Jang79c58a42014-06-16 16:45:36 +090076 super(source);
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090077 mTargetAddress = targetAddress;
Jungshik Jang250cda52014-07-02 17:32:22 +090078 mLastKeycode = keycode;
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090079 }
80
81 @Override
82 public boolean start() {
Jungshik Jang250cda52014-07-02 17:32:22 +090083 sendKeyDown(mLastKeycode);
Jinsuk Kim8566be32014-10-02 12:48:29 +090084 mLastSendKeyTime = getCurrentTime();
Jungshik Jang250cda52014-07-02 17:32:22 +090085 // finish action for non-repeatable key.
86 if (!HdmiCecKeycode.isRepeatableKey(mLastKeycode)) {
87 sendKeyUp();
88 finish();
89 return true;
90 }
Jinsuk Kimc4228a72015-01-23 15:39:00 +090091 mState = STATE_CHECKING_LONGPRESS;
92 addTimer(mState, AWAIT_LONGPRESS_MS);
Jinsuk Kimcd97baf2014-05-02 16:27:32 +090093 return true;
94 }
95
Jinsuk Kim8566be32014-10-02 12:48:29 +090096 private long getCurrentTime() {
97 return System.currentTimeMillis();
98 }
99
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900100 /**
101 * Called when a key event should be handled for the action.
102 *
Jungshik Jang250cda52014-07-02 17:32:22 +0900103 * @param keycode key code of {@link KeyEvent} object
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900104 * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN}
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900105 */
Jungshik Jang250cda52014-07-02 17:32:22 +0900106 void processKeyEvent(int keycode, boolean isPressed) {
Jinsuk Kimc4228a72015-01-23 15:39:00 +0900107 if (mState != STATE_CHECKING_LONGPRESS && mState != STATE_PROCESSING_KEYCODE) {
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900108 Slog.w(TAG, "Not in a valid state");
109 return;
110 }
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900111 if (isPressed) {
Jinsuk Kim8566be32014-10-02 12:48:29 +0900112 // A new key press event that comes in with a key code different from the last
113 // one becomes a new key code to be used for press-and-hold operation.
Jungshik Jang250cda52014-07-02 17:32:22 +0900114 if (keycode != mLastKeycode) {
Jungshik Janga1a086a2014-07-07 09:30:58 +0900115 sendKeyDown(keycode);
Jinsuk Kim8566be32014-10-02 12:48:29 +0900116 mLastSendKeyTime = getCurrentTime();
Jungshik Jang250cda52014-07-02 17:32:22 +0900117 if (!HdmiCecKeycode.isRepeatableKey(keycode)) {
Jungshik Jang18563b12014-07-07 10:38:17 +0900118 sendKeyUp();
Jungshik Jang250cda52014-07-02 17:32:22 +0900119 finish();
120 return;
121 }
Jinsuk Kim8566be32014-10-02 12:48:29 +0900122 } else {
123 // Press-and-hold key transmission takes place if Android key inputs are
124 // repeatedly coming in and more than IRT_MS has passed since the last
125 // press-and-hold key transmission.
126 if (getCurrentTime() - mLastSendKeyTime >= IRT_MS) {
127 sendKeyDown(keycode);
128 mLastSendKeyTime = getCurrentTime();
129 }
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900130 }
Jinsuk Kim8566be32014-10-02 12:48:29 +0900131 mActionTimer.clearTimerMessage();
132 addTimer(mState, AWAIT_RELEASE_KEY_MS);
133 mLastKeycode = keycode;
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900134 } else {
Jinsuk Kim8566be32014-10-02 12:48:29 +0900135 // Key release event indicates that the action shall be finished. Send UCR
136 // command and terminate the action. Other release events are ignored.
Jungshik Jang250cda52014-07-02 17:32:22 +0900137 if (keycode == mLastKeycode) {
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900138 sendKeyUp();
139 finish();
140 }
141 }
142 }
143
Jungshik Jang250cda52014-07-02 17:32:22 +0900144 private void sendKeyDown(int keycode) {
Jungshik Jang73483b6b2014-09-26 14:00:59 +0900145 byte[] cecKeycodeAndParams = HdmiCecKeycode.androidKeyToCecKey(keycode);
146 if (cecKeycodeAndParams == null) {
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900147 return;
148 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900149 sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
Jungshik Jang73483b6b2014-09-26 14:00:59 +0900150 mTargetAddress, cecKeycodeAndParams));
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900151 }
152
153 private void sendKeyUp() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900154 sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
155 mTargetAddress));
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900156 }
157
158 @Override
159 public boolean processCommand(HdmiCecMessage cmd) {
160 // Send key action doesn't need any incoming CEC command, hence does not consume it.
161 return false;
162 }
163
164 @Override
165 public void handleTimerEvent(int state) {
Jinsuk Kimc4228a72015-01-23 15:39:00 +0900166 switch (mState) {
167 case STATE_CHECKING_LONGPRESS:
168 // The first key press lasts long enough to start press-and-hold.
169 mActionTimer.clearTimerMessage();
170 mState = STATE_PROCESSING_KEYCODE;
171 sendKeyDown(mLastKeycode);
172 mLastSendKeyTime = getCurrentTime();
173 addTimer(mState, AWAIT_RELEASE_KEY_MS);
174 break;
175 case STATE_PROCESSING_KEYCODE:
176 // Timeout on waiting for the release key event. Send UCR and quit the action.
177 sendKeyUp();
178 finish();
179 break;
180 default:
181 Slog.w(TAG, "Not in a valid state");
182 break;
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900183 }
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900184 }
Jinsuk Kimcd97baf2014-05-02 16:27:32 +0900185}