diff options
| -rw-r--r-- | services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java | 33 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java | 280 |
2 files changed, 300 insertions, 13 deletions
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java index 909fcda26c39..66fc0d9c1760 100644 --- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java +++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.util.SparseIntArray; @@ -108,7 +109,10 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction { private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) { mPowerStatus.clear(); for (HdmiDeviceInfo info : deviceInfos) { - mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus()); + if (localDevice().mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0 + || info.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) { + mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus()); + } } } @@ -117,19 +121,22 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction { localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false); resetPowerStatus(deviceInfos); for (HdmiDeviceInfo info : deviceInfos) { - final int logicalAddress = info.getLogicalAddress(); - sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), - logicalAddress), - new SendMessageCallback() { - @Override - public void onSendCompleted(int error) { - // If fails to send <Give Device Power Status>, - // update power status into UNKNOWN. - if (error != SendMessageResult.SUCCESS) { - updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true); + if (localDevice().mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0 + || info.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) { + final int logicalAddress = info.getLogicalAddress(); + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), + logicalAddress), + new SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + // If fails to send <Give Device Power Status>, + // update power status into UNKNOWN. + if (error != SendMessageResult.SUCCESS) { + updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true); + } } - } - }); + }); + } } mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java new file mode 100644 index 000000000000..b8dfd5672056 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java @@ -0,0 +1,280 @@ +/* + * 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_BROADCAST; +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_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.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.hdmi.HdmiPortInfo; +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; +import java.util.concurrent.TimeUnit; + +/** Tests for {@link ActiveSourceAction} */ +@SmallTest +@RunWith(JUnit4.class) +public class PowerStatusMonitorActionTest { + + private Context mContextSpy; + private HdmiControlService mHdmiControlService; + private FakeNativeWrapper mNativeWrapper; + + private TestLooper mTestLooper = new TestLooper(); + private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); + private int mPhysicalAddress; + private HdmiCecLocalDeviceTv mTvDevice; + + @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); + + HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy); + + 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 + protected PowerManager getPowerManager() { + return powerManager; + } + + @Override + protected void writeStringSystemProperty(String key, String value) { + // do nothing + } + + @Override + protected HdmiCecConfig getHdmiCecConfig() { + return hdmiCecConfig; + } + }; + + Looper looper = mTestLooper.getLooper(); + mHdmiControlService.setIoLooper(looper); + mNativeWrapper = new FakeNativeWrapper(); + HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper( + this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); + mHdmiControlService.setCecController(hdmiCecController); + mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService)); + mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService)); + mTvDevice = new HdmiCecLocalDeviceTv(mHdmiControlService); + mTvDevice.init(); + mLocalDevices.add(mTvDevice); + mTestLooper.dispatchAll(); + HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[2]; + hdmiPortInfo[0] = + new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false); + hdmiPortInfo[1] = + new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false); + mNativeWrapper.setPortInfo(hdmiPortInfo); + mHdmiControlService.initService(); + mPhysicalAddress = 0x0000; + mNativeWrapper.setPhysicalAddress(mPhysicalAddress); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + } + + @Test + public void sourceDevice_1_4_updatesPowerState() { + sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000); + + PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice); + action.start(); + assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_UNKNOWN); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + ADDR_TV, + ADDR_PLAYBACK_1); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + reportPowerStatus(ADDR_PLAYBACK_1, false, HdmiControlManager.POWER_STATUS_ON); + assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON); + + mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(60)); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + reportPowerStatus(ADDR_PLAYBACK_1, false, HdmiControlManager.POWER_STATUS_STANDBY); + assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_STANDBY); + } + + private void assertPowerStatus(int logicalAddress, int powerStatus) { + HdmiDeviceInfo deviceInfo = mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo( + logicalAddress); + assertThat(deviceInfo).isNotNull(); + assertThat(deviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus); + } + + @Test + public void sourceDevice_2_0_doesNotUpdatePowerState() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000); + reportPowerStatus(ADDR_PLAYBACK_1, true, HdmiControlManager.POWER_STATUS_ON); + mTestLooper.dispatchAll(); + + PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice); + action.start(); + + assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + ADDR_TV, + ADDR_PLAYBACK_1); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus); + + mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(60)); + mTestLooper.dispatchAll(); + + assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON); + } + + @Test + public void mixedSourceDevices_localDevice_1_4_updatesAll() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_1_4_B); + mTestLooper.dispatchAll(); + sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000); + sendMessageFromPlaybackDevice(ADDR_PLAYBACK_2, 0x2000); + reportPowerStatus(ADDR_PLAYBACK_2, true, HdmiControlManager.POWER_STATUS_ON); + + assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_UNKNOWN); + assertPowerStatus(ADDR_PLAYBACK_2, HdmiControlManager.POWER_STATUS_ON); + + PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice); + action.start(); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + ADDR_TV, + ADDR_PLAYBACK_1); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + ADDR_TV, + ADDR_PLAYBACK_2); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus2); + } + + @Test + public void mixedSourceDevices_localDevice_2_0_onlyUpdates_1_4() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + mTestLooper.dispatchAll(); + sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000); + sendMessageFromPlaybackDevice(ADDR_PLAYBACK_2, 0x2000); + reportPowerStatus(ADDR_PLAYBACK_2, true, HdmiControlManager.POWER_STATUS_ON); + + PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice); + action.start(); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + ADDR_TV, + ADDR_PLAYBACK_1); + + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + ADDR_TV, + ADDR_PLAYBACK_2); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus2); + } + + private void sendMessageFromPlaybackDevice(int logicalAddress, int physicalAddress) { + HdmiCecMessage playbackDevice = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + logicalAddress, physicalAddress, HdmiDeviceInfo.DEVICE_PLAYBACK); + mNativeWrapper.onCecMessage(playbackDevice); + mTestLooper.dispatchAll(); + } + + private void reportPowerStatus(int logicalAddress, boolean broadcast, int powerStatus) { + int destination = broadcast ? ADDR_BROADCAST : ADDR_TV; + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + logicalAddress, destination, + powerStatus); + mNativeWrapper.onCecMessage(reportPowerStatus); + mTestLooper.dispatchAll(); + } +} |