1 /* 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 */ 16 package com.android.server.hdmi; 17 18 import static com.android.server.hdmi.HdmiConfig.IRT_MS; 19 20 import android.util.Slog; 21 import android.view.KeyEvent; 22 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 */ 35 final class SendKeyAction extends HdmiCecFeatureAction { 36 private static final String TAG = "SendKeyAction"; 37 38 // 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 44 // 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 49 // 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; 57 58 // 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. 62 private int mLastKeycode; 63 64 // 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 68 /** 69 * Constructor. 70 * 71 * @param source {@link HdmiCecLocalDevice} instance 72 * @param targetAddress logical address of the device to send the keys to 73 * @param keycode remote control key code as defined in {@link KeyEvent} 74 */ 75 SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keycode) { 76 super(source); 77 mTargetAddress = targetAddress; 78 mLastKeycode = keycode; 79 } 80 81 @Override 82 public boolean start() { 83 sendKeyDown(mLastKeycode); 84 mLastSendKeyTime = getCurrentTime(); 85 // finish action for non-repeatable key. 86 if (!HdmiCecKeycode.isRepeatableKey(mLastKeycode)) { 87 sendKeyUp(); 88 finish(); 89 return true; 90 } 91 mState = STATE_CHECKING_LONGPRESS; 92 addTimer(mState, AWAIT_LONGPRESS_MS); 93 return true; 94 } 95 96 private long getCurrentTime() { 97 return System.currentTimeMillis(); 98 } 99 100 /** 101 * Called when a key event should be handled for the action. 102 * 103 * @param keycode key code of {@link KeyEvent} object 104 * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN} 105 */ 106 void processKeyEvent(int keycode, boolean isPressed) { 107 if (mState != STATE_CHECKING_LONGPRESS && mState != STATE_PROCESSING_KEYCODE) { 108 Slog.w(TAG, "Not in a valid state"); 109 return; 110 } 111 if (isPressed) { 112 // 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. 114 if (keycode != mLastKeycode) { 115 sendKeyDown(keycode); 116 mLastSendKeyTime = getCurrentTime(); 117 if (!HdmiCecKeycode.isRepeatableKey(keycode)) { 118 sendKeyUp(); 119 finish(); 120 return; 121 } 122 } 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 } 130 } 131 mActionTimer.clearTimerMessage(); 132 addTimer(mState, AWAIT_RELEASE_KEY_MS); 133 mLastKeycode = keycode; 134 } else { 135 // Key release event indicates that the action shall be finished. Send UCR 136 // command and terminate the action. Other release events are ignored. 137 if (keycode == mLastKeycode) { 138 sendKeyUp(); 139 finish(); 140 } 141 } 142 } 143 144 private void sendKeyDown(int keycode) { 145 byte[] cecKeycodeAndParams = HdmiCecKeycode.androidKeyToCecKey(keycode); 146 if (cecKeycodeAndParams == null) { 147 return; 148 } 149 sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), 150 mTargetAddress, cecKeycodeAndParams)); 151 } 152 153 private void sendKeyUp() { 154 sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(), 155 mTargetAddress)); 156 } 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) { 166 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; 183 } 184 } 185 } 186