diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java index c684a56ec5d48..cb47bb25f1525 100644 --- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -23,6 +23,8 @@ import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.RemoteException; import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; /** @@ -47,7 +49,8 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { // State in which we wait for to come in response to the command // we have sent. - private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; + @VisibleForTesting + static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; // State in which we wait for the device power status to switch to 'Standby'. // We wait till the status becomes 'Standby' before we send @@ -56,11 +59,13 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { // State in which we wait for the device power status to switch to 'on'. We wait // maximum 100 seconds (20 * 5) before we give up and just send . - private static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3; + @VisibleForTesting + static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3; private final HdmiDeviceInfo mTarget; private final IHdmiControlCallback mCallback; private final HdmiCecMessage mGivePowerStatus; + private final boolean mIsCec20; private int mPowerStatusCounter = 0; @@ -71,13 +76,22 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { * @param target target logical device that will be a new active source * @param callback callback object */ - public DeviceSelectAction(HdmiCecLocalDeviceTv source, - HdmiDeviceInfo target, IHdmiControlCallback callback) { + DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target, + IHdmiControlCallback callback) { + this(source, target, callback, + source.getDeviceInfo().getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0 + && target.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0); + } + + @VisibleForTesting + DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target, + IHdmiControlCallback callback, boolean isCec20) { super(source); mCallback = callback; mTarget = target; mGivePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( getSourceAddress(), getTargetAddress()); + mIsCec20 = isCec20; } int getTargetAddress() { @@ -86,8 +100,18 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { @Override public boolean start() { - // Seq #9 - queryDevicePowerStatus(); + if (mIsCec20) { + sendSetStreamPath(); + } + if (!mIsCec20 || mTarget.getDevicePowerStatus() + == HdmiControlManager.POWER_STATUS_UNKNOWN) { + queryDevicePowerStatus(); + } else if (mTarget.getDevicePowerStatus() == HdmiControlManager.POWER_STATUS_ON) { + invokeCallbackAndFinish(HdmiControlManager.RESULT_SUCCESS); + return true; + } + mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; + addTimer(mState, HdmiConfig.TIMEOUT_MS); return true; } @@ -96,14 +120,10 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { @Override public void onSendCompleted(int error) { if (error != SendMessageResult.SUCCESS) { - invokeCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED); - finish(); - return; + invokeCallbackAndFinish(HdmiControlManager.RESULT_COMMUNICATION_FAILED); } } }); - mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; - addTimer(mState, HdmiConfig.TIMEOUT_MS); } @Override @@ -113,7 +133,6 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { } int opcode = cmd.getOpcode(); byte[] params = cmd.getParams(); - switch (mState) { case STATE_WAIT_FOR_REPORT_POWER_STATUS: if (opcode == Constants.MESSAGE_REPORT_POWER_STATUS) { @@ -129,21 +148,23 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { private boolean handleReportPowerStatus(int powerStatus) { switch (powerStatus) { case HdmiControlManager.POWER_STATUS_ON: - sendSetStreamPath(); + selectDevice(); return true; case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY: if (mPowerStatusCounter < 4) { mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY; addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS); } else { - sendSetStreamPath(); + selectDevice(); } return true; case HdmiControlManager.POWER_STATUS_STANDBY: if (mPowerStatusCounter == 0) { turnOnDevice(); + mState = STATE_WAIT_FOR_DEVICE_POWER_ON; + addTimer(mState, TIMEOUT_POWER_ON_MS); } else { - sendSetStreamPath(); + selectDevice(); } return true; case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON: @@ -151,33 +172,13 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { mState = STATE_WAIT_FOR_DEVICE_POWER_ON; addTimer(mState, TIMEOUT_POWER_ON_MS); } else { - sendSetStreamPath(); + selectDevice(); } return true; } return false; } - private void turnOnDevice() { - sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), - HdmiCecKeycode.CEC_KEYCODE_POWER); - sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), - HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION); - mState = STATE_WAIT_FOR_DEVICE_POWER_ON; - addTimer(mState, TIMEOUT_POWER_ON_MS); - } - - private void sendSetStreamPath() { - // Turn the active source invalidated, which remains so till comes from - // the selected device. - tv().getActiveSource().invalidate(); - tv().setActivePath(mTarget.getPhysicalAddress()); - sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( - getSourceAddress(), mTarget.getPhysicalAddress())); - invokeCallback(HdmiControlManager.RESULT_SUCCESS); - finish(); - } - @Override public void handleTimerEvent(int timeoutState) { if (mState != timeoutState) { @@ -187,28 +188,54 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { switch (mState) { case STATE_WAIT_FOR_REPORT_POWER_STATUS: if (tv().isPowerStandbyOrTransient()) { - invokeCallback(HdmiControlManager.RESULT_INCORRECT_MODE); - finish(); + invokeCallbackAndFinish(HdmiControlManager.RESULT_INCORRECT_MODE); return; } - sendSetStreamPath(); + selectDevice(); break; case STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY: case STATE_WAIT_FOR_DEVICE_POWER_ON: mPowerStatusCounter++; queryDevicePowerStatus(); + mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; + addTimer(mState, HdmiConfig.TIMEOUT_MS); break; } } - private void invokeCallback(int result) { - if (mCallback == null) { - return; - } - try { - mCallback.onComplete(result); - } catch (RemoteException e) { - Slog.e(TAG, "Callback failed:" + e); + private void turnOnDevice() { + if (!mIsCec20) { + sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), + HdmiCecKeycode.CEC_KEYCODE_POWER); + sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), + HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION); } } + + private void selectDevice() { + if (!mIsCec20) { + sendSetStreamPath(); + } + invokeCallbackAndFinish(HdmiControlManager.RESULT_SUCCESS); + } + + private void sendSetStreamPath() { + // Turn the active source invalidated, which remains so till comes from + // the selected device. + tv().getActiveSource().invalidate(); + tv().setActivePath(mTarget.getPhysicalAddress()); + sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( + getSourceAddress(), mTarget.getPhysicalAddress())); + } + + private void invokeCallbackAndFinish(int result) { + if (mCallback != null) { + try { + mCallback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Callback failed:" + e); + } + } + finish(); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java new file mode 100644 index 0000000000000..f49cbca12fd89 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_ON; +import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_STANDBY; +import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; + +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; +import static com.android.server.hdmi.Constants.ADDR_TV; +import static com.android.server.hdmi.DeviceSelectAction.STATE_WAIT_FOR_DEVICE_POWER_ON; +import static com.android.server.hdmi.DeviceSelectAction.STATE_WAIT_FOR_REPORT_POWER_STATUS; +import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; + +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.hdmi.HdmiPortInfo; +import android.hardware.hdmi.IHdmiControlCallback; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; +import android.os.Looper; +import android.os.PowerManager; +import android.os.test.TestLooper; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@SmallTest +@RunWith(JUnit4.class) +public class DeviceSelectActionTest { + + private static final int PORT_1 = 1; + private static final int PORT_2 = 1; + private static final int PHYSICAL_ADDRESS_PLAYBACK_1 = 0x1000; + private static final int PHYSICAL_ADDRESS_PLAYBACK_2 = 0x2000; + + private static final byte[] POWER_ON = new byte[] { POWER_STATUS_ON }; + private static final byte[] POWER_STANDBY = new byte[] { POWER_STATUS_STANDBY }; + private static final byte[] POWER_TRANSIENT_TO_ON = new byte[] { POWER_STATUS_TRANSIENT_TO_ON }; + private static final HdmiCecMessage REPORT_POWER_STATUS_ON = new HdmiCecMessage( + ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON); + private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = new HdmiCecMessage( + ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY); + private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = new HdmiCecMessage( + ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON); + private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath( + ADDR_TV, PHYSICAL_ADDRESS_PLAYBACK_1); + private static final HdmiDeviceInfo INFO_PLAYBACK_1 = new HdmiDeviceInfo( + ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1, HdmiDeviceInfo.DEVICE_PLAYBACK, + 0x1234, "Playback 1", + HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); + private static final HdmiDeviceInfo INFO_PLAYBACK_2 = new HdmiDeviceInfo( + ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2, HdmiDeviceInfo.DEVICE_PLAYBACK, + 0x1234, "Playback 2", + HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); + + private HdmiControlService mHdmiControlService; + private HdmiCecController mHdmiCecController; + private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv; + private FakeNativeWrapper mNativeWrapper; + private Looper mMyLooper; + private TestLooper mTestLooper = new TestLooper(); + private ArrayList mLocalDevices = new ArrayList<>(); + + @Mock + private IPowerManager mIPowerManagerMock; + @Mock + private IThermalService mIThermalServiceMock; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + Context context = InstrumentationRegistry.getTargetContext(); + mMyLooper = mTestLooper.getLooper(); + PowerManager powerManager = new PowerManager(context, mIPowerManagerMock, + mIThermalServiceMock, new Handler(mMyLooper)); + + HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context); + + mHdmiControlService = + new HdmiControlService(InstrumentationRegistry.getTargetContext()) { + @Override + boolean isControlEnabled() { + return true; + } + + @Override + void wakeUp() { + } + + @Override + protected void writeStringSystemProperty(String key, String value) { + // do nothing + } + + @Override + boolean isPowerStandbyOrTransient() { + return false; + } + + @Override + protected PowerManager getPowerManager() { + return powerManager; + } + + @Override + protected HdmiCecConfig getHdmiCecConfig() { + return hdmiCecConfig; + } + }; + + mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService); + mHdmiCecLocalDeviceTv.init(); + mHdmiControlService.setIoLooper(mMyLooper); + mNativeWrapper = new FakeNativeWrapper(); + mHdmiCecController = HdmiCecController.createWithNativeWrapper( + mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); + mHdmiControlService.setCecController(mHdmiCecController); + mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService)); + mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService)); + mLocalDevices.add(mHdmiCecLocalDeviceTv); + HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2]; + hdmiPortInfos[0] = + new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_1, + true, false, false); + hdmiPortInfos[1] = + new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_2, + true, false, false); + mNativeWrapper.setPortInfo(hdmiPortInfos); + mHdmiControlService.initService(); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mNativeWrapper.setPhysicalAddress(0x0000); + mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_1); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_2); + } + + private static class TestActionTimer implements ActionTimer { + private int mState; + + @Override + public void sendTimerMessage(int state, long delayMillis) { + mState = state; + } + + @Override + public void clearTimerMessage() { + } + + private int getState() { + return mState; + } + } + + private static class TestCallback extends IHdmiControlCallback.Stub { + private final ArrayList mCallbackResult = new ArrayList(); + + @Override + public void onComplete(int result) { + mCallbackResult.add(result); + } + + private int getResult() { + assertThat(mCallbackResult.size()).isEqualTo(1); + return mCallbackResult.get(0); + } + } + + private DeviceSelectAction createDeviceSelectAction(TestActionTimer actionTimer, + TestCallback callback, + boolean isCec20) { + HdmiDeviceInfo hdmiDeviceInfo = + mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(ADDR_PLAYBACK_1); + DeviceSelectAction action = new DeviceSelectAction(mHdmiCecLocalDeviceTv, + hdmiDeviceInfo, callback, isCec20); + action.setActionTimer(actionTimer); + return action; + } + + @Test + public void testDeviceSelect_DeviceInPowerOnStatus_Cec14b() { + // TV was watching playback2 device connected at port 2, and wants to select + // playback1. + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, + /*isCec20=*/false); + mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, + "testDeviceSelect"); + action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_ON); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() { + mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, + "testDeviceSelect"); + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, + /*isCec20=*/false); + action.start(); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_STANDBY); + mTestLooper.dispatchAll(); + HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( + ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); + assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.processCommand(REPORT_POWER_STATUS_ON); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b() { + mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, + "testDeviceSelect"); + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, + /*isCec20=*/false); + action.start(); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_STANDBY); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_ON); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b() { + mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, + "testDeviceSelect"); + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, + /*isCec20=*/false); + action.start(); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_STANDBY); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.handleTimerEvent(STATE_WAIT_FOR_REPORT_POWER_STATUS); + // Give up getting power status, and just send + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void testDeviceSelect_DeviceInPowerOnStatus_Cec20() { + mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, + HdmiControlManager.POWER_STATUS_ON); + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, + /*isCec20=*/true); + mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, + "testDeviceSelect"); + action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void testDeviceSelect_DeviceInPowerUnknownStatus_Cec20() { + mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, + HdmiControlManager.POWER_STATUS_UNKNOWN); + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, + /*isCec20=*/true); + mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, + "testDeviceSelect"); + action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_ON); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test + public void testDeviceSelect_DeviceInStandbyStatus_Cec20() { + mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, + HdmiControlManager.POWER_STATUS_STANDBY); + mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, + "testDeviceSelect"); + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, + /*isCec20=*/true); + action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); + action.processCommand(REPORT_POWER_STATUS_STANDBY); + mTestLooper.dispatchAll(); + HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( + ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(userControlPressed); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); + action.processCommand(REPORT_POWER_STATUS_ON); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } +}