diff options
| author | 2020-05-29 13:52:50 +0000 | |
|---|---|---|
| committer | 2020-05-29 13:52:50 +0000 | |
| commit | 8d3583e81ea5879602a32b857d3b266ce7d11603 (patch) | |
| tree | 616a138a991201fa6e2558b20bea7e86dd27a8d0 | |
| parent | d220529254c319dba900258f13ba8441f7262eaa (diff) | |
| parent | 281db8f6ddc7a68328694abece06d2914324b4ad (diff) | |
Merge "CEC: Send Active Source as an action" into rvc-dev
5 files changed, 330 insertions, 31 deletions
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceAction.java b/services/core/java/com/android/server/hdmi/ActiveSourceAction.java new file mode 100644 index 000000000000..3c4dae0f6467 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/ActiveSourceAction.java @@ -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 + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 67861c28b37e..d6993b2b38e9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -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(); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index ae008b4bfa7a..df6b40dbbcb8 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -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)); } /** diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java new file mode 100644 index 000000000000..50086affcbc5 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java @@ -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); + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index b76211895ab0..145e1ec56e4a 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -81,11 +81,21 @@ public class HdmiCecLocalDevicePlaybackTest { } @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(); + + HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); - assertThat(hasSendKeyAction()).isTrue(); + 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(); + + HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN); + HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); - assertThat(hasSendKeyAction()).isTrue(); + 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(); + + HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_MUTE); + HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); - assertThat(hasSendKeyAction()).isTrue(); + 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(); + + HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); - assertThat(hasSendKeyAction()).isFalse(); + 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); } } |