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

Commit 140ad1bc authored by Paul Colta's avatar Paul Colta
Browse files

HDMICEC: Avoid sending undesired <Active Source> messages

The bug occurs if a playback device wakes up from the standby state, trigerring CEC initialization,
when a message of type <Routing Change> or <Set Stream Path> is
requesting it to become the active source. Because the playback device
hasn't finished allocating LAs to local devices, the
ActiveSourceAction triggered by the previous message is deferred. Before
it has a LA allocated, the playback device ignores incoming messages of type <Routing Change> or <Set Stream Path>, not being aware of any active source changes.

This fix buffers the <Routing Change> and <Set Stream Path> messages.
After LAs allocation is done, the playback device can cancel any
queued ActiveSourceAction if there are any messages of these types or <Active Source> in
the buffer.

Bug: 235434112
Test: make && atest HdmiCecLocalDevicePlaybackTest
Change-Id: I318e090dcdb57ab023a1b401ec2503a21955007b
parent c8aca220
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -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++) {
+13 −2
Original line number Diff line number Diff line
@@ -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.
     */
+10 −1
Original line number Diff line number Diff line
@@ -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) {
+19 −0
Original line number Diff line number Diff line
@@ -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;
+7 −4
Original line number Diff line number Diff line
@@ -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);
Loading