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
This commit is contained in:
Nathalie Le Clair
2022-11-03 11:45:00 +01:00
parent c02f2fb278
commit 69c298b0e3
3 changed files with 98 additions and 44 deletions

View File

@@ -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 <Terminate ARC> 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

View File

@@ -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);
// <Report ARC Initiated> 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);
}
}

View File

@@ -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);