HDMI: More reliable DSM setting disabling

Wait for ArcTerminationActionFromAvr to finish before giving up the LA 5
during Dynamic Soundbar mode disabling process.

Add callback inside ArcTerminationActionFromAvr.

Transfer the callback in HdmiCecLocalDeviceAudioSystem#handleRequestArcTermination so it doesn't get lost.

Callback is not transfered in the following situation, in HdmiCecLocalDeviceAudioSystem#initArcOnFromAvr ARC termination is interrupted by ARC initialization from AVR. This scenario would occur if user toggles DSM off then on before ARC termination process is completed, then the local device audio system tries establishing ARC connection when logical address allocation finished. In this case, DSM would be enabled and the callback from ARC termination action that retriggers logical address allocation wouldn't be necessary.

Bug: 260322563
Test: make && atest com.android.server.hdmi.HdmiControlServiceTest
Change-Id: Ib0a8b3a7da79da8cf3f9724a685a55c80760a755
This commit is contained in:
Paul Colta
2022-11-28 13:54:07 +01:00
parent 4ae8fc3e60
commit 753fe4ea90
6 changed files with 126 additions and 11 deletions

View File

@@ -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 <Report ARC Terminated> in time.
audioSystem().setArcStatus(false);
HdmiLogger.debug("handleTerminateArcTimeout");
finish();
finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
}
}

View File

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

View File

@@ -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);
}
/**

View File

@@ -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<Integer> mCallbackResult = new ArrayList<Integer>();
@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);
}
}

View File

@@ -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<Integer> mCallbackResult = new ArrayList<Integer>();
@Override
public void onComplete(int result) {
mCallbackResult.add(result);
}
}
}

View File

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