CEC: Buffer Cec messages while allocating logical address

CEC messages arriving before logical address allocation was being
discarded. Handles them by introducing a message buffer class
to keep them till the address allocation is finished.

Also updated per-device message buffer to use copied version of
messages list for iterating to avoid the possible concurrent
modification exception.

Bug: 18896770
Change-Id: Ifb74fd265510de6dde322e0b3bc5b504fecb4daa
This commit is contained in:
Jinsuk Kim
2015-01-16 15:20:17 +09:00
parent faf92d7256
commit 964c00dd7b
2 changed files with 93 additions and 10 deletions

View File

@@ -77,11 +77,13 @@ final class DelayedMessageBuffer {
} }
void processAllMessages() { void processAllMessages() {
for (HdmiCecMessage message : mBuffer) { // Use the copied buffer.
ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
mBuffer.clear();
for (HdmiCecMessage message : copiedBuffer) {
mDevice.onMessage(message); mDevice.onMessage(message);
HdmiLogger.debug("Processing message:" + message); HdmiLogger.debug("Processing message:" + message);
} }
mBuffer.clear();
} }
/** /**
@@ -95,15 +97,21 @@ final class DelayedMessageBuffer {
* are associated with * are associated with
*/ */
void processMessagesForDevice(int address) { void processMessagesForDevice(int address) {
ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
mBuffer.clear();
HdmiLogger.debug("Checking message for address:" + address); HdmiLogger.debug("Checking message for address:" + address);
for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { for (HdmiCecMessage message : copiedBuffer) {
HdmiCecMessage message = iter.next(); if (message.getSource() != address) {
if (message.getSource() != address) continue; mBuffer.add(message);
continue;
}
if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
&& !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) continue; && !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) {
mBuffer.add(message);
continue;
}
mDevice.onMessage(message); mDevice.onMessage(message);
HdmiLogger.debug("Processing message:" + message); HdmiLogger.debug("Processing message:" + message);
iter.remove();
} }
} }
@@ -119,13 +127,15 @@ final class DelayedMessageBuffer {
* @param address logical address of the device to be the active source * @param address logical address of the device to be the active source
*/ */
void processActiveSource(int address) { void processActiveSource(int address) {
for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
HdmiCecMessage message = iter.next(); mBuffer.clear();
for (HdmiCecMessage message : copiedBuffer) {
if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
&& message.getSource() == address) { && message.getSource() == address) {
mDevice.onMessage(message); mDevice.onMessage(message);
HdmiLogger.debug("Processing message:" + message); HdmiLogger.debug("Processing message:" + message);
iter.remove(); } else {
mBuffer.add(message);
} }
} }
} }

View File

@@ -286,6 +286,69 @@ public final class HdmiControlService extends SystemService {
@ServiceThreadOnly @ServiceThreadOnly
private int mLastInputMhl = Constants.INVALID_PORT_ID; private int mLastInputMhl = Constants.INVALID_PORT_ID;
// Set to true if the logical address allocation is completed.
private boolean mAddressAllocated = false;
// Buffer for processing the incoming cec messages while allocating logical addresses.
private final class CecMessageBuffer {
private List<HdmiCecMessage> mBuffer = new ArrayList<>();
public void bufferMessage(HdmiCecMessage message) {
switch (message.getOpcode()) {
case Constants.MESSAGE_ACTIVE_SOURCE:
bufferActiveSource(message);
break;
case Constants.MESSAGE_IMAGE_VIEW_ON:
case Constants.MESSAGE_TEXT_VIEW_ON:
bufferImageOrTextViewOn(message);
break;
// Add here if new message that needs to buffer
default:
// Do not need to buffer messages other than above
break;
}
}
public void processMessages() {
for (final HdmiCecMessage message : mBuffer) {
runOnServiceThread(new Runnable() {
@Override
public void run() {
handleCecCommand(message);
}
});
}
mBuffer.clear();
}
private void bufferActiveSource(HdmiCecMessage message) {
if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) {
mBuffer.add(message);
}
}
private void bufferImageOrTextViewOn(HdmiCecMessage message) {
if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) &&
!replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) {
mBuffer.add(message);
}
}
// Returns true if the message is replaced
private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) {
for (int i = 0; i < mBuffer.size(); i++) {
HdmiCecMessage bufferedMessage = mBuffer.get(i);
if (bufferedMessage.getOpcode() == opcode) {
mBuffer.set(i, message);
return true;
}
}
return false;
}
}
private CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer();
public HdmiControlService(Context context) { public HdmiControlService(Context context) {
super(context); super(context);
mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE)); mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
@@ -474,6 +537,7 @@ public final class HdmiControlService extends SystemService {
} }
private void initializeCec(int initiatedBy) { private void initializeCec(int initiatedBy) {
mAddressAllocated = false;
mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED); mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
initializeLocalDevices(initiatedBy); initializeLocalDevices(initiatedBy);
} }
@@ -504,6 +568,8 @@ public final class HdmiControlService extends SystemService {
mCecController.clearLogicalAddress(); mCecController.clearLogicalAddress();
final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>(); final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>();
final int[] finished = new int[1]; final int[] finished = new int[1];
mAddressAllocated = allocatingDevices.isEmpty();
for (final HdmiCecLocalDevice localDevice : allocatingDevices) { for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
mCecController.allocateLogicalAddress(localDevice.getType(), mCecController.allocateLogicalAddress(localDevice.getType(),
localDevice.getPreferredAddress(), new AllocateAddressCallback() { localDevice.getPreferredAddress(), new AllocateAddressCallback() {
@@ -524,12 +590,14 @@ public final class HdmiControlService extends SystemService {
// Address allocation completed for all devices. Notify each device. // Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) { if (allocatingDevices.size() == ++finished[0]) {
mAddressAllocated = true;
if (initiatedBy != INITIATED_BY_HOTPLUG) { if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call onInitializeCecComplete() // In case of the hotplug we don't call onInitializeCecComplete()
// since we reallocate the logical address only. // since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy); onInitializeCecComplete(initiatedBy);
} }
notifyAddressAllocated(allocatedDevices, initiatedBy); notifyAddressAllocated(allocatedDevices, initiatedBy);
mCecMessageBuffer.processMessages();
} }
} }
}); });
@@ -762,6 +830,10 @@ public final class HdmiControlService extends SystemService {
@ServiceThreadOnly @ServiceThreadOnly
boolean handleCecCommand(HdmiCecMessage message) { boolean handleCecCommand(HdmiCecMessage message) {
assertRunOnServiceThread(); assertRunOnServiceThread();
if (!mAddressAllocated) {
mCecMessageBuffer.bufferMessage(message);
return true;
}
int errorCode = mMessageValidator.isValid(message); int errorCode = mMessageValidator.isValid(message);
if (errorCode != HdmiCecMessageValidator.OK) { if (errorCode != HdmiCecMessageValidator.OK) {
// We'll not response on the messages with the invalid source or destination // We'll not response on the messages with the invalid source or destination
@@ -1990,6 +2062,7 @@ public final class HdmiControlService extends SystemService {
device.onStandby(mStandbyMessageReceived); device.onStandby(mStandbyMessageReceived);
} }
mStandbyMessageReceived = false; mStandbyMessageReceived = false;
mAddressAllocated = false;
mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED); mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
} }