SendKeyAction.java revision cd3445cc8b7a1a08f34bf955c6d2357a5d5481c3
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 */ 16package com.android.server.hdmi; 17 18import android.hardware.hdmi.HdmiCecMessage; 19import android.util.Slog; 20import android.view.KeyEvent; 21 22/** 23 * Feature action that transmits remote control key command (User Control Press/ 24 * User Control Release) to CEC bus. 25 * 26 * <p>This action is created when a new key event is passed to CEC service. It optionally 27 * does key repeat (a.k.a. press-and-hold) operation until it receives a key release event. 28 * If another key press event is received before the key in use is released, CEC service 29 * does not create a new action but recycles the current one by updating the key used 30 * for press-and-hold operation. 31 * 32 * <p>Package-private, accessed by {@link HdmiControlService} only. 33 */ 34final class SendKeyAction extends FeatureAction { 35 private static final String TAG = "SendKeyAction"; 36 37 // State in which the action is at work. The state is set in {@link #start()} and 38 // persists throughout the process till it is set back to {@code STATE_NONE} at the end. 39 private static final int STATE_PROCESSING_KEYCODE = 1; 40 41 // IRT(Initiator Repetition Time) in millisecond as recommended in the standard. 42 // Outgoing UCP commands, when in 'Press and Hold' mode, should be this much apart 43 // from the adjacent one so as not to place unnecessarily heavy load on the CEC line. 44 // TODO: This value might need tweaking per product basis. Consider putting it 45 // in config.xml to allow customization. 46 private static final int IRT_MS = 450; 47 48 // Logical address of the device to which the UCP/UCP commands are sent. 49 private final int mTargetAddress; 50 51 // The key code of the last key press event the action is passed via processKeyEvent. 52 private int mLastKeyCode; 53 54 /** 55 * Constructor. 56 * 57 * @param source {@link HdmiCecLocalDevice} instance 58 * @param targetAddress logical address of the device to send the keys to 59 * @param keyCode remote control key code as defined in {@link KeyEvent} 60 */ 61 SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) { 62 super(source); 63 mTargetAddress = targetAddress; 64 mLastKeyCode = keyCode; 65 } 66 67 @Override 68 public boolean start() { 69 sendKeyDown(mLastKeyCode); 70 mState = STATE_PROCESSING_KEYCODE; 71 addTimer(mState, IRT_MS); 72 return true; 73 } 74 75 /** 76 * Called when a key event should be handled for the action. 77 * 78 * @param keyCode key code of {@link KeyEvent} object 79 * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN} 80 * @param param additional parameter that comes with the key event 81 */ 82 void processKeyEvent(int keyCode, boolean isPressed) { 83 if (mState != STATE_PROCESSING_KEYCODE) { 84 Slog.w(TAG, "Not in a valid state"); 85 return; 86 } 87 // A new key press event that comes in with a key code different from the last 88 // one sets becomes a new key code to be used for press-and-hold operation. 89 // Removes any pending timer and starts a new timer for itself. 90 // Key release event indicates that the action shall be finished. Send UCR 91 // command and terminate the action. Other release events are ignored. 92 if (isPressed) { 93 if (keyCode != mLastKeyCode) { 94 mActionTimer.clearTimerMessage(); 95 sendKeyDown(keyCode); 96 addTimer(mState, IRT_MS); 97 mLastKeyCode = keyCode; 98 } 99 } else { 100 if (keyCode == mLastKeyCode) { 101 sendKeyUp(); 102 finish(); 103 } 104 } 105 } 106 107 private void sendKeyDown(int keyCode) { 108 byte[] keyCodeAndParam = getCecKeyCodeAndParam(keyCode); 109 if (keyCodeAndParam == null) { 110 return; 111 } 112 sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), 113 mTargetAddress, keyCodeAndParam)); 114 } 115 116 private void sendKeyUp() { 117 sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(), 118 mTargetAddress)); 119 } 120 121 @Override 122 public boolean processCommand(HdmiCecMessage cmd) { 123 // Send key action doesn't need any incoming CEC command, hence does not consume it. 124 return false; 125 } 126 127 @Override 128 public void handleTimerEvent(int state) { 129 // Timer event occurs every IRT_MS milliseconds to perform key-repeat (or press-and-hold) 130 // operation. If the last received key code is as same as the one with which the action 131 // is started, plus there was no key release event in last IRT_MS timeframe, send a UCP 132 // command and start another timer to schedule the next press-and-hold command. 133 if (mState != STATE_PROCESSING_KEYCODE) { 134 Slog.w(TAG, "Not in a valid state"); 135 return; 136 } 137 sendKeyDown(mLastKeyCode); 138 addTimer(mState, IRT_MS); 139 } 140 141 // Converts the Android key code to corresponding CEC key code definition. Those CEC keys 142 // with additional parameters should be mapped from individual Android key code. 'Select 143 // Broadcast' with the parameter 'cable', for instance, shall have its counterpart such as 144 // KeyEvent.KEYCODE_TV_BROADCAST_CABLE. 145 // The return byte array contains both UI command (keycode) and optional parameter. 146 private byte[] getCecKeyCodeAndParam(int keyCode) { 147 return HdmiCecKeycode.androidKeyToCecKey(keyCode); 148 } 149} 150