Merge "HDMI: Improve logic for sending and resending source changing messages" into main
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