blob: 0e47b3f656e63e2ba20ebf509cd72c68c6b1ff87 [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 Chalko167529c2018-07-18 13:11:31 -070022import android.annotation.Nullable;
Amy4aef3b402019-01-18 14:17:55 -080023import android.content.ActivityNotFoundException;
Amy225d55a2018-09-06 11:03:51 -070024import android.content.Intent;
shubangd932cb52018-09-14 17:55:16 -070025import android.hardware.hdmi.HdmiControlManager;
Nick Chalkob89d6ff2018-05-25 10:29:01 -070026import android.hardware.hdmi.HdmiDeviceInfo;
Amy59176da2018-10-12 16:30:54 -070027import android.hardware.hdmi.HdmiPortInfo;
shubangd932cb52018-09-14 17:55:16 -070028import android.hardware.hdmi.IHdmiControlCallback;
Nick Chalko167529c2018-07-18 13:11:31 -070029import android.media.AudioDeviceInfo;
Nick Chalkod70c4132018-08-27 16:22:53 -070030import android.media.AudioFormat;
Amy2a6c3dc2018-06-05 17:31:55 -070031import android.media.AudioManager;
Shubang81170da2018-07-12 18:02:52 -070032import android.media.AudioSystem;
Amy225d55a2018-09-06 11:03:51 -070033import android.media.tv.TvContract;
Amyedd71e32019-02-11 13:39:39 -080034import android.media.tv.TvInputInfo;
35import android.media.tv.TvInputManager.TvInputCallback;
Nick Chalkob89d6ff2018-05-25 10:29:01 -070036import android.os.SystemProperties;
Amy0c2e29f2018-10-23 12:17:52 -070037import android.provider.Settings.Global;
Amy89e93af2018-10-15 09:56:54 -070038import android.util.Slog;
39import android.util.SparseArray;
Nick Chalko167529c2018-07-18 13:11:31 -070040
Amy87eda822018-06-06 17:56:39 -070041import com.android.internal.annotations.GuardedBy;
Amy6506bd62018-07-02 17:29:36 -070042import com.android.internal.annotations.VisibleForTesting;
Nick Chalkob9e48e22018-10-23 06:59:39 -070043import com.android.internal.util.IndentingPrintWriter;
Nick Chalko167529c2018-07-18 13:11:31 -070044import com.android.server.hdmi.Constants.AudioCodec;
Amyc00cd4e2018-10-16 20:21:33 -070045import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
Amy4a922662018-06-05 17:31:55 -070046import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Amyd984bc62018-11-28 18:53:54 -080047import com.android.server.hdmi.HdmiUtils.CodecSad;
48import com.android.server.hdmi.HdmiUtils.DeviceConfig;
Nick Chalkob89d6ff2018-05-25 10:29:01 -070049
Amyd984bc62018-11-28 18:53:54 -080050import org.xmlpull.v1.XmlPullParserException;
51
52import java.io.File;
53import java.io.FileInputStream;
54import java.io.IOException;
55import java.io.InputStream;
Amy89e93af2018-10-15 09:56:54 -070056import java.io.UnsupportedEncodingException;
Nick Chalkod70c4132018-08-27 16:22:53 -070057import java.util.ArrayList;
58import java.util.Arrays;
Amyec126a52018-10-30 16:51:14 -070059import java.util.Collections;
Amy34037422018-09-06 13:21:08 -070060import java.util.HashMap;
Amyc00cd4e2018-10-16 20:21:33 -070061import java.util.List;
Nick Chalkod70c4132018-08-27 16:22:53 -070062import java.util.stream.Collectors;
63
Amy34037422018-09-06 13:21:08 -070064
Nick Chalkob89d6ff2018-05-25 10:29:01 -070065/**
Nick Chalkof32fcea2018-07-17 16:17:40 -070066 * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
67 * system.
Nick Chalkob89d6ff2018-05-25 10:29:01 -070068 */
Amy848a9f22018-08-27 17:21:26 -070069public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
Nick Chalkob89d6ff2018-05-25 10:29:01 -070070
Amy4a922662018-06-05 17:31:55 -070071 private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
72
Amy9b91e8c2018-06-11 17:26:26 -070073 // Whether the System Audio Control feature is enabled or not. True by default.
74 @GuardedBy("mLock")
75 private boolean mSystemAudioControlFeatureEnabled;
Nick Chalkof32fcea2018-07-17 16:17:40 -070076
Amy613330e2019-03-22 16:53:59 -070077 /**
78 * Indicates if the TV that the current device is connected to supports System Audio Mode or not
79 *
80 * <p>If the current device has no information on this, keep mTvSystemAudioModeSupport null
81 *
82 * <p>The boolean will be reset to null every time when the current device goes to standby
83 * or loses its physical address.
84 */
85 private Boolean mTvSystemAudioModeSupport = null;
Shubangec668962018-07-13 19:03:19 -070086
Shubang81170da2018-07-12 18:02:52 -070087 // Whether ARC is available or not. "true" means that ARC is established between TV and
88 // AVR as audio receiver.
Nick Chalko167529c2018-07-18 13:11:31 -070089 @ServiceThreadOnly private boolean mArcEstablished = false;
Shubang81170da2018-07-12 18:02:52 -070090
Amy34037422018-09-06 13:21:08 -070091 // If the current device uses TvInput for ARC. We assume all other inputs also use TvInput
92 // when ARC is using TvInput.
Amy225d55a2018-09-06 11:03:51 -070093 private boolean mArcIntentUsed = SystemProperties
94 .get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, "0").contains("tvinput");
Amy6a6d6182018-08-20 17:38:47 -070095
Amy34037422018-09-06 13:21:08 -070096 // Keeps the mapping (HDMI port ID to TV input URI) to keep track of the TV inputs ready to
Amyedd71e32019-02-11 13:39:39 -080097 // accept input switching request from HDMI devices.
98 @GuardedBy("mLock")
99 private final HashMap<Integer, String> mPortIdToTvInputs = new HashMap<>();
100
101 // A map from TV input id to HDMI device info.
102 @GuardedBy("mLock")
103 private final HashMap<String, HdmiDeviceInfo> mTvInputsToDeviceInfo = new HashMap<>();
Amy34037422018-09-06 13:21:08 -0700104
Amyec126a52018-10-30 16:51:14 -0700105 // Copy of mDeviceInfos to guarantee thread-safety.
106 @GuardedBy("mLock")
107 private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
108
Amy5c6026b2018-10-12 14:42:43 -0700109 // Map-like container of all cec devices.
110 // device id is used as key of container.
111 private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
112
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700113 protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
114 super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
Amy0c2e29f2018-10-23 12:17:52 -0700115 mRoutingControlFeatureEnabled =
Amy77e672c2018-10-31 15:55:40 -0700116 mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
Amy79db52f2018-10-23 12:45:17 -0700117 mSystemAudioControlFeatureEnabled =
118 mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700119 }
120
Amyd984bc62018-11-28 18:53:54 -0800121 private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
122
Amyedd71e32019-02-11 13:39:39 -0800123 private final TvInputCallback mTvInputCallback = new TvInputCallback() {
124 @Override
125 public void onInputAdded(String inputId) {
126 addOrUpdateTvInput(inputId);
127 }
128
129 @Override
130 public void onInputRemoved(String inputId) {
131 removeTvInput(inputId);
132 }
133
134 @Override
135 public void onInputUpdated(String inputId) {
136 addOrUpdateTvInput(inputId);
137 }
138 };
139
140 @ServiceThreadOnly
141 private void addOrUpdateTvInput(String inputId) {
142 assertRunOnServiceThread();
143 synchronized (mLock) {
144 TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId);
145 if (tvInfo == null) {
146 return;
147 }
148 HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo();
149 if (info == null) {
150 return;
151 }
152 mPortIdToTvInputs.put(info.getPortId(), inputId);
153 mTvInputsToDeviceInfo.put(inputId, info);
154 }
155 }
156
157 @ServiceThreadOnly
158 private void removeTvInput(String inputId) {
159 assertRunOnServiceThread();
160 synchronized (mLock) {
161 if (mTvInputsToDeviceInfo.get(inputId) == null) {
162 return;
163 }
164 int portId = mTvInputsToDeviceInfo.get(inputId).getPortId();
165 mPortIdToTvInputs.remove(portId);
166 mTvInputsToDeviceInfo.remove(inputId);
167 }
168 }
169
Amy5c6026b2018-10-12 14:42:43 -0700170 /**
171 * Called when a device is newly added or a new device is detected or
Amy1e4a8cc2018-10-15 10:18:14 -0700172 * an existing device is updated.
Amy5c6026b2018-10-12 14:42:43 -0700173 *
174 * @param info device info of a new device.
175 */
176 @ServiceThreadOnly
177 final void addCecDevice(HdmiDeviceInfo info) {
178 assertRunOnServiceThread();
179 HdmiDeviceInfo old = addDeviceInfo(info);
Amy89e93af2018-10-15 09:56:54 -0700180 if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
Amy5c6026b2018-10-12 14:42:43 -0700181 // The addition of the device itself should not be notified.
Amy89e93af2018-10-15 09:56:54 -0700182 // Note that different logical address could still be the same local device.
Amy5c6026b2018-10-12 14:42:43 -0700183 return;
184 }
185 if (old == null) {
186 invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
187 } else if (!old.equals(info)) {
188 invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
189 invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
190 }
191 }
192
193 /**
194 * Called when a device is removed or removal of device is detected.
195 *
196 * @param address a logical address of a device to be removed
197 */
198 @ServiceThreadOnly
199 final void removeCecDevice(int address) {
200 assertRunOnServiceThread();
201 HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
202
203 mCecMessageCache.flushMessagesFrom(address);
204 invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
205 }
206
207 /**
Amy89e93af2018-10-15 09:56:54 -0700208 * Called when a device is updated.
209 *
210 * @param info device info of the updating device.
211 */
212 @ServiceThreadOnly
213 final void updateCecDevice(HdmiDeviceInfo info) {
214 assertRunOnServiceThread();
215 HdmiDeviceInfo old = addDeviceInfo(info);
216
217 if (old == null) {
218 invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
219 } else if (!old.equals(info)) {
220 invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
221 }
222 }
223
224 /**
225 * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
Amy5c6026b2018-10-12 14:42:43 -0700226 * logical address as new device info's.
227 *
228 * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
229 * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
230 * that has the same logical address as new one has.
231 */
232 @ServiceThreadOnly
Amy89e93af2018-10-15 09:56:54 -0700233 @VisibleForTesting
234 protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
Amy5c6026b2018-10-12 14:42:43 -0700235 assertRunOnServiceThread();
236 HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
237 if (oldDeviceInfo != null) {
238 removeDeviceInfo(deviceInfo.getId());
239 }
240 mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
Amyec126a52018-10-30 16:51:14 -0700241 updateSafeDeviceInfoList();
Amy5c6026b2018-10-12 14:42:43 -0700242 return oldDeviceInfo;
243 }
244
245 /**
246 * Remove a device info corresponding to the given {@code logicalAddress}.
247 * It returns removed {@link HdmiDeviceInfo} if exists.
248 *
249 * @param id id of device to be removed
250 * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
251 */
252 @ServiceThreadOnly
253 private HdmiDeviceInfo removeDeviceInfo(int id) {
254 assertRunOnServiceThread();
255 HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
256 if (deviceInfo != null) {
257 mDeviceInfos.remove(id);
258 }
Amyec126a52018-10-30 16:51:14 -0700259 updateSafeDeviceInfoList();
Amy5c6026b2018-10-12 14:42:43 -0700260 return deviceInfo;
261 }
262
263 /**
264 * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
265 *
266 * @param logicalAddress logical address of the device to be retrieved
267 * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
268 * Returns null if no logical address matched
269 */
270 @ServiceThreadOnly
271 HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
272 assertRunOnServiceThread();
273 return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
274 }
275
Amyec126a52018-10-30 16:51:14 -0700276 @ServiceThreadOnly
277 private void updateSafeDeviceInfoList() {
278 assertRunOnServiceThread();
279 List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
280 synchronized (mLock) {
281 mSafeAllDeviceInfos = copiedDevices;
282 }
283 }
284
285 @GuardedBy("mLock")
286 List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
287 ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
288 for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
289 infoList.add(info);
290 }
291 return infoList;
292 }
293
Amy5c6026b2018-10-12 14:42:43 -0700294 private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
295 mService.invokeDeviceEventListeners(info, status);
296 }
297
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700298 @Override
Amy4a922662018-06-05 17:31:55 -0700299 @ServiceThreadOnly
Amy59176da2018-10-12 16:30:54 -0700300 void onHotplug(int portId, boolean connected) {
301 assertRunOnServiceThread();
302 if (connected) {
303 mService.wakeUp();
304 }
305 if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
306 mCecMessageCache.flushAll();
Amyedd71e32019-02-11 13:39:39 -0800307 } else if (!connected && mPortIdToTvInputs.get(portId) != null) {
308 String tvInputId = mPortIdToTvInputs.get(portId);
309 HdmiDeviceInfo info = mTvInputsToDeviceInfo.get(tvInputId);
310 if (info == null) {
311 return;
312 }
313 // Update with TIF on the device removal. TIF callback will update
314 // mPortIdToTvInputs and mPortIdToTvInputs.
315 removeCecDevice(info.getLogicalAddress());
Amy59176da2018-10-12 16:30:54 -0700316 }
317 }
318
319 @Override
320 @ServiceThreadOnly
Amyedd71e32019-02-11 13:39:39 -0800321 protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
322 super.disableDevice(initiatedByCec, callback);
323 assertRunOnServiceThread();
324 mService.unregisterTvInputCallback(mTvInputCallback);
Amyd5a15142019-03-22 11:22:05 -0700325 // TODO(b/129088603): check disableDevice and onStandby behaviors per spec
Amyedd71e32019-02-11 13:39:39 -0800326 }
327
328 @Override
329 @ServiceThreadOnly
Amyb887fa02018-06-21 11:22:13 -0700330 protected void onStandby(boolean initiatedByCec, int standbyAction) {
331 assertRunOnServiceThread();
Amy8ecb0012019-05-09 19:06:42 -0700332 // Invalidate the internal active source record when goes to standby
333 // This set will also update mIsActiveSource
334 mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS);
Amy613330e2019-03-22 16:53:59 -0700335 mTvSystemAudioModeSupport = null;
Amy6506bd62018-07-02 17:29:36 -0700336 // Record the last state of System Audio Control before going to standby
337 synchronized (mLock) {
Amy59c06c12019-01-18 15:35:15 -0800338 mService.writeStringSystemProperty(
Nick Chalkof32fcea2018-07-17 16:17:40 -0700339 Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
Amy489454f2019-01-24 19:06:57 -0800340 isSystemAudioActivated() ? "true" : "false");
Amy6506bd62018-07-02 17:29:36 -0700341 }
Shubangbc2aab32018-07-19 16:41:17 -0700342 terminateSystemAudioMode();
Amyb887fa02018-06-21 11:22:13 -0700343 }
344
345 @Override
346 @ServiceThreadOnly
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700347 protected void onAddressAllocated(int logicalAddress, int reason) {
348 assertRunOnServiceThread();
Amyb9d7f432018-11-30 15:08:30 -0800349 if (reason == mService.INITIATED_BY_ENABLE_CEC) {
350 mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(),
351 getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST);
352 }
Nick Chalkof32fcea2018-07-17 16:17:40 -0700353 mService.sendCecCommand(
354 HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
355 mAddress, mService.getPhysicalAddress(), mDeviceType));
356 mService.sendCecCommand(
357 HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, mService.getVendorId()));
Amyedd71e32019-02-11 13:39:39 -0800358 mService.registerTvInputCallback(mTvInputCallback);
Amy86347ba2019-04-04 16:22:54 -0700359 // Some TVs, for example Mi TV, need ARC on before turning System Audio Mode on
360 // to request Short Audio Descriptor. Since ARC and SAM are independent,
361 // we can turn on ARC anyways when audio system device just boots up.
362 initArcOnFromAvr();
Nick Chalkof32fcea2018-07-17 16:17:40 -0700363 int systemAudioControlOnPowerOnProp =
364 SystemProperties.getInt(
365 PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
366 ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON);
367 boolean lastSystemAudioControlStatus =
368 SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
Amy6506bd62018-07-02 17:29:36 -0700369 systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
Amyc00cd4e2018-10-16 20:21:33 -0700370 clearDeviceInfoList();
371 launchDeviceDiscovery();
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700372 startQueuedActions();
373 }
374
Shubang Lu00b976a2018-08-01 18:11:46 -0700375 @Override
376 protected int findKeyReceiverAddress() {
Amy2a5e2452018-11-29 20:32:20 -0800377 if (getActiveSource().isValid()) {
378 return getActiveSource().logicalAddress;
379 }
380 return Constants.ADDR_INVALID;
Shubang Lu00b976a2018-08-01 18:11:46 -0700381 }
382
Amy6506bd62018-07-02 17:29:36 -0700383 @VisibleForTesting
384 protected void systemAudioControlOnPowerOn(
Nick Chalkof32fcea2018-07-17 16:17:40 -0700385 int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
386 if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
387 || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
Amy79db52f2018-10-23 12:45:17 -0700388 && lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) {
Amy6506bd62018-07-02 17:29:36 -0700389 addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
390 }
391 }
392
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700393 @Override
Amy4a922662018-06-05 17:31:55 -0700394 @ServiceThreadOnly
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700395 protected int getPreferredAddress() {
396 assertRunOnServiceThread();
Nick Chalkof32fcea2018-07-17 16:17:40 -0700397 return SystemProperties.getInt(
Amy06dc4cd2018-08-09 18:22:40 -0700398 Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, Constants.ADDR_UNREGISTERED);
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700399 }
400
401 @Override
Amy4a922662018-06-05 17:31:55 -0700402 @ServiceThreadOnly
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700403 protected void setPreferredAddress(int addr) {
404 assertRunOnServiceThread();
Amy59c06c12019-01-18 15:35:15 -0800405 mService.writeStringSystemProperty(
Nick Chalkof32fcea2018-07-17 16:17:40 -0700406 Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr));
Nick Chalkob89d6ff2018-05-25 10:29:01 -0700407 }
Amy4a922662018-06-05 17:31:55 -0700408
409 @Override
410 @ServiceThreadOnly
Amy89e93af2018-10-15 09:56:54 -0700411 protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
412 assertRunOnServiceThread();
413 int path = HdmiUtils.twoBytesToInt(message.getParams());
414 int address = message.getSource();
415 int type = message.getParams()[2];
416
417 // Ignore if [Device Discovery Action] is going on.
418 if (hasAction(DeviceDiscoveryAction.class)) {
419 Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
420 return true;
421 }
422
423 // Update the device info with TIF, note that the same device info could have added in
424 // device discovery and we do not want to override it with default OSD name. Therefore we
425 // need the following check to skip redundant device info updating.
426 HdmiDeviceInfo oldDevice = getCecDeviceInfo(address);
427 if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
428 addCecDevice(new HdmiDeviceInfo(
429 address, path, mService.pathToPortId(path), type,
430 Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address)));
431 // if we are adding a new device info, send out a give osd name command
432 // to update the name of the device in TIF
433 mService.sendCecCommand(
434 HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
435 return true;
436 }
437
438 Slog.w(TAG, "Device info exists. Not updating on Physical Address.");
439 return true;
440 }
441
442 @Override
Amy1e4a8cc2018-10-15 10:18:14 -0700443 protected boolean handleReportPowerStatus(HdmiCecMessage command) {
444 int newStatus = command.getParams()[0] & 0xFF;
445 updateDevicePowerStatus(command.getSource(), newStatus);
446 return true;
447 }
448
449 @Override
Amy89e93af2018-10-15 09:56:54 -0700450 @ServiceThreadOnly
451 protected boolean handleSetOsdName(HdmiCecMessage message) {
452 int source = message.getSource();
453 String osdName;
454 HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
455 // If the device is not in device list, ignore it.
456 if (deviceInfo == null) {
457 Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
458 return true;
459 }
460 try {
461 osdName = new String(message.getParams(), "US-ASCII");
462 } catch (UnsupportedEncodingException e) {
463 Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
464 return true;
465 }
466
467 if (deviceInfo.getDisplayName().equals(osdName)) {
468 Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
469 return true;
470 }
471
472 Slog.d(TAG, "Updating device OSD name from "
473 + deviceInfo.getDisplayName()
474 + " to " + osdName);
475 updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
476 deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
477 deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
478 return true;
479 }
480
481 @Override
482 @ServiceThreadOnly
Amy4a922662018-06-05 17:31:55 -0700483 protected boolean handleInitiateArc(HdmiCecMessage message) {
484 assertRunOnServiceThread();
485 // TODO(amyjojo): implement initiate arc handler
486 HdmiLogger.debug(TAG + "Stub handleInitiateArc");
487 return true;
488 }
489
490 @Override
491 @ServiceThreadOnly
492 protected boolean handleReportArcInitiate(HdmiCecMessage message) {
493 assertRunOnServiceThread();
494 // TODO(amyjojo): implement report arc initiate handler
495 HdmiLogger.debug(TAG + "Stub handleReportArcInitiate");
496 return true;
497 }
498
499 @Override
500 @ServiceThreadOnly
501 protected boolean handleReportArcTermination(HdmiCecMessage message) {
502 assertRunOnServiceThread();
503 // TODO(amyjojo): implement report arc terminate handler
504 HdmiLogger.debug(TAG + "Stub handleReportArcTermination");
505 return true;
506 }
Amy2a6c3dc2018-06-05 17:31:55 -0700507
508 @Override
509 @ServiceThreadOnly
510 protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
511 assertRunOnServiceThread();
Amy79db52f2018-10-23 12:45:17 -0700512 if (isSystemAudioControlFeatureEnabled()) {
513 reportAudioStatus(message.getSource());
514 } else {
515 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
516 }
Amy2a6c3dc2018-06-05 17:31:55 -0700517 return true;
518 }
519
Amy87eda822018-06-06 17:56:39 -0700520 @Override
521 @ServiceThreadOnly
522 protected boolean handleGiveSystemAudioModeStatus(HdmiCecMessage message) {
523 assertRunOnServiceThread();
Amy27ae4032019-01-31 16:42:16 -0800524 // If the audio system is initiating the system audio mode on and TV asks the sam status at
525 // the same time, respond with true. Since we know TV supports sam in this situation.
526 // If the query comes from STB, we should respond with the current sam status and the STB
527 // should listen to the <Set System Audio Mode> broadcasting.
528 boolean isSystemAudioModeOnOrTurningOn = isSystemAudioActivated();
529 if (!isSystemAudioModeOnOrTurningOn
530 && message.getSource() == Constants.ADDR_TV
531 && hasAction(SystemAudioInitiationActionFromAvr.class)) {
532 isSystemAudioModeOnOrTurningOn = true;
533 }
Nick Chalkof32fcea2018-07-17 16:17:40 -0700534 mService.sendCecCommand(
535 HdmiCecMessageBuilder.buildReportSystemAudioMode(
Amy27ae4032019-01-31 16:42:16 -0800536 mAddress, message.getSource(), isSystemAudioModeOnOrTurningOn));
Amy87eda822018-06-06 17:56:39 -0700537 return true;
538 }
539
Amy4e7ff1a2018-06-07 16:24:31 -0700540 @Override
541 @ServiceThreadOnly
542 protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
543 assertRunOnServiceThread();
Amy03afe482018-09-18 16:57:45 -0700544 removeAction(ArcInitiationActionFromAvr.class);
Amy59c06c12019-01-18 15:35:15 -0800545 if (!mService.readBooleanSystemProperty(Constants.PROPERTY_ARC_SUPPORT, true)) {
Amy77c7c982018-07-23 18:27:36 -0700546 mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
547 } else if (!isDirectConnectToTv()) {
548 HdmiLogger.debug("AVR device is not directly connected with TV");
549 mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
550 } else {
551 addAndStartAction(new ArcInitiationActionFromAvr(this));
552 }
Amy4e7ff1a2018-06-07 16:24:31 -0700553 return true;
554 }
555
556 @Override
557 @ServiceThreadOnly
558 protected boolean handleRequestArcTermination(HdmiCecMessage message) {
559 assertRunOnServiceThread();
Amy77c7c982018-07-23 18:27:36 -0700560 if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
561 mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
562 } else if (!isArcEnabled()) {
563 HdmiLogger.debug("ARC is not established between TV and AVR device");
564 mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
565 } else {
Amy03afe482018-09-18 16:57:45 -0700566 removeAction(ArcTerminationActionFromAvr.class);
Amy77c7c982018-07-23 18:27:36 -0700567 addAndStartAction(new ArcTerminationActionFromAvr(this));
568 }
Amy4e7ff1a2018-06-07 16:24:31 -0700569 return true;
570 }
571
Nick Chalko6f5d69e2018-07-17 16:07:11 -0700572 @ServiceThreadOnly
573 protected boolean handleRequestShortAudioDescriptor(HdmiCecMessage message) {
574 assertRunOnServiceThread();
Nick Chalko6f5d69e2018-07-17 16:07:11 -0700575 HdmiLogger.debug(TAG + "Stub handleRequestShortAudioDescriptor");
Nick Chalko167529c2018-07-18 13:11:31 -0700576 if (!isSystemAudioControlFeatureEnabled()) {
577 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
578 return true;
579 }
580 if (!isSystemAudioActivated()) {
581 mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
582 return true;
583 }
Amyd984bc62018-11-28 18:53:54 -0800584
585 List<DeviceConfig> config = null;
586 File file = new File(SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH);
587 if (file.exists()) {
588 try {
589 InputStream in = new FileInputStream(file);
590 config = HdmiUtils.ShortAudioDescriptorXmlParser.parse(in);
591 in.close();
592 } catch (IOException e) {
593 Slog.e(TAG, "Error reading file: " + file, e);
594 } catch (XmlPullParserException e) {
595 Slog.e(TAG, "Unable to parse file: " + file, e);
596 }
Nick Chalko167529c2018-07-18 13:11:31 -0700597 }
Amyd984bc62018-11-28 18:53:54 -0800598
Nick Chalko167529c2018-07-18 13:11:31 -0700599 @AudioCodec int[] audioFormatCodes = parseAudioFormatCodes(message.getParams());
Amyd984bc62018-11-28 18:53:54 -0800600 byte[] sadBytes;
601 if (config != null && config.size() > 0) {
602 sadBytes = getSupportedShortAudioDescriptorsFromConfig(config, audioFormatCodes);
603 } else {
604 AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo();
605 if (deviceInfo == null) {
606 mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
607 return true;
608 }
609
610 sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes);
611 }
612
Nick Chalko167529c2018-07-18 13:11:31 -0700613 if (sadBytes.length == 0) {
614 mService.maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
615 } else {
616 mService.sendCecCommand(
617 HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
618 mAddress, message.getSource(), sadBytes));
619 }
Nick Chalko6f5d69e2018-07-17 16:07:11 -0700620 return true;
621 }
622
Nick Chalko167529c2018-07-18 13:11:31 -0700623 private byte[] getSupportedShortAudioDescriptors(
624 AudioDeviceInfo deviceInfo, @AudioCodec int[] audioFormatCodes) {
Nick Chalkod70c4132018-08-27 16:22:53 -0700625 ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
626 for (@AudioCodec int audioFormatCode : audioFormatCodes) {
627 byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioFormatCode);
628 if (sad != null) {
629 if (sad.length == 3) {
630
631 sads.add(sad);
632 } else {
633 HdmiLogger.warning(
634 "Dropping Short Audio Descriptor with length %d for requested codec %x",
635 sad.length, audioFormatCode);
636 }
637 }
638 }
Amyd984bc62018-11-28 18:53:54 -0800639 return getShortAudioDescriptorBytes(sads);
640 }
641
642 private byte[] getSupportedShortAudioDescriptorsFromConfig(
643 List<DeviceConfig> deviceConfig, @AudioCodec int[] audioFormatCodes) {
644 DeviceConfig deviceConfigToUse = null;
645 for (DeviceConfig device : deviceConfig) {
646 // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
647 if (device.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) {
648 deviceConfigToUse = device;
649 break;
650 }
651 }
652 if (deviceConfigToUse == null) {
653 // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
654 Slog.w(TAG, "sadConfig.xml does not have required device info for "
655 + "VX_AUDIO_DEVICE_IN_HDMI_ARC");
656 return new byte[0];
657 }
658 HashMap<Integer, byte[]> map = new HashMap<>();
659 ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
660 for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) {
661 map.put(codecSad.audioCodec, codecSad.sad);
662 }
663 for (int i = 0; i < audioFormatCodes.length; i++) {
664 if (map.containsKey(audioFormatCodes[i])) {
665 byte[] sad = map.get(audioFormatCodes[i]);
666 if (sad != null && sad.length == 3) {
667 sads.add(sad);
668 }
669 }
670 }
671 return getShortAudioDescriptorBytes(sads);
672 }
673
674 private byte[] getShortAudioDescriptorBytes(ArrayList<byte[]> sads) {
Nick Chalkod70c4132018-08-27 16:22:53 -0700675 // Short Audio Descriptors are always 3 bytes long.
676 byte[] bytes = new byte[sads.size() * 3];
677 int index = 0;
678 for (byte[] sad : sads) {
679 System.arraycopy(sad, 0, bytes, index, 3);
680 index += 3;
681 }
682 return bytes;
683 }
684
685 /**
686 * Returns a 3 byte short audio descriptor as described in CEC 1.4 table 29 or null if the
687 * audioFormatCode is not supported.
688 */
689 @Nullable
690 private byte[] getSupportedShortAudioDescriptor(
691 AudioDeviceInfo deviceInfo, @AudioCodec int audioFormatCode) {
692 switch (audioFormatCode) {
693 case Constants.AUDIO_CODEC_NONE: {
694 return null;
695 }
696 case Constants.AUDIO_CODEC_LPCM: {
697 return getLpcmShortAudioDescriptor(deviceInfo);
698 }
699 // TODO(b/80297701): implement the rest of the codecs
700 case Constants.AUDIO_CODEC_DD:
701 case Constants.AUDIO_CODEC_MPEG1:
702 case Constants.AUDIO_CODEC_MP3:
703 case Constants.AUDIO_CODEC_MPEG2:
704 case Constants.AUDIO_CODEC_AAC:
705 case Constants.AUDIO_CODEC_DTS:
706 case Constants.AUDIO_CODEC_ATRAC:
707 case Constants.AUDIO_CODEC_ONEBITAUDIO:
708 case Constants.AUDIO_CODEC_DDP:
709 case Constants.AUDIO_CODEC_DTSHD:
710 case Constants.AUDIO_CODEC_TRUEHD:
711 case Constants.AUDIO_CODEC_DST:
712 case Constants.AUDIO_CODEC_WMAPRO:
713 default: {
714 return null;
715 }
716 }
717 }
718
719 @Nullable
720 private byte[] getLpcmShortAudioDescriptor(AudioDeviceInfo deviceInfo) {
721 // TODO(b/80297701): implement
722 return null;
Nick Chalko167529c2018-07-18 13:11:31 -0700723 }
724
725 @Nullable
726 private AudioDeviceInfo getSystemAudioDeviceInfo() {
Nick Chalkod70c4132018-08-27 16:22:53 -0700727 AudioManager audioManager = mService.getContext().getSystemService(AudioManager.class);
728 if (audioManager == null) {
729 HdmiLogger.error(
730 "Error getting system audio device because AudioManager not available.");
731 return null;
732 }
733 AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
734 HdmiLogger.debug("Found %d audio input devices", devices.length);
735 for (AudioDeviceInfo device : devices) {
736 HdmiLogger.debug("%s at port %s", device.getProductName(), device.getPort());
737 HdmiLogger.debug("Supported encodings are %s",
738 Arrays.stream(device.getEncodings()).mapToObj(
739 AudioFormat::toLogFriendlyEncoding
740 ).collect(Collectors.joining(", ")));
741 // TODO(b/80297701) use the actual device type that system audio mode is connected to.
742 if (device.getType() == AudioDeviceInfo.TYPE_HDMI_ARC) {
743 return device;
744 }
745 }
Nick Chalko167529c2018-07-18 13:11:31 -0700746 return null;
747 }
748
749 @AudioCodec
750 private int[] parseAudioFormatCodes(byte[] params) {
751 @AudioCodec int[] audioFormatCodes = new int[params.length];
752 for (int i = 0; i < params.length; i++) {
753 byte val = params[i];
754 audioFormatCodes[i] =
Amy06dc4cd2018-08-09 18:22:40 -0700755 val >= 1 && val <= Constants.AUDIO_CODEC_MAX ? val : Constants.AUDIO_CODEC_NONE;
Nick Chalko167529c2018-07-18 13:11:31 -0700756 }
757 return audioFormatCodes;
758 }
759
Amy9b91e8c2018-06-11 17:26:26 -0700760 @Override
761 @ServiceThreadOnly
762 protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
763 assertRunOnServiceThread();
764 boolean systemAudioStatusOn = message.getParams().length != 0;
Amy06dc4cd2018-08-09 18:22:40 -0700765 // Check if the request comes from a non-TV device.
766 // Need to check if TV supports System Audio Control
767 // if non-TV device tries to turn on the feature
768 if (message.getSource() != Constants.ADDR_TV) {
769 if (systemAudioStatusOn) {
770 handleSystemAudioModeOnFromNonTvDevice(message);
771 return true;
772 }
773 } else {
774 // If TV request the feature on
775 // cache TV supporting System Audio Control
776 // until Audio System loses its physical address.
777 setTvSystemAudioModeSupport(true);
778 }
779 // If TV or Audio System does not support the feature,
780 // will send abort command.
781 if (!checkSupportAndSetSystemAudioMode(systemAudioStatusOn)) {
Amy9b91e8c2018-06-11 17:26:26 -0700782 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
783 return true;
784 }
785
Nick Chalkof32fcea2018-07-17 16:17:40 -0700786 mService.sendCecCommand(
787 HdmiCecMessageBuilder.buildSetSystemAudioMode(
788 mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
Amy98c311f2019-04-09 13:14:47 -0700789
790 if (systemAudioStatusOn) {
791 int sourcePhysicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
792 if (sourcePhysicalAddress != getActiveSource().physicalAddress) {
793 // If the Active Source recorded by the current device is not synced up with TV,
794 // TODO(amyjojo): update Active Source internally
795 }
796 switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
797 }
Amy9b91e8c2018-06-11 17:26:26 -0700798 return true;
799 }
800
801 @Override
802 @ServiceThreadOnly
803 protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
804 assertRunOnServiceThread();
Amy06dc4cd2018-08-09 18:22:40 -0700805 if (!checkSupportAndSetSystemAudioMode(
806 HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
Amy9b91e8c2018-06-11 17:26:26 -0700807 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
808 }
809 return true;
810 }
811
812 @Override
813 @ServiceThreadOnly
814 protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
815 assertRunOnServiceThread();
Amy06dc4cd2018-08-09 18:22:40 -0700816 if (!checkSupportAndSetSystemAudioMode(
817 HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
Amy9b91e8c2018-06-11 17:26:26 -0700818 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
819 }
820 return true;
821 }
822
Shubang81170da2018-07-12 18:02:52 -0700823 @ServiceThreadOnly
824 void setArcStatus(boolean enabled) {
825 // TODO(shubang): add tests
826 assertRunOnServiceThread();
827
828 HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled);
829 // 1. Enable/disable ARC circuit.
830 enableAudioReturnChannel(enabled);
831 // 2. Notify arc status to audio service.
832 notifyArcStatusToAudioService(enabled);
833 // 3. Update arc status;
834 mArcEstablished = enabled;
835 }
836
Nick Chalko167529c2018-07-18 13:11:31 -0700837 /** Switch hardware ARC circuit in the system. */
Shubang81170da2018-07-12 18:02:52 -0700838 @ServiceThreadOnly
839 private void enableAudioReturnChannel(boolean enabled) {
840 assertRunOnServiceThread();
841 mService.enableAudioReturnChannel(
Nick Chalko167529c2018-07-18 13:11:31 -0700842 SystemProperties.getInt(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, 0),
Shubang81170da2018-07-12 18:02:52 -0700843 enabled);
844 }
845
846 private void notifyArcStatusToAudioService(boolean enabled) {
847 // Note that we don't set any name to ARC.
Nick Chalko167529c2018-07-18 13:11:31 -0700848 mService.getAudioManager()
Amy06dc4cd2018-08-09 18:22:40 -0700849 .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", "");
Shubang81170da2018-07-12 18:02:52 -0700850 }
851
Nick Chalko01b979c2018-10-19 14:54:30 -0700852 void reportAudioStatus(int source) {
Amy2a6c3dc2018-06-05 17:31:55 -0700853 assertRunOnServiceThread();
854
855 int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
856 boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
857 int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
Nick Chalko01b979c2018-10-19 14:54:30 -0700858 int minVolume = mService.getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC);
Amy2a6c3dc2018-06-05 17:31:55 -0700859 int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
Dmitry Shmidt8bac7b82018-10-22 13:24:38 -0700860 HdmiLogger.debug("Reporting volume %d (%d-%d) as CEC volume %d", volume,
Nick Chalko01b979c2018-10-19 14:54:30 -0700861 minVolume, maxVolume, scaledVolume);
Amy2a6c3dc2018-06-05 17:31:55 -0700862
Nick Chalkof32fcea2018-07-17 16:17:40 -0700863 mService.sendCecCommand(
864 HdmiCecMessageBuilder.buildReportAudioStatus(
Nick Chalko01b979c2018-10-19 14:54:30 -0700865 mAddress, source, scaledVolume, mute));
Amy2a6c3dc2018-06-05 17:31:55 -0700866 }
Amy9b91e8c2018-06-11 17:26:26 -0700867
Amy06dc4cd2018-08-09 18:22:40 -0700868 /**
869 * Method to check if device support System Audio Control. If so, wake up device if necessary.
870 *
871 * <p> then call {@link #setSystemAudioMode(boolean)} to turn on or off System Audio Mode
872 * @param newSystemAudioMode turning feature on or off. True is on. False is off.
873 * @return true or false.
874 *
875 * <p>False when device does not support the feature. Otherwise returns true.
876 */
877 protected boolean checkSupportAndSetSystemAudioMode(boolean newSystemAudioMode) {
Amy9b91e8c2018-06-11 17:26:26 -0700878 if (!isSystemAudioControlFeatureEnabled()) {
Nick Chalkof32fcea2018-07-17 16:17:40 -0700879 HdmiLogger.debug(
880 "Cannot turn "
881 + (newSystemAudioMode ? "on" : "off")
882 + "system audio mode "
883 + "because the System Audio Control feature is disabled.");
Amy9b91e8c2018-06-11 17:26:26 -0700884 return false;
885 }
Nick Chalkof32fcea2018-07-17 16:17:40 -0700886 HdmiLogger.debug(
887 "System Audio Mode change[old:%b new:%b]",
Amy489454f2019-01-24 19:06:57 -0800888 isSystemAudioActivated(), newSystemAudioMode);
Amyae4ee342018-10-09 16:55:14 -0700889 // Wake up device if System Audio Control is turned on
890 if (newSystemAudioMode) {
Amy0fd41e32018-06-21 13:40:27 -0700891 mService.wakeUp();
Amyac1a55c2018-07-13 16:38:23 -0700892 }
Amy06dc4cd2018-08-09 18:22:40 -0700893 setSystemAudioMode(newSystemAudioMode);
894 return true;
895 }
896
897 /**
898 * Real work to turn on or off System Audio Mode.
899 *
900 * Use {@link #checkSupportAndSetSystemAudioMode(boolean)}
901 * if trying to turn on or off the feature.
902 */
903 private void setSystemAudioMode(boolean newSystemAudioMode) {
Amyac1a55c2018-07-13 16:38:23 -0700904 int targetPhysicalAddress = getActiveSource().physicalAddress;
Amy00638112018-10-31 17:47:17 -0700905 int port = mService.pathToPortId(targetPhysicalAddress);
Amy6a6d6182018-08-20 17:38:47 -0700906 if (newSystemAudioMode && port >= 0) {
Amyac1a55c2018-07-13 16:38:23 -0700907 switchToAudioInput();
Amy0fd41e32018-06-21 13:40:27 -0700908 }
Amycce55e02018-08-20 15:42:25 -0700909 // Mute device when feature is turned off and unmute device when feature is turned on.
910 // PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE is false when device never needs to be muted.
911 boolean currentMuteStatus =
912 mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
Amy61fc25f2018-11-02 17:53:21 -0700913 if (currentMuteStatus == newSystemAudioMode) {
Amy59c06c12019-01-18 15:35:15 -0800914 if (mService.readBooleanSystemProperty(
Amy61fc25f2018-11-02 17:53:21 -0700915 Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
916 || newSystemAudioMode) {
917 mService.getAudioManager()
918 .adjustStreamVolume(
919 AudioManager.STREAM_MUSIC,
920 newSystemAudioMode
921 ? AudioManager.ADJUST_UNMUTE
922 : AudioManager.ADJUST_MUTE,
923 0);
924 }
Amycce55e02018-08-20 15:42:25 -0700925 }
Amy9b91e8c2018-06-11 17:26:26 -0700926 updateAudioManagerForSystemAudio(newSystemAudioMode);
927 synchronized (mLock) {
Amy489454f2019-01-24 19:06:57 -0800928 if (isSystemAudioActivated() != newSystemAudioMode) {
929 mService.setSystemAudioActivated(newSystemAudioMode);
Amy9b91e8c2018-06-11 17:26:26 -0700930 mService.announceSystemAudioModeChange(newSystemAudioMode);
931 }
932 }
Amyacc1dab2018-09-06 17:09:51 -0700933 // Init arc whenever System Audio Mode is on
Amy03afe482018-09-18 16:57:45 -0700934 // Since some TVs don't request ARC on with System Audio Mode on request
935 if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
Amy46ad11f2019-06-03 16:59:01 -0700936 && isDirectConnectToTv() && mService.isSystemAudioActivated()) {
Amyf05bbe12019-04-22 15:50:49 -0700937 if (!hasAction(ArcInitiationActionFromAvr.class)) {
Amy03afe482018-09-18 16:57:45 -0700938 addAndStartAction(new ArcInitiationActionFromAvr(this));
Amy03afe482018-09-18 16:57:45 -0700939 }
Amyacc1dab2018-09-06 17:09:51 -0700940 }
Amy9b91e8c2018-06-11 17:26:26 -0700941 }
942
Amyac1a55c2018-07-13 16:38:23 -0700943 protected void switchToAudioInput() {
944 // TODO(b/111396634): switch input according to PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT
945 }
946
Amy77c7c982018-07-23 18:27:36 -0700947 protected boolean isDirectConnectToTv() {
948 int myPhysicalAddress = mService.getPhysicalAddress();
949 return (myPhysicalAddress & Constants.ROUTING_PATH_TOP_MASK) == myPhysicalAddress;
950 }
951
Amy9b91e8c2018-06-11 17:26:26 -0700952 private void updateAudioManagerForSystemAudio(boolean on) {
953 int device = mService.getAudioManager().setHdmiSystemAudioSupported(on);
954 HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
955 }
956
Amy79db52f2018-10-23 12:45:17 -0700957 void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
958 setSystemAudioControlFeatureEnabled(enabled);
959 if (enabled) {
960 addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
961 }
962 }
963
Nick Chalko167529c2018-07-18 13:11:31 -0700964 @ServiceThreadOnly
965 void setSystemAudioControlFeatureEnabled(boolean enabled) {
966 assertRunOnServiceThread();
967 synchronized (mLock) {
968 mSystemAudioControlFeatureEnabled = enabled;
969 }
970 }
971
shubangd932cb52018-09-14 17:55:16 -0700972 @ServiceThreadOnly
Amy0c2e29f2018-10-23 12:17:52 -0700973 void setRoutingControlFeatureEnables(boolean enabled) {
974 assertRunOnServiceThread();
975 synchronized (mLock) {
976 mRoutingControlFeatureEnabled = enabled;
977 }
978 }
979
980 @ServiceThreadOnly
shubangd932cb52018-09-14 17:55:16 -0700981 void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
982 assertRunOnServiceThread();
Amybaa97042019-03-22 16:33:48 -0700983 if (!mService.isValidPortId(portId)) {
984 invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
985 return;
986 }
shubangd932cb52018-09-14 17:55:16 -0700987 if (portId == getLocalActivePort()) {
988 invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
989 return;
990 }
shubangd932cb52018-09-14 17:55:16 -0700991 if (!mService.isControlEnabled()) {
Amyf9fa6172018-10-11 16:47:12 -0700992 setRoutingPort(portId);
shubangd932cb52018-09-14 17:55:16 -0700993 setLocalActivePort(portId);
994 invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
995 return;
996 }
Amyf9fa6172018-10-11 16:47:12 -0700997 int oldPath = getRoutingPort() != Constants.CEC_SWITCH_HOME
998 ? mService.portIdToPath(getRoutingPort())
shubangd932cb52018-09-14 17:55:16 -0700999 : getDeviceInfo().getPhysicalAddress();
Amyf9fa6172018-10-11 16:47:12 -07001000 int newPath = mService.portIdToPath(portId);
shubangd932cb52018-09-14 17:55:16 -07001001 if (oldPath == newPath) {
1002 return;
1003 }
Amyf9fa6172018-10-11 16:47:12 -07001004 setRoutingPort(portId);
shubangd932cb52018-09-14 17:55:16 -07001005 setLocalActivePort(portId);
1006 HdmiCecMessage routingChange =
1007 HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
1008 mService.sendCecCommand(routingChange);
1009 }
1010
Nick Chalko167529c2018-07-18 13:11:31 -07001011 boolean isSystemAudioControlFeatureEnabled() {
Amy9b91e8c2018-06-11 17:26:26 -07001012 synchronized (mLock) {
1013 return mSystemAudioControlFeatureEnabled;
1014 }
1015 }
Nick Chalkof28c7b52018-06-14 07:46:00 -07001016
Shubang595a1832018-06-27 17:52:18 -07001017 protected boolean isSystemAudioActivated() {
Amy489454f2019-01-24 19:06:57 -08001018 return mService.isSystemAudioActivated();
Shubang595a1832018-06-27 17:52:18 -07001019 }
1020
Shubang2fd186e2018-07-18 16:53:12 -07001021 protected void terminateSystemAudioMode() {
1022 // remove pending initiation actions
1023 removeAction(SystemAudioInitiationActionFromAvr.class);
Shubangbc2aab32018-07-19 16:41:17 -07001024 if (!isSystemAudioActivated()) {
1025 return;
Shubang2fd186e2018-07-18 16:53:12 -07001026 }
1027
Amy06dc4cd2018-08-09 18:22:40 -07001028 if (checkSupportAndSetSystemAudioMode(false)) {
Shubangbc2aab32018-07-19 16:41:17 -07001029 // send <Set System Audio Mode> [“Off”]
1030 mService.sendCecCommand(
1031 HdmiCecMessageBuilder.buildSetSystemAudioMode(
1032 mAddress, Constants.ADDR_BROADCAST, false));
Shubang2fd186e2018-07-18 16:53:12 -07001033 }
1034 }
1035
Nick Chalkof28c7b52018-06-14 07:46:00 -07001036 /** Reports if System Audio Mode is supported by the connected TV */
1037 interface TvSystemAudioModeSupportedCallback {
1038
1039 /** {@code supported} is true if the TV is connected and supports System Audio Mode. */
1040 void onResult(boolean supported);
Nick Chalkof28c7b52018-06-14 07:46:00 -07001041 }
1042
1043 /**
1044 * Queries the connected TV to detect if System Audio Mode is supported by the TV.
1045 *
1046 * <p>This query may take up to 2 seconds to complete.
1047 *
1048 * <p>The result of the query may be cached until Audio device type is put in standby or loses
1049 * its physical address.
1050 */
1051 void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
Amy613330e2019-03-22 16:53:59 -07001052 if (mTvSystemAudioModeSupport == null) {
Shubangec668962018-07-13 19:03:19 -07001053 addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
1054 } else {
Amy613330e2019-03-22 16:53:59 -07001055 callback.onResult(mTvSystemAudioModeSupport);
Shubangec668962018-07-13 19:03:19 -07001056 }
1057 }
1058
Amy06dc4cd2018-08-09 18:22:40 -07001059 /**
1060 * Handler of System Audio Mode Request on from non TV device
1061 */
1062 void handleSystemAudioModeOnFromNonTvDevice(HdmiCecMessage message) {
1063 if (!isSystemAudioControlFeatureEnabled()) {
1064 HdmiLogger.debug(
1065 "Cannot turn on" + "system audio mode "
1066 + "because the System Audio Control feature is disabled.");
1067 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
1068 return;
1069 }
Amyae4ee342018-10-09 16:55:14 -07001070 // Wake up device
1071 mService.wakeUp();
Amy00a122a2019-02-19 12:45:20 -08001072 // If Audio device is the active source or is on the active path,
1073 // enable system audio mode without querying TV's support on sam.
1074 // This is per HDMI spec 1.4b CEC 13.15.4.2.
1075 if (mService.pathToPortId(getActiveSource().physicalAddress)
1076 != Constants.INVALID_PORT_ID) {
1077 setSystemAudioMode(true);
1078 mService.sendCecCommand(
1079 HdmiCecMessageBuilder.buildSetSystemAudioMode(
1080 mAddress, Constants.ADDR_BROADCAST, true));
1081 return;
1082 }
Amy06dc4cd2018-08-09 18:22:40 -07001083 // Check if TV supports System Audio Control.
1084 // Handle broadcasting setSystemAudioMode on or aborting message on callback.
1085 queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
1086 public void onResult(boolean supported) {
1087 if (supported) {
1088 setSystemAudioMode(true);
1089 mService.sendCecCommand(
1090 HdmiCecMessageBuilder.buildSetSystemAudioMode(
1091 mAddress, Constants.ADDR_BROADCAST, true));
1092 } else {
1093 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
1094 }
1095 }
1096 });
1097 }
1098
Shubangec668962018-07-13 19:03:19 -07001099 void setTvSystemAudioModeSupport(boolean supported) {
1100 mTvSystemAudioModeSupport = supported;
Nick Chalkof28c7b52018-06-14 07:46:00 -07001101 }
Amy77c7c982018-07-23 18:27:36 -07001102
1103 @VisibleForTesting
1104 protected boolean isArcEnabled() {
1105 synchronized (mLock) {
1106 return mArcEstablished;
1107 }
1108 }
Amy93a97b92018-08-17 14:58:41 -07001109
Amy86347ba2019-04-04 16:22:54 -07001110 private void initArcOnFromAvr() {
Amyf05bbe12019-04-22 15:50:49 -07001111 removeAction(ArcTerminationActionFromAvr.class);
Amy86347ba2019-04-04 16:22:54 -07001112 if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
1113 && isDirectConnectToTv() && !isArcEnabled()) {
1114 removeAction(ArcInitiationActionFromAvr.class);
1115 addAndStartAction(new ArcInitiationActionFromAvr(this));
1116 }
1117 }
1118
Amy225d55a2018-09-06 11:03:51 -07001119 @Override
1120 protected void switchInputOnReceivingNewActivePath(int physicalAddress) {
Amy00638112018-10-31 17:47:17 -07001121 int port = mService.pathToPortId(physicalAddress);
Amy34037422018-09-06 13:21:08 -07001122 if (isSystemAudioActivated() && port < 0) {
1123 // If system audio mode is on and the new active source is not under the current device,
1124 // Will switch to ARC input.
1125 // TODO(b/115637145): handle system aduio without ARC
Amy225d55a2018-09-06 11:03:51 -07001126 routeToInputFromPortId(Constants.CEC_SWITCH_ARC);
Amy34037422018-09-06 13:21:08 -07001127 } else if (mIsSwitchDevice && port >= 0) {
1128 // If current device is a switch and the new active source is under it,
1129 // will switch to the corresponding active path.
1130 routeToInputFromPortId(port);
Amy225d55a2018-09-06 11:03:51 -07001131 }
1132 }
1133
Amy225d55a2018-09-06 11:03:51 -07001134 protected void routeToInputFromPortId(int portId) {
Amy0c2e29f2018-10-23 12:17:52 -07001135 if (!isRoutingControlFeatureEnabled()) {
1136 HdmiLogger.debug("Routing Control Feature is not enabled.");
1137 return;
1138 }
Amy225d55a2018-09-06 11:03:51 -07001139 if (mArcIntentUsed) {
1140 routeToTvInputFromPortId(portId);
1141 } else {
1142 // TODO(): implement input switching for devices not using TvInput.
1143 }
1144 }
1145
1146 protected void routeToTvInputFromPortId(int portId) {
1147 if (portId < 0 || portId >= Constants.CEC_SWITCH_PORT_MAX) {
1148 HdmiLogger.debug("Invalid port number for Tv Input switching.");
1149 return;
1150 }
Amy03afe482018-09-18 16:57:45 -07001151 // Wake up if the current device if ready to route.
Amyae4ee342018-10-09 16:55:14 -07001152 mService.wakeUp();
Amy2d8f4e62019-05-02 16:29:19 -07001153 // Switch to HOME if the current active port is not HOME yet
1154 if (portId == Constants.CEC_SWITCH_HOME
1155 && mService.isPlaybackDevice()
1156 && getLocalActivePort() != Constants.CEC_SWITCH_HOME) {
Amy225d55a2018-09-06 11:03:51 -07001157 switchToHomeTvInput();
1158 } else if (portId == Constants.CEC_SWITCH_ARC) {
1159 switchToTvInput(SystemProperties.get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT));
Amy03afe482018-09-18 16:57:45 -07001160 setLocalActivePort(portId);
Amy225d55a2018-09-06 11:03:51 -07001161 return;
1162 } else {
Amyedd71e32019-02-11 13:39:39 -08001163 String uri = mPortIdToTvInputs.get(portId);
Amy34037422018-09-06 13:21:08 -07001164 if (uri != null) {
Amyedd71e32019-02-11 13:39:39 -08001165 switchToTvInput(uri);
Amy34037422018-09-06 13:21:08 -07001166 } else {
1167 HdmiLogger.debug("Port number does not match any Tv Input.");
1168 return;
1169 }
Amy225d55a2018-09-06 11:03:51 -07001170 }
1171
1172 setLocalActivePort(portId);
Amy03afe482018-09-18 16:57:45 -07001173 setRoutingPort(portId);
Amy225d55a2018-09-06 11:03:51 -07001174 }
1175
1176 // For device to switch to specific TvInput with corresponding URI.
1177 private void switchToTvInput(String uri) {
Amy4aef3b402019-01-18 14:17:55 -08001178 try {
1179 mService.getContext().startActivity(new Intent(Intent.ACTION_VIEW,
1180 TvContract.buildChannelUriForPassthroughInput(uri))
1181 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
1182 } catch (ActivityNotFoundException e) {
1183 Slog.e(TAG, "Can't find activity to switch to " + uri, e);
1184 }
Amy225d55a2018-09-06 11:03:51 -07001185 }
1186
1187 // For device using TvInput to switch to Home.
1188 private void switchToHomeTvInput() {
Amy4aef3b402019-01-18 14:17:55 -08001189 try {
1190 Intent activityIntent = new Intent(Intent.ACTION_MAIN)
1191 .addCategory(Intent.CATEGORY_HOME)
1192 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
1193 | Intent.FLAG_ACTIVITY_SINGLE_TOP
1194 | Intent.FLAG_ACTIVITY_NEW_TASK
1195 | Intent.FLAG_ACTIVITY_NO_ANIMATION);
1196 mService.getContext().startActivity(activityIntent);
1197 } catch (ActivityNotFoundException e) {
1198 Slog.e(TAG, "Can't find activity to switch to HOME", e);
1199 }
Amy225d55a2018-09-06 11:03:51 -07001200 }
Amy79b54e92018-09-11 17:02:28 -07001201
1202 @Override
1203 protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
Amy00638112018-10-31 17:47:17 -07001204 int port = mService.pathToPortId(physicalAddress);
Amy79b54e92018-09-11 17:02:28 -07001205 // Routing change or information sent from switches under the current device can be ignored.
1206 if (port > 0) {
1207 return;
1208 }
1209 // When other switches route to some other devices not under the current device,
1210 // check system audio mode status and do ARC switch if needed.
1211 if (port < 0 && isSystemAudioActivated()) {
1212 handleRoutingChangeAndInformationForSystemAudio();
1213 return;
1214 }
1215 // When other switches route to the current device
1216 // and the current device is also a switch.
1217 if (port == 0) {
1218 handleRoutingChangeAndInformationForSwitch(message);
1219 }
1220 }
1221
1222 // Handle the system audio(ARC) part of the logic on receiving routing change or information.
1223 private void handleRoutingChangeAndInformationForSystemAudio() {
Amy79b54e92018-09-11 17:02:28 -07001224 // TODO(b/115637145): handle system aduio without ARC
1225 routeToInputFromPortId(Constants.CEC_SWITCH_ARC);
1226 }
1227
1228 // Handle the routing control part of the logic on receiving routing change or information.
1229 private void handleRoutingChangeAndInformationForSwitch(HdmiCecMessage message) {
Amy03afe482018-09-18 16:57:45 -07001230 if (getRoutingPort() == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
Amy79b54e92018-09-11 17:02:28 -07001231 routeToInputFromPortId(Constants.CEC_SWITCH_HOME);
Amy123ec402018-09-25 10:56:31 -07001232 mService.setAndBroadcastActiveSourceFromOneDeviceType(
1233 message.getSource(), mService.getPhysicalAddress());
Amy79b54e92018-09-11 17:02:28 -07001234 return;
1235 }
1236
Amy238f64f2018-11-30 11:27:56 -08001237 int routingInformationPath = mService.portIdToPath(getRoutingPort());
Amy79b54e92018-09-11 17:02:28 -07001238 // If current device is already the leaf of the whole HDMI system, will do nothing.
1239 if (routingInformationPath == mService.getPhysicalAddress()) {
1240 HdmiLogger.debug("Current device can't assign valid physical address"
1241 + "to devices under it any more. "
Amy4aef3b402019-01-18 14:17:55 -08001242 + "It's physical address is "
1243 + routingInformationPath);
Amy79b54e92018-09-11 17:02:28 -07001244 return;
1245 }
1246 // Otherwise will switch to the current active port and broadcast routing information.
1247 mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingInformation(
1248 mAddress, routingInformationPath));
Amy03afe482018-09-18 16:57:45 -07001249 routeToInputFromPortId(getRoutingPort());
Amy79b54e92018-09-11 17:02:28 -07001250 }
Amy1e4a8cc2018-10-15 10:18:14 -07001251
1252 protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
1253 HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
1254 if (info == null) {
1255 Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
1256 return;
1257 }
1258
1259 if (info.getDevicePowerStatus() == newPowerStatus) {
1260 return;
1261 }
1262
1263 HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
1264 // addDeviceInfo replaces old device info with new one if exists.
1265 addDeviceInfo(newInfo);
1266
1267 invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
1268 }
Amyc00cd4e2018-10-16 20:21:33 -07001269
1270 @ServiceThreadOnly
1271 private void launchDeviceDiscovery() {
1272 assertRunOnServiceThread();
Amy47fe0b12018-11-06 14:45:18 -08001273 if (hasAction(DeviceDiscoveryAction.class)) {
1274 Slog.i(TAG, "Device Discovery Action is in progress. Restarting.");
1275 removeAction(DeviceDiscoveryAction.class);
1276 }
Amyc00cd4e2018-10-16 20:21:33 -07001277 DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
1278 new DeviceDiscoveryCallback() {
1279 @Override
1280 public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
1281 for (HdmiDeviceInfo info : deviceInfos) {
1282 addCecDevice(info);
1283 }
1284 }
1285 });
1286 addAndStartAction(action);
1287 }
1288
1289 // Clear all device info.
1290 @ServiceThreadOnly
1291 private void clearDeviceInfoList() {
1292 assertRunOnServiceThread();
1293 for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
1294 if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
1295 continue;
1296 }
1297 invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
1298 }
1299 mDeviceInfos.clear();
Amyec126a52018-10-30 16:51:14 -07001300 updateSafeDeviceInfoList();
Amyc00cd4e2018-10-16 20:21:33 -07001301 }
Nick Chalkob9e48e22018-10-23 06:59:39 -07001302
1303 @Override
1304 protected void dump(IndentingPrintWriter pw) {
1305 pw.println("HdmiCecLocalDeviceAudioSystem:");
1306 pw.increaseIndent();
Amy77e672c2018-10-31 15:55:40 -07001307 pw.println("isRoutingFeatureEnabled " + isRoutingControlFeatureEnabled());
Nick Chalkob9e48e22018-10-23 06:59:39 -07001308 pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
1309 pw.println("mTvSystemAudioModeSupport: " + mTvSystemAudioModeSupport);
1310 pw.println("mArcEstablished: " + mArcEstablished);
1311 pw.println("mArcIntentUsed: " + mArcIntentUsed);
Amy2e4b25c2018-10-30 18:08:49 -07001312 pw.println("mRoutingPort: " + getRoutingPort());
1313 pw.println("mLocalActivePort: " + getLocalActivePort());
Amyedd71e32019-02-11 13:39:39 -08001314 HdmiUtils.dumpMap(pw, "mPortIdToTvInputs:", mPortIdToTvInputs);
1315 HdmiUtils.dumpMap(pw, "mTvInputsToDeviceInfo:", mTvInputsToDeviceInfo);
Nick Chalkob9e48e22018-10-23 06:59:39 -07001316 HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
1317 pw.decreaseIndent();
1318 super.dump(pw);
1319 }
Nick Chalkob89d6ff2018-05-25 10:29:01 -07001320}