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

Commit 0ed23ada authored by Paul Colta's avatar Paul Colta
Browse files

HDMI: Delay OTP in case standby process started

If there was a standby request right after wake up started, OneTouchPlay
would've started before the local device had the chance to disable the
action. The result was the device waking up instead of going to sleep.
This is an edge case, but the CTS mentioned below wouldn't pass without
this CL.

Test: run cts -m CtsHdmiCecHostTestCases -t android.hdmicec.cts.common.HdmiCecPowerStatusTest#cect_hf4_6_22_interruptedPowerOn
Bug: 306080951
Bug: 320388559
Change-Id: I4181cbdcdcc3683ee11e539748da0af6907ce603
parent 3fca7acc
Loading
Loading
Loading
Loading
+40 −10
Original line number Diff line number Diff line
@@ -45,6 +45,13 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
    @VisibleForTesting
    static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1;

    // State in which the action is delayed. If the action starts and
    // {@link PowerManager#isInteractive} returns false, it could indicate the beginning of a
    // standby process. In this scenario, the action will be removed when
    // {@link HdmiCecLocalDeviceSource#disableDevice} is called, therefore we delay the action.
    @VisibleForTesting
    static final int STATE_CHECK_STANDBY_PROCESS_STARTED = 2;

    // The maximum number of times we send <Give Device Power Status> before we give up.
    // We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
    private static final int LOOP_COUNTER_MAX = 10;
@@ -87,6 +94,22 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
    boolean start() {
        // Because only source device can create this action, it's safe to cast.
        mSource = source();

        if (!mSource.mService.getPowerManager().isInteractive()) {
            Slog.d(TAG, "PowerManager is not interactive. Delay the action to check if standby"
                    + " started!");
            mState = STATE_CHECK_STANDBY_PROCESS_STARTED;
            addTimer(mState, HdmiConfig.TIMEOUT_MS);
        } else {
            startAction();
        }

        return true;
    }

    private void startAction() {
        Slog.i(TAG, "Start action.");

        sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));

        boolean is20TargetOnBefore = mIsCec20 && getTargetDevicePowerStatus(mSource, mTargetAddress,
@@ -116,12 +139,11 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
                    maySendActiveSource();
                }
                finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
                return true;
                return;
            }
        }
        mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
        addTimer(mState, HdmiConfig.TIMEOUT_MS);
        return true;
    }

    private void setAndBroadcastActiveSource() {
@@ -174,7 +196,8 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
        if (mState != state) {
            return;
        }
        if (state == STATE_WAITING_FOR_REPORT_POWER_STATUS) {
        switch (state) {
            case STATE_WAITING_FOR_REPORT_POWER_STATUS:
                if (mPowerStatusCounter++ < LOOP_COUNTER_MAX) {
                    queryDevicePowerStatus();
                    addTimer(mState, HdmiConfig.TIMEOUT_MS);
@@ -182,6 +205,13 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
                    // Couldn't wake up the TV for whatever reason. Report failure.
                    finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
                }
                return;
            case STATE_CHECK_STANDBY_PROCESS_STARTED:
                Slog.d(TAG, "Action was not removed, start the action.");
                startAction();
                return;
            default:
                return;
        }
    }

+52 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.hdmi;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
import static com.android.server.hdmi.OneTouchPlayAction.STATE_WAITING_FOR_REPORT_POWER_STATUS;

import static com.google.common.truth.Truth.assertThat;
@@ -46,6 +47,7 @@ import org.junit.runners.JUnit4;

import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/** Tests for {@link OneTouchPlayAction} */
@SmallTest
@@ -70,6 +72,7 @@ public class OneTouchPlayActionTest {

    private Context mContextSpy;
    private HdmiControlService mHdmiControlService;
    private HdmiCecController mHdmiCecController;
    private FakeNativeWrapper mNativeWrapper;
    private FakePowerManagerWrapper mPowerManager;
    private FakeHdmiCecConfig mHdmiCecConfig;
@@ -108,10 +111,10 @@ public class OneTouchPlayActionTest {
        mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
        setHdmiControlEnabled(hdmiControlEnabled);
        mNativeWrapper = new FakeNativeWrapper();
        HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
                this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
        mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
        mHdmiControlService.setCecController(hdmiCecController);
        mHdmiControlService.setCecController(mHdmiCecController);
        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
        mHdmiControlService.initService();
        mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
@@ -618,6 +621,53 @@ public class OneTouchPlayActionTest {
        assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
    }

    @Test
    public void onWakeUp_notInteractive_startOneTouchPlay() throws Exception {
        setUp(true);

        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mPowerManager.setInteractive(false);
        mTestLooper.dispatchAll();

        assertThat(mPowerManager.isInteractive()).isFalse();
        mNativeWrapper.clearResultMessages();
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
        mTestLooper.dispatchAll();

        HdmiCecMessage textViewOn =
                HdmiCecMessageBuilder.buildTextViewOn(
                        mHdmiControlService.playback().getDeviceInfo().getLogicalAddress(),
                        ADDR_TV);
        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
    }


    @Test
    public void onWakeUp_interruptedByOnStandby_notInteractive_OneTouchPlayNotStarted()
            throws Exception {
        setUp(true);
        long allocationDelay = TimeUnit.SECONDS.toMillis(1);
        mHdmiCecController.setLogicalAddressAllocationDelay(allocationDelay);
        mTestLooper.dispatchAll();

        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
        mPowerManager.setInteractive(false);
        mTestLooper.dispatchAll();

        assertThat(mPowerManager.isInteractive()).isFalse();
        mNativeWrapper.clearResultMessages();
        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
        mTestLooper.dispatchAll();

        HdmiCecMessage textViewOn =
                HdmiCecMessageBuilder.buildTextViewOn(
                        mHdmiControlService.playback().getDeviceInfo().getLogicalAddress(),
                        ADDR_TV);
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);

    }

    private static class TestActionTimer implements ActionTimer {
        private int mState;