Merge "HDMICEC: Add deviceSelect API for Playback devices"
This commit is contained in:
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user