Merge "HDMICEC: Avoid sending undesired <Active Source> messages"
This commit is contained in:
@@ -48,6 +48,12 @@ final class CecMessageBuffer {
|
||||
case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
|
||||
bufferSystemAudioModeRequest(message);
|
||||
return true;
|
||||
case Constants.MESSAGE_ROUTING_CHANGE:
|
||||
bufferRoutingChange(message);
|
||||
return true;
|
||||
case Constants.MESSAGE_SET_STREAM_PATH:
|
||||
bufferSetStreamPath(message);
|
||||
return true;
|
||||
// Add here if new message that needs to buffer
|
||||
default:
|
||||
// Do not need to buffer messages other than above
|
||||
@@ -89,6 +95,22 @@ final class CecMessageBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
private void bufferRoutingChange(HdmiCecMessage message) {
|
||||
if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ROUTING_CHANGE)) {
|
||||
mBuffer.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void bufferSetStreamPath(HdmiCecMessage message) {
|
||||
if (!replaceMessageIfBuffered(message, Constants.MESSAGE_SET_STREAM_PATH)) {
|
||||
mBuffer.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
public List<HdmiCecMessage> getBuffer() {
|
||||
return new ArrayList<>(mBuffer);
|
||||
}
|
||||
|
||||
// Returns true if the message is replaced
|
||||
private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) {
|
||||
for (int i = 0; i < mBuffer.size(); i++) {
|
||||
|
||||
@@ -147,6 +147,9 @@ final class HdmiCecController {
|
||||
|
||||
private final HdmiCecAtomWriter mHdmiCecAtomWriter;
|
||||
|
||||
// This variable is used for testing, in order to delay the logical address allocation.
|
||||
private long mLogicalAddressAllocationDelay = 0;
|
||||
|
||||
// Private constructor. Use HdmiCecController.create().
|
||||
private HdmiCecController(
|
||||
HdmiControlService service, NativeWrapper nativeWrapper, HdmiCecAtomWriter atomWriter) {
|
||||
@@ -215,12 +218,12 @@ final class HdmiCecController {
|
||||
final AllocateAddressCallback callback) {
|
||||
assertRunOnServiceThread();
|
||||
|
||||
runOnIoThread(new Runnable() {
|
||||
mIoHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
|
||||
}
|
||||
});
|
||||
}, mLogicalAddressAllocationDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,6 +388,14 @@ final class HdmiCecController {
|
||||
mNativeWrapperImpl.nativeSetLanguage(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used for testing, in order to delay the logical address allocation.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setLogicalAddressAllocationDelay(long delay) {
|
||||
mLogicalAddressAllocationDelay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the language code is well-formed.
|
||||
*/
|
||||
|
||||
@@ -649,6 +649,13 @@ abstract class HdmiCecLocalDevice {
|
||||
return Constants.NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after logical address allocation is finished, allowing a local device to react to
|
||||
* messages in the buffer before they are processed. This method may be used to cancel deferred
|
||||
* actions.
|
||||
*/
|
||||
protected void preprocessBufferedMessages(List<HdmiCecMessage> bufferedMessages) {}
|
||||
|
||||
@Constants.RcProfile
|
||||
protected abstract int getRcProfile();
|
||||
|
||||
@@ -963,8 +970,10 @@ abstract class HdmiCecLocalDevice {
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
final void handleAddressAllocated(int logicalAddress, int reason) {
|
||||
final void handleAddressAllocated(
|
||||
int logicalAddress, List<HdmiCecMessage> bufferedMessages, int reason) {
|
||||
assertRunOnServiceThread();
|
||||
preprocessBufferedMessages(bufferedMessages);
|
||||
mPreferredAddress = logicalAddress;
|
||||
updateDeviceFeatures();
|
||||
if (mService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
|
||||
|
||||
@@ -530,6 +530,25 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after logical address allocation is finished, allowing a local device to react to
|
||||
* messages in the buffer before they are processed. This method may be used to cancel deferred
|
||||
* actions.
|
||||
*/
|
||||
@Override
|
||||
protected void preprocessBufferedMessages(List<HdmiCecMessage> bufferedMessages) {
|
||||
for (HdmiCecMessage message: bufferedMessages) {
|
||||
// Prevent the device from broadcasting <Active Source> message if the active path
|
||||
// changed during address allocation.
|
||||
if (message.getOpcode() == Constants.MESSAGE_ROUTING_CHANGE
|
||||
|| message.getOpcode() == Constants.MESSAGE_SET_STREAM_PATH
|
||||
|| message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
|
||||
removeAction(ActiveSourceAction.class);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int findKeyReceiverAddress() {
|
||||
return Constants.ADDR_TV;
|
||||
|
||||
@@ -1079,9 +1079,10 @@ public class HdmiControlService extends SystemService {
|
||||
@ServiceThreadOnly
|
||||
private void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) {
|
||||
assertRunOnServiceThread();
|
||||
List<HdmiCecMessage> bufferedMessages = mCecMessageBuffer.getBuffer();
|
||||
for (HdmiCecLocalDevice device : devices) {
|
||||
int address = device.getDeviceInfo().getLogicalAddress();
|
||||
device.handleAddressAllocated(address, initiatedBy);
|
||||
device.handleAddressAllocated(address, bufferedMessages, initiatedBy);
|
||||
}
|
||||
if (isTvDeviceEnabled()) {
|
||||
tv().setSelectRequestBuffer(mSelectRequestBuffer);
|
||||
@@ -1361,8 +1362,9 @@ public class HdmiControlService extends SystemService {
|
||||
|
||||
@Constants.HandleMessageResult int handleMessageResult =
|
||||
dispatchMessageToLocalDevice(message);
|
||||
if (handleMessageResult == Constants.NOT_HANDLED
|
||||
&& !mAddressAllocated
|
||||
// mAddressAllocated is false during address allocation, meaning there is no device to
|
||||
// handle the message, so it should be buffered, if possible.
|
||||
if (!mAddressAllocated
|
||||
&& mCecMessageBuffer.bufferMessage(message)) {
|
||||
return Constants.HANDLED;
|
||||
}
|
||||
@@ -3286,7 +3288,8 @@ public class HdmiControlService extends SystemService {
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
private void onWakeUp(@WakeReason final int wakeUpAction) {
|
||||
@VisibleForTesting
|
||||
protected void onWakeUp(@WakeReason final int wakeUpAction) {
|
||||
assertRunOnServiceThread();
|
||||
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON,
|
||||
false);
|
||||
|
||||
@@ -47,7 +47,6 @@ import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SmallTest
|
||||
@@ -79,6 +78,7 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
private TestLooper mTestLooper = new TestLooper();
|
||||
private FakePowerManagerWrapper mPowerManager;
|
||||
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
|
||||
private ArrayList<Integer> mLocalDeviceTypes = new ArrayList<>();
|
||||
private int mPlaybackPhysicalAddress;
|
||||
private int mPlaybackLogicalAddress;
|
||||
private boolean mWokenUp;
|
||||
@@ -91,9 +91,11 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
mMyLooper = mTestLooper.getLooper();
|
||||
|
||||
mLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_PLAYBACK);
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
mLocalDeviceTypes, new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
|
||||
@Override
|
||||
void wakeUp() {
|
||||
mWokenUp = true;
|
||||
@@ -120,16 +122,6 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPowerStandby() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPowerStandbyOrTransient() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canGoToStandby() {
|
||||
return true;
|
||||
@@ -165,6 +157,7 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mNativeWrapper.clearResultMessages();
|
||||
mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
|
||||
HdmiProperties.playback_device_action_on_routing_control_values.NONE;
|
||||
mHdmiControlService.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1199,6 +1192,9 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
|
||||
mPlaybackPhysicalAddress);
|
||||
mHdmiCecLocalDevicePlayback.dispatchMessage(setStreamPath);
|
||||
// The ActiveSourceAction created from the message above is deferred until the device wakes
|
||||
// up.
|
||||
mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
|
||||
mTestLooper.dispatchAll();
|
||||
HdmiCecMessage activeSource =
|
||||
HdmiCecMessageBuilder.buildActiveSource(
|
||||
@@ -2088,4 +2084,111 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleRoutingChange_addressNotAllocated_removeActiveSourceAction() {
|
||||
long allocationDelay = TimeUnit.SECONDS.toMillis(60);
|
||||
mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
|
||||
HdmiProperties
|
||||
.playback_device_action_on_routing_control_values
|
||||
.WAKE_UP_AND_SEND_ACTIVE_SOURCE;
|
||||
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
|
||||
"HdmiCecLocalDevicePlaybackTest");
|
||||
HdmiCecMessage routingChangeToPlayback =
|
||||
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
|
||||
mPlaybackPhysicalAddress);
|
||||
HdmiCecMessage routingChangeToTv =
|
||||
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, mPlaybackPhysicalAddress,
|
||||
0x0000);
|
||||
HdmiCecMessage unexpectedMessage =
|
||||
HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
|
||||
mPlaybackPhysicalAddress);
|
||||
// 1. DUT goes to sleep.
|
||||
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
|
||||
// Delay allocate logical address in order to trigger message buffering.
|
||||
mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
|
||||
mNativeWrapper.onCecMessage(routingChangeToPlayback);
|
||||
mTestLooper.dispatchAll();
|
||||
// 2. DUT wakes up and defer ActiveSourceAction.
|
||||
mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mHdmiCecLocalDevicePlayback.getActions(ActiveSourceAction.class)).isNotEmpty();
|
||||
// 3. DUT buffers <Routing Change> message to TV.
|
||||
mNativeWrapper.onCecMessage(routingChangeToTv);
|
||||
mTestLooper.dispatchAll();
|
||||
// 4. Allocation is finished and the ActiveSourceAction is removed from the queue.
|
||||
// No <Active Source> message is sent by the DUT.
|
||||
mTestLooper.moveTimeForward(allocationDelay);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mHdmiCecLocalDevicePlayback.getActions(ActiveSourceAction.class)).isEmpty();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(unexpectedMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleSetStreamPath_addressNotAllocated_removeActiveSourceAction() {
|
||||
long allocationDelay = TimeUnit.SECONDS.toMillis(60);
|
||||
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
|
||||
"HdmiCecLocalDevicePlaybackTest");
|
||||
HdmiCecMessage setStreamPathToPlayback =
|
||||
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress);
|
||||
HdmiCecMessage setStreamPathToTv =
|
||||
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x0000);
|
||||
HdmiCecMessage unexpectedMessage =
|
||||
HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
|
||||
mPlaybackPhysicalAddress);
|
||||
// 1. DUT goes to sleep.
|
||||
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
|
||||
// Delay allocate logical address in order to trigger message buffering.
|
||||
mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
|
||||
mNativeWrapper.onCecMessage(setStreamPathToPlayback);
|
||||
mTestLooper.dispatchAll();
|
||||
// 2. DUT wakes up and defer ActiveSourceAction.
|
||||
mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mHdmiCecLocalDevicePlayback.getActions(ActiveSourceAction.class)).isNotEmpty();
|
||||
// 3. DUT buffers <Set Stream Path> message to TV.
|
||||
mNativeWrapper.onCecMessage(setStreamPathToTv);
|
||||
mTestLooper.dispatchAll();
|
||||
// 4. Allocation is finished and the ActiveSourceAction is removed from the queue.
|
||||
// No <Active Source> message is sent by the DUT.
|
||||
mTestLooper.moveTimeForward(allocationDelay);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mHdmiCecLocalDevicePlayback.getActions(ActiveSourceAction.class)).isEmpty();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(unexpectedMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleActiveSource_addressNotAllocated_removeActiveSourceAction() {
|
||||
long allocationDelay = TimeUnit.SECONDS.toMillis(60);
|
||||
|
||||
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
|
||||
"HdmiCecLocalDevicePlaybackTest");
|
||||
|
||||
HdmiCecMessage setStreamPathToPlayback =
|
||||
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress);
|
||||
HdmiCecMessage activeSourceFromTv =
|
||||
HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
|
||||
HdmiCecMessage unexpectedMessage =
|
||||
HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
|
||||
mPlaybackPhysicalAddress);
|
||||
// 1. DUT goes to sleep.
|
||||
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
|
||||
// Delay allocate logical address in order to trigger message buffering.
|
||||
mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
|
||||
mNativeWrapper.onCecMessage(setStreamPathToPlayback);
|
||||
mTestLooper.dispatchAll();
|
||||
// 2. DUT wakes up and defer ActiveSourceAction.
|
||||
mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mHdmiCecLocalDevicePlayback.getActions(ActiveSourceAction.class)).isNotEmpty();
|
||||
// 3. DUT buffers <Active Source> message from TV.
|
||||
mNativeWrapper.onCecMessage(activeSourceFromTv);
|
||||
mTestLooper.dispatchAll();
|
||||
// 4. Allocation is finished and the ActiveSourceAction is removed from the queue.
|
||||
// No <Active Source> message is sent by the DUT.
|
||||
mTestLooper.moveTimeForward(allocationDelay);
|
||||
mTestLooper.dispatchAll();
|
||||
assertThat(mHdmiCecLocalDevicePlayback.getActions(ActiveSourceAction.class)).isEmpty();
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(unexpectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user