Merge "HDMICEC: Add deviceSelect API for Playback devices"

This commit is contained in:
Paul Colța
2021-09-02 14:04:42 +00:00
committed by Android (Google) Code Review
6 changed files with 743 additions and 16 deletions

View File

@@ -0,0 +1,251 @@
/*
* Copyright (C) 2021 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 android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
/**
* Handles an action that selects a logical device as a new active source.
*
* Triggered by {@link HdmiPlaybackClient}, attempts to select the given target device
* for a new active source. A >Routing Change< command is issued in order to select
* the target device. If that doesn't succeed a >Set Stream Path< command is sent.
* It does its best to wake up the target in standby mode, before issuing the device
* select command.
*/
final class DeviceSelectActionFromPlayback extends HdmiCecFeatureAction {
private static final String TAG = "DeviceSelectActionFromPlayback";
// Time in milliseconds we wait for the device power status to switch to 'Standby'
private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000;
// Time in milliseconds we wait for the device power status to turn to 'On'.
private static final int TIMEOUT_POWER_ON_MS = 5 * 1000;
// The number of times we try to wake up the target device before we give up
// and just send <Routing Change>.
private static final int LOOP_COUNTER_MAX = 2;
// State in which we wait for <Report Power Status> to come in response to the command
// <Give Device Power Status> we have sent.
@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 <Routing Change>
// to wake up the device again.
private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2;
// 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 <Set Stream Path>.
@VisibleForTesting
static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3;
// State in which we wait for <Active Source> to come in response to the command
// <Routing Change> we have sent.
@VisibleForTesting
static final int STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE = 4;
// State in which we wait for <Active Source> to come in response to the command
// <Set Stream Path> we have sent.
@VisibleForTesting
private static final int STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_SET_STREAM_PATH = 5;
private final HdmiDeviceInfo mTarget;
private final HdmiCecMessage mGivePowerStatus;
private final boolean mIsCec20;
private int mPowerStatusCounter = 0;
/**
* Constructor.
*
* @param source {@link HdmiCecLocalDevice} instance
* @param target target logical device that will be a new active source
* @param callback callback object
*/
DeviceSelectActionFromPlayback(HdmiCecLocalDevicePlayback 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
DeviceSelectActionFromPlayback(HdmiCecLocalDevicePlayback source, HdmiDeviceInfo target,
IHdmiControlCallback callback, boolean isCec20) {
super(source, callback);
mTarget = target;
mGivePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
getSourceAddress(), getTargetAddress());
mIsCec20 = isCec20;
}
private int getTargetAddress() {
return mTarget.getLogicalAddress();
}
private int getTargetPath() {
return mTarget.getPhysicalAddress();
}
@Override
public boolean start() {
// This <Routing Change> message wakes up the target device in most cases.
sendRoutingChange();
if (!mIsCec20) {
queryDevicePowerStatus();
} else {
int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork()
.getCecDeviceInfo(getTargetAddress());
if (targetDevice != null) {
targetPowerStatus = targetDevice.getDevicePowerStatus();
}
if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
queryDevicePowerStatus();
} else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
mState = STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
return true;
}
}
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
return true;
}
private void queryDevicePowerStatus() {
sendCommand(
mGivePowerStatus,
new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
if (error != SendMessageResult.SUCCESS) {
finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
return;
}
}
});
}
@Override
public boolean processCommand(HdmiCecMessage cmd) {
if (cmd.getSource() != getTargetAddress()) {
return false;
}
int opcode = cmd.getOpcode();
byte[] params = cmd.getParams();
if (opcode == Constants.MESSAGE_ACTIVE_SOURCE) {
// The target device was successfully set as the active source
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
return true;
}
if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
&& opcode == Constants.MESSAGE_REPORT_POWER_STATUS) {
return handleReportPowerStatus(params[0]);
}
return false;
}
private boolean handleReportPowerStatus(int powerStatus) {
switch (powerStatus) {
case HdmiControlManager.POWER_STATUS_ON:
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 {
selectDevice();
}
return true;
case HdmiControlManager.POWER_STATUS_STANDBY:
if (mPowerStatusCounter == 0) {
sendRoutingChange();
mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
addTimer(mState, TIMEOUT_POWER_ON_MS);
} else {
selectDevice();
}
return true;
case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
if (mPowerStatusCounter < LOOP_COUNTER_MAX) {
mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
addTimer(mState, TIMEOUT_POWER_ON_MS);
} else {
selectDevice();
}
return true;
}
return false;
}
private void selectDevice() {
sendRoutingChange();
mState = STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
}
@Override
void handleTimerEvent(int timeoutState) {
if (mState != timeoutState) {
Slog.w(TAG, "Timer in a wrong state. Ignored.");
return;
}
switch (mState) {
case STATE_WAIT_FOR_REPORT_POWER_STATUS:
selectDevice();
addTimer(mState, HdmiConfig.TIMEOUT_MS);
break;
case STATE_WAIT_FOR_DEVICE_POWER_ON:
mPowerStatusCounter++;
queryDevicePowerStatus();
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
break;
case STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE:
sendSetStreamPath();
mState = STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_SET_STREAM_PATH;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
break;
case STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_SET_STREAM_PATH:
finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
break;
}
}
private void sendRoutingChange() {
sendCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
playback().getActiveSource().physicalAddress, getTargetPath()));
}
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
getTargetPath()));
}
}

View File

@@ -260,6 +260,20 @@ abstract class HdmiCecLocalDevice {
return onMessage(message);
}
@ServiceThreadOnly
@VisibleForTesting
protected boolean isAlreadyActiveSource(HdmiDeviceInfo targetDevice, int targetAddress,
IHdmiControlCallback callback) {
ActiveSource active = getActiveSource();
if (targetDevice.getDevicePowerStatus() == HdmiControlManager.POWER_STATUS_ON
&& active.isValid()
&& targetAddress == active.logicalAddress) {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
return true;
}
return false;
}
@ServiceThreadOnly
@Constants.HandleMessageResult
protected final int onMessage(HdmiCecMessage message) {

View File

@@ -124,6 +124,39 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
String.valueOf(addr));
}
/**
* Performs the action 'device select' or 'one touch play' initiated by a Playback device.
*
* @param id id of HDMI device to select
* @param callback callback object to report the result with
*/
@ServiceThreadOnly
void deviceSelect(int id, IHdmiControlCallback callback) {
assertRunOnServiceThread();
synchronized (mLock) {
if (id == getDeviceInfo().getId()) {
mService.oneTouchPlay(callback);
return;
}
}
HdmiDeviceInfo targetDevice = mService.getHdmiCecNetwork().getDeviceInfo(id);
if (targetDevice == null) {
invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
return;
}
int targetAddress = targetDevice.getLogicalAddress();
if (isAlreadyActiveSource(targetDevice, targetAddress, callback)) {
return;
}
if (!mService.isControlEnabled()) {
setActiveSource(targetDevice, "HdmiCecLocalDevicePlayback#deviceSelect()");
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
return;
}
removeAction(DeviceSelectActionFromPlayback.class);
addAndStartAction(new DeviceSelectActionFromPlayback(this, targetDevice, callback));
}
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {

View File

@@ -242,11 +242,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
int targetAddress = targetDevice.getLogicalAddress();
ActiveSource active = getActiveSource();
if (targetDevice.getDevicePowerStatus() == HdmiControlManager.POWER_STATUS_ON
&& active.isValid()
&& targetAddress == active.logicalAddress) {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
if (isAlreadyActiveSource(targetDevice, targetAddress, callback)) {
return;
}
if (targetAddress == Constants.ADDR_INTERNAL) {

View File

@@ -1685,7 +1685,8 @@ public class HdmiControlService extends SystemService {
return;
}
HdmiCecLocalDeviceTv tv = tv();
if (tv == null) {
HdmiCecLocalDevicePlayback playback = playback();
if (tv == null && playback == null) {
if (!mAddressAllocated) {
mSelectRequestBuffer.set(SelectRequestBuffer.newDeviceSelect(
HdmiControlService.this, deviceId, callback));
@@ -1698,20 +1699,24 @@ public class HdmiControlService extends SystemService {
invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
if (device != null) {
if (device.getPortId() == tv.getActivePortId()) {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
if (tv != null) {
HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
if (device != null) {
if (device.getPortId() == tv.getActivePortId()) {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
return;
}
// Upon selecting MHL device, we send RAP[Content On] to wake up
// the connected mobile device, start routing control to switch ports.
// callback is handled by MHL action.
device.turnOn(callback);
tv.doManualPortSwitching(device.getPortId(), null);
return;
}
// Upon selecting MHL device, we send RAP[Content On] to wake up
// the connected mobile device, start routing control to switch ports.
// callback is handled by MHL action.
device.turnOn(callback);
tv.doManualPortSwitching(device.getPortId(), null);
tv.deviceSelect(deviceId, callback);
return;
}
tv.deviceSelect(deviceId, callback);
playback.deviceSelect(deviceId, callback);
}
});
}

View File

@@ -0,0 +1,428 @@
/*
* 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_PLAYBACK_3;
import static com.android.server.hdmi.DeviceSelectActionFromPlayback.STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE;
import static com.android.server.hdmi.DeviceSelectActionFromPlayback.STATE_WAIT_FOR_DEVICE_POWER_ON;
import static com.android.server.hdmi.DeviceSelectActionFromPlayback.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.IHdmiControlCallback;
import android.os.Looper;
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.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
@SmallTest
@RunWith(JUnit4.class)
public class DeviceSelectActionFromPlaybackTest {
private static final int PORT_1 = 1;
private static final int PORT_2 = 1;
private static final int PORT_3 = 1;
private static final int PHYSICAL_ADDRESS_PLAYBACK_1 = 0x1000;
private static final int PHYSICAL_ADDRESS_PLAYBACK_2 = 0x2000;
private static final int PHYSICAL_ADDRESS_PLAYBACK_3 = 0x3000;
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_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = new HdmiCecMessage(
ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = new HdmiCecMessage(
ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS,
POWER_TRANSIENT_TO_ON);
private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath(
ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_2);
private static final HdmiCecMessage ROUTING_CHANGE = HdmiCecMessageBuilder.buildRoutingChange(
ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_2);
private static final HdmiCecMessage ACTIVE_SOURCE = HdmiCecMessageBuilder.buildActiveSource(
ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2);
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 static final HdmiDeviceInfo INFO_PLAYBACK_3 = new HdmiDeviceInfo(
ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3, PORT_3, HdmiDeviceInfo.DEVICE_PLAYBACK,
0x1234, "Playback 3",
HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
private HdmiCecNetwork mHdmiCecNetwork;
private HdmiControlService mHdmiControlService;
private HdmiMhlControllerStub mHdmiMhlControllerStub;
private FakeNativeWrapper mNativeWrapper;
private FakePowerManagerWrapper mPowerManager;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
}
@Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@Override
boolean isPowerStandbyOrTransient() {
return false;
}
};
mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
mHdmiCecLocalDevicePlayback.init();
mHdmiControlService.setIoLooper(mMyLooper);
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
mHdmiCecController, mHdmiMhlControllerStub);
mHdmiControlService.setHdmiCecNetwork(mHdmiCecNetwork);
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mNativeWrapper.setPhysicalAddress(0x0000);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_1);
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_2);
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_3);
}
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<Integer> mCallbackResult = new ArrayList<Integer>();
@Override
public void onComplete(int result) {
mCallbackResult.add(result);
}
private int getResult() {
assertThat(mCallbackResult.size()).isEqualTo(1);
return mCallbackResult.get(0);
}
}
private DeviceSelectActionFromPlayback createDeviceSelectActionFromPlayback(
TestActionTimer actionTimer,
TestCallback callback,
boolean isCec20) {
HdmiDeviceInfo hdmiDeviceInfo =
mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(ADDR_PLAYBACK_2);
DeviceSelectActionFromPlayback action = new DeviceSelectActionFromPlayback(
mHdmiCecLocalDevicePlayback,
hdmiDeviceInfo, callback, isCec20);
action.setActionTimer(actionTimer);
return action;
}
@Test
public void testDeviceSelect_DeviceInPowerOnStatus_Cec14b() {
// TV was watching playback3 device connected at port 3, and wants to select
// playback2.
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/false);
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() {
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
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(ROUTING_CHANGE);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b() {
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
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(ROUTING_CHANGE);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b() {
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
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 <Routing Change>
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_ReachSetStreamPath_Cec14b() {
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/false);
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(
STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
action.handleTimerEvent(STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_ReachSetStreamPathDeviceInPowerOnStatus_Cec20() {
mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
HdmiControlManager.POWER_STATUS_ON);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/true);
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(
STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
action.handleTimerEvent(STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInPowerOnStatus_Cec20() {
mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
HdmiControlManager.POWER_STATUS_ON);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/true);
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInPowerUnknownStatus_Cec20() {
mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
HdmiControlManager.POWER_STATUS_UNKNOWN);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/true);
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_ON);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyStatus_Cec20() {
mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
HdmiControlManager.POWER_STATUS_STANDBY);
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/true);
action.start();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.processCommand(REPORT_POWER_STATUS_STANDBY);
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
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(ROUTING_CHANGE);
action.processCommand(ACTIVE_SOURCE);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
}