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