From 964c00dd7b270dcf80aea3450bbfc23502965cce Mon Sep 17 00:00:00 2001 From: Jinsuk Kim Date: Fri, 16 Jan 2015 15:20:17 +0900 Subject: [PATCH] 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 --- .../android/server/hdmi/DelayedMessageBuffer.java | 30 ++++++--- .../android/server/hdmi/HdmiControlService.java | 73 ++++++++++++++++++++++ 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java index d3ecff0f188a..c908145a6c72 100644 --- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java +++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java @@ -77,11 +77,13 @@ final class DelayedMessageBuffer { } void processAllMessages() { - for (HdmiCecMessage message : mBuffer) { + // Use the copied buffer. + ArrayList copiedBuffer = new ArrayList(mBuffer); + mBuffer.clear(); + for (HdmiCecMessage message : copiedBuffer) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); } - mBuffer.clear(); } /** @@ -95,15 +97,21 @@ final class DelayedMessageBuffer { * are associated with */ void processMessagesForDevice(int address) { + ArrayList copiedBuffer = new ArrayList(mBuffer); + mBuffer.clear(); HdmiLogger.debug("Checking message for address:" + address); - for (Iterator iter = mBuffer.iterator(); iter.hasNext(); ) { - HdmiCecMessage message = iter.next(); - if (message.getSource() != address) continue; + for (HdmiCecMessage message : copiedBuffer) { + if (message.getSource() != address) { + mBuffer.add(message); + continue; + } 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); 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 */ void processActiveSource(int address) { - for (Iterator iter = mBuffer.iterator(); iter.hasNext(); ) { - HdmiCecMessage message = iter.next(); + ArrayList copiedBuffer = new ArrayList(mBuffer); + mBuffer.clear(); + for (HdmiCecMessage message : copiedBuffer) { if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE && message.getSource() == address) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); - iter.remove(); + } else { + mBuffer.add(message); } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index cc8c53c653f2..37909cca4887 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -286,6 +286,69 @@ public final class HdmiControlService extends SystemService { @ServiceThreadOnly 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 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) { super(context); mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE)); @@ -474,6 +537,7 @@ public final class HdmiControlService extends SystemService { } private void initializeCec(int initiatedBy) { + mAddressAllocated = false; mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED); initializeLocalDevices(initiatedBy); } @@ -504,6 +568,8 @@ public final class HdmiControlService extends SystemService { mCecController.clearLogicalAddress(); final ArrayList allocatedDevices = new ArrayList<>(); final int[] finished = new int[1]; + mAddressAllocated = allocatingDevices.isEmpty(); + for (final HdmiCecLocalDevice localDevice : allocatingDevices) { mCecController.allocateLogicalAddress(localDevice.getType(), localDevice.getPreferredAddress(), new AllocateAddressCallback() { @@ -524,12 +590,14 @@ public final class HdmiControlService extends SystemService { // Address allocation completed for all devices. Notify each device. if (allocatingDevices.size() == ++finished[0]) { + mAddressAllocated = true; if (initiatedBy != INITIATED_BY_HOTPLUG) { // In case of the hotplug we don't call onInitializeCecComplete() // since we reallocate the logical address only. onInitializeCecComplete(initiatedBy); } notifyAddressAllocated(allocatedDevices, initiatedBy); + mCecMessageBuffer.processMessages(); } } }); @@ -762,6 +830,10 @@ public final class HdmiControlService extends SystemService { @ServiceThreadOnly boolean handleCecCommand(HdmiCecMessage message) { assertRunOnServiceThread(); + if (!mAddressAllocated) { + mCecMessageBuffer.bufferMessage(message); + return true; + } int errorCode = mMessageValidator.isValid(message); if (errorCode != HdmiCecMessageValidator.OK) { // 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); } mStandbyMessageReceived = false; + mAddressAllocated = false; mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED); } -- 2.11.0