diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 72ff5b55de5bf..6303bdc63c36b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -899,10 +899,16 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void startArcAction(boolean enabled) { + startArcAction(enabled, null); + } + + @ServiceThreadOnly + void startArcAction(boolean enabled, IHdmiControlCallback callback) { assertRunOnServiceThread(); HdmiDeviceInfo info = getAvrDeviceInfo(); if (info == null) { Slog.w(TAG, "Failed to start arc action; No AVR device."); + invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); return; } if (!canStartArcUpdateAction(info.getLogicalAddress(), enabled)) { @@ -910,25 +916,37 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (enabled && !isConnectedToArcPort(info.getPhysicalAddress())) { displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); } + invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE); return; } if (enabled && mService.earcBlocksArcConnection()) { Slog.i(TAG, "ARC connection blocked because eARC connection is established or being " + "established."); + invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE); return; } - // Terminate opposite action and start action if not exist. + // Terminate opposite action and create an action with callback. if (enabled) { removeAction(RequestArcTerminationAction.class); - if (!hasAction(RequestArcInitiationAction.class)) { - addAndStartAction(new RequestArcInitiationAction(this, info.getLogicalAddress())); + if (hasAction(RequestArcInitiationAction.class)) { + RequestArcInitiationAction existingInitiationAction = + getActions(RequestArcInitiationAction.class).get(0); + existingInitiationAction.addCallback(callback); + } else { + addAndStartAction( + new RequestArcInitiationAction(this, info.getLogicalAddress(), callback)); } } else { removeAction(RequestArcInitiationAction.class); - if (!hasAction(RequestArcTerminationAction.class)) { - addAndStartAction(new RequestArcTerminationAction(this, info.getLogicalAddress())); + if (hasAction(RequestArcTerminationAction.class)) { + RequestArcTerminationAction existingTerminationAction = + getActions(RequestArcTerminationAction.class).get(0); + existingTerminationAction.addCallback(callback); + } else { + addAndStartAction( + new RequestArcTerminationAction(this, info.getLogicalAddress(), callback)); } } } @@ -1036,9 +1054,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return Constants.ABORT_REFUSED; } - // In case where is started by - // need to clean up RequestArcInitiationAction. - removeAction(RequestArcInitiationAction.class); + // In case where is started by , this message is + // handled in RequestArcInitiationAction as well. SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this, message.getSource(), true); addAndStartAction(action); @@ -1072,9 +1089,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return Constants.HANDLED; } // Do not check ARC configuration since the AVR might have been already removed. - // Clean up RequestArcTerminationAction in case was started by - // . - removeAction(RequestArcTerminationAction.class); + // In case where is started by , this + // message is handled in RequestArcTerminationAction as well. SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this, message.getSource(), false); addAndStartAction(action); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 8c4a5cab6a29e..36ed5b52039ce 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -3237,6 +3237,9 @@ public class HdmiControlService extends SystemService { } private void invokeCallback(IHdmiControlCallback callback, int result) { + if (callback == null) { + return; + } try { callback.onComplete(result); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java index 3d9a2905f4fbc..54c8c00b88892 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java @@ -16,7 +16,9 @@ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.hdmi.IHdmiControlCallback; /** * Base feature action class for <Request ARC Initiation>/<Request ARC Termination>. @@ -35,41 +37,19 @@ abstract class RequestArcAction extends HdmiCecFeatureAction { * * @param source {@link HdmiCecLocalDevice} instance * @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type + * @param callback callback to inform about the status of the action * @throws IllegalArgumentException if device type of sourceAddress and avrAddress * is invalid */ - RequestArcAction(HdmiCecLocalDevice source, int avrAddress) { - super(source); + RequestArcAction(HdmiCecLocalDevice source, int avrAddress, IHdmiControlCallback callback) { + super(source, callback); HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV); HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; } - @Override - boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE - || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) { - return false; - } - int opcode = cmd.getOpcode(); - switch (opcode) { - // Handles only here and, both and - // are handled in HdmiControlService itself because both can be - // received without or . - case Constants.MESSAGE_FEATURE_ABORT: - int originalOpcode = cmd.getParams()[0] & 0xFF; - if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) { - disableArcTransmission(); - finish(); - return true; - } else if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION) { - tv().disableArc(); - finish(); - return true; - } - return false; - } - return false; + RequestArcAction(HdmiCecLocalDevice source, int avrAddress) { + this(source, avrAddress, null); } protected final void disableArcTransmission() { @@ -86,6 +66,6 @@ abstract class RequestArcAction extends HdmiCecFeatureAction { } HdmiLogger.debug("[T] RequestArcAction."); disableArcTransmission(); - finish(); + finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); } } diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java index 3b7f1dd0d9715..db21a33ae3253 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java @@ -16,6 +16,8 @@ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; /** @@ -35,6 +37,16 @@ final class RequestArcInitiationAction extends RequestArcAction { super(source, avrAddress); } + /** + * @Constructor + * + * For more details look at {@link RequestArcAction#RequestArcAction}. + */ + RequestArcInitiationAction(HdmiCecLocalDevice source, int avrAddress, + IHdmiControlCallback callback) { + super(source, avrAddress, callback); + } + @Override boolean start() { // Seq #38 @@ -49,10 +61,34 @@ final class RequestArcInitiationAction extends RequestArcAction { if (error != SendMessageResult.SUCCESS) { // Turn off ARC status if fails. tv().disableArc(); - finish(); + finishWithCallback(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); } } }); return true; } + + @Override + boolean processCommand(HdmiCecMessage cmd) { + if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE + || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) { + return false; + } + int opcode = cmd.getOpcode(); + switch (opcode) { + case Constants.MESSAGE_FEATURE_ABORT: + int originalOpcode = cmd.getParams()[0] & 0xFF; + if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION) { + tv().disableArc(); + finishWithCallback(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + return true; + } + return false; + case Constants.MESSAGE_INITIATE_ARC: + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); + // This message still needs to be handled in HdmiCecLocalDeviceTv as well. + return false; + } + return false; + } } diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java index 8b5a293102331..85128b69b5788 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java @@ -16,6 +16,8 @@ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; /** @@ -35,6 +37,16 @@ final class RequestArcTerminationAction extends RequestArcAction { super(source, avrAddress); } + /** + * @Constructor + * + * @see RequestArcAction#RequestArcAction + */ + RequestArcTerminationAction(HdmiCecLocalDevice source, int avrAddress, + IHdmiControlCallback callback) { + super(source, avrAddress, callback); + } + @Override boolean start() { mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE; @@ -49,10 +61,34 @@ final class RequestArcTerminationAction extends RequestArcAction { // If failed to send , start "Disabled" ARC // transmission action. disableArcTransmission(); - finish(); + finishWithCallback(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); } } }); return true; } + + @Override + boolean processCommand(HdmiCecMessage cmd) { + if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE + || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) { + return false; + } + int opcode = cmd.getOpcode(); + switch (opcode) { + case Constants.MESSAGE_FEATURE_ABORT: + int originalOpcode = cmd.getParams()[0] & 0xFF; + if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) { + disableArcTransmission(); + finishWithCallback(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + return true; + } + return false; + case Constants.MESSAGE_TERMINATE_ARC: + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); + // This message still needs to be handled in HdmiCecLocalDeviceTv as well. + return false; + } + return false; + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 689fcd1ce2288..ba44cac2dc08e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -39,6 +39,7 @@ import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; +import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.media.AudioManager; import android.os.Looper; @@ -209,6 +210,20 @@ public class HdmiCecLocalDeviceTvTest { mNativeWrapper.clearResultMessages(); } + 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 initialPowerStateIsStandby() { assertThat(mHdmiCecLocalDeviceTv.getPowerStatus()).isEqualTo( @@ -892,12 +907,12 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.dispatchAll(); // [Not in correct mode] not sent - HdmiCecMessage featureAbortMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand( + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( ADDR_TV, ADDR_PLAYBACK_1, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_NOT_IN_CORRECT_MODE); - assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortMessage); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbort); // uses volume range [0, 100]; STREAM_MUSIC uses range [0, 25] verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5), anyInt()); @@ -918,12 +933,12 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.dispatchAll(); // [Not in correct mode] sent - HdmiCecMessage featureAbortMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand( + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( ADDR_TV, ADDR_PLAYBACK_1, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_NOT_IN_CORRECT_MODE); - assertThat(mNativeWrapper.getResultMessages()).contains(featureAbortMessage); + assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort); // AudioManager not notified of volume change verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(), @@ -1081,12 +1096,12 @@ public class HdmiCecLocalDeviceTvTest { mNativeWrapper.onCecMessage(requestArcInitiation); mTestLooper.dispatchAll(); - HdmiCecMessage featureAbortMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand( + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( ADDR_TV, ADDR_AUDIO_SYSTEM, Constants.MESSAGE_INITIATE_ARC, Constants.ABORT_NOT_IN_CORRECT_MODE); - assertThat(mNativeWrapper.getResultMessages()).contains(featureAbortMessage); + assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort); } @Test @@ -1156,4 +1171,337 @@ public class HdmiCecLocalDeviceTvTest { ADDR_AUDIO_SYSTEM); assertThat(mNativeWrapper.getResultMessages()).contains(reportArcTerminated); } + + @Test + public void startArcAction_initiation_noAvr() { + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + } + + @Test + public void startArcAction_initiation_portNotConnected() { + // Emulate Audio device on port 0x2000 (supports ARC) + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + // Emulate port disconnect + mNativeWrapper.setPortConnectionStatus(2, false); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_INCORRECT_MODE); + } + + @Test + public void startArcAction_initiation_portDoesNotSupportArc() { + // Emulate Audio device on port 0x1000 (Doesn´t support ARC) + mNativeWrapper.setPortConnectionStatus(1, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_INCORRECT_MODE); + } + + @Test + public void startArcAction_initiation_indirectPhysicalAddress() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2320, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_INCORRECT_MODE); + } + + @Test + public void startArcAction_initiation_earcBlocksArc() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + mEarcBlocksArc = true; + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_INCORRECT_MODE); + } + + @Test + public void startArcAction_initiation_messageNotAcked() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + mNativeWrapper.setMessageSendResult( + Constants.MESSAGE_REQUEST_ARC_INITIATION, SendMessageResult.NACK); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcInitiation); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + } + + @Test + public void startArcAction_initiation_timeout() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcInitiation); + mTestLooper.moveTimeForward(TIMEOUT_MS); + mTestLooper.dispatchAll(); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TIMEOUT); + } + + @Test + public void startArcAction_initiation_featureAbort() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcInitiation); + + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + Constants.MESSAGE_REQUEST_ARC_INITIATION, + Constants.ABORT_NOT_IN_CORRECT_MODE); + mNativeWrapper.onCecMessage(featureAbort); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + } + + @Test + public void startArcAction_initiation_success() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(true, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcInitiation); + + HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc( + ADDR_AUDIO_SYSTEM, + ADDR_TV); + mNativeWrapper.onCecMessage(initiateArc); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void startArcAction_termination_noAvr() { + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(false, callback); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + } + + @Test + public void startArcAction_termination_portDoesNotSupportArc() { + // Emulate Audio device on port 0x1000 (Doesn´t support ARC) + mNativeWrapper.setPortConnectionStatus(1, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(false, callback); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_INCORRECT_MODE); + } + + @Test + public void startArcAction_termination_messageNotAcked() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + mNativeWrapper.setMessageSendResult( + Constants.MESSAGE_REQUEST_ARC_TERMINATION, SendMessageResult.NACK); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(false, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcTermination); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + } + + @Test + public void startArcAction_termination_timeout() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(false, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcTermination); + mTestLooper.moveTimeForward(TIMEOUT_MS); + mTestLooper.dispatchAll(); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TIMEOUT); + } + + @Test + public void startArcAction_termination_featureAbort() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(false, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcTermination); + + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + Constants.MESSAGE_REQUEST_ARC_TERMINATION, + Constants.ABORT_NOT_IN_CORRECT_MODE); + mNativeWrapper.onCecMessage(featureAbort); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + } + + @Test + public void startArcAction_termination_success() { + // Emulate Audio device on port 0x2000 (Supports ARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + TestCallback callback = new TestCallback(); + + mHdmiCecLocalDeviceTv.startArcAction(false, callback); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcTermination); + + HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc( + ADDR_AUDIO_SYSTEM, + ADDR_TV); + mNativeWrapper.onCecMessage(terminateArc); + mTestLooper.dispatchAll(); + + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } }