diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java new file mode 100644 index 0000000000000..ed17de5e8c350 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.hdmi; + +import android.hardware.tv.cec.V1_0.SendMessageResult; + +/** + * Feature action that handles Audio Return Channel initiated by AVR devices. + */ +public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction { + // TODO(shubang): add tests + + // State in which waits for ARC response. + private static final int STATE_WAITING_FOR_INITIATE_ARC_RESPONSE = 1; + private static final int STATE_ARC_INITIATED = 2; + + // the required maximum response time specified in CEC 9.2 + private static final int TIMEOUT_MS = 1000; + + ArcInitiationActionFromAvr(HdmiCecLocalDevice source) { + super(source); + } + + @Override + boolean start() { + audioSystem().setArcStatus(true); + mState = STATE_WAITING_FOR_INITIATE_ARC_RESPONSE; + addTimer(mState, TIMEOUT_MS); + sendInitiateArc(); + return true; + } + + @Override + boolean processCommand(HdmiCecMessage cmd) { + if (mState != STATE_WAITING_FOR_INITIATE_ARC_RESPONSE) { + return false; + } + switch (cmd.getOpcode()) { + case Constants.MESSAGE_FEATURE_ABORT: + case Constants.MESSAGE_REPORT_ARC_TERMINATED: + audioSystem().setArcStatus(false); + finish(); + return true; + case Constants.MESSAGE_REPORT_ARC_INITIATED: + mState = STATE_ARC_INITIATED; + finish(); + return true; + } + return false; + } + + @Override + void handleTimerEvent(int state) { + if (mState != state) { + return; + } + + switch (mState) { + case STATE_WAITING_FOR_INITIATE_ARC_RESPONSE: + handleInitiateArcTimeout(); + break; + } + } + + protected void sendInitiateArc() { + sendCommand(HdmiCecMessageBuilder.buildInitiateArc(getSourceAddress(), Constants.ADDR_TV), + result -> { + if (result != SendMessageResult.SUCCESS) { + audioSystem().setArcStatus(false); + finish(); + } + }); + } + + private void handleInitiateArcTimeout() { + HdmiLogger.debug("handleInitiateArcTimeout"); + audioSystem().setArcStatus(false); + finish(); + } + +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index e762d7fd3d62e..b15734b607f01 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -21,6 +21,7 @@ import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONT import android.hardware.hdmi.HdmiDeviceInfo; import android.media.AudioManager; +import android.media.AudioSystem; import android.os.SystemProperties; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -45,6 +46,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice { private boolean mTvSystemAudioModeSupport; + // Whether ARC is available or not. "true" means that ARC is established between TV and + // AVR as audio receiver. + @ServiceThreadOnly + private boolean mArcEstablished = false; + protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); mSystemAudioControlFeatureEnabled = true; @@ -256,6 +262,39 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice { return true; } + @ServiceThreadOnly + void setArcStatus(boolean enabled) { + // TODO(shubang): add tests + assertRunOnServiceThread(); + + HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled); + // 1. Enable/disable ARC circuit. + enableAudioReturnChannel(enabled); + // 2. Notify arc status to audio service. + notifyArcStatusToAudioService(enabled); + // 3. Update arc status; + mArcEstablished = enabled; + } + + /** + * Switch hardware ARC circuit in the system. + */ + @ServiceThreadOnly + private void enableAudioReturnChannel(boolean enabled) { + assertRunOnServiceThread(); + mService.enableAudioReturnChannel( + SystemProperties.getInt( + Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, 0), + enabled); + } + + private void notifyArcStatusToAudioService(boolean enabled) { + // Note that we don't set any name to ARC. + mService.getAudioManager().setWiredDeviceConnectionState( + AudioSystem.DEVICE_IN_HDMI, + enabled ? 1 : 0, "", ""); + } + private void reportAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread();