Merge "CEC: Send Active Source as an action" into rvc-dev am: 8d3583e81e
Change-Id: Ia871f7931c05e6fbd3a7a21c0d614637ff4654ea
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.HdmiDeviceInfo;
|
||||
|
||||
/**
|
||||
* Action that sends {@code <Active Source>} to make this device the currently active source.
|
||||
*
|
||||
* Playback devices will also send {@code <Report Menu Status>} to make them a target for {@code
|
||||
* <User Control Pressed>} messages.
|
||||
*/
|
||||
public class ActiveSourceAction extends HdmiCecFeatureAction {
|
||||
|
||||
private static final int STATE_STARTED = 1;
|
||||
private static final int STATE_FINISHED = 2;
|
||||
|
||||
private final int mDestination;
|
||||
|
||||
ActiveSourceAction(HdmiCecLocalDevice source, int destination) {
|
||||
super(source);
|
||||
mDestination = destination;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean start() {
|
||||
mState = STATE_STARTED;
|
||||
sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
|
||||
source().mService.getPhysicalAddress()));
|
||||
|
||||
if (source().getType() == HdmiDeviceInfo.DEVICE_PLAYBACK) {
|
||||
// Reports menu-status active to receive <User Control Pressed>.
|
||||
sendCommand(
|
||||
HdmiCecMessageBuilder.buildReportMenuStatus(getSourceAddress(), mDestination,
|
||||
Constants.MENU_STATE_ACTIVATED));
|
||||
}
|
||||
mState = STATE_FINISHED;
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean processCommand(HdmiCecMessage cmd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleTimerEvent(int state) {
|
||||
// No response expected
|
||||
}
|
||||
}
|
||||
@@ -251,17 +251,6 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void maySendActiveSource(int dest) {
|
||||
if (mIsActiveSource) {
|
||||
mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
|
||||
mAddress, mService.getPhysicalAddress()));
|
||||
// Always reports menu-status active to receive RCP.
|
||||
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
|
||||
mAddress, dest, Constants.MENU_STATE_ACTIVATED));
|
||||
}
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
|
||||
assertRunOnServiceThread();
|
||||
|
||||
@@ -237,10 +237,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
|
||||
}
|
||||
|
||||
protected void maySendActiveSource(int dest) {
|
||||
if (mIsActiveSource) {
|
||||
mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
|
||||
mAddress, mService.getPhysicalAddress()));
|
||||
if (!mIsActiveSource) {
|
||||
return;
|
||||
}
|
||||
addAndStartAction(new ActiveSourceAction(this, dest));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.hdmi.Constants.ADDR_TV;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.IThermalService;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.os.test.TestLooper;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** Tests for {@link ActiveSourceAction} */
|
||||
@SmallTest
|
||||
@RunWith(JUnit4.class)
|
||||
public class ActiveSourceActionTest {
|
||||
|
||||
private Context mContextSpy;
|
||||
private HdmiControlService mHdmiControlService;
|
||||
private FakeNativeWrapper mNativeWrapper;
|
||||
|
||||
private TestLooper mTestLooper = new TestLooper();
|
||||
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
|
||||
private int mPhysicalAddress;
|
||||
|
||||
@Mock private IPowerManager mIPowerManagerMock;
|
||||
@Mock private IThermalService mIThermalServiceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
|
||||
PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
|
||||
mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
|
||||
when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
|
||||
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
|
||||
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
|
||||
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return new AudioManager() {
|
||||
@Override
|
||||
public void setWiredDeviceConnectionState(
|
||||
int type, int state, String address, String name) {
|
||||
// Do nothing.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
void wakeUp() {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPowerStandby() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
PowerManager getPowerManager() {
|
||||
return powerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeStringSystemProperty(String key, String value) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
Looper looper = mTestLooper.getLooper();
|
||||
mHdmiControlService.setIoLooper(looper);
|
||||
mNativeWrapper = new FakeNativeWrapper();
|
||||
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
|
||||
this.mHdmiControlService, mNativeWrapper);
|
||||
mHdmiControlService.setCecController(hdmiCecController);
|
||||
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
|
||||
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
|
||||
mHdmiControlService.initPortInfo();
|
||||
mPhysicalAddress = 0x2000;
|
||||
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playbackDevice_sendsActiveSource_sendsMenuStatus() {
|
||||
HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
|
||||
mHdmiControlService);
|
||||
playbackDevice.init();
|
||||
mLocalDevices.add(playbackDevice);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
HdmiCecFeatureAction action = new com.android.server.hdmi.ActiveSourceAction(
|
||||
playbackDevice, ADDR_TV);
|
||||
playbackDevice.addAndStartAction(action);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
|
||||
playbackDevice.mAddress, mPhysicalAddress);
|
||||
HdmiCecMessage menuStatus = HdmiCecMessageBuilder.buildReportMenuStatus(
|
||||
playbackDevice.mAddress, ADDR_TV, Constants.MENU_STATE_ACTIVATED);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(menuStatus);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void audioDevice_sendsActiveSource_noMenuStatus() {
|
||||
HdmiCecLocalDeviceAudioSystem audioDevice = new HdmiCecLocalDeviceAudioSystem(
|
||||
mHdmiControlService);
|
||||
audioDevice.init();
|
||||
mLocalDevices.add(audioDevice);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
HdmiCecFeatureAction action = new com.android.server.hdmi.ActiveSourceAction(
|
||||
audioDevice, ADDR_TV);
|
||||
audioDevice.addAndStartAction(action);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(audioDevice.mAddress,
|
||||
mPhysicalAddress);
|
||||
HdmiCecMessage menuStatus = HdmiCecMessageBuilder.buildReportMenuStatus(
|
||||
audioDevice.mAddress, ADDR_TV, Constants.MENU_STATE_ACTIVATED);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(menuStatus);
|
||||
}
|
||||
}
|
||||
@@ -80,11 +80,21 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPlaybackDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeStringSystemProperty(String key, String value) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPowerStandby() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
PowerManager getPowerManager() {
|
||||
return powerManager;
|
||||
@@ -103,10 +113,10 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
|
||||
mHdmiControlService.initPortInfo();
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
mTestLooper.dispatchAll();
|
||||
mNativeWrapper.clearResultMessages();
|
||||
mPlaybackPhysicalAddress = 0x2000;
|
||||
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
|
||||
mTestLooper.dispatchAll();
|
||||
mNativeWrapper.clearResultMessages();
|
||||
}
|
||||
|
||||
// Playback device does not handle routing control related feature right now
|
||||
@@ -178,8 +188,16 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(hasSendKeyAction()).isTrue();
|
||||
HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
|
||||
HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
|
||||
HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(keyPressed);
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(keyReleased);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -187,8 +205,16 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(hasSendKeyAction()).isTrue();
|
||||
HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
|
||||
HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN);
|
||||
HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(keyPressed);
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(keyReleased);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -196,8 +222,16 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(hasSendKeyAction()).isTrue();
|
||||
HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
|
||||
HdmiCecKeycode.CEC_KEYCODE_MUTE);
|
||||
HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(keyPressed);
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(keyReleased);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,8 +239,16 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(hasSendKeyAction()).isFalse();
|
||||
HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
|
||||
HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
|
||||
HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyPressed);
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyReleased);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -214,8 +256,16 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(hasSendKeyAction()).isFalse();
|
||||
HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
|
||||
HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
|
||||
HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyPressed);
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyReleased);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,18 +273,45 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true);
|
||||
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(hasSendKeyAction()).isFalse();
|
||||
HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
|
||||
HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
|
||||
HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyPressed);
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyReleased);
|
||||
}
|
||||
|
||||
private boolean hasSendKeyAction() {
|
||||
boolean match = false;
|
||||
for (HdmiCecFeatureAction action : mHdmiCecLocalDevicePlayback.mActions) {
|
||||
if (action instanceof SendKeyAction) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
@Test
|
||||
public void handleSetStreamPath_broadcastsActiveSource() {
|
||||
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
|
||||
mPlaybackPhysicalAddress);
|
||||
mHdmiCecLocalDevicePlayback.dispatchMessage(setStreamPath);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
|
||||
mHdmiCecLocalDevicePlayback.mAddress, mPlaybackPhysicalAddress);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleSetStreamPath_afterHotplug_broadcastsActiveSource() {
|
||||
mHdmiControlService.onHotplug(1, false);
|
||||
mHdmiControlService.onHotplug(1, true);
|
||||
|
||||
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
|
||||
mPlaybackPhysicalAddress);
|
||||
mHdmiCecLocalDevicePlayback.dispatchMessage(setStreamPath);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
|
||||
mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
|
||||
mPlaybackPhysicalAddress);
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user