HDMICEC: Add deviceSelect API for Playback devices

Create DeviceSelectActionFromPlayback class and add unit tests for it.
Implement deviceSelect inside the HdmiCecLocalDevicePlayback.
Move the Active Source validation from deviceSelect HdmiCecLocalDeviceTv to the abstract class in order to make it visible for HdmiCecLocalDevicePlayback and avoid duplicate code.
Modify the deviceSelect method from HdmiControlService so the playback devices can handle device selection.

Bug: 196934198
Test: make && atest DeviceSelectActionFromPlaybackTest
Test: Manually run the shell command "deviceselect <device id>"
Change-Id: I53c776a042170980b61e823ee67230e1870fa21d
This commit is contained in:
Paul Colța
2021-08-17 11:16:51 +00:00
parent 12f914e3f6
commit dcf47e903a
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 &gt;Routing Change&lt; command is issued in order to select
* the target device. If that doesn't succeed a &gt;Set Stream Path&lt; 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);
}
}