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

Commit 18d9609d authored by Paul Colta's avatar Paul Colta
Browse files

HDMI: Improve RequestActiveSourceAction

When the user wakes up the TV using its own remote, a CEC message will be sent to switch to the expected source (the TV's internal source or another HDMI input). The process to send this message is triggered from LauncherX, outside the framework. With this change, the action to send <Request Active Source> is delayed to in order to wait for a possible LauncherX's request and can be cancelled if an <Active Source> message was received or the TV switched to another input.

Test: atest HdmiCecLocalDeviceTvTest && manual tests
Bug: 330615500
Change-Id: I1936bd28ffe7d6bc5d536b5034233650944b0cfa
parent f1413f13
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -206,6 +206,10 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        launchDeviceDiscovery();
        startQueuedActions();
        if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
            if (hasAction(RequestActiveSourceAction.class)) {
                Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting.");
                removeAction(RequestActiveSourceAction.class);
            }
            addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
                @Override
                public void onComplete(int result) {
@@ -1308,6 +1312,8 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                mService.sendCecCommand(
                        HdmiCecMessageBuilder.buildActiveSource(
                                getDeviceInfo().getLogicalAddress(), activePath));
                updateActiveSource(getDeviceInfo().getLogicalAddress(), activePath,
                        "HdmiCecLocalDeviceTv#launchRoutingControl()");
            }
        }
    }
+7 −0
Original line number Diff line number Diff line
@@ -1645,6 +1645,13 @@ public class HdmiControlService extends SystemService {
            case Constants.MESSAGE_ROUTING_CHANGE:
            case Constants.MESSAGE_SET_STREAM_PATH:
            case Constants.MESSAGE_TEXT_VIEW_ON:
                // RequestActiveSourceAction is started after the TV finished logical address
                // allocation. This action is used by the TV to get the active source from the CEC
                // network. If the TV sent a source changing CEC message, this action does not have
                // to continue anymore.
                if (isTvDevice()) {
                    tv().removeAction(RequestActiveSourceAction.class);
                }
                sendCecCommandWithRetries(command, callback);
                break;
            default:
+29 −10
Original line number Diff line number Diff line
@@ -21,13 +21,20 @@ import android.hardware.hdmi.IHdmiControlCallback;
import android.util.Slog;

/**
 * Feature action that sends <Request Active Source> message and waits for <Active Source>.
 * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV
 * panels.
 * This action has a delay before sending <Request Active Source>. This is because it should wait
 * for a possible request from LauncherX and can be cancelled if an <Active Source> message was
 * received or the TV switched to another input.
 */
public class RequestActiveSourceAction extends HdmiCecFeatureAction {
    private static final String TAG = "RequestActiveSourceAction";

    // State to wait for the LauncherX to call the CEC API.
    private static final int STATE_WAIT_FOR_LAUNCHERX_API_CALL = 1;

    // State to wait for the <Active Source> message.
    private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1;
    private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 2;

    // Number of retries <Request Active Source> is sent if no device answers this message.
    private static final int MAX_SEND_RETRY_COUNT = 1;
@@ -43,10 +50,12 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction {
    boolean start() {
        Slog.v(TAG, "RequestActiveSourceAction started.");

        sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
        mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL;

        mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
        addTimer(mState, HdmiConfig.TIMEOUT_MS);
        // We wait for default timeout to allow the message triggered by the LauncherX API call to
        // be sent by the TV and another default timeout in case the message has to be answered
        // (e.g. TV sent a <Set Stream Path> or <Routing Change>).
        addTimer(mState, HdmiConfig.TIMEOUT_MS * 2);
        return true;
    }

@@ -65,13 +74,23 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction {
        if (mState != state) {
            return;
        }
        if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) {

        switch (mState) {
            case STATE_WAIT_FOR_LAUNCHERX_API_CALL:
                mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
                sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
                addTimer(mState, HdmiConfig.TIMEOUT_MS);
                return;
            case STATE_WAIT_FOR_ACTIVE_SOURCE:
                if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) {
                    sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
                    addTimer(mState, HdmiConfig.TIMEOUT_MS);
                } else {
                    finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
                }
                return;
            default:
                return;
        }
    }
}
+96 −9
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE;
import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;

import static com.google.common.truth.Truth.assertThat;

@@ -1787,9 +1789,17 @@ public class HdmiCecLocalDeviceTvTest {
        HdmiCecMessage activeSourceFromTv =
                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);

        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
        // Go to standby to invalidate the active source on the local device s.t. the
        // RequestActiveSourceAction will start.
        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();

        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();

        // Skip the LauncherX API timeout.
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
        mTestLooper.dispatchAll();

        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1798,6 +1808,10 @@ public class HdmiCecLocalDeviceTvTest {
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
        mTestLooper.dispatchAll();

        // Assume there was a retry and the action did not finish earlier.
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
        mTestLooper.dispatchAll();

        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
    }

@@ -1807,9 +1821,18 @@ public class HdmiCecLocalDeviceTvTest {
                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
        HdmiCecMessage activeSourceFromTv =
                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();

        // Go to standby to invalidate the active source on the local device s.t. the
        // RequestActiveSourceAction will start.
        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();

        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();

        // Skip the LauncherX API timeout.
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
        mTestLooper.dispatchAll();

        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1834,8 +1857,18 @@ public class HdmiCecLocalDeviceTvTest {
                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
        HdmiCecMessage activeSourceFromTv =
                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);

        // Go to standby to invalidate the active source on the local device s.t. the
        // RequestActiveSourceAction will start.
        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();

        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();

        // Skip the LauncherX API timeout.
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
        mTestLooper.dispatchAll();

        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1854,8 +1887,16 @@ public class HdmiCecLocalDeviceTvTest {
                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
        HdmiCecMessage activeSourceFromTv =
                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);

        // Go to standby to invalidate the active source on the local device s.t. the
        // RequestActiveSourceAction will start.
        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();

        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();

        HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder()
                .setLogicalAddress(ADDR_PLAYBACK_1)
                .setPhysicalAddress(0x1000)
@@ -1869,6 +1910,10 @@ public class HdmiCecLocalDeviceTvTest {
        mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice);
        mTestLooper.dispatchAll();

        // Skip the LauncherX API timeout.
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
        mTestLooper.dispatchAll();

        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
        mNativeWrapper.clearResultMessages();
        mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null);
@@ -1881,6 +1926,41 @@ public class HdmiCecLocalDeviceTvTest {
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
    }

    @Test
    public void onAddressAllocated_sendSourceChangingMessage_noRequestActiveSourceMessage() {
        HdmiCecMessage requestActiveSource =
                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
        HdmiCecMessage activeSourceFromTv =
                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
        HdmiCecMessage setStreamPathFromTv =
                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2000);

        // Go to standby to invalidate the active source on the local device s.t. the
        // RequestActiveSourceAction will start.
        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();

        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();

        // Even if the device at the end of this path doesn't answer to this message, TV should not
        // continue the RequestActiveSourceAction.
        mHdmiControlService.sendCecCommand(setStreamPathFromTv);

        // Skip the LauncherX API timeout.
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
        mTestLooper.dispatchAll();

        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource);
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
        mTestLooper.dispatchAll();

        // Assume there was a retry and the action did not finish earlier.
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
        mTestLooper.dispatchAll();

        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
    }

    @Test
    public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() {
@@ -1907,7 +1987,12 @@ public class HdmiCecLocalDeviceTvTest {
        HdmiCecMessage activeSourceFromTv =
                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);

        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_WAKE_UP_MESSAGE);
        // Go to standby to invalidate the active source on the local device s.t. the
        // TV will send <Active Source> when it selects its internal source.
        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();

        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();

        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
@@ -1937,6 +2022,8 @@ public class HdmiCecLocalDeviceTvTest {
    public void handleStandby_fromActiveSource_standby() {
        mPowerManager.setInteractive(true);
        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
        mTestLooper.dispatchAll();

        mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000,
                "HdmiCecLocalDeviceTvTest");
        mTestLooper.dispatchAll();