Loading services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java +21 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.RemoteException; import android.util.Slog; Loading @@ -42,6 +43,10 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { private final int mTargetAddress; private final List<IHdmiControlCallback> mCallbacks = new ArrayList<>(); // Retry the power status query as it might happen when the target device is waking up. In // that case a device may be quite busy and can fail to respond within the 2s timeout. private int mRetriesOnTimeout = 1; static DevicePowerStatusAction create(HdmiCecLocalDevice source, int targetAddress, IHdmiControlCallback callback) { if (source == null || callback == null) { Loading @@ -68,7 +73,15 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { private void queryDevicePowerStatus() { sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), mTargetAddress)); mTargetAddress), error -> { // Don't retry on timeout if the remote device didn't even ACK the message. Assume // the device is not present or not capable of CEC. if (error == SendMessageResult.NACK) { // Got no response from TV. Report status 'unknown'. invokeCallback(HdmiControlManager.POWER_STATUS_UNKNOWN); finish(); } }); } @Override Loading @@ -92,6 +105,12 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { return; } if (state == STATE_WAITING_FOR_REPORT_POWER_STATUS) { if (mRetriesOnTimeout > 0) { mRetriesOnTimeout--; start(); return; } // Got no response from TV. Report status 'unknown'. invokeCallback(HdmiControlManager.POWER_STATUS_UNKNOWN); finish(); Loading services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java 0 → 100644 +217 −0 Original line number Diff line number Diff line /* * 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.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; 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 DevicePowerStatusAction} */ @SmallTest @RunWith(JUnit4.class) public class DevicePowerStatusActionTest { private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1; private Context mContextSpy; private HdmiControlService mHdmiControlService; private HdmiCecLocalDevice mPlaybackDevice; private FakeNativeWrapper mNativeWrapper; private TestLooper mTestLooper = new TestLooper(); private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); private int mPhysicalAddress; private DevicePowerStatusAction mDevicePowerStatusAction; @Mock private IHdmiControlCallback mCallbackMock; @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)); mHdmiControlService.initService(); mPhysicalAddress = 0x2000; mNativeWrapper.setPhysicalAddress(mPhysicalAddress); mPlaybackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); mPlaybackDevice.init(); mLocalDevices.add(mPlaybackDevice); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mDevicePowerStatusAction = DevicePowerStatusAction.create(mPlaybackDevice, ADDR_TV, mCallbackMock); mTestLooper.dispatchAll(); } @Test public void queryDisplayStatus_sendsRequestAndHandlesResponse() throws Exception { mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); mTestLooper.dispatchAll(); HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( mPlaybackDevice.mAddress, ADDR_TV); assertThat(mNativeWrapper.getResultMessages()).contains(expected); HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); mNativeWrapper.onCecMessage(response); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); } @Test public void queryDisplayStatus_sendsRequest_nack() throws Exception { mNativeWrapper.setMessageSendResult(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, SendMessageResult.NACK); mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_UNKNOWN); } @Test public void queryDisplayStatus_sendsRequest_timeout_retriesSuccessfully() throws Exception { mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( mPlaybackDevice.mAddress, ADDR_TV); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expected); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expected); HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); mNativeWrapper.onCecMessage(response); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); } @Test public void queryDisplayStatus_sendsRequest_timeout_retriesFailure() throws Exception { mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); mTestLooper.dispatchAll(); HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( mPlaybackDevice.mAddress, ADDR_TV); assertThat(mNativeWrapper.getResultMessages()).contains(expected); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expected); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_UNKNOWN); } } Loading
services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java +21 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.RemoteException; import android.util.Slog; Loading @@ -42,6 +43,10 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { private final int mTargetAddress; private final List<IHdmiControlCallback> mCallbacks = new ArrayList<>(); // Retry the power status query as it might happen when the target device is waking up. In // that case a device may be quite busy and can fail to respond within the 2s timeout. private int mRetriesOnTimeout = 1; static DevicePowerStatusAction create(HdmiCecLocalDevice source, int targetAddress, IHdmiControlCallback callback) { if (source == null || callback == null) { Loading @@ -68,7 +73,15 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { private void queryDevicePowerStatus() { sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), mTargetAddress)); mTargetAddress), error -> { // Don't retry on timeout if the remote device didn't even ACK the message. Assume // the device is not present or not capable of CEC. if (error == SendMessageResult.NACK) { // Got no response from TV. Report status 'unknown'. invokeCallback(HdmiControlManager.POWER_STATUS_UNKNOWN); finish(); } }); } @Override Loading @@ -92,6 +105,12 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { return; } if (state == STATE_WAITING_FOR_REPORT_POWER_STATUS) { if (mRetriesOnTimeout > 0) { mRetriesOnTimeout--; start(); return; } // Got no response from TV. Report status 'unknown'. invokeCallback(HdmiControlManager.POWER_STATUS_UNKNOWN); finish(); Loading
services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java 0 → 100644 +217 −0 Original line number Diff line number Diff line /* * 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.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; 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 DevicePowerStatusAction} */ @SmallTest @RunWith(JUnit4.class) public class DevicePowerStatusActionTest { private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1; private Context mContextSpy; private HdmiControlService mHdmiControlService; private HdmiCecLocalDevice mPlaybackDevice; private FakeNativeWrapper mNativeWrapper; private TestLooper mTestLooper = new TestLooper(); private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); private int mPhysicalAddress; private DevicePowerStatusAction mDevicePowerStatusAction; @Mock private IHdmiControlCallback mCallbackMock; @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)); mHdmiControlService.initService(); mPhysicalAddress = 0x2000; mNativeWrapper.setPhysicalAddress(mPhysicalAddress); mPlaybackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); mPlaybackDevice.init(); mLocalDevices.add(mPlaybackDevice); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mDevicePowerStatusAction = DevicePowerStatusAction.create(mPlaybackDevice, ADDR_TV, mCallbackMock); mTestLooper.dispatchAll(); } @Test public void queryDisplayStatus_sendsRequestAndHandlesResponse() throws Exception { mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); mTestLooper.dispatchAll(); HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( mPlaybackDevice.mAddress, ADDR_TV); assertThat(mNativeWrapper.getResultMessages()).contains(expected); HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); mNativeWrapper.onCecMessage(response); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); } @Test public void queryDisplayStatus_sendsRequest_nack() throws Exception { mNativeWrapper.setMessageSendResult(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, SendMessageResult.NACK); mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_UNKNOWN); } @Test public void queryDisplayStatus_sendsRequest_timeout_retriesSuccessfully() throws Exception { mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( mPlaybackDevice.mAddress, ADDR_TV); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expected); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expected); HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); mNativeWrapper.onCecMessage(response); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); } @Test public void queryDisplayStatus_sendsRequest_timeout_retriesFailure() throws Exception { mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); mTestLooper.dispatchAll(); HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( mPlaybackDevice.mAddress, ADDR_TV); assertThat(mNativeWrapper.getResultMessages()).contains(expected); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expected); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_UNKNOWN); } }