diff --git a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java index 4855be68e6761..ccb263362abbb 100644 --- a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java +++ b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java @@ -15,6 +15,8 @@ */ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; /** @@ -33,6 +35,10 @@ public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction { super(source); } + ArcTerminationActionFromAvr(HdmiCecLocalDevice source, IHdmiControlCallback callback) { + super(source, callback); + } + @Override boolean start() { mState = STATE_WAITING_FOR_INITIATE_ARC_RESPONSE; @@ -47,10 +53,19 @@ public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction { return false; } switch (cmd.getOpcode()) { + case Constants.MESSAGE_FEATURE_ABORT: + int originalOpcode = cmd.getParams()[0] & 0xFF; + if (originalOpcode == Constants.MESSAGE_TERMINATE_ARC) { + mState = STATE_ARC_TERMINATED; + audioSystem().processArcTermination(); + finishWithCallback(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + return true; + } + return false; case Constants.MESSAGE_REPORT_ARC_TERMINATED: mState = STATE_ARC_TERMINATED; audioSystem().processArcTermination(); - finish(); + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); return true; } return false; @@ -79,7 +94,7 @@ public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction { audioSystem().setArcStatus(false); } HdmiLogger.debug("Terminate ARC was not successfully sent."); - finish(); + finishWithCallback(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); } }); } @@ -88,6 +103,6 @@ public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction { // Disable ARC if TV didn't respond with in time. audioSystem().setArcStatus(false); HdmiLogger.debug("handleTerminateArcTimeout"); - finish(); + finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index ccaa9255dbd44..a026c4b59ec5d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -458,8 +458,16 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { HdmiLogger.debug("ARC is not established between TV and AVR device"); return Constants.ABORT_NOT_IN_CORRECT_MODE; } else { - removeAction(ArcTerminationActionFromAvr.class); - addAndStartAction(new ArcTerminationActionFromAvr(this)); + if (!getActions(ArcTerminationActionFromAvr.class).isEmpty() + && !getActions(ArcTerminationActionFromAvr.class).get(0).mCallbacks.isEmpty()) { + IHdmiControlCallback callback = + getActions(ArcTerminationActionFromAvr.class).get(0).mCallbacks.get(0); + removeAction(ArcTerminationActionFromAvr.class); + addAndStartAction(new ArcTerminationActionFromAvr(this, callback)); + } else { + removeAction(ArcTerminationActionFromAvr.class); + addAndStartAction(new ArcTerminationActionFromAvr(this)); + } return Constants.HANDLED; } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 43cd71ae519c2..2f15e579b2149 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -878,16 +878,30 @@ public class HdmiControlService extends SystemService { Slog.w(TAG, "Device type doesn't support ARC."); return; } + boolean isArcEnabled = false; if (settingValue == SOUNDBAR_MODE_DISABLED && audioSystem != null) { - if (audioSystem.isArcEnabled()) { - audioSystem.addAndStartAction(new ArcTerminationActionFromAvr(audioSystem)); - } + isArcEnabled = audioSystem.isArcEnabled(); if (isSystemAudioActivated()) { audioSystem.terminateSystemAudioMode(); } + if (isArcEnabled) { + if (audioSystem.hasAction(ArcTerminationActionFromAvr.class)) { + audioSystem.removeAction(ArcTerminationActionFromAvr.class); + } + audioSystem.addAndStartAction(new ArcTerminationActionFromAvr(audioSystem, + new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + mAddressAllocated = false; + initializeCecLocalDevices(INITIATED_BY_SOUNDBAR_MODE); + } + })); + } + } + if (!isArcEnabled) { + mAddressAllocated = false; + initializeCecLocalDevices(INITIATED_BY_SOUNDBAR_MODE); } - mAddressAllocated = false; - initializeCecLocalDevices(INITIATED_BY_SOUNDBAR_MODE); } /** diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java index 09cd47a99cba4..2cb46da9d2073 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java @@ -24,6 +24,8 @@ import static org.mockito.Mockito.spy; import android.content.Context; import android.content.ContextWrapper; +import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.media.AudioManager; import android.os.Looper; @@ -52,6 +54,7 @@ public class ArcTerminationActionFromAvrTest { private Context mContextSpy; private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem; private FakePowerManagerWrapper mPowerManager; + private TestCallback mCallback; private ArcTerminationActionFromAvr mAction; private FakeNativeWrapper mNativeWrapper; @@ -112,7 +115,9 @@ public class ArcTerminationActionFromAvrTest { } }; mHdmiCecLocalDeviceAudioSystem.init(); - mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem); + mCallback = new TestCallback(); + mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem, + mCallback); mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem); hdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY); @@ -121,6 +126,20 @@ public class ArcTerminationActionFromAvrTest { mTestLooper.dispatchAll(); } + private static class TestCallback extends IHdmiControlCallback.Stub { + private final ArrayList mCallbackResult = new ArrayList(); + + @Override + public void onComplete(int result) { + mCallbackResult.add(result); + } + + private int getResult() { + assertThat(mCallbackResult.size()).isEqualTo(1); + return mCallbackResult.get(0); + } + } + @Test public void testSendMessage_sendFailed() { mNativeWrapper.setMessageSendResult(Constants.MESSAGE_TERMINATE_ARC, @@ -133,6 +152,7 @@ public class ArcTerminationActionFromAvrTest { assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc); assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse(); + assertThat(mCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); } @Test @@ -149,6 +169,7 @@ public class ArcTerminationActionFromAvrTest { mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse(); + assertThat(mCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_TIMEOUT); } @Test @@ -167,5 +188,28 @@ public class ArcTerminationActionFromAvrTest { mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse(); + assertThat(mCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void testReportArcTerminated_featureAbort() { + mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction); + mTestLooper.dispatchAll(); + HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc( + Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV); + + assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc); + + HdmiCecMessage arcTerminatedResponse = HdmiCecMessageBuilder.buildFeatureAbortCommand( + Constants.ADDR_TV, + Constants.ADDR_AUDIO_SYSTEM, + Constants.MESSAGE_TERMINATE_ARC, + Constants.ABORT_REFUSED); + + mNativeWrapper.onCecMessage(arcTerminatedResponse); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse(); + assertThat(mCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index 7c6c990500f3f..de2c2181d3abc 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -534,6 +534,25 @@ public class HdmiCecLocalDeviceAudioSystemTest { assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); } + @Test + public void handleRequestArcTerminate_callbackIsPreserved() throws Exception { + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceAudioSystem.setArcStatus(true); + assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue(); + mHdmiCecLocalDeviceAudioSystem.addAndStartAction( + new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem, callback)); + + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)) + .isEqualTo(Constants.HANDLED); + + mTestLooper.dispatchAll(); + assertThat(mHdmiCecLocalDeviceAudioSystem.getActions( + ArcTerminationActionFromAvr.class).get(0).mCallbacks.get(0)).isEqualTo(callback); + } + @Test public void handleRequestArcInit_arcIsNotSupported() throws Exception { HdmiCecMessage message = @@ -880,4 +899,13 @@ public class HdmiCecLocalDeviceAudioSystemTest { assertThat(mNativeWrapper.getResultMessages()).doesNotContain( systemAudioModeRequest_fromAudioSystem); } + + private static class TestCallback extends IHdmiControlCallback.Stub { + private final ArrayList mCallbackResult = new ArrayList(); + + @Override + public void onComplete(int result) { + mCallbackResult.add(result); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index ef2b21211297c..49a0a9a52c47d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -1036,6 +1036,7 @@ public class HdmiControlServiceTest { @Test public void setSoundbarMode_enabled_addAudioSystemLocalDevice() { + mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); // Initialize the local devices excluding the audio system. mHdmiControlServiceSpy.clearCecLocalDevices(); mLocalDevices.remove(mAudioSystemDeviceSpy); @@ -1053,6 +1054,7 @@ public class HdmiControlServiceTest { @Test public void setSoundbarMode_disabled_removeAudioSystemLocalDevice() { + mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); // Initialize the local devices excluding the audio system. mHdmiControlServiceSpy.clearCecLocalDevices(); mLocalDevices.remove(mAudioSystemDeviceSpy); @@ -1073,6 +1075,10 @@ public class HdmiControlServiceTest { HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE, HdmiControlManager.SOUNDBAR_MODE_DISABLED); mTestLooper.dispatchAll(); + + // Wait for ArcTerminationActionFromAvr timeout for the logical address allocation to start. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); assertThat(mHdmiControlServiceSpy.audioSystem()).isNull(); }