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

Commit a443087f authored by Paul Colța's avatar Paul Colța Committed by Android (Google) Code Review
Browse files

Merge "HDMI: Improve logic for sending and resending source changing messages" into main

parents e6c351c9 307ec6b6
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -191,6 +191,11 @@ abstract class HdmiCecFeatureAction {
        mService.sendCecCommand(cmd, callback);
    }

    protected final void sendCommandWithoutRetries(HdmiCecMessage cmd,
            HdmiControlService.SendMessageCallback callback) {
        mService.sendCecCommandWithoutRetries(cmd, callback);
    }

    protected final void addAndStartAction(HdmiCecFeatureAction action) {
        mSource.addAndStartAction(action);
    }
+7 −0
Original line number Diff line number Diff line
@@ -1013,6 +1013,12 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
        assertRunOnServiceThread();
        mActions.add(action);
        if (mService.isPowerStandby() || !mService.isAddressAllocated()) {
            if (action.getClass() == ResendCecCommandAction.class) {
                Slog.i(TAG, "Not ready to start ResendCecCommandAction. "
                        + "This action is cancelled.");
                removeAction(action);
                return;
            }
            Slog.i(TAG, "Not ready to start action. Queued for deferred start:" + action);
            return;
        }
@@ -1295,6 +1301,7 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
        removeAction(AbsoluteVolumeAudioStatusAction.class);
        removeAction(SetAudioVolumeLevelDiscoveryAction.class);
        removeAction(ActiveSourceAction.class);
        removeAction(ResendCecCommandAction.class);

        mPendingActionClearedCallback =
                new PendingActionClearedCallback() {
+41 −26
Original line number Diff line number Diff line
@@ -1518,29 +1518,13 @@ public class HdmiControlService extends SystemService {
        }
    }

    /**
     * Transmit a CEC command to CEC bus.
     *
     * @param command CEC command to send out
     * @param callback interface used to the result of send command
     */
    @ServiceThreadOnly
    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
        assertRunOnServiceThread();
        if (command.getValidationResult() == HdmiCecMessageValidator.OK
                && verifyPhysicalAddresses(command)) {
            mCecController.sendCommand(command, callback);
        } else {
            HdmiLogger.error("Invalid message type:" + command);
            if (callback != null) {
                callback.onSendCompleted(SendMessageResult.FAIL);
            }
        }
    void sendCecCommand(HdmiCecMessage command) {
        sendCecCommand(command, null);
    }

    @ServiceThreadOnly
    void sendCecCommand(HdmiCecMessage command) {
        assertRunOnServiceThread();
    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
        switch (command.getOpcode()) {
            case Constants.MESSAGE_ACTIVE_SOURCE:
            case Constants.MESSAGE_IMAGE_VIEW_ON:
@@ -1548,24 +1532,55 @@ public class HdmiControlService extends SystemService {
            case Constants.MESSAGE_ROUTING_CHANGE:
            case Constants.MESSAGE_SET_STREAM_PATH:
            case Constants.MESSAGE_TEXT_VIEW_ON:
                sendCecCommandWithRetries(command);
                sendCecCommandWithRetries(command, callback);
                break;
            default:
                sendCecCommand(command, null);
                sendCecCommandWithoutRetries(command, callback);
        }
    }

    /**
     * Create a {@link SendCecCommandAction} that will retry to send the CEC message in case it
     * fails.
     * @param command that has to be sent in the CEC bus.
     * Create a {@link ResendCecCommandAction} that will retry sending the CEC message if it fails.
     * @param command  command to be sent on the CEC bus.
     * @param callback callback for handling the result of sending the command.
     */
    @ServiceThreadOnly
    public void sendCecCommandWithRetries(HdmiCecMessage command) {
    private void sendCecCommandWithRetries(HdmiCecMessage command,
            @Nullable SendMessageCallback callback) {
        assertRunOnServiceThread();
        HdmiCecLocalDevice localDevice = getAllCecLocalDevices().get(0);
        if (localDevice != null) {
            localDevice.addAndStartAction(new SendCecCommandAction(localDevice, command));
            sendCecCommandWithoutRetries(command, new SendMessageCallback() {
                @Override
                public void onSendCompleted(int result) {
                    if (result != SendMessageResult.SUCCESS) {
                        localDevice.addAndStartAction(new
                                ResendCecCommandAction(localDevice, command, callback));
                    }
                }
            });
        }
    }


    /**
     * Transmit a CEC command to CEC bus.
     *
     * @param command CEC command to send out
     * @param callback interface used to the result of send command
     */
    @ServiceThreadOnly
    void sendCecCommandWithoutRetries(HdmiCecMessage command,
            @Nullable SendMessageCallback callback) {
        assertRunOnServiceThread();
        if (command.getValidationResult() == HdmiCecMessageValidator.OK
                && verifyPhysicalAddresses(command)) {
            mCecController.sendCommand(command, callback);
        } else {
            HdmiLogger.error("Invalid message type:" + command);
            if (callback != null) {
                callback.onSendCompleted(SendMessageResult.FAIL);
            }
        }
    }

+23 −11
Original line number Diff line number Diff line
@@ -16,23 +16,29 @@

package com.android.server.hdmi;

import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.hardware.tv.hdmi.cec.SendMessageResult;
import android.util.Slog;

import com.android.server.hdmi.HdmiControlService.SendMessageCallback;

/**
 * Action that sends a CEC command. If the message fails to be sent, it tries again for
 * RETRANSMISSION_COUNT times.
 * Action that retries RETRANSMISSION_COUNT times to send a CEC command when the first attempt to
 * send the message failed.
 *
 * This action starts with a delay of SEND_COMMAND_RETRY_MS milliseconds.
 *
 * If this action can't be started it will be canceled and not deferred.
 * See {@link HdmiCecLocalDevice#addAndStartAction}.
 */
public class SendCecCommandAction extends HdmiCecFeatureAction {
    private static final String TAG = "SendCecCommandAction";
    private static final int RETRANSMISSION_COUNT = 2;
public class ResendCecCommandAction extends HdmiCecFeatureAction {
    private static final String TAG = "ResendCecCommandAction";
    private static final int RETRANSMISSION_COUNT = 1;
    private static final int STATE_WAIT_FOR_RESEND_COMMAND = 1;
    static final int SEND_COMMAND_RETRY_MS = 300;

    private final HdmiCecMessage mCommand;
    private int mRetransmissionCount = 0;
    private final SendMessageCallback mResultCallback;
    private final SendMessageCallback mCallback = new SendMessageCallback(){
        @Override
        public void onSendCompleted(int result) {
@@ -41,20 +47,26 @@ public class SendCecCommandAction extends HdmiCecFeatureAction {
                mState = STATE_WAIT_FOR_RESEND_COMMAND;
                addTimer(mState, SEND_COMMAND_RETRY_MS);
            } else {
                if (mResultCallback != null) {
                    mResultCallback.onSendCompleted(result);
                }
                finish();
            }
        }
    };

    SendCecCommandAction(HdmiCecLocalDevice source, HdmiCecMessage command) {
    ResendCecCommandAction(HdmiCecLocalDevice source, HdmiCecMessage command,
            SendMessageCallback callback) {
        super(source);
        mCommand = command;
        mResultCallback = callback;
        mState = STATE_WAIT_FOR_RESEND_COMMAND;
        addTimer(mState, SEND_COMMAND_RETRY_MS);
    }

    @Override
    boolean start() {
        Slog.d(TAG, "SendCecCommandAction started");
        sendCommand(mCommand, mCallback);
        Slog.d(TAG, "ResendCecCommandAction started");
        return true;
    }

@@ -66,8 +78,8 @@ public class SendCecCommandAction extends HdmiCecFeatureAction {
            return;
        }
        if (mState == STATE_WAIT_FOR_RESEND_COMMAND) {
            Slog.d(TAG, "sendCecCommand failed, retry");
            sendCommand(mCommand, mCallback);
            Slog.d(TAG, "sendCommandWithoutRetries failed, retry");
            sendCommandWithoutRetries(mCommand, mCallback);
        }
    }

+86 −4
Original line number Diff line number Diff line
@@ -17,11 +17,12 @@
package com.android.server.hdmi;

import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.SendCecCommandAction.SEND_COMMAND_RETRY_MS;
import static com.android.server.hdmi.ResendCecCommandAction.SEND_COMMAND_RETRY_MS;

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

import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
@@ -38,11 +39,11 @@ import org.junit.runners.JUnit4;

import java.util.Collections;

/** Tests for {@link SendCecCommandAction} */
/** Tests for {@link ResendCecCommandAction} */
@SmallTest
@Presubmit
@RunWith(JUnit4.class)
public class SendCecCommandActionPlaybackTest {
public class ResendCecCommandActionPlaybackTest {
    private static final String TAG = "SendCecCommandActionPlaybackTest";
    private HdmiControlService mHdmiControlService;
    private HdmiCecLocalDevice mPlaybackDevice;
@@ -51,6 +52,7 @@ public class SendCecCommandActionPlaybackTest {
    private Looper mMyLooper;
    private TestLooper mTestLooper = new TestLooper();
    private int mPhysicalAddress;
    private boolean mIsPowerStandby;

    @Before
    public void setUp() throws Exception {
@@ -63,7 +65,7 @@ public class SendCecCommandActionPlaybackTest {

            @Override
            boolean isPowerStandby() {
                return false;
                return mIsPowerStandby;
            }

            @Override
@@ -71,6 +73,7 @@ public class SendCecCommandActionPlaybackTest {
                // do nothing
            }
        };
        mIsPowerStandby = false;

        mMyLooper = mTestLooper.getLooper();
        mHdmiControlService.setIoLooper(mMyLooper);
@@ -148,6 +151,35 @@ public class SendCecCommandActionPlaybackTest {
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
    }

    @Test
    public void sendCecCommand_inactiveSource_onStandby_powerControlModeNone_sendMessage() {
        mPlaybackDevice.mService.getHdmiCecConfig().setStringValue(
                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                HdmiControlManager.POWER_CONTROL_MODE_NONE);
        mPlaybackDevice.setActiveSource(mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
                mPhysicalAddress, "SendCecCommandActionPlaybackTest");
        mIsPowerStandby = true;
        mPlaybackDevice.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();
        HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
                mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);

        assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);
    }

    @Test
    public void sendCecCommand_inactiveSource_onStandby_initiatedByCec_sendMessage() {
        mPlaybackDevice.setActiveSource(mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
                mPhysicalAddress, "SendCecCommandActionPlaybackTest");
        mIsPowerStandby = true;
        mPlaybackDevice.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();
        HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
                mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);

        assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);
    }

    @Test
    public void sendCecCommand_routingChange_sendMessageFails_resendMessage() {
        mNativeWrapper.setMessageSendResult(Constants.MESSAGE_ROUTING_CHANGE,
@@ -293,4 +325,54 @@ public class SendCecCommandActionPlaybackTest {
        mTestLooper.dispatchAll();
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPhysicalAddress);
    }

    @Test
    public void sendCecCommand_inactiveSource_sendMessageFails_afterStandby_noResendMessage() {
        mNativeWrapper.setMessageSendResult(Constants.MESSAGE_INACTIVE_SOURCE,
                SendMessageResult.BUSY);
        mPlaybackDevice.setActiveSource(mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
                mPhysicalAddress, "SendCecCommandActionPlaybackTest");
        mIsPowerStandby = true;
        mPlaybackDevice.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
        mTestLooper.dispatchAll();
        HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
                mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
        assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);

        mNativeWrapper.clearResultMessages();
        mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);

        mTestLooper.moveTimeForward(SEND_COMMAND_RETRY_MS);
        mTestLooper.dispatchAll();
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
    }

    @Test
    public void sendCecCommand_onStandby_removeAction_noResendMessage() {
        mNativeWrapper.setMessageSendResult(Constants.MESSAGE_INACTIVE_SOURCE,
                SendMessageResult.BUSY);
        mTestLooper.dispatchAll();
        HdmiCecMessage inactiveSourceMessage = HdmiCecMessageBuilder.buildInactiveSource(
                mPlaybackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
        mHdmiControlService.sendCecCommand(inactiveSourceMessage);
        mTestLooper.dispatchAll();
        assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSourceMessage);
        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);

        mNativeWrapper.clearResultMessages();
        mTestLooper.moveTimeForward(SEND_COMMAND_RETRY_MS);
        mTestLooper.dispatchAll();
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);

        mTestLooper.dispatchAll();
        mHdmiControlService.onWakeUp(HdmiControlService.WAKE_UP_SCREEN_ON);
        mTestLooper.dispatchAll();
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);

        mTestLooper.moveTimeForward(SEND_COMMAND_RETRY_MS);
        mTestLooper.dispatchAll();
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSourceMessage);
    }
}
Loading