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