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 &lt;Report Audio Status&gt; 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