Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 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 | |
| 17 | package com.android.server.hdmi; |
| 18 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 19 | import static com.android.server.hdmi.Constants.MESSAGE_FEATURE_ABORT; |
| 20 | import static com.android.server.hdmi.Constants.MESSAGE_REPORT_AUDIO_STATUS; |
| 21 | import static com.android.server.hdmi.Constants.MESSAGE_USER_CONTROL_PRESSED; |
Jinsuk Kim | 8566be3 | 2014-10-02 12:48:29 +0900 | [diff] [blame] | 22 | import static com.android.server.hdmi.HdmiConfig.IRT_MS; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 23 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 24 | import android.media.AudioManager; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 25 | |
| 26 | /** |
| 27 | * Feature action that transmits volume change to Audio Receiver. |
| 28 | * <p> |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 29 | * This action is created when a user pressed volume up/down. However, Android only provides a |
| 30 | * listener for delta of some volume change instead of individual key event. Also it's hard to know |
| 31 | * Audio Receiver's number of volume steps for a single volume control key. Because of this, it |
| 32 | * sends key-down event until IRT timeout happens, and it will send key-up event if no additional |
| 33 | * volume change happens; otherwise, it will send again key-down as press and hold feature does. |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 34 | */ |
Jungshik Jang | b509c2e | 2014-08-07 13:45:01 +0900 | [diff] [blame] | 35 | final class VolumeControlAction extends HdmiCecFeatureAction { |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 36 | private static final String TAG = "VolumeControlAction"; |
| 37 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 38 | // State that wait for next volume press. |
| 39 | private static final int STATE_WAIT_FOR_NEXT_VOLUME_PRESS = 1; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 40 | private static final int MAX_VOLUME = 100; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 41 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 42 | private static final int UNKNOWN_AVR_VOLUME = -1; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 43 | |
| 44 | private final int mAvrAddress; |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 45 | private boolean mIsVolumeUp; |
| 46 | private long mLastKeyUpdateTime; |
| 47 | private int mLastAvrVolume; |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 48 | private boolean mLastAvrMute; |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 49 | private boolean mSentKeyPressed; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 50 | |
| 51 | /** |
| 52 | * Scale a custom volume value to cec volume scale. |
| 53 | * |
| 54 | * @param volume volume value in custom scale |
| 55 | * @param scale scale of volume (max volume) |
| 56 | * @return a volume scaled to cec volume range |
| 57 | */ |
| 58 | public static int scaleToCecVolume(int volume, int scale) { |
| 59 | return (volume * MAX_VOLUME) / scale; |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Scale a cec volume which is in range of 0 to 100 to custom volume level. |
| 64 | * |
| 65 | * @param cecVolume volume value in cec volume scale. It should be in a range of [0-100] |
| 66 | * @param scale scale of custom volume (max volume) |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 67 | * @return a volume scaled to custom volume range |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 68 | */ |
| 69 | public static int scaleToCustomVolume(int cecVolume, int scale) { |
| 70 | return (cecVolume * scale) / MAX_VOLUME; |
| 71 | } |
| 72 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 73 | VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, boolean isVolumeUp) { |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 74 | super(source); |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 75 | mAvrAddress = avrAddress; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 76 | mIsVolumeUp = isVolumeUp; |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 77 | mLastAvrVolume = UNKNOWN_AVR_VOLUME; |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 78 | mLastAvrMute = false; |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 79 | mSentKeyPressed = false; |
| 80 | |
| 81 | updateLastKeyUpdateTime(); |
| 82 | } |
| 83 | |
| 84 | private void updateLastKeyUpdateTime() { |
| 85 | mLastKeyUpdateTime = System.currentTimeMillis(); |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | @Override |
| 89 | boolean start() { |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 90 | mState = STATE_WAIT_FOR_NEXT_VOLUME_PRESS; |
| 91 | sendVolumeKeyPressed(); |
| 92 | resetTimer(); |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 93 | return true; |
| 94 | } |
| 95 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 96 | private void sendVolumeKeyPressed() { |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 97 | sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress, |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 98 | mIsVolumeUp ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 99 | : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 100 | mSentKeyPressed = true; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 101 | } |
| 102 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 103 | private void resetTimer() { |
| 104 | mActionTimer.clearTimerMessage(); |
| 105 | addTimer(STATE_WAIT_FOR_NEXT_VOLUME_PRESS, IRT_MS); |
| 106 | } |
| 107 | |
| 108 | void handleVolumeChange(boolean isVolumeUp) { |
| 109 | if (mIsVolumeUp != isVolumeUp) { |
| 110 | HdmiLogger.debug("Volume Key Status Changed[old:%b new:%b]", mIsVolumeUp, isVolumeUp); |
| 111 | sendVolumeKeyReleased(); |
| 112 | mIsVolumeUp = isVolumeUp; |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 113 | sendVolumeKeyPressed(); |
| 114 | resetTimer(); |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 115 | } |
| 116 | updateLastKeyUpdateTime(); |
| 117 | } |
| 118 | |
| 119 | private void sendVolumeKeyReleased() { |
| 120 | sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( |
| 121 | getSourceAddress(), mAvrAddress)); |
| 122 | mSentKeyPressed = false; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | @Override |
| 126 | boolean processCommand(HdmiCecMessage cmd) { |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 127 | if (mState != STATE_WAIT_FOR_NEXT_VOLUME_PRESS || cmd.getSource() != mAvrAddress) { |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 128 | return false; |
| 129 | } |
| 130 | |
| 131 | switch (cmd.getOpcode()) { |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 132 | case MESSAGE_REPORT_AUDIO_STATUS: |
| 133 | return handleReportAudioStatus(cmd); |
| 134 | case MESSAGE_FEATURE_ABORT: |
| 135 | return handleFeatureAbort(cmd); |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 136 | } |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 137 | return false; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 138 | } |
| 139 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 140 | private boolean handleReportAudioStatus(HdmiCecMessage cmd) { |
| 141 | byte params[] = cmd.getParams(); |
| 142 | boolean mute = (params[0] & 0x80) == 0x80; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 143 | int volume = params[0] & 0x7F; |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 144 | mLastAvrVolume = volume; |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 145 | mLastAvrMute = mute; |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 146 | if (shouldUpdateAudioVolume(mute)) { |
| 147 | HdmiLogger.debug("Force volume change[mute:%b, volume=%d]", mute, volume); |
| 148 | tv().setAudioStatus(mute, volume); |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 149 | mLastAvrVolume = UNKNOWN_AVR_VOLUME; |
| 150 | mLastAvrMute = false; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 151 | } |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 152 | return true; |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 153 | } |
| 154 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 155 | private boolean shouldUpdateAudioVolume(boolean mute) { |
| 156 | // Do nothing if in mute. |
| 157 | if (mute) { |
| 158 | return true; |
| 159 | } |
| 160 | |
| 161 | // Update audio status if current volume position is edge of volume bar, |
| 162 | // i.e max or min volume. |
| 163 | AudioManager audioManager = tv().getService().getAudioManager(); |
| 164 | int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); |
| 165 | if (mIsVolumeUp) { |
| 166 | int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); |
| 167 | return currentVolume == maxVolume; |
| 168 | } else { |
| 169 | return currentVolume == 0; |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | private boolean handleFeatureAbort(HdmiCecMessage cmd) { |
| 174 | int originalOpcode = cmd.getParams()[0] & 0xFF; |
| 175 | // Since it sends <User Control Released> only when it finishes this action, |
| 176 | // it takes care of <User Control Pressed> only here. |
| 177 | if (originalOpcode == MESSAGE_USER_CONTROL_PRESSED) { |
| 178 | finish(); |
| 179 | return true; |
| 180 | } |
| 181 | return false; |
| 182 | } |
| 183 | |
| 184 | @Override |
| 185 | protected void clear() { |
| 186 | super.clear(); |
| 187 | if (mSentKeyPressed) { |
| 188 | sendVolumeKeyReleased(); |
| 189 | } |
| 190 | if (mLastAvrVolume != UNKNOWN_AVR_VOLUME) { |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 191 | tv().setAudioStatus(mLastAvrMute, mLastAvrVolume); |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 192 | mLastAvrVolume = UNKNOWN_AVR_VOLUME; |
Jungshik Jang | 5352081c | 2014-09-22 15:14:49 +0900 | [diff] [blame] | 193 | mLastAvrMute = false; |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 194 | } |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | @Override |
| 198 | void handleTimerEvent(int state) { |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 199 | if (state != STATE_WAIT_FOR_NEXT_VOLUME_PRESS) { |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 200 | return; |
| 201 | } |
| 202 | |
Jungshik Jang | 2e8f1b6 | 2014-09-03 08:28:02 +0900 | [diff] [blame] | 203 | if (System.currentTimeMillis() - mLastKeyUpdateTime >= IRT_MS) { |
| 204 | finish(); |
| 205 | } else { |
| 206 | sendVolumeKeyPressed(); |
| 207 | resetTimer(); |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 208 | } |
Jungshik Jang | 8fa36b1 | 2014-06-25 15:51:36 +0900 | [diff] [blame] | 209 | } |
| 210 | } |