18fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang/* 28fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Copyright (C) 2014 The Android Open Source Project 38fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 48fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License"); 58fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * you may not use this file except in compliance with the License. 68fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * You may obtain a copy of the License at 78fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 88fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * http://www.apache.org/licenses/LICENSE-2.0 98fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 108fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Unless required by applicable law or agreed to in writing, software 118fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS, 128fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * See the License for the specific language governing permissions and 148fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * limitations under the License. 158fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 168fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 178fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jangpackage com.android.server.hdmi; 188fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 192e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jangimport static com.android.server.hdmi.Constants.MESSAGE_FEATURE_ABORT; 202e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jangimport static com.android.server.hdmi.Constants.MESSAGE_REPORT_AUDIO_STATUS; 212e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jangimport static com.android.server.hdmi.Constants.MESSAGE_USER_CONTROL_PRESSED; 228566be3c51d97d9a4b62bfec52b1c88ade65dd43Jinsuk Kimimport static com.android.server.hdmi.HdmiConfig.IRT_MS; 238fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 242e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jangimport android.media.AudioManager; 258fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 268fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang/** 278fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Feature action that transmits volume change to Audio Receiver. 288fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * <p> 292e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang * This action is created when a user pressed volume up/down. However, Android only provides a 302e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang * listener for delta of some volume change instead of individual key event. Also it's hard to know 312e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang * Audio Receiver's number of volume steps for a single volume control key. Because of this, it 322e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang * sends key-down event until IRT timeout happens, and it will send key-up event if no additional 332e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang * volume change happens; otherwise, it will send again key-down as press and hold feature does. 348fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 35b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangfinal class VolumeControlAction extends HdmiCecFeatureAction { 368fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final String TAG = "VolumeControlAction"; 378fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 382e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang // State that wait for next volume press. 392e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private static final int STATE_WAIT_FOR_NEXT_VOLUME_PRESS = 1; 408fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final int MAX_VOLUME = 100; 418fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 422e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private static final int UNKNOWN_AVR_VOLUME = -1; 438fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 448fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private final int mAvrAddress; 452e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private boolean mIsVolumeUp; 462e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private long mLastKeyUpdateTime; 472e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private int mLastAvrVolume; 485352081c662299b618335bf3024058fa04ef2dfdJungshik Jang private boolean mLastAvrMute; 492e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private boolean mSentKeyPressed; 508fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 518fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang /** 528fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Scale a custom volume value to cec volume scale. 538fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 548fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param volume volume value in custom scale 558fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param scale scale of volume (max volume) 568fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @return a volume scaled to cec volume range 578fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 588fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang public static int scaleToCecVolume(int volume, int scale) { 598fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return (volume * MAX_VOLUME) / scale; 608fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 618fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 628fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang /** 638fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Scale a cec volume which is in range of 0 to 100 to custom volume level. 648fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 658fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param cecVolume volume value in cec volume scale. It should be in a range of [0-100] 668fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param scale scale of custom volume (max volume) 672e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang * @return a volume scaled to custom volume range 688fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 698fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang public static int scaleToCustomVolume(int cecVolume, int scale) { 708fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return (cecVolume * scale) / MAX_VOLUME; 718fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 728fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 732e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, boolean isVolumeUp) { 748fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang super(source); 758fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang mAvrAddress = avrAddress; 768fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang mIsVolumeUp = isVolumeUp; 772e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mLastAvrVolume = UNKNOWN_AVR_VOLUME; 785352081c662299b618335bf3024058fa04ef2dfdJungshik Jang mLastAvrMute = false; 792e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mSentKeyPressed = false; 802e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang 812e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang updateLastKeyUpdateTime(); 822e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 832e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang 842e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private void updateLastKeyUpdateTime() { 852e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mLastKeyUpdateTime = System.currentTimeMillis(); 868fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 878fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 888fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang @Override 898fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang boolean start() { 902e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mState = STATE_WAIT_FOR_NEXT_VOLUME_PRESS; 912e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang sendVolumeKeyPressed(); 922e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang resetTimer(); 938fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return true; 948fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 958fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 962e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private void sendVolumeKeyPressed() { 972e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress, 982e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mIsVolumeUp ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP 992e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); 1002e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mSentKeyPressed = true; 1018fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1028fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1032e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private void resetTimer() { 1042e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mActionTimer.clearTimerMessage(); 1052e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang addTimer(STATE_WAIT_FOR_NEXT_VOLUME_PRESS, IRT_MS); 1068fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1078fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1082e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang void handleVolumeChange(boolean isVolumeUp) { 1092e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (mIsVolumeUp != isVolumeUp) { 1102e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang HdmiLogger.debug("Volume Key Status Changed[old:%b new:%b]", mIsVolumeUp, isVolumeUp); 1112e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang sendVolumeKeyReleased(); 1122e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mIsVolumeUp = isVolumeUp; 1135352081c662299b618335bf3024058fa04ef2dfdJungshik Jang sendVolumeKeyPressed(); 1145352081c662299b618335bf3024058fa04ef2dfdJungshik Jang resetTimer(); 1152e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1162e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang updateLastKeyUpdateTime(); 1178fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1188fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1192e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private void sendVolumeKeyReleased() { 1202e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( 1212e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang getSourceAddress(), mAvrAddress)); 1222e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mSentKeyPressed = false; 1238fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1248fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1258fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang @Override 1268fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang boolean processCommand(HdmiCecMessage cmd) { 1272e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (mState != STATE_WAIT_FOR_NEXT_VOLUME_PRESS || cmd.getSource() != mAvrAddress) { 1288fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return false; 1298fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1308fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1318fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang switch (cmd.getOpcode()) { 1322e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang case MESSAGE_REPORT_AUDIO_STATUS: 1332e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return handleReportAudioStatus(cmd); 1342e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang case MESSAGE_FEATURE_ABORT: 1352e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return handleFeatureAbort(cmd); 1368fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1375352081c662299b618335bf3024058fa04ef2dfdJungshik Jang return false; 1388fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1398fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1402e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private boolean handleReportAudioStatus(HdmiCecMessage cmd) { 1412e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang byte params[] = cmd.getParams(); 1422e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang boolean mute = (params[0] & 0x80) == 0x80; 1438fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang int volume = params[0] & 0x7F; 1442e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mLastAvrVolume = volume; 1455352081c662299b618335bf3024058fa04ef2dfdJungshik Jang mLastAvrMute = mute; 1462e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (shouldUpdateAudioVolume(mute)) { 1472e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang HdmiLogger.debug("Force volume change[mute:%b, volume=%d]", mute, volume); 1482e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang tv().setAudioStatus(mute, volume); 1495352081c662299b618335bf3024058fa04ef2dfdJungshik Jang mLastAvrVolume = UNKNOWN_AVR_VOLUME; 1505352081c662299b618335bf3024058fa04ef2dfdJungshik Jang mLastAvrMute = false; 1512e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1522e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return true; 1532e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1542e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang 1552e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private boolean shouldUpdateAudioVolume(boolean mute) { 1562e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang // Do nothing if in mute. 1572e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (mute) { 1582e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return true; 1592e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1602e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang 1612e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang // Update audio status if current volume position is edge of volume bar, 1622e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang // i.e max or min volume. 1632e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang AudioManager audioManager = tv().getService().getAudioManager(); 1642e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 1658fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (mIsVolumeUp) { 1662e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 1672e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return currentVolume == maxVolume; 1688fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } else { 1692e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return currentVolume == 0; 1708fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1712e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1728fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1732e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang private boolean handleFeatureAbort(HdmiCecMessage cmd) { 1742e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang int originalOpcode = cmd.getParams()[0] & 0xFF; 1752e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang // Since it sends <User Control Released> only when it finishes this action, 1762e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang // it takes care of <User Control Pressed> only here. 1772e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (originalOpcode == MESSAGE_USER_CONTROL_PRESSED) { 1782e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang finish(); 1792e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return true; 1802e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1812e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang return false; 1828fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1838fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1842e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang @Override 1852e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang protected void clear() { 1862e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang super.clear(); 1872e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (mSentKeyPressed) { 1882e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang sendVolumeKeyReleased(); 1892e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1902e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (mLastAvrVolume != UNKNOWN_AVR_VOLUME) { 1915352081c662299b618335bf3024058fa04ef2dfdJungshik Jang tv().setAudioStatus(mLastAvrMute, mLastAvrVolume); 1922e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang mLastAvrVolume = UNKNOWN_AVR_VOLUME; 1935352081c662299b618335bf3024058fa04ef2dfdJungshik Jang mLastAvrMute = false; 1942e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } 1958fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1968fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1978fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang @Override 1988fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang void handleTimerEvent(int state) { 1992e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (state != STATE_WAIT_FOR_NEXT_VOLUME_PRESS) { 2008fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return; 2018fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 2028fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 2032e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang if (System.currentTimeMillis() - mLastKeyUpdateTime >= IRT_MS) { 2042e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang finish(); 2052e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang } else { 2062e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang sendVolumeKeyPressed(); 2072e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang resetTimer(); 2088fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 2098fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 2108fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang} 211