VolumeControlAction.java revision 8566be3c51d97d9a4b62bfec52b1c88ade65dd43
16e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi/* 26e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * Copyright (C) 2014 The Android Open Source Project 36e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * 46e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * Licensed under the Apache License, Version 2.0 (the "License"); 56e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * you may not use this file except in compliance with the License. 66e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * You may obtain a copy of the License at 76e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * 86e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * http://www.apache.org/licenses/LICENSE-2.0 96e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * 106e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * Unless required by applicable law or agreed to in writing, software 116e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * distributed under the License is distributed on an "AS IS" BASIS, 126e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * See the License for the specific language governing permissions and 146e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * limitations under the License. 156e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi */ 166e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 176e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivipackage com.android.server.hdmi; 186e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 196e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Triviimport static com.android.server.hdmi.Constants.MESSAGE_FEATURE_ABORT; 206e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Triviimport static com.android.server.hdmi.Constants.MESSAGE_REPORT_AUDIO_STATUS; 216e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Triviimport static com.android.server.hdmi.Constants.MESSAGE_USER_CONTROL_PRESSED; 226e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Triviimport static com.android.server.hdmi.HdmiConfig.IRT_MS; 236e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 246e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Triviimport android.media.AudioManager; 256e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 266e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi/** 276e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * Feature action that transmits volume change to Audio Receiver. 286e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * <p> 296e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * This action is created when a user pressed volume up/down. However, Android only provides a 306e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * listener for delta of some volume change instead of individual key event. Also it's hard to know 316e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * Audio Receiver's number of volume steps for a single volume control key. Because of this, it 326e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * sends key-down event until IRT timeout happens, and it will send key-up event if no additional 336e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * volume change happens; otherwise, it will send again key-down as press and hold feature does. 346e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi */ 356e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivifinal class VolumeControlAction extends HdmiCecFeatureAction { 366e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private static final String TAG = "VolumeControlAction"; 376e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 386e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi // State that wait for next volume press. 396e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private static final int STATE_WAIT_FOR_NEXT_VOLUME_PRESS = 1; 406e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private static final int MAX_VOLUME = 100; 416e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 426e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private static final int UNKNOWN_AVR_VOLUME = -1; 436e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 446e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private final int mAvrAddress; 456e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private boolean mIsVolumeUp; 466e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private long mLastKeyUpdateTime; 476e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private int mLastAvrVolume; 486e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private boolean mLastAvrMute; 496e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private boolean mSentKeyPressed; 506e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 516e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi /** 526e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * Scale a custom volume value to cec volume scale. 536e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * 546e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * @param volume volume value in custom scale 556e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * @param scale scale of volume (max volume) 566e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * @return a volume scaled to cec volume range 576e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi */ 586e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi public static int scaleToCecVolume(int volume, int scale) { 596e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi return (volume * MAX_VOLUME) / scale; 606e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 616e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 626e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi /** 636e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * Scale a cec volume which is in range of 0 to 100 to custom volume level. 646e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * 656e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * @param cecVolume volume value in cec volume scale. It should be in a range of [0-100] 666e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * @param scale scale of custom volume (max volume) 676e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi * @return a volume scaled to custom volume range 686e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi */ 696e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi public static int scaleToCustomVolume(int cecVolume, int scale) { 706e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi return (cecVolume * scale) / MAX_VOLUME; 716e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 726e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 736e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, boolean isVolumeUp) { 746e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi super(source); 756e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mAvrAddress = avrAddress; 766e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mIsVolumeUp = isVolumeUp; 776e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mLastAvrVolume = UNKNOWN_AVR_VOLUME; 786e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mLastAvrMute = false; 796e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mSentKeyPressed = false; 806e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 816e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi updateLastKeyUpdateTime(); 826e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 836e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 846e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private void updateLastKeyUpdateTime() { 856e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mLastKeyUpdateTime = System.currentTimeMillis(); 866e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 876e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 886e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi @Override 896e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi boolean start() { 906e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mState = STATE_WAIT_FOR_NEXT_VOLUME_PRESS; 916e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi sendVolumeKeyPressed(); 926e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi resetTimer(); 936e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi return true; 946e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 956e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 966e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private void sendVolumeKeyPressed() { 976e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress, 986e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mIsVolumeUp ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP 996e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); 1006e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mSentKeyPressed = true; 1016e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 1026e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 1036e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private void resetTimer() { 1046e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mActionTimer.clearTimerMessage(); 1056e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi addTimer(STATE_WAIT_FOR_NEXT_VOLUME_PRESS, IRT_MS); 1066e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 1076e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 1086e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi void handleVolumeChange(boolean isVolumeUp) { 1096e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi if (mIsVolumeUp != isVolumeUp) { 1106e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi HdmiLogger.debug("Volume Key Status Changed[old:%b new:%b]", mIsVolumeUp, isVolumeUp); 1116e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi sendVolumeKeyReleased(); 1126e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mIsVolumeUp = isVolumeUp; 1136e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi sendVolumeKeyPressed(); 1146e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi resetTimer(); 1156e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 1166e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi updateLastKeyUpdateTime(); 1176e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 1186e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 1196e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi private void sendVolumeKeyReleased() { 1206e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( 1216e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi getSourceAddress(), mAvrAddress)); 1226e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi mSentKeyPressed = false; 1236e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 1246e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi 1256e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi @Override 1266e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi boolean processCommand(HdmiCecMessage cmd) { 1276e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi if (mState != STATE_WAIT_FOR_NEXT_VOLUME_PRESS || cmd.getSource() != mAvrAddress) { 1286e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi return false; 1296e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi } 130d7ecf117cfac5f2b90a0dc6c62b56dcce0715971Glenn Kasten 1316e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi switch (cmd.getOpcode()) { 1326e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi case MESSAGE_REPORT_AUDIO_STATUS: 1336e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi return handleReportAudioStatus(cmd); 1346e7e174807fc639c49125ced8962aa369370fbf0Jean-Michel Trivi case MESSAGE_FEATURE_ABORT: 135 return handleFeatureAbort(cmd); 136 } 137 return false; 138 } 139 140 private boolean handleReportAudioStatus(HdmiCecMessage cmd) { 141 byte params[] = cmd.getParams(); 142 boolean mute = (params[0] & 0x80) == 0x80; 143 int volume = params[0] & 0x7F; 144 mLastAvrVolume = volume; 145 mLastAvrMute = mute; 146 if (shouldUpdateAudioVolume(mute)) { 147 HdmiLogger.debug("Force volume change[mute:%b, volume=%d]", mute, volume); 148 tv().setAudioStatus(mute, volume); 149 mLastAvrVolume = UNKNOWN_AVR_VOLUME; 150 mLastAvrMute = false; 151 } 152 return true; 153 } 154 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) { 191 tv().setAudioStatus(mLastAvrMute, mLastAvrVolume); 192 mLastAvrVolume = UNKNOWN_AVR_VOLUME; 193 mLastAvrMute = false; 194 } 195 } 196 197 @Override 198 void handleTimerEvent(int state) { 199 if (state != STATE_WAIT_FOR_NEXT_VOLUME_PRESS) { 200 return; 201 } 202 203 if (System.currentTimeMillis() - mLastKeyUpdateTime >= IRT_MS) { 204 finish(); 205 } else { 206 sendVolumeKeyPressed(); 207 resetTimer(); 208 } 209 } 210} 211