diff options
| author | 2015-01-07 16:45:14 +0900 | |
|---|---|---|
| committer | 2015-01-08 12:51:22 +0900 | |
| commit | 6e26f7f7b09dfd8495ec5478a7a4713dab346bc1 (patch) | |
| tree | 9ad2104ed87d2b49ae5fe9189171ef1f36f22b77 | |
| parent | d8e99c5861b09de8f709c9284240e2c3115507bb (diff) | |
CEC: Handle <Active Source> arriving before TvInputCallback.onAddInput
Resolves a timing-related bug that fails to process the command
<Active Source> at TV boot up. Checks if TV input required for
the command is ready to accept the request. If not, makes sure
the command is buffered, and processed later when the input is
ready.
Bug: 18896770
Change-Id: Id17e5e8468519b17daf61c962dd718ccc56fb0ea
3 files changed, 66 insertions, 13 deletions
diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java index 922a2c14c4cc..d3ecff0f188a 100644 --- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java +++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java @@ -88,24 +88,22 @@ final class DelayedMessageBuffer { * Process messages from a given logical device. Called by * {@link NewDeviceAction} actions when they finish adding the device * information. - * <p><Active Source> is not processed in this method but processed - * separately via {@link #processActiveSource()}. + * <p><Active Source> is processed only when the TV input is ready. + * If not, {@link #processActiveSource()} will be invoked later to handle it. * * @param address logical address of CEC device which the messages to process * are associated with */ void processMessagesForDevice(int address) { - HdmiLogger.debug("Processing message for address:" + address); + HdmiLogger.debug("Checking message for address:" + address); for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { HdmiCecMessage message = iter.next(); - if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) { - continue; - } - if (message.getSource() == address) { - mDevice.onMessage(message); - HdmiLogger.debug("Processing message:" + message); - iter.remove(); - } + if (message.getSource() != address) continue; + if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE + && !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) continue; + mDevice.onMessage(message); + HdmiLogger.debug("Processing message:" + message); + iter.remove(); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 4f8b9fbc71f5..1b478885e9cb 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -191,6 +191,17 @@ abstract class HdmiCecLocalDevice { protected abstract void setPreferredAddress(int addr); /** + * Returns true if the TV input associated with the CEC device is ready + * to accept further processing such as input switching. This is used + * to buffer certain CEC commands and process it later if the input is not + * ready yet. For other types of local devices(non-TV), this method returns + * true by default to let the commands be processed right away. + */ + protected boolean isInputReady(int deviceId) { + return true; + } + + /** * Dispatch incoming message. * * @param message incoming message diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 90dbd1765200..a8941619dc2d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -63,6 +63,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.HashMap; /** * Represent a logical device of type TV residing in Android system. @@ -143,12 +144,44 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { public void onInputAdded(String inputId) { TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId); HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo(); - if (info != null && info.isCecDevice()) { - mDelayedMessageBuffer.processActiveSource(info.getLogicalAddress()); + if (info == null) return; + addTvInput(inputId, info.getId()); + if (info.isCecDevice()) { + processDelayedActiveSource(info.getLogicalAddress()); } } + + @Override + public void onInputRemoved(String inputId) { + removeTvInput(inputId); + } }; + // Keeps the mapping (TV input ID, HDMI device ID) to keep track of the TV inputs ready to + // accept input switching request from HDMI devices. Requests for which the corresponding + // input ID is not yet registered by TV input framework need to be buffered for delayed + // processing. + private final HashMap<String, Integer> mTvInputs = new HashMap<>(); + + @ServiceThreadOnly + private void addTvInput(String inputId, int deviceId) { + assertRunOnServiceThread(); + mTvInputs.put(inputId, deviceId); + } + + @ServiceThreadOnly + private void removeTvInput(String inputId) { + assertRunOnServiceThread(); + mTvInputs.remove(inputId); + } + + @Override + @ServiceThreadOnly + protected boolean isInputReady(int deviceId) { + assertRunOnServiceThread(); + return mTvInputs.containsValue(deviceId); + } + HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_TV); mPrevPortId = Constants.INVALID_PORT_ID; @@ -168,6 +201,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( mAddress, mService.getVendorId())); mCecSwitches.add(mService.getPhysicalAddress()); // TV is a CEC switch too. + mTvInputs.clear(); mSkipRoutingControl = (reason == HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE); launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC && reason != HdmiControlService.INITIATED_BY_BOOT_UP); @@ -447,8 +481,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); if (info == null) { if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) { + HdmiLogger.debug("Device info not found: %X; buffering the command", logicalAddress); mDelayedMessageBuffer.add(message); } + } else if (!isInputReady(info.getId())) { + HdmiLogger.debug("Input not ready for device: %X; buffering the command", info.getId()); + mDelayedMessageBuffer.add(message); } else { ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); @@ -1776,6 +1814,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mDelayedMessageBuffer.processMessagesForDevice(address); } + @ServiceThreadOnly + void processDelayedActiveSource(int address) { + assertRunOnServiceThread(); + mDelayedMessageBuffer.processActiveSource(address); + } + @Override protected void dump(final IndentingPrintWriter pw) { super.dump(pw); |