From 69c298b0e3228a7ac3e7b4bd40c050292ac9f777 Mon Sep 17 00:00:00 2001 From: Nathalie Le Clair Date: Thu, 3 Nov 2022 11:45:00 +0100 Subject: [PATCH] Terminate ARC before enabling eARC in the HAL This is required by the eARC specification: an ARC connection and an eARC connection cannot exist simultaneously. The HAL can only initiate an eARC connection once the framework has informed it with a setEarcEnabled HAL call. The framework waits until ARC has been terminated to do this. It's enough to terminate ARC before enabling eARC in the HAL: there is no need to terminate it again once the HAL reports that eARC is connected succesfully. Reason: in all situations where the eARC HAL could report that eARC got connected, ARC has been terminated before: 1. On hotplug in: this means that there was a hotplug out before --> eARC is in the IDLE state --> ARC was disconnected when we entered the IDLE state. 2. On boot: Both ARC and eARC are disconnected before boot, and on boot, eARC gets attempted first. 3. On wake-up: ARC was terminated on the last standby. 4. When eARC gets enabled in the HAL: this CL explicitly terminates ARC. Bug: 262573690 Test: atest Change-Id: I91d20fd382c4e4e4102683ace5f2ef55a10e1048 --- .../server/hdmi/HdmiControlService.java | 45 ++++++++++++---- .../server/hdmi/HdmiCecLocalDeviceTvTest.java | 45 ++++++++++++++++ .../server/hdmi/HdmiControlServiceTest.java | 52 +++++++------------ 3 files changed, 98 insertions(+), 44 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 503442e4a5f6f..f66f8ea3ba771 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -393,7 +393,7 @@ public class HdmiControlService extends SystemService { // and the eARC HAL is present. @GuardedBy("mLock") @VisibleForTesting - protected boolean mEarcSupported; + private boolean mEarcSupported; // Set to true while the eARC feature is enabled. @GuardedBy("mLock") @@ -726,7 +726,7 @@ public class HdmiControlService extends SystemService { if (isEarcEnabled()) { initializeEarc(INITIATED_BY_BOOT_UP); } else { - setEarcEnabledInHal(false); + setEarcEnabledInHal(false, false); } } @@ -3522,7 +3522,7 @@ public class HdmiControlService extends SystemService { } initializeEarc(startReason); } else { - setEarcEnabledInHal(false); + setEarcEnabledInHal(false, false); } } // TODO: Initialize MHL local devices. @@ -4418,8 +4418,17 @@ public class HdmiControlService extends SystemService { private void initializeEarc(int initiatedBy) { Slog.i(TAG, "eARC initialized, reason = " + initiatedBy); - setEarcEnabledInHal(true); initializeEarcLocalDevice(initiatedBy); + + if (initiatedBy == INITIATED_BY_ENABLE_EARC) { + // Since ARC and eARC cannot be connected simultaneously, we need to terminate ARC + // before even enabling eARC. + setEarcEnabledInHal(true, true); + } else { + // On boot, wake-up, and hotplug in, eARC will always be attempted before ARC. + // So there is no need to explicitly terminate ARC before enabling eARC. + setEarcEnabledInHal(true, false); + } } @ServiceThreadOnly @@ -4475,7 +4484,7 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly private void onDisableEarc() { disableEarcLocalDevice(); - setEarcEnabledInHal(false); + setEarcEnabledInHal(false, false); clearEarcLocalDevice(); } @@ -4509,12 +4518,28 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly @VisibleForTesting - protected void setEarcEnabledInHal(boolean enabled) { + protected void setEarcEnabledInHal(boolean enabled, boolean terminateArcFirst) { assertRunOnServiceThread(); - mEarcController.setEarcEnabled(enabled); - mCecController.setHpdSignalType( - enabled ? Constants.HDMI_HPD_TYPE_STATUS_BIT : Constants.HDMI_HPD_TYPE_PHYSICAL, - mEarcPortId); + if (terminateArcFirst) { + startArcAction(false, new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) throws RemoteException { + // Independently of the result (i.e. independently of whether the ARC RX device + // responded with or not), we always end up terminating ARC in + // the HAL. As soon as we do that, we can enable eARC in the HAL. + mEarcController.setEarcEnabled(enabled); + mCecController.setHpdSignalType( + enabled ? Constants.HDMI_HPD_TYPE_STATUS_BIT + : Constants.HDMI_HPD_TYPE_PHYSICAL, + mEarcPortId); + } + }); + } else { + mEarcController.setEarcEnabled(enabled); + mCecController.setHpdSignalType( + enabled ? Constants.HDMI_HPD_TYPE_STATUS_BIT : Constants.HDMI_HPD_TYPE_PHYSICAL, + mEarcPortId); + } } @ServiceThreadOnly 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 ba44cac2dc08e..5dd29fda794d0 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -1504,4 +1504,49 @@ public class HdmiCecLocalDeviceTvTest { assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); } + + @Test + public void enableEarc_terminateArc() { + // Emulate Audio device on port 0x2000 (supports ARC and eARC) + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc( + ADDR_AUDIO_SYSTEM, + ADDR_TV); + + mNativeWrapper.onCecMessage(initiateArc); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + // should only be sent after SAD querying is done + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated); + + // Finish querying SADs + assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY); + mNativeWrapper.clearResultMessages(); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated); + mNativeWrapper.clearResultMessages(); + + mHdmiControlService.setEarcEnabled(HdmiControlManager.EARC_FEATURE_ENABLED); + mTestLooper.dispatchAll(); + + HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination( + ADDR_TV, + ADDR_AUDIO_SYSTEM); + + assertThat(mNativeWrapper.getResultMessages()).contains(requestArcTermination); + } } 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 b948fd0a590b9..1c33fb984ca04 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -145,6 +145,7 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.setPowerManager(mPowerManager); mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mHdmiControlServiceSpy.setAudioManager(mAudioManager); + mHdmiControlServiceSpy.setEarcSupported(true); mTestLooper.dispatchAll(); } @@ -1089,7 +1090,6 @@ public class HdmiControlServiceTest { @Test public void disableEarc_clearEarcLocalDevice() { - mHdmiControlServiceSpy.setEarcSupported(true); mHdmiControlServiceSpy.clearEarcLocalDevice(); mHdmiControlServiceSpy.addEarcLocalDevice( new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy)); @@ -1102,7 +1102,6 @@ public class HdmiControlServiceTest { @Test public void disableCec_doNotClearEarcLocalDevice() { - mHdmiControlServiceSpy.setEarcSupported(true); mHdmiControlServiceSpy.clearEarcLocalDevice(); mHdmiControlServiceSpy.addEarcLocalDevice( new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy)); @@ -1115,7 +1114,6 @@ public class HdmiControlServiceTest { @Test public void enableCec_initializeCecLocalDevices() { - mHdmiControlServiceSpy.setEarcSupported(true); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.setCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); mTestLooper.dispatchAll(); @@ -1127,7 +1125,6 @@ public class HdmiControlServiceTest { @Test public void enableEarc_initializeEarcLocalDevices() { - mHdmiControlServiceSpy.setEarcSupported(true); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.setEarcEnabled(HdmiControlManager.EARC_FEATURE_DISABLED); mTestLooper.dispatchAll(); @@ -1139,7 +1136,6 @@ public class HdmiControlServiceTest { @Test public void disableCec_DoNotInformHalAboutEarc() { - mHdmiControlServiceSpy.setEarcSupported(true); mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); @@ -1149,7 +1145,7 @@ public class HdmiControlServiceTest { HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(anyBoolean()); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(anyBoolean(), anyBoolean()); } @Test @@ -1157,15 +1153,14 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_ENABLED); - mHdmiControlServiceSpy.setEarcSupported(true); mTestLooper.dispatchAll(); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_DISABLED); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(false); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(true); + verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(false, false); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(eq(true), anyBoolean()); } @Test @@ -1173,14 +1168,13 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); - mHdmiControlServiceSpy.setEarcSupported(true); mTestLooper.dispatchAll(); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(anyBoolean()); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(anyBoolean(), anyBoolean()); } @Test @@ -1188,15 +1182,14 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_DISABLED); - mHdmiControlServiceSpy.setEarcSupported(true); mTestLooper.dispatchAll(); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_ENABLED); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(true); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(false); + verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(true, true); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(eq(false), anyBoolean()); } @Test @@ -1204,13 +1197,15 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_ENABLED); - mHdmiControlServiceSpy.setEarcSupported(true); + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); mTestLooper.dispatchAll(); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.initService(); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(true); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(false); + verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(true, false); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(eq(false), anyBoolean()); } @Test @@ -1218,13 +1213,12 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_DISABLED); - mHdmiControlServiceSpy.setEarcSupported(true); mTestLooper.dispatchAll(); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.initService(); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(false); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(true); + verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(false, false); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(eq(true), anyBoolean()); } @Test @@ -1232,13 +1226,12 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_ENABLED); - mHdmiControlServiceSpy.setEarcSupported(true); mTestLooper.dispatchAll(); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.onWakeUp(WAKE_UP_SCREEN_ON); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(true); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(false); + verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(true, false); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(eq(false), anyBoolean()); } @Test @@ -1246,18 +1239,16 @@ public class HdmiControlServiceTest { mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( HdmiControlManager.SETTING_NAME_EARC_ENABLED, HdmiControlManager.EARC_FEATURE_DISABLED); - mHdmiControlServiceSpy.setEarcSupported(true); mTestLooper.dispatchAll(); Mockito.clearInvocations(mHdmiControlServiceSpy); mHdmiControlServiceSpy.onWakeUp(WAKE_UP_SCREEN_ON); mTestLooper.dispatchAll(); - verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(false); - verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(true); + verify(mHdmiControlServiceSpy, times(1)).setEarcEnabledInHal(false, false); + verify(mHdmiControlServiceSpy, times(0)).setEarcEnabledInHal(eq(true), anyBoolean()); } @Test public void earcIdle_blocksArcConnection() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); localDeviceTx.handleEarcStateChange(Constants.HDMI_EARC_STATUS_IDLE); @@ -1267,7 +1258,6 @@ public class HdmiControlServiceTest { @Test public void earcPending_blocksArcConnection() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); localDeviceTx.handleEarcStateChange(Constants.HDMI_EARC_STATUS_EARC_PENDING); @@ -1277,7 +1267,6 @@ public class HdmiControlServiceTest { @Test public void earcEnabled_blocksArcConnection() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); localDeviceTx.handleEarcStateChange(Constants.HDMI_EARC_STATUS_EARC_CONNECTED); @@ -1287,7 +1276,6 @@ public class HdmiControlServiceTest { @Test public void arcPending_doesNotBlockArcConnection() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); localDeviceTx.handleEarcStateChange(Constants.HDMI_EARC_STATUS_ARC_PENDING); @@ -1297,7 +1285,6 @@ public class HdmiControlServiceTest { @Test public void earcStatusBecomesIdle_terminateArc() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); mHdmiControlServiceSpy.addEarcLocalDevice(localDeviceTx); @@ -1307,7 +1294,6 @@ public class HdmiControlServiceTest { @Test public void earcStatusBecomesEnabled_doNothing() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); mHdmiControlServiceSpy.addEarcLocalDevice(localDeviceTx); @@ -1317,7 +1303,6 @@ public class HdmiControlServiceTest { @Test public void earcStatusBecomesPending_doNothing() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); mHdmiControlServiceSpy.addEarcLocalDevice(localDeviceTx); @@ -1327,7 +1312,6 @@ public class HdmiControlServiceTest { @Test public void earcStatusBecomesNotEnabled_initiateArc() { - mHdmiControlServiceSpy.mEarcSupported = true; mHdmiControlServiceSpy.clearEarcLocalDevice(); HdmiEarcLocalDeviceTx localDeviceTx = new HdmiEarcLocalDeviceTx(mHdmiControlServiceSpy); mHdmiControlServiceSpy.addEarcLocalDevice(localDeviceTx);