blob: cd38b1fb2ac64ef82875ae184470e7e9207e032a [file] [log] [blame]
Jungshik Jang8fa36b12014-06-25 15:51:36 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.hdmi;
18
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090019import static com.android.server.hdmi.Constants.MESSAGE_FEATURE_ABORT;
20import static com.android.server.hdmi.Constants.MESSAGE_REPORT_AUDIO_STATUS;
21import static com.android.server.hdmi.Constants.MESSAGE_USER_CONTROL_PRESSED;
Jinsuk Kim8566be32014-10-02 12:48:29 +090022import static com.android.server.hdmi.HdmiConfig.IRT_MS;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090023
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090024import android.media.AudioManager;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090025
26/**
27 * Feature action that transmits volume change to Audio Receiver.
28 * <p>
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090029 * This action is created when a user pressed volume up/down. However, Android only provides a
30 * listener for delta of some volume change instead of individual key event. Also it's hard to know
31 * Audio Receiver's number of volume steps for a single volume control key. Because of this, it
32 * sends key-down event until IRT timeout happens, and it will send key-up event if no additional
33 * volume change happens; otherwise, it will send again key-down as press and hold feature does.
Jungshik Jang8fa36b12014-06-25 15:51:36 +090034 */
Jungshik Jangb509c2e2014-08-07 13:45:01 +090035final class VolumeControlAction extends HdmiCecFeatureAction {
Jungshik Jang8fa36b12014-06-25 15:51:36 +090036 private static final String TAG = "VolumeControlAction";
37
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090038 // State that wait for next volume press.
39 private static final int STATE_WAIT_FOR_NEXT_VOLUME_PRESS = 1;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090040 private static final int MAX_VOLUME = 100;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090041
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090042 private static final int UNKNOWN_AVR_VOLUME = -1;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090043
44 private final int mAvrAddress;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090045 private boolean mIsVolumeUp;
46 private long mLastKeyUpdateTime;
47 private int mLastAvrVolume;
Jungshik Jang5352081c2014-09-22 15:14:49 +090048 private boolean mLastAvrMute;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090049 private boolean mSentKeyPressed;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090050
51 /**
52 * Scale a custom volume value to cec volume scale.
53 *
54 * @param volume volume value in custom scale
55 * @param scale scale of volume (max volume)
56 * @return a volume scaled to cec volume range
57 */
58 public static int scaleToCecVolume(int volume, int scale) {
59 return (volume * MAX_VOLUME) / scale;
60 }
61
62 /**
63 * Scale a cec volume which is in range of 0 to 100 to custom volume level.
64 *
65 * @param cecVolume volume value in cec volume scale. It should be in a range of [0-100]
66 * @param scale scale of custom volume (max volume)
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090067 * @return a volume scaled to custom volume range
Jungshik Jang8fa36b12014-06-25 15:51:36 +090068 */
69 public static int scaleToCustomVolume(int cecVolume, int scale) {
70 return (cecVolume * scale) / MAX_VOLUME;
71 }
72
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090073 VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, boolean isVolumeUp) {
Jungshik Jang8fa36b12014-06-25 15:51:36 +090074 super(source);
Jungshik Jang8fa36b12014-06-25 15:51:36 +090075 mAvrAddress = avrAddress;
Jungshik Jang8fa36b12014-06-25 15:51:36 +090076 mIsVolumeUp = isVolumeUp;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090077 mLastAvrVolume = UNKNOWN_AVR_VOLUME;
Jungshik Jang5352081c2014-09-22 15:14:49 +090078 mLastAvrMute = false;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090079 mSentKeyPressed = false;
80
81 updateLastKeyUpdateTime();
82 }
83
84 private void updateLastKeyUpdateTime() {
85 mLastKeyUpdateTime = System.currentTimeMillis();
Jungshik Jang8fa36b12014-06-25 15:51:36 +090086 }
87
88 @Override
89 boolean start() {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090090 mState = STATE_WAIT_FOR_NEXT_VOLUME_PRESS;
91 sendVolumeKeyPressed();
92 resetTimer();
Jungshik Jang8fa36b12014-06-25 15:51:36 +090093 return true;
94 }
95
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090096 private void sendVolumeKeyPressed() {
Jungshik Jang8fa36b12014-06-25 15:51:36 +090097 sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress,
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090098 mIsVolumeUp ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
Jungshik Jang8fa36b12014-06-25 15:51:36 +090099 : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900100 mSentKeyPressed = true;
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900101 }
102
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900103 private void resetTimer() {
104 mActionTimer.clearTimerMessage();
105 addTimer(STATE_WAIT_FOR_NEXT_VOLUME_PRESS, IRT_MS);
106 }
107
108 void handleVolumeChange(boolean isVolumeUp) {
109 if (mIsVolumeUp != isVolumeUp) {
110 HdmiLogger.debug("Volume Key Status Changed[old:%b new:%b]", mIsVolumeUp, isVolumeUp);
111 sendVolumeKeyReleased();
112 mIsVolumeUp = isVolumeUp;
Jungshik Jang5352081c2014-09-22 15:14:49 +0900113 sendVolumeKeyPressed();
114 resetTimer();
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900115 }
116 updateLastKeyUpdateTime();
117 }
118
119 private void sendVolumeKeyReleased() {
120 sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
121 getSourceAddress(), mAvrAddress));
122 mSentKeyPressed = false;
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900123 }
124
125 @Override
126 boolean processCommand(HdmiCecMessage cmd) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900127 if (mState != STATE_WAIT_FOR_NEXT_VOLUME_PRESS || cmd.getSource() != mAvrAddress) {
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900128 return false;
129 }
130
131 switch (cmd.getOpcode()) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900132 case MESSAGE_REPORT_AUDIO_STATUS:
133 return handleReportAudioStatus(cmd);
134 case MESSAGE_FEATURE_ABORT:
135 return handleFeatureAbort(cmd);
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900136 }
Jungshik Jang5352081c2014-09-22 15:14:49 +0900137 return false;
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900138 }
139
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900140 private boolean handleReportAudioStatus(HdmiCecMessage cmd) {
141 byte params[] = cmd.getParams();
142 boolean mute = (params[0] & 0x80) == 0x80;
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900143 int volume = params[0] & 0x7F;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900144 mLastAvrVolume = volume;
Jungshik Jang5352081c2014-09-22 15:14:49 +0900145 mLastAvrMute = mute;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900146 if (shouldUpdateAudioVolume(mute)) {
147 HdmiLogger.debug("Force volume change[mute:%b, volume=%d]", mute, volume);
148 tv().setAudioStatus(mute, volume);
Jungshik Jang5352081c2014-09-22 15:14:49 +0900149 mLastAvrVolume = UNKNOWN_AVR_VOLUME;
150 mLastAvrMute = false;
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900151 }
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900152 return true;
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900153 }
154
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900155 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) {
Jungshik Jang5352081c2014-09-22 15:14:49 +0900191 tv().setAudioStatus(mLastAvrMute, mLastAvrVolume);
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900192 mLastAvrVolume = UNKNOWN_AVR_VOLUME;
Jungshik Jang5352081c2014-09-22 15:14:49 +0900193 mLastAvrMute = false;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900194 }
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900195 }
196
197 @Override
198 void handleTimerEvent(int state) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900199 if (state != STATE_WAIT_FOR_NEXT_VOLUME_PRESS) {
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900200 return;
201 }
202
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900203 if (System.currentTimeMillis() - mLastKeyUpdateTime >= IRT_MS) {
204 finish();
205 } else {
206 sendVolumeKeyPressed();
207 resetTimer();
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900208 }
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900209 }
210}