1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.hdmi; 18 19 import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_ON; 20 import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_STANDBY; 21 import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; 22 23 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; 24 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; 25 import static com.android.server.hdmi.Constants.ADDR_TV; 26 import static com.android.server.hdmi.DeviceSelectAction.STATE_WAIT_FOR_DEVICE_POWER_ON; 27 import static com.android.server.hdmi.DeviceSelectAction.STATE_WAIT_FOR_REPORT_POWER_STATUS; 28 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; 29 30 import static com.google.common.truth.Truth.assertThat; 31 32 import android.content.Context; 33 import android.hardware.hdmi.HdmiControlManager; 34 import android.hardware.hdmi.HdmiDeviceInfo; 35 import android.hardware.hdmi.HdmiPortInfo; 36 import android.hardware.hdmi.IHdmiControlCallback; 37 import android.os.Handler; 38 import android.os.IPowerManager; 39 import android.os.IThermalService; 40 import android.os.Looper; 41 import android.os.PowerManager; 42 import android.os.test.TestLooper; 43 44 import androidx.test.InstrumentationRegistry; 45 import androidx.test.filters.SmallTest; 46 47 import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer; 48 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.junit.runners.JUnit4; 53 import org.mockito.Mock; 54 import org.mockito.MockitoAnnotations; 55 56 import java.util.ArrayList; 57 58 @SmallTest 59 @RunWith(JUnit4.class) 60 public class DeviceSelectActionTest { 61 62 private static final int PORT_1 = 1; 63 private static final int PORT_2 = 1; 64 private static final int PHYSICAL_ADDRESS_PLAYBACK_1 = 0x1000; 65 private static final int PHYSICAL_ADDRESS_PLAYBACK_2 = 0x2000; 66 67 private static final byte[] POWER_ON = new byte[] { POWER_STATUS_ON }; 68 private static final byte[] POWER_STANDBY = new byte[] { POWER_STATUS_STANDBY }; 69 private static final byte[] POWER_TRANSIENT_TO_ON = new byte[] { POWER_STATUS_TRANSIENT_TO_ON }; 70 private static final HdmiCecMessage REPORT_POWER_STATUS_ON = new HdmiCecMessage( 71 ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON); 72 private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = new HdmiCecMessage( 73 ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY); 74 private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = new HdmiCecMessage( 75 ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON); 76 private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath( 77 ADDR_TV, PHYSICAL_ADDRESS_PLAYBACK_1); 78 private static final HdmiDeviceInfo INFO_PLAYBACK_1 = new HdmiDeviceInfo( 79 ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1, HdmiDeviceInfo.DEVICE_PLAYBACK, 80 0x1234, "Playback 1", 81 HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); 82 private static final HdmiDeviceInfo INFO_PLAYBACK_2 = new HdmiDeviceInfo( 83 ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2, HdmiDeviceInfo.DEVICE_PLAYBACK, 84 0x1234, "Playback 2", 85 HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B); 86 87 private HdmiControlService mHdmiControlService; 88 private HdmiCecController mHdmiCecController; 89 private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv; 90 private FakeNativeWrapper mNativeWrapper; 91 private Looper mMyLooper; 92 private TestLooper mTestLooper = new TestLooper(); 93 private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); 94 95 @Mock 96 private IPowerManager mIPowerManagerMock; 97 @Mock 98 private IThermalService mIThermalServiceMock; 99 100 @Before setUp()101 public void setUp() { 102 MockitoAnnotations.initMocks(this); 103 104 Context context = InstrumentationRegistry.getTargetContext(); 105 mMyLooper = mTestLooper.getLooper(); 106 107 mHdmiControlService = 108 new HdmiControlService(InstrumentationRegistry.getTargetContext()) { 109 @Override 110 boolean isControlEnabled() { 111 return true; 112 } 113 114 @Override 115 void wakeUp() { 116 } 117 118 @Override 119 protected void writeStringSystemProperty(String key, String value) { 120 // do nothing 121 } 122 123 @Override 124 boolean isPowerStandbyOrTransient() { 125 return false; 126 } 127 128 @Override 129 protected PowerManager getPowerManager() { 130 return new PowerManager(context, mIPowerManagerMock, 131 mIThermalServiceMock, new Handler(mMyLooper)); 132 } 133 }; 134 135 mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService); 136 mHdmiCecLocalDeviceTv.init(); 137 mHdmiControlService.setIoLooper(mMyLooper); 138 mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context)); 139 mNativeWrapper = new FakeNativeWrapper(); 140 mHdmiCecController = HdmiCecController.createWithNativeWrapper( 141 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); 142 mHdmiControlService.setCecController(mHdmiCecController); 143 mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService)); 144 mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService)); 145 mLocalDevices.add(mHdmiCecLocalDeviceTv); 146 HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2]; 147 hdmiPortInfos[0] = 148 new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_1, 149 true, false, false); 150 hdmiPortInfos[1] = 151 new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_2, 152 true, false, false); 153 mNativeWrapper.setPortInfo(hdmiPortInfos); 154 mHdmiControlService.initService(); 155 mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); 156 mNativeWrapper.setPhysicalAddress(0x0000); 157 mTestLooper.dispatchAll(); 158 mNativeWrapper.clearResultMessages(); 159 mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_1); 160 mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_2); 161 } 162 163 private static class TestActionTimer implements ActionTimer { 164 private int mState; 165 166 @Override sendTimerMessage(int state, long delayMillis)167 public void sendTimerMessage(int state, long delayMillis) { 168 mState = state; 169 } 170 171 @Override clearTimerMessage()172 public void clearTimerMessage() { 173 } 174 getState()175 private int getState() { 176 return mState; 177 } 178 } 179 180 private static class TestCallback extends IHdmiControlCallback.Stub { 181 private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>(); 182 183 @Override onComplete(int result)184 public void onComplete(int result) { 185 mCallbackResult.add(result); 186 } 187 getResult()188 private int getResult() { 189 assertThat(mCallbackResult.size()).isEqualTo(1); 190 return mCallbackResult.get(0); 191 } 192 } 193 createDeviceSelectAction(TestActionTimer actionTimer, TestCallback callback, boolean isCec20)194 private DeviceSelectAction createDeviceSelectAction(TestActionTimer actionTimer, 195 TestCallback callback, 196 boolean isCec20) { 197 HdmiDeviceInfo hdmiDeviceInfo = 198 mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(ADDR_PLAYBACK_1); 199 DeviceSelectAction action = new DeviceSelectAction(mHdmiCecLocalDeviceTv, 200 hdmiDeviceInfo, callback, isCec20); 201 action.setActionTimer(actionTimer); 202 return action; 203 } 204 205 @Test testDeviceSelect_DeviceInPowerOnStatus_Cec14b()206 public void testDeviceSelect_DeviceInPowerOnStatus_Cec14b() { 207 // TV was watching playback2 device connected at port 2, and wants to select 208 // playback1. 209 TestActionTimer actionTimer = new TestActionTimer(); 210 TestCallback callback = new TestCallback(); 211 DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, 212 /*isCec20=*/false); 213 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 214 "testDeviceSelect"); 215 action.start(); 216 mTestLooper.dispatchAll(); 217 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 218 mNativeWrapper.clearResultMessages(); 219 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 220 action.processCommand(REPORT_POWER_STATUS_ON); 221 mTestLooper.dispatchAll(); 222 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 223 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 224 } 225 226 @Test testDeviceSelect_DeviceInStandbyStatus_Cec14b()227 public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() { 228 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 229 "testDeviceSelect"); 230 TestActionTimer actionTimer = new TestActionTimer(); 231 TestCallback callback = new TestCallback(); 232 DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, 233 /*isCec20=*/false); 234 action.start(); 235 mTestLooper.dispatchAll(); 236 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 237 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 238 action.processCommand(REPORT_POWER_STATUS_STANDBY); 239 mTestLooper.dispatchAll(); 240 HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( 241 ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); 242 assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed); 243 mNativeWrapper.clearResultMessages(); 244 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 245 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 246 action.processCommand(REPORT_POWER_STATUS_ON); 247 mTestLooper.dispatchAll(); 248 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 249 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 250 } 251 252 @Test testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b()253 public void testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b() { 254 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 255 "testDeviceSelect"); 256 TestActionTimer actionTimer = new TestActionTimer(); 257 TestCallback callback = new TestCallback(); 258 DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, 259 /*isCec20=*/false); 260 action.start(); 261 mTestLooper.dispatchAll(); 262 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 263 mNativeWrapper.clearResultMessages(); 264 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 265 action.processCommand(REPORT_POWER_STATUS_STANDBY); 266 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 267 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 268 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 269 action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON); 270 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 271 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 272 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 273 action.processCommand(REPORT_POWER_STATUS_ON); 274 mTestLooper.dispatchAll(); 275 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 276 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 277 } 278 279 @Test testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b()280 public void testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b() { 281 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 282 "testDeviceSelect"); 283 TestActionTimer actionTimer = new TestActionTimer(); 284 TestCallback callback = new TestCallback(); 285 DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, 286 /*isCec20=*/false); 287 action.start(); 288 mTestLooper.dispatchAll(); 289 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 290 mNativeWrapper.clearResultMessages(); 291 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 292 action.processCommand(REPORT_POWER_STATUS_STANDBY); 293 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 294 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 295 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 296 action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON); 297 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 298 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 299 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 300 action.handleTimerEvent(STATE_WAIT_FOR_REPORT_POWER_STATUS); 301 // Give up getting power status, and just send <Set Stream Path> 302 mTestLooper.dispatchAll(); 303 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 304 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 305 } 306 307 @Test testDeviceSelect_DeviceInPowerOnStatus_Cec20()308 public void testDeviceSelect_DeviceInPowerOnStatus_Cec20() { 309 mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, 310 HdmiControlManager.POWER_STATUS_ON); 311 TestActionTimer actionTimer = new TestActionTimer(); 312 TestCallback callback = new TestCallback(); 313 DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, 314 /*isCec20=*/true); 315 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 316 "testDeviceSelect"); 317 action.start(); 318 mTestLooper.dispatchAll(); 319 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 320 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 321 } 322 323 @Test testDeviceSelect_DeviceInPowerUnknownStatus_Cec20()324 public void testDeviceSelect_DeviceInPowerUnknownStatus_Cec20() { 325 mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, 326 HdmiControlManager.POWER_STATUS_UNKNOWN); 327 TestActionTimer actionTimer = new TestActionTimer(); 328 TestCallback callback = new TestCallback(); 329 DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, 330 /*isCec20=*/true); 331 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 332 "testDeviceSelect"); 333 action.start(); 334 mTestLooper.dispatchAll(); 335 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 336 mNativeWrapper.clearResultMessages(); 337 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 338 action.processCommand(REPORT_POWER_STATUS_ON); 339 mTestLooper.dispatchAll(); 340 assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); 341 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 342 } 343 344 @Test testDeviceSelect_DeviceInStandbyStatus_Cec20()345 public void testDeviceSelect_DeviceInStandbyStatus_Cec20() { 346 mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, 347 HdmiControlManager.POWER_STATUS_STANDBY); 348 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 349 "testDeviceSelect"); 350 TestActionTimer actionTimer = new TestActionTimer(); 351 TestCallback callback = new TestCallback(); 352 DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, 353 /*isCec20=*/true); 354 action.start(); 355 mTestLooper.dispatchAll(); 356 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 357 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 358 action.processCommand(REPORT_POWER_STATUS_STANDBY); 359 mTestLooper.dispatchAll(); 360 HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( 361 ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); 362 assertThat(mNativeWrapper.getResultMessages()).doesNotContain(userControlPressed); 363 mNativeWrapper.clearResultMessages(); 364 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 365 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 366 action.processCommand(REPORT_POWER_STATUS_ON); 367 mTestLooper.dispatchAll(); 368 assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); 369 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 370 } 371 } 372