summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/hdmi/SendKeyAction.java
blob: ed978e011dd10a4648833b10c43795f0229decb5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.hdmi;

import static com.android.server.hdmi.Constants.IRT_MS;

import android.util.Slog;
import android.view.KeyEvent;

/**
 * Feature action that transmits remote control key command (User Control Press/
 * User Control Release) to CEC bus.
 *
 * <p>This action is created when a new key event is passed to CEC service. It optionally
 * does key repeat (a.k.a. press-and-hold) operation until it receives a key release event.
 * If another key press event is received before the key in use is released, CEC service
 * does not create a new action but recycles the current one by updating the key used
 * for press-and-hold operation.
 *
 * <p>Package-private, accessed by {@link HdmiControlService} only.
 */
final class SendKeyAction extends HdmiCecFeatureAction {
    private static final String TAG = "SendKeyAction";

    // State in which the action is at work. The state is set in {@link #start()} and
    // persists throughout the process till it is set back to {@code STATE_NONE} at the end.
    private static final int STATE_PROCESSING_KEYCODE = 1;

    // Logical address of the device to which the UCP/UCP commands are sent.
    private final int mTargetAddress;

    // The key code of the last key press event the action is passed via processKeyEvent.
    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}
     */
    SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keycode) {
        super(source);
        mTargetAddress = targetAddress;
        mLastKeycode = keycode;
    }

    @Override
    public boolean start() {
        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;
    }

    /**
     * Called when a key event should be handled for the action.
     *
     * @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) {
        if (mState != STATE_PROCESSING_KEYCODE) {
            Slog.w(TAG, "Not in a valid state");
            return;
        }
        // A new key press event that comes in with a key code different from the last
        // one sets becomes a new key code to be used for press-and-hold operation.
        // Removes any pending timer and starts a new timer for itself.
        // 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) {
                sendKeyDown(keycode);
                if (!HdmiCecKeycode.isRepeatableKey(keycode)) {
                    sendKeyUp();
                    finish();
                    return;
                }
                mActionTimer.clearTimerMessage();
                addTimer(mState, IRT_MS);
                mLastKeycode = keycode;
            }
        } else {
            if (keycode == mLastKeycode) {
                sendKeyUp();
                finish();
            }
        }
    }

    private void sendKeyDown(int keycode) {
        int cecKeycode = HdmiCecKeycode.androidKeyToCecKey(keycode);
        if (cecKeycode == HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
            return;
        }
        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
                mTargetAddress, new byte[] { (byte) (cecKeycode & 0xFF) }));
    }

    private void sendKeyUp() {
        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
                mTargetAddress));
    }

    @Override
    public boolean processCommand(HdmiCecMessage cmd) {
        // Send key action doesn't need any incoming CEC command, hence does not consume it.
        return false;
    }

    @Override
    public void handleTimerEvent(int state) {
        // Timer event occurs every IRT_MS milliseconds to perform key-repeat (or press-and-hold)
        // operation. If the last received key code is as same as the one with which the action
        // is started, plus there was no key release event in last IRT_MS timeframe, send a UCP
        // command and start another timer to schedule the next press-and-hold command.
        if (mState != STATE_PROCESSING_KEYCODE) {
            Slog.w(TAG, "Not in a valid state");
            return;
        }
        sendKeyDown(mLastKeycode);
        addTimer(mState, IRT_MS);
    }
}