From 25d5bf6ab1f69e0dffbeec34ec268ae5aec5edea Mon Sep 17 00:00:00 2001 From: Amy Date: Fri, 21 Sep 2018 16:36:52 -0700 Subject: [PATCH] Request Active Source when ARC is enabled in ArcInitiationActionFromAvr. ag/5086550 This will trigger ARC input switch if users turn on System Audio Mode when watching Active Source not under the current device. Test: atest com.android.server.hdmi Bug: 80296911 Change-Id: I3fbc336892131291ec3e67e5f75ff99f592e2f23 --- .../hdmi/ArcInitiationActionFromAvr.java | 27 ++- .../hdmi/ArcInitiationActionFromAvrTest.java | 163 ++++++++++++++++++ 2 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java index ed17de5e8c350..18d328d3f971d 100644 --- a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java +++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java @@ -21,14 +21,15 @@ 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; + private static final int MAX_RETRY_COUNT = 5; + + private int mSendRequestActiveSourceRetryCount = 0; ArcInitiationActionFromAvr(HdmiCecLocalDevice source) { super(source); @@ -56,7 +57,12 @@ public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction { return true; case Constants.MESSAGE_REPORT_ARC_INITIATED: mState = STATE_ARC_INITIATED; - finish(); + if (audioSystem().getActiveSource().physicalAddress != getSourcePath() + && audioSystem().isSystemAudioActivated()) { + sendRequestActiveSource(); + } else { + finish(); + } return true; } return false; @@ -91,4 +97,19 @@ public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction { finish(); } + protected void sendRequestActiveSource() { + sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()), + result -> { + if (result != SendMessageResult.SUCCESS) { + if (mSendRequestActiveSourceRetryCount < MAX_RETRY_COUNT) { + mSendRequestActiveSourceRetryCount++; + sendRequestActiveSource(); + } else { + finish(); + } + } else { + finish(); + } + }); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java new file mode 100644 index 0000000000000..8b0e8abf069d2 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java @@ -0,0 +1,163 @@ +/* + * 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 static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; + +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.Nullable; +import android.app.Instrumentation; +import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.tv.cec.V1_0.SendMessageResult; +import android.os.Looper; +import android.os.test.TestLooper; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.ArrayList; + +/** Tests for {@link ArcInitiationActionFromAvrTest} */ +@SmallTest +@RunWith(JUnit4.class) +public class ArcInitiationActionFromAvrTest { + + private HdmiDeviceInfo mDeviceInfoForTests; + private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem; + private HdmiCecController mHdmiCecController; + private HdmiControlService mHdmiControlService; + private FakeNativeWrapper mNativeWrapper; + private ArcInitiationActionFromAvr mAction; + + private TestLooper mTestLooper = new TestLooper(); + private boolean mSendCecCommandSuccess; + private boolean mShouldDispatchARCInitiated; + private boolean mArcInitSent; + private boolean mRequestActiveSourceSent; + private Instrumentation mInstrumentation; + private ArrayList mLocalDevices = new ArrayList<>(); + + @Before + public void setUp() { + mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1); + + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + + mHdmiControlService = + new HdmiControlService(mInstrumentation.getTargetContext()) { + @Override + void sendCecCommand( + HdmiCecMessage command, @Nullable SendMessageCallback callback) { + switch (command.getOpcode()) { + case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE: + if (callback != null) { + callback.onSendCompleted( + mSendCecCommandSuccess + ? SendMessageResult.SUCCESS + : SendMessageResult.NACK); + } + mRequestActiveSourceSent = true; + break; + case Constants.MESSAGE_INITIATE_ARC: + if (callback != null) { + callback.onSendCompleted( + mSendCecCommandSuccess + ? SendMessageResult.SUCCESS + : SendMessageResult.NACK); + } + mArcInitSent = true; + if (mShouldDispatchARCInitiated) { + mHdmiCecLocalDeviceAudioSystem.dispatchMessage( + HdmiCecMessageBuilder.buildReportArcInitiated( + Constants.ADDR_TV, + Constants.ADDR_AUDIO_SYSTEM)); + } + break; + default: + } + } + + @Override + boolean isPowerStandby() { + return false; + } + + @Override + boolean isAddressAllocated() { + return true; + } + + @Override + Looper getServiceLooper() { + return mTestLooper.getLooper(); + } + }; + + mHdmiCecLocalDeviceAudioSystem = + new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) { + @Override + HdmiDeviceInfo getDeviceInfo() { + return mDeviceInfoForTests; + } + + @Override + void setArcStatus(boolean enabled) { + // do nothing + } + + @Override + protected boolean isSystemAudioActivated() { + return true; + } + }; + + mHdmiCecLocalDeviceAudioSystem.init(); + Looper looper = mTestLooper.getLooper(); + mHdmiControlService.setIoLooper(looper); + mNativeWrapper = new FakeNativeWrapper(); + mHdmiCecController = + HdmiCecController.createWithNativeWrapper(this.mHdmiControlService, mNativeWrapper); + mHdmiControlService.setCecController(mHdmiCecController); + mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService)); + mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService)); + mHdmiControlService.initPortInfo(); + mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem); + + mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + } + + @Test + public void arcInitiation_requestActiveSource() { + mSendCecCommandSuccess = true; + mShouldDispatchARCInitiated = true; + mRequestActiveSourceSent = false; + mArcInitSent = false; + + mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction); + mTestLooper.dispatchAll(); + + assertThat(mArcInitSent).isTrue(); + assertThat(mRequestActiveSourceSent).isTrue(); + } +}