HDMI: Improve logic for sending and resending source changing messages
Rename SendCecCommandAction to ResendCecCommandAction. The action is now only created if the first attempt to send the message fails. Use the action when sending a source changing message together with a callback. Test: atest com.android.server.hdmi && atest android.hdmicec.cts.playback.HdmiCecRoutingControlTest#cect_11_2_2_4_InactiveSourceOnStandby Bug: 289470244 Change-Id: Ib1b124751b9dbdd9ce3d2d1496c5451dc48b0309
This commit is contained in:
@@ -191,6 +191,11 @@ abstract class HdmiCecFeatureAction {
|
||||
mService.sendCecCommand(cmd, callback);
|
||||
}
|
||||
|
||||
protected final void sendCommandWithoutRetries(HdmiCecMessage cmd,
|
||||
HdmiControlService.SendMessageCallback callback) {
|
||||
mService.sendCecCommandWithoutRetries(cmd, callback);
|
||||
}
|
||||
|
||||
protected final void addAndStartAction(HdmiCecFeatureAction action) {
|
||||
mSource.addAndStartAction(action);
|
||||
}
|
||||
|
||||
@@ -1013,6 +1013,12 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
|
||||
assertRunOnServiceThread();
|
||||
mActions.add(action);
|
||||
if (mService.isPowerStandby() || !mService.isAddressAllocated()) {
|
||||
if (action.getClass() == ResendCecCommandAction.class) {
|
||||
Slog.i(TAG, "Not ready to start ResendCecCommandAction. "
|
||||
+ "This action is cancelled.");
|
||||
removeAction(action);
|
||||
return;
|
||||
}
|
||||
Slog.i(TAG, "Not ready to start action. Queued for deferred start:" + action);
|
||||
return;
|
||||
}
|
||||
@@ -1295,6 +1301,7 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
|
||||
removeAction(AbsoluteVolumeAudioStatusAction.class);
|
||||
removeAction(SetAudioVolumeLevelDiscoveryAction.class);
|
||||
removeAction(ActiveSourceAction.class);
|
||||
removeAction(ResendCecCommandAction.class);
|
||||
|
||||
mPendingActionClearedCallback =
|
||||
new PendingActionClearedCallback() {
|
||||
|
||||
@@ -1518,6 +1518,51 @@ public class HdmiControlService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
void sendCecCommand(HdmiCecMessage command) {
|
||||
sendCecCommand(command, null);
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
|
||||
switch (command.getOpcode()) {
|
||||
case Constants.MESSAGE_ACTIVE_SOURCE:
|
||||
case Constants.MESSAGE_IMAGE_VIEW_ON:
|
||||
case Constants.MESSAGE_INACTIVE_SOURCE:
|
||||
case Constants.MESSAGE_ROUTING_CHANGE:
|
||||
case Constants.MESSAGE_SET_STREAM_PATH:
|
||||
case Constants.MESSAGE_TEXT_VIEW_ON:
|
||||
sendCecCommandWithRetries(command, callback);
|
||||
break;
|
||||
default:
|
||||
sendCecCommandWithoutRetries(command, callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ResendCecCommandAction} that will retry sending the CEC message if it fails.
|
||||
* @param command command to be sent on the CEC bus.
|
||||
* @param callback callback for handling the result of sending the command.
|
||||
*/
|
||||
@ServiceThreadOnly
|
||||
private void sendCecCommandWithRetries(HdmiCecMessage command,
|
||||
@Nullable SendMessageCallback callback) {
|
||||
assertRunOnServiceThread();
|
||||
HdmiCecLocalDevice localDevice = getAllCecLocalDevices().get(0);
|
||||
if (localDevice != null) {
|
||||
sendCecCommandWithoutRetries(command, new SendMessageCallback() {
|
||||
@Override
|
||||
public void onSendCompleted(int result) {
|
||||
if (result != SendMessageResult.SUCCESS) {
|
||||
localDevice.addAndStartAction(new
|
||||
ResendCecCommandAction(localDevice, command, callback));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transmit a CEC command to CEC bus.
|
||||
*
|
||||
@@ -1525,7 +1570,8 @@ public class HdmiControlService extends SystemService {
|
||||
* @param callback interface used to the result of send command
|
||||
*/
|
||||
@ServiceThreadOnly
|
||||
void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
|
||||
void sendCecCommandWithoutRetries(HdmiCecMessage command,
|
||||
@Nullable SendMessageCallback callback) {
|
||||
assertRunOnServiceThread();
|
||||
if (command.getValidationResult() == HdmiCecMessageValidator.OK
|
||||
&& verifyPhysicalAddresses(command)) {
|
||||
@@ -1538,37 +1584,6 @@ public class HdmiControlService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
void sendCecCommand(HdmiCecMessage command) {
|
||||
assertRunOnServiceThread();
|
||||
switch (command.getOpcode()) {
|
||||
case Constants.MESSAGE_ACTIVE_SOURCE:
|
||||
case Constants.MESSAGE_IMAGE_VIEW_ON:
|
||||
case Constants.MESSAGE_INACTIVE_SOURCE:
|
||||
case Constants.MESSAGE_ROUTING_CHANGE:
|
||||
case Constants.MESSAGE_SET_STREAM_PATH:
|
||||
case Constants.MESSAGE_TEXT_VIEW_ON:
|
||||
sendCecCommandWithRetries(command);
|
||||
break;
|
||||
default:
|
||||
sendCecCommand(command, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SendCecCommandAction} that will retry to send the CEC message in case it
|
||||
* fails.
|
||||
* @param command that has to be sent in the CEC bus.
|
||||
*/
|
||||
@ServiceThreadOnly
|
||||
public void sendCecCommandWithRetries(HdmiCecMessage command) {
|
||||
assertRunOnServiceThread();
|
||||
HdmiCecLocalDevice localDevice = getAllCecLocalDevices().get(0);
|
||||
if (localDevice != null) {
|
||||
localDevice.addAndStartAction(new SendCecCommandAction(localDevice, command));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send <Feature Abort> command on the given CEC message if possible.
|
||||
* If the aborted message is invalid, then it wont send the message.
|
||||
|
||||
@@ -16,23 +16,29 @@
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import android.hardware.tv.cec.V1_0.SendMessageResult;
|
||||
import android.hardware.tv.hdmi.cec.SendMessageResult;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
|
||||
|
||||
/**
|
||||
* Action that sends a CEC command. If the message fails to be sent, it tries again for
|
||||
* RETRANSMISSION_COUNT times.
|
||||
* Action that retries RETRANSMISSION_COUNT times to send a CEC command when the first attempt to
|
||||
* send the message failed.
|
||||
*
|
||||
* This action starts with a delay of SEND_COMMAND_RETRY_MS milliseconds.
|
||||
*
|
||||
* If this action can't be started it will be canceled and not deferred.
|
||||
* See {@link HdmiCecLocalDevice#addAndStartAction}.
|
||||
*/
|
||||
public class SendCecCommandAction extends HdmiCecFeatureAction {
|
||||
private static final String TAG = "SendCecCommandAction";
|
||||
private static final int RETRANSMISSION_COUNT = 2;
|
||||
public class ResendCecCommandAction extends HdmiCecFeatureAction {
|
||||
private static final String TAG = "ResendCecCommandAction";
|
||||
private static final int RETRANSMISSION_COUNT = 1;
|
||||
private static final int STATE_WAIT_FOR_RESEND_COMMAND = 1;
|
||||
static final int SEND_COMMAND_RETRY_MS = 300;
|
||||
|
||||
private final HdmiCecMessage mCommand;
|
||||
private int mRetransmissionCount = 0;
|
||||
private final SendMessageCallback mResultCallback;
|
||||
private final SendMessageCallback mCallback = new SendMessageCallback(){
|
||||
@Override
|
||||
public void onSendCompleted(int result) {
|
||||
@@ -41,20 +47,26 @@ public class SendCecCommandAction extends HdmiCecFeatureAction {
|
||||
mState = STATE_WAIT_FOR_RESEND_COMMAND;
|
||||
addTimer(mState, SEND_COMMAND_RETRY_MS);
|
||||
} else {
|
||||
if (mResultCallback != null) {
|
||||
mResultCallback.onSendCompleted(result);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SendCecCommandAction(HdmiCecLocalDevice source, HdmiCecMessage command) {
|
||||
ResendCecCommandAction(HdmiCecLocalDevice source, HdmiCecMessage command,
|
||||
SendMessageCallback callback) {
|
||||
super(source);
|
||||
mCommand = command;
|
||||
mResultCallback = callback;
|
||||
mState = STATE_WAIT_FOR_RESEND_COMMAND;
|
||||
addTimer(mState, SEND_COMMAND_RETRY_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean start() {
|
||||
Slog.d(TAG, "SendCecCommandAction started");
|
||||
sendCommand(mCommand, mCallback);
|
||||
Slog.d(TAG, "ResendCecCommandAction started");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -66,8 +78,8 @@ public class SendCecCommandAction extends HdmiCecFeatureAction {
|
||||
return;
|
||||
}
|
||||
if (mState == STATE_WAIT_FOR_RESEND_COMMAND) {
|
||||
Slog.d(TAG, "sendCecCommand failed, retry");
|
||||
sendCommand(mCommand, mCallback);
|
||||
Slog.d(TAG, "sendCommandWithoutRetries failed, retry");
|
||||
sendCommandWithoutRetries(mCommand, mCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.SendCecCommandAction.SEND_COMMAND_RETRY_MS;
|
||||
import static com.android.server.hdmi.ResendCecCommandAction.SEND_COMMAND_RETRY_MS;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.hardware.tv.cec.V1_0.SendMessageResult;
|
||||
import android.os.Looper;
|
||||
@@ -38,11 +39,11 @@ import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/** Tests for {@link SendCecCommandAction} */
|
||||
/** Tests for {@link ResendCecCommandAction} */
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(JUnit4.class)
|
||||
public class SendCecCommandActionPlaybackTest {
|
||||
public class ResendCecCommandActionPlaybackTest {
|
||||
private static final String TAG = "SendCecCommandActionPlaybackTest";
|
||||
private HdmiControlService mHdmiControlService;
|
||||
private HdmiCecLocalDevice mPlaybackDevice;
|
||||
@@ -51,6 +52,7 @@ public class SendCecCommandActionPlaybackTest {
|
||||
private Looper mMyLooper;
|
||||
private TestLooper mTestLooper = new TestLooper();
|
||||
private int mPhysicalAddress;
|
||||
private boolean mIsPowerStandby;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -63,7 +65,7 @@ public class SendCecCommandActionPlaybackTest {
|
||||
|
||||
@Override
|
||||
boolean isPowerStandby() {
|
||||
return false;
|
||||
return mIsPowerStandby;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,6 +73,7 @@ public class SendCecCommandActionPlaybackTest {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
mIsPowerStandby = false;
|
||||
|
||||
mMyLooper = mTestLooper.getLooper();
|
||||
mHdmiControlService.setIoLooper(mMyLooper);
|
||||
@@ -148,6 +151,35 @@ public class SendCecCommandActionPlaybackTest {
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendCecCommand_inactiveSource_onStandby_powerControlModeNone_sendMessage() {
|
||||
mPlaybackDevice.mService.getHdmiCecConfig().setStringValue(
|
||||
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
|
||||
HdmiControlManager.POWER_CONTROL_MODE_NONE);
|
||||
mPlaybackDevice.setActiveSource(mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
|
||||
mPhysicalAddress, "SendCecCommandActionPlaybackTest");
|
||||
mIsPowerStandby = true;
|
||||
mPlaybackDevice.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
|
||||
mTestLooper.dispatchAll();
|
||||
HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
|
||||
mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendCecCommand_inactiveSource_onStandby_initiatedByCec_sendMessage() {
|
||||
mPlaybackDevice.setActiveSource(mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
|
||||
mPhysicalAddress, "SendCecCommandActionPlaybackTest");
|
||||
mIsPowerStandby = true;
|
||||
mPlaybackDevice.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
|
||||
mTestLooper.dispatchAll();
|
||||
HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
|
||||
mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendCecCommand_routingChange_sendMessageFails_resendMessage() {
|
||||
mNativeWrapper.setMessageSendResult(Constants.MESSAGE_ROUTING_CHANGE,
|
||||
@@ -293,4 +325,54 @@ public class SendCecCommandActionPlaybackTest {
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPhysicalAddress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendCecCommand_inactiveSource_sendMessageFails_afterStandby_noResendMessage() {
|
||||
mNativeWrapper.setMessageSendResult(Constants.MESSAGE_INACTIVE_SOURCE,
|
||||
SendMessageResult.BUSY);
|
||||
mPlaybackDevice.setActiveSource(mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
|
||||
mPhysicalAddress, "SendCecCommandActionPlaybackTest");
|
||||
mIsPowerStandby = true;
|
||||
mPlaybackDevice.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
|
||||
mTestLooper.dispatchAll();
|
||||
HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
|
||||
mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);
|
||||
|
||||
mNativeWrapper.clearResultMessages();
|
||||
mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
|
||||
|
||||
mTestLooper.moveTimeForward(SEND_COMMAND_RETRY_MS);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendCecCommand_onStandby_removeAction_noResendMessage() {
|
||||
mNativeWrapper.setMessageSendResult(Constants.MESSAGE_INACTIVE_SOURCE,
|
||||
SendMessageResult.BUSY);
|
||||
mTestLooper.dispatchAll();
|
||||
HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
|
||||
mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
|
||||
mHdmiControlService.sendCecCommand(inactiveSourceMessage);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);
|
||||
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
|
||||
|
||||
mNativeWrapper.clearResultMessages();
|
||||
mTestLooper.moveTimeForward(SEND_COMMAND_RETRY_MS);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
|
||||
|
||||
mTestLooper.moveTimeForward(SEND_COMMAND_RETRY_MS);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.SendCecCommandAction.SEND_COMMAND_RETRY_MS;
|
||||
import static com.android.server.hdmi.ResendCecCommandAction.SEND_COMMAND_RETRY_MS;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
@@ -38,11 +38,11 @@ import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/** Tests for {@link SendCecCommandAction} */
|
||||
/** Tests for {@link ResendCecCommandAction} */
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(JUnit4.class)
|
||||
public class SendCecCommandActionTvTest {
|
||||
public class ResendCecCommandActionTvTest {
|
||||
private static final String TAG = "SendCecCommandActionTvTest";
|
||||
private HdmiControlService mHdmiControlService;
|
||||
private HdmiCecLocalDevice mTvDevice;
|
||||
Reference in New Issue
Block a user