Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e26f7f7 authored by Jinsuk Kim's avatar Jinsuk Kim
Browse files

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
parent d8e99c58
Loading
Loading
Loading
Loading
+9 −11
Original line number Diff line number Diff line
@@ -88,26 +88,24 @@ final class DelayedMessageBuffer {
     * Process messages from a given logical device. Called by
     * {@link NewDeviceAction} actions when they finish adding the device
     * information.
     * <p>&lt;Active Source&gt; is not processed in this method but processed
     * separately via {@link #processActiveSource()}.
     * <p>&lt;Active Source&gt; 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) {
            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();
        }
    }
    }

    /**
     * Process &lt;Active Source&gt;.
+11 −0
Original line number Diff line number Diff line
@@ -190,6 +190,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.
     *
+46 −2
Original line number Diff line number Diff line
@@ -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);