VolumeControlAction.java revision b509c2ecd99619248b7a07fb0fa978bb27f25cc3
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 19c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport static com.android.server.hdmi.Constants.IRT_MS; 208fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 218fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jangimport com.android.internal.util.Preconditions; 228fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 238fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang/** 248fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Feature action that transmits volume change to Audio Receiver. 258fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * <p> 268fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * This action is created when a user pressed volume up/down. However, Since Android only provides a 278fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * listener for delta of some volume change, we will set a target volume, and check reported volume 288fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * from Audio Receiver(AVR). If TV receives no <Report Audio Status> from AVR, this action 298fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * will be finished in {@link #IRT_MS} * {@link #VOLUME_CHANGE_TIMEOUT_MAX_COUNT} (ms). 308fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 31b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangfinal class VolumeControlAction extends HdmiCecFeatureAction { 328fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final String TAG = "VolumeControlAction"; 338fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 348fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final int VOLUME_MUTE = 101; 358fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final int VOLUME_RESTORE = 102; 368fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final int MAX_VOLUME = 100; 378fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final int MIN_VOLUME = 0; 388fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 398fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // State where to wait for <Report Audio Status> 408fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final int STATE_WAIT_FOR_REPORT_VOLUME_STATUS = 1; 418fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 428fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // Maximum count of time out used to finish volume action. 438fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private static final int VOLUME_CHANGE_TIMEOUT_MAX_COUNT = 2; 448fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 458fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private final int mAvrAddress; 468fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private final int mTargetVolume; 478fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private final boolean mIsVolumeUp; 488fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private int mTimeoutCount; 498fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 508fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang /** 518fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Create a {@link VolumeControlAction} for mute/restore change 528fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 538fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param source source device sending volume change 548fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param avrAddress address of audio receiver 558fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param mute whether to mute sound or not. {@code true} for mute on; {@code false} for mute 568fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * off, i.e restore volume 578fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @return newly created {@link VolumeControlAction} 588fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 598fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang public static VolumeControlAction ofMute(HdmiCecLocalDevice source, int avrAddress, 608fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang boolean mute) { 618fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return new VolumeControlAction(source, avrAddress, mute ? VOLUME_MUTE : VOLUME_RESTORE, 628fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang false); 638fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 648fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 658fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang /** 668fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Create a {@link VolumeControlAction} for volume up/down change 678fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 688fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param source source device sending volume change 698fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param avrAddress address of audio receiver 708fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param targetVolume target volume to be set to AVR. It should be in range of [0-100] 718fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param isVolumeUp whether to volume up or not. {@code true} for volume up; {@code false} for 728fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * volume down 738fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @return newly created {@link VolumeControlAction} 748fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 758fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang public static VolumeControlAction ofVolumeChange(HdmiCecLocalDevice source, int avrAddress, 768fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang int targetVolume, boolean isVolumeUp) { 778fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang Preconditions.checkArgumentInRange(targetVolume, MIN_VOLUME, MAX_VOLUME, "volume"); 788fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return new VolumeControlAction(source, avrAddress, targetVolume, isVolumeUp); 798fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 808fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 818fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang /** 828fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Scale a custom volume value to cec volume scale. 838fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 848fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param volume volume value in custom scale 858fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param scale scale of volume (max volume) 868fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @return a volume scaled to cec volume range 878fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 888fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang public static int scaleToCecVolume(int volume, int scale) { 898fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return (volume * MAX_VOLUME) / scale; 908fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 918fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 928fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang /** 938fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * Scale a cec volume which is in range of 0 to 100 to custom volume level. 948fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * 958fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param cecVolume volume value in cec volume scale. It should be in a range of [0-100] 968fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @param scale scale of custom volume (max volume) 978fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang * @return a volume value scaled to custom volume range 988fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang */ 998fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang public static int scaleToCustomVolume(int cecVolume, int scale) { 1008fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return (cecVolume * scale) / MAX_VOLUME; 1018fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1028fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1038fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, int targetVolume, 1048fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang boolean isVolumeUp) { 1058fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang super(source); 1068fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1078fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang mAvrAddress = avrAddress; 1088fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang mTargetVolume = targetVolume; 1098fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang mIsVolumeUp = isVolumeUp; 1108fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1118fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1128fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang @Override 1138fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang boolean start() { 1148fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (isForMute()) { 1158fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendMuteChange(mTargetVolume == VOLUME_MUTE); 1168fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang finish(); 1178fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return true; 1188fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1198fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1208fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang startVolumeChange(); 1218fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return true; 1228fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1238fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1248fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1258fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private boolean isForMute() { 1268fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return mTargetVolume == VOLUME_MUTE || mTargetVolume == VOLUME_RESTORE; 1278fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1288fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1298fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private void startVolumeChange() { 1308fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang mTimeoutCount = 0; 1318fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendVolumeChange(mIsVolumeUp); 1328fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang mState = STATE_WAIT_FOR_REPORT_VOLUME_STATUS; 1338fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang addTimer(mState, IRT_MS); 1348fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1358fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1368fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private void sendVolumeChange(boolean up) { 1378fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress, 1388fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang up ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP 1398fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); 1408fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1418fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1428fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private void sendMuteChange(boolean mute) { 1438fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendUserControlPressedAndReleased(mAvrAddress, 144210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang mute ? HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION : 145210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION); 1468fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1478fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1488fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang @Override 1498fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang boolean processCommand(HdmiCecMessage cmd) { 1508fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (mState != STATE_WAIT_FOR_REPORT_VOLUME_STATUS) { 1518fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return false; 1528fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1538fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1548fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang switch (cmd.getOpcode()) { 155c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case Constants.MESSAGE_REPORT_AUDIO_STATUS: 1568fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang handleReportAudioStatus(cmd); 1578fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return true; 158c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case Constants.MESSAGE_FEATURE_ABORT: 1598fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // TODO: handle feature abort. 1608fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang finish(); 1618fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return true; 1628fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang default: 1638fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return false; 1648fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1658fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1668fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1678fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private void handleReportAudioStatus(HdmiCecMessage cmd) { 1688fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang byte[] params = cmd.getParams(); 1698fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang int volume = params[0] & 0x7F; 1708fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // Update volume with new value. 1718fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // Note that it will affect system volume change. 1728fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang tv().setAudioStatus(false, volume); 1738fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (mIsVolumeUp) { 1748fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (mTargetVolume <= volume) { 1758fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang finishWithVolumeChangeRelease(); 1768fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return; 1778fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1788fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } else { 1798fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (mTargetVolume >= volume) { 1808fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang finishWithVolumeChangeRelease(); 1818fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return; 1828fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1838fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1848fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1858fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // Clear action status and send another volume change command. 1868fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang clear(); 1878fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang startVolumeChange(); 1888fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1898fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1908fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang private void finishWithVolumeChangeRelease() { 1918fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( 1928fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang getSourceAddress(), mAvrAddress)); 1938fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang finish(); 1948fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 1958fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 1968fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang @Override 1978fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang void handleTimerEvent(int state) { 1988fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (mState != STATE_WAIT_FOR_REPORT_VOLUME_STATUS) { 1998fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return; 2008fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 2018fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 2028fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // If no report volume action after IRT * VOLUME_CHANGE_TIMEOUT_MAX_COUNT just stop volume 2038fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang // action. 2048fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang if (++mTimeoutCount == VOLUME_CHANGE_TIMEOUT_MAX_COUNT) { 2058fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang finishWithVolumeChangeRelease(); 2068fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang return; 2078fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 2088fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang 2098fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang sendVolumeChange(mIsVolumeUp); 2108fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang addTimer(mState, IRT_MS); 2118fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang } 2128fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang} 213