blob: 7ba8e1433e1421aead7474f6c74d24585e1cc6e6 [file] [log] [blame]
Nick Chalkob89d6ff2018-05-25 10:29:01 -07001/*
2 * Copyright (C) 2018 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 */
16package com.android.server.hdmi;
17
Amy6506bd62018-07-02 17:29:36 -070018import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
19import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
20import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
21
Nick Chalkob89d6ff2018-05-25 10:29:01 -070022import android.hardware.hdmi.HdmiDeviceInfo;
Amy2a6c3dc2018-06-05 17:31:55 -070023import android.media.AudioManager;
Nick Chalkob89d6ff2018-05-25 10:29:01 -070024import android.os.SystemProperties;
Amy87eda822018-06-06 17:56:39 -070025import com.android.internal.annotations.GuardedBy;
Amy6506bd62018-07-02 17:29:36 -070026import com.android.internal.annotations.VisibleForTesting;
Amy4a922662018-06-05 17:31:55 -070027import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Nick Chalkob89d6ff2018-05-25 10:29:01 -070028
29/**
Nick Chalkof32fcea2018-07-17 16:17:40 -070030 * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
31 * system.
Nick Chalkob89d6ff2018-05-25 10:29:01 -070032 */
33public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
34
Amy4a922662018-06-05 17:31:55 -070035 private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
36
Amy87eda822018-06-06 17:56:39 -070037 // Whether System audio mode is activated or not.
38 // This becomes true only when all system audio sequences are finished.
39 @GuardedBy("mLock")
40 private boolean mSystemAudioActivated;
41
Amy9b91e8c2018-06-11 17:26:26 -070042 // Whether the System Audio Control feature is enabled or not. True by default.
43 @GuardedBy("mLock")
44 private boolean mSystemAudioControlFeatureEnabled;
Nick Chalkof32fcea2018-07-17 16:17:40 -070045
Shubangc480a712018-06-11 18:02:42 -070046 protected Integer mSystemAudioSource;
Amy9b91e8c2018-06-11 17:26:26 -070047
Shubangec668962018-07-13 19:03:19 -070048 private boolean mTvSystemAudioModeSupport;
49
Nick Chalkob89d6ff2018-05-25 10:29:01 -070050 protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
51 super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
Amy9b91e8c2018-06-11 17:26:26 -070052 mSystemAudioControlFeatureEnabled = true;
53 // TODO(amyjojo) make System Audio Control controllable by users
54 /*mSystemAudioControlFeatureEnabled =
Nick Chalkof32fcea2018-07-17 16:17:40 -070055 mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
Nick Chalkob89d6ff2018-05-25 10:29:01 -070056 }
57
58 @Override
Amy4a922662018-06-05 17:31:55 -070059 @ServiceThreadOnly
Amyb887fa02018-06-21 11:22:13 -070060 protected void onStandby(boolean initiatedByCec, int standbyAction) {
61 assertRunOnServiceThread();
Shubangec668962018-07-13 19:03:19 -070062 mTvSystemAudioModeSupport = false;
Amy6506bd62018-07-02 17:29:36 -070063 // Record the last state of System Audio Control before going to standby
64 synchronized (mLock) {
Nick Chalkof32fcea2018-07-17 16:17:40 -070065 SystemProperties.set(
66 Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
67 mSystemAudioActivated ? "true" : "false");
Amy6506bd62018-07-02 17:29:36 -070068 }
Amyb887fa02018-06-21 11:22:13 -070069 if (setSystemAudioMode(false)) {
Amy6506bd62018-07-02 17:29:36 -070070 mService.sendCecCommand(
Nick Chalkof32fcea2018-07-17 16:17:40 -070071 HdmiCecMessageBuilder.buildSetSystemAudioMode(
72 mAddress, Constants.ADDR_BROADCAST, false));
Amyb887fa02018-06-21 11:22:13 -070073 }
74 }
75
76 @Override
77 @ServiceThreadOnly
Nick Chalkob89d6ff2018-05-25 10:29:01 -070078 protected void onAddressAllocated(int logicalAddress, int reason) {
79 assertRunOnServiceThread();
Nick Chalkof32fcea2018-07-17 16:17:40 -070080 mService.sendCecCommand(
81 HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
82 mAddress, mService.getPhysicalAddress(), mDeviceType));
83 mService.sendCecCommand(
84 HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, mService.getVendorId()));
85 int systemAudioControlOnPowerOnProp =
86 SystemProperties.getInt(
87 PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
88 ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON);
89 boolean lastSystemAudioControlStatus =
90 SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
Amy6506bd62018-07-02 17:29:36 -070091 systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
Nick Chalkob89d6ff2018-05-25 10:29:01 -070092 startQueuedActions();
93 }
94
Amy6506bd62018-07-02 17:29:36 -070095 @VisibleForTesting
96 protected void systemAudioControlOnPowerOn(
Nick Chalkof32fcea2018-07-17 16:17:40 -070097 int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
98 if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
99 || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
100 && lastSystemAudioControlStatus)) {
Amy6506bd62018-07-02 17:29:36 -0700101 addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
102 }
103 }
104
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700105 @Override
Amy4a922662018-06-05 17:31:55 -0700106 @ServiceThreadOnly
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700107 protected int getPreferredAddress() {
108 assertRunOnServiceThread();
Nick Chalkof32fcea2018-07-17 16:17:40 -0700109 return SystemProperties.getInt(
110 Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, Constants.ADDR_UNREGISTERED);
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700111 }
112
113 @Override
Amy4a922662018-06-05 17:31:55 -0700114 @ServiceThreadOnly
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700115 protected void setPreferredAddress(int addr) {
116 assertRunOnServiceThread();
Nick Chalkof32fcea2018-07-17 16:17:40 -0700117 SystemProperties.set(
118 Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr));
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700119 }
Amy4a922662018-06-05 17:31:55 -0700120
121 @Override
122 @ServiceThreadOnly
123 protected boolean handleReportAudioStatus(HdmiCecMessage message) {
124 assertRunOnServiceThread();
125 // TODO(amyjojo): implement report audio status handler
126 HdmiLogger.debug(TAG + "Stub handleReportAudioStatus");
127 return true;
128 }
129
130 @Override
131 @ServiceThreadOnly
132 protected boolean handleInitiateArc(HdmiCecMessage message) {
133 assertRunOnServiceThread();
134 // TODO(amyjojo): implement initiate arc handler
135 HdmiLogger.debug(TAG + "Stub handleInitiateArc");
136 return true;
137 }
138
139 @Override
140 @ServiceThreadOnly
141 protected boolean handleReportArcInitiate(HdmiCecMessage message) {
142 assertRunOnServiceThread();
143 // TODO(amyjojo): implement report arc initiate handler
144 HdmiLogger.debug(TAG + "Stub handleReportArcInitiate");
145 return true;
146 }
147
148 @Override
149 @ServiceThreadOnly
150 protected boolean handleReportArcTermination(HdmiCecMessage message) {
151 assertRunOnServiceThread();
152 // TODO(amyjojo): implement report arc terminate handler
153 HdmiLogger.debug(TAG + "Stub handleReportArcTermination");
154 return true;
155 }
Amy2a6c3dc2018-06-05 17:31:55 -0700156
157 @Override
158 @ServiceThreadOnly
159 protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
160 assertRunOnServiceThread();
161
162 reportAudioStatus(message);
163 return true;
164 }
165
Amy87eda822018-06-06 17:56:39 -0700166 @Override
167 @ServiceThreadOnly
168 protected boolean handleGiveSystemAudioModeStatus(HdmiCecMessage message) {
169 assertRunOnServiceThread();
Nick Chalkof32fcea2018-07-17 16:17:40 -0700170 mService.sendCecCommand(
171 HdmiCecMessageBuilder.buildReportSystemAudioMode(
172 mAddress, message.getSource(), mSystemAudioActivated));
Amy87eda822018-06-06 17:56:39 -0700173 return true;
174 }
175
Amy4e7ff1a2018-06-07 16:24:31 -0700176 @Override
177 @ServiceThreadOnly
178 protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
179 assertRunOnServiceThread();
180 // TODO(b/80296911): Check if ARC supported.
181
182 // TODO(b/80296911): Check if port is ready to accept.
183
184 // TODO(b/80296911): if both true, activate ARC functinality and
Nick Chalkof32fcea2018-07-17 16:17:40 -0700185 mService.sendCecCommand(
186 HdmiCecMessageBuilder.buildInitiateArc(mAddress, message.getSource()));
Amy4e7ff1a2018-06-07 16:24:31 -0700187 // TODO(b/80296911): else, send <Feature Abort>["Unrecongnized opcode"]
188
189 return true;
190 }
191
192 @Override
193 @ServiceThreadOnly
194 protected boolean handleRequestArcTermination(HdmiCecMessage message) {
195 assertRunOnServiceThread();
196 // TODO(b/80297105): Check if ARC supported.
197
198 // TODO(b/80297105): Check is currently in arc.
199
200 // TODO(b/80297105): If both true, deactivate ARC functionality and
Nick Chalkof32fcea2018-07-17 16:17:40 -0700201 mService.sendCecCommand(
202 HdmiCecMessageBuilder.buildTerminateArc(mAddress, message.getSource()));
Amy4e7ff1a2018-06-07 16:24:31 -0700203 // TODO(b/80297105): else, send <Feature Abort>["Unrecongnized opcode"]
204
205 return true;
206 }
207
Nick Chalko6f5d69e2018-07-17 16:07:11 -0700208 @ServiceThreadOnly
209 protected boolean handleRequestShortAudioDescriptor(HdmiCecMessage message) {
210 assertRunOnServiceThread();
211 // TODO(b/80297701): implement request short audio descriptor
212 HdmiLogger.debug(TAG + "Stub handleRequestShortAudioDescriptor");
213 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
214 return true;
215 }
216
Amy9b91e8c2018-06-11 17:26:26 -0700217 @Override
218 @ServiceThreadOnly
219 protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
220 assertRunOnServiceThread();
221 boolean systemAudioStatusOn = message.getParams().length != 0;
222 if (!setSystemAudioMode(systemAudioStatusOn)) {
223 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
224 return true;
225 }
226
Shubangde728822018-07-16 16:46:51 -0700227 mSystemAudioSource = systemAudioStatusOn ? message.getSource() : null;
Nick Chalkof32fcea2018-07-17 16:17:40 -0700228 mService.sendCecCommand(
229 HdmiCecMessageBuilder.buildSetSystemAudioMode(
230 mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
Amy9b91e8c2018-06-11 17:26:26 -0700231 return true;
232 }
233
234 @Override
235 @ServiceThreadOnly
236 protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
237 assertRunOnServiceThread();
238 if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
239 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
240 }
241 return true;
242 }
243
244 @Override
245 @ServiceThreadOnly
246 protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
247 assertRunOnServiceThread();
248 if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
249 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
250 }
251 return true;
252 }
253
Amy2a6c3dc2018-06-05 17:31:55 -0700254 private void reportAudioStatus(HdmiCecMessage message) {
255 assertRunOnServiceThread();
256
257 int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
258 boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
259 int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
260 int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
261
Nick Chalkof32fcea2018-07-17 16:17:40 -0700262 mService.sendCecCommand(
263 HdmiCecMessageBuilder.buildReportAudioStatus(
264 mAddress, message.getSource(), scaledVolume, mute));
Amy2a6c3dc2018-06-05 17:31:55 -0700265 }
Amy9b91e8c2018-06-11 17:26:26 -0700266
267 protected boolean setSystemAudioMode(boolean newSystemAudioMode) {
268 if (!isSystemAudioControlFeatureEnabled()) {
Nick Chalkof32fcea2018-07-17 16:17:40 -0700269 HdmiLogger.debug(
270 "Cannot turn "
271 + (newSystemAudioMode ? "on" : "off")
272 + "system audio mode "
273 + "because the System Audio Control feature is disabled.");
Amy9b91e8c2018-06-11 17:26:26 -0700274 return false;
275 }
Nick Chalkof32fcea2018-07-17 16:17:40 -0700276 HdmiLogger.debug(
277 "System Audio Mode change[old:%b new:%b]",
278 mSystemAudioActivated, newSystemAudioMode);
Amy0fd41e32018-06-21 13:40:27 -0700279 // Wake up device if System Audio Control is turned on but device is still on standby
280 if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
281 mService.wakeUp();
282 // TODO(amyjojo): Switch to the corresponding input
283 }
284 // Mute device when feature is turned off and unmute device when feature is turned on
285 boolean currentMuteStatus =
Nick Chalkof32fcea2018-07-17 16:17:40 -0700286 mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
Amy0fd41e32018-06-21 13:40:27 -0700287 if (currentMuteStatus == newSystemAudioMode) {
Nick Chalkof32fcea2018-07-17 16:17:40 -0700288 mService.getAudioManager()
289 .adjustStreamVolume(
290 AudioManager.STREAM_MUSIC,
291 newSystemAudioMode
292 ? AudioManager.ADJUST_UNMUTE
293 : AudioManager.ADJUST_MUTE,
294 0);
Amy0fd41e32018-06-21 13:40:27 -0700295 }
Amy9b91e8c2018-06-11 17:26:26 -0700296 updateAudioManagerForSystemAudio(newSystemAudioMode);
297 synchronized (mLock) {
298 if (mSystemAudioActivated != newSystemAudioMode) {
299 mSystemAudioActivated = newSystemAudioMode;
300 mService.announceSystemAudioModeChange(newSystemAudioMode);
301 }
302 }
303 return true;
304 }
305
306 private void updateAudioManagerForSystemAudio(boolean on) {
307 int device = mService.getAudioManager().setHdmiSystemAudioSupported(on);
308 HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
309 }
310
311 protected boolean isSystemAudioControlFeatureEnabled() {
312 synchronized (mLock) {
313 return mSystemAudioControlFeatureEnabled;
314 }
315 }
Nick Chalkof28c7b52018-06-14 07:46:00 -0700316
Shubang595a1832018-06-27 17:52:18 -0700317 protected boolean isSystemAudioActivated() {
318 synchronized (mLock) {
319 return mSystemAudioActivated;
320 }
321 }
322
Nick Chalkof28c7b52018-06-14 07:46:00 -0700323 /** Reports if System Audio Mode is supported by the connected TV */
324 interface TvSystemAudioModeSupportedCallback {
325
326 /** {@code supported} is true if the TV is connected and supports System Audio Mode. */
327 void onResult(boolean supported);
Nick Chalkof28c7b52018-06-14 07:46:00 -0700328 }
329
330 /**
331 * Queries the connected TV to detect if System Audio Mode is supported by the TV.
332 *
333 * <p>This query may take up to 2 seconds to complete.
334 *
335 * <p>The result of the query may be cached until Audio device type is put in standby or loses
336 * its physical address.
337 */
338 void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
Shubangec668962018-07-13 19:03:19 -0700339 if (!mTvSystemAudioModeSupport) {
340 addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
341 } else {
342 callback.onResult(true);
343 }
344 }
345
346 void setTvSystemAudioModeSupport(boolean supported) {
347 mTvSystemAudioModeSupport = supported;
Nick Chalkof28c7b52018-06-14 07:46:00 -0700348 }
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700349}