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.SystemService.PHASE_SYSTEM_SERVICES_READY; 24 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; 25 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; 26 import static com.android.server.hdmi.Constants.ADDR_TV; 27 import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_DEVICE_POWER_ON; 28 import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_REPORT_POWER_STATUS; 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.Looper; 38 import android.os.test.TestLooper; 39 import android.platform.test.annotations.Presubmit; 40 41 import androidx.test.InstrumentationRegistry; 42 import androidx.test.filters.SmallTest; 43 44 import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer; 45 46 import org.junit.Before; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.junit.runners.JUnit4; 50 51 import java.util.ArrayList; 52 import java.util.Collections; 53 54 @SmallTest 55 @Presubmit 56 @RunWith(JUnit4.class) 57 public class DeviceSelectActionFromTvTest { 58 59 private static final int PORT_1 = 1; 60 private static final int PORT_2 = 1; 61 private static final int PHYSICAL_ADDRESS_PLAYBACK_1 = 0x1000; 62 private static final int PHYSICAL_ADDRESS_PLAYBACK_2 = 0x2000; 63 64 private static final byte[] POWER_ON = new byte[] { POWER_STATUS_ON }; 65 private static final byte[] POWER_STANDBY = new byte[] { POWER_STATUS_STANDBY }; 66 private static final byte[] POWER_TRANSIENT_TO_ON = new byte[] { POWER_STATUS_TRANSIENT_TO_ON }; 67 private static final HdmiCecMessage REPORT_POWER_STATUS_ON = HdmiCecMessage.build( 68 ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON); 69 private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = HdmiCecMessage.build( 70 ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY); 71 private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = HdmiCecMessage.build( 72 ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON); 73 private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath( 74 ADDR_TV, PHYSICAL_ADDRESS_PLAYBACK_1); 75 private static final HdmiDeviceInfo INFO_PLAYBACK_1 = HdmiDeviceInfo.cecDeviceBuilder() 76 .setLogicalAddress(ADDR_PLAYBACK_1) 77 .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_1) 78 .setPortId(PORT_1) 79 .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK) 80 .setVendorId(0x1234) 81 .setDisplayName("Plyback 1") 82 .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON) 83 .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B) 84 .build(); 85 private static final HdmiDeviceInfo INFO_PLAYBACK_2 = HdmiDeviceInfo.cecDeviceBuilder() 86 .setLogicalAddress(ADDR_PLAYBACK_2) 87 .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_2) 88 .setPortId(PORT_2) 89 .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK) 90 .setVendorId(0x1234) 91 .setDisplayName("Playback 2") 92 .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON) 93 .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B) 94 .build(); 95 96 private HdmiControlService mHdmiControlService; 97 private HdmiCecController mHdmiCecController; 98 private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv; 99 private FakeNativeWrapper mNativeWrapper; 100 private FakePowerManagerWrapper mPowerManager; 101 private Looper mMyLooper; 102 private TestLooper mTestLooper = new TestLooper(); 103 104 @Before setUp()105 public void setUp() { 106 Context context = InstrumentationRegistry.getTargetContext(); 107 mMyLooper = mTestLooper.getLooper(); 108 109 FakeAudioFramework audioFramework = new FakeAudioFramework(); 110 111 mHdmiControlService = 112 new HdmiControlService(InstrumentationRegistry.getTargetContext(), 113 Collections.singletonList(HdmiDeviceInfo.DEVICE_TV), 114 audioFramework.getAudioManager(), 115 audioFramework.getAudioDeviceVolumeManager()) { 116 @Override 117 boolean isCecControlEnabled() { 118 return true; 119 } 120 121 @Override 122 protected void writeStringSystemProperty(String key, String value) { 123 // do nothing 124 } 125 126 @Override 127 boolean isPowerStandbyOrTransient() { 128 return false; 129 } 130 }; 131 132 133 mHdmiControlService.setIoLooper(mMyLooper); 134 mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context)); 135 mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper()); 136 mNativeWrapper = new FakeNativeWrapper(); 137 mHdmiCecController = HdmiCecController.createWithNativeWrapper( 138 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); 139 mHdmiControlService.setCecController(mHdmiCecController); 140 mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService)); 141 HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2]; 142 hdmiPortInfos[0] = 143 new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_1) 144 .setCecSupported(true) 145 .setMhlSupported(false) 146 .setArcSupported(false) 147 .build(); 148 hdmiPortInfos[1] = 149 new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_2) 150 .setCecSupported(true) 151 .setMhlSupported(false) 152 .setArcSupported(false) 153 .build(); 154 mNativeWrapper.setPortInfo(hdmiPortInfos); 155 mHdmiControlService.initService(); 156 mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY); 157 mPowerManager = new FakePowerManagerWrapper(context); 158 mHdmiControlService.setPowerManager(mPowerManager); 159 mNativeWrapper.setPhysicalAddress(0x0000); 160 mTestLooper.dispatchAll(); 161 mNativeWrapper.clearResultMessages(); 162 mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_1); 163 mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_2); 164 mHdmiCecLocalDeviceTv = mHdmiControlService.tv(); 165 } 166 167 private static class TestActionTimer implements ActionTimer { 168 private int mState; 169 170 @Override sendTimerMessage(int state, long delayMillis)171 public void sendTimerMessage(int state, long delayMillis) { 172 mState = state; 173 } 174 175 @Override clearTimerMessage()176 public void clearTimerMessage() { 177 } 178 getState()179 private int getState() { 180 return mState; 181 } 182 } 183 184 private static class TestCallback extends IHdmiControlCallback.Stub { 185 private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>(); 186 187 @Override onComplete(int result)188 public void onComplete(int result) { 189 mCallbackResult.add(result); 190 } 191 getResult()192 private int getResult() { 193 assertThat(mCallbackResult.size()).isEqualTo(1); 194 return mCallbackResult.get(0); 195 } 196 } 197 createDeviceSelectAction(TestActionTimer actionTimer, TestCallback callback, boolean isCec20)198 private DeviceSelectActionFromTv createDeviceSelectAction(TestActionTimer actionTimer, 199 TestCallback callback, 200 boolean isCec20) { 201 HdmiDeviceInfo hdmiDeviceInfo = 202 mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(ADDR_PLAYBACK_1); 203 DeviceSelectActionFromTv action = new DeviceSelectActionFromTv(mHdmiCecLocalDeviceTv, 204 hdmiDeviceInfo, callback, isCec20); 205 action.setActionTimer(actionTimer); 206 return action; 207 } 208 209 @Test testDeviceSelect_DeviceInPowerOnStatus_Cec14b()210 public void testDeviceSelect_DeviceInPowerOnStatus_Cec14b() { 211 // TV was watching playback2 device connected at port 2, and wants to select 212 // playback1. 213 TestActionTimer actionTimer = new TestActionTimer(); 214 TestCallback callback = new TestCallback(); 215 DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback, 216 /*isCec20=*/false); 217 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 218 "testDeviceSelect"); 219 action.start(); 220 mTestLooper.dispatchAll(); 221 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 222 mNativeWrapper.clearResultMessages(); 223 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 224 action.processCommand(REPORT_POWER_STATUS_ON); 225 mTestLooper.dispatchAll(); 226 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 227 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 228 } 229 230 @Test testDeviceSelect_DeviceInStandbyStatus_Cec14b()231 public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() { 232 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 233 "testDeviceSelect"); 234 TestActionTimer actionTimer = new TestActionTimer(); 235 TestCallback callback = new TestCallback(); 236 DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback, 237 /*isCec20=*/false); 238 action.start(); 239 mTestLooper.dispatchAll(); 240 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 241 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 242 action.processCommand(REPORT_POWER_STATUS_STANDBY); 243 mTestLooper.dispatchAll(); 244 HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( 245 ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); 246 assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed); 247 mNativeWrapper.clearResultMessages(); 248 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 249 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 250 action.processCommand(REPORT_POWER_STATUS_ON); 251 mTestLooper.dispatchAll(); 252 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 253 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 254 } 255 256 @Test testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b()257 public void testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b() { 258 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 259 "testDeviceSelect"); 260 TestActionTimer actionTimer = new TestActionTimer(); 261 TestCallback callback = new TestCallback(); 262 DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback, 263 /*isCec20=*/false); 264 action.start(); 265 mTestLooper.dispatchAll(); 266 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 267 mNativeWrapper.clearResultMessages(); 268 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 269 action.processCommand(REPORT_POWER_STATUS_STANDBY); 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_TRANSIENT_TO_ON); 274 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 275 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 276 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 277 action.processCommand(REPORT_POWER_STATUS_ON); 278 mTestLooper.dispatchAll(); 279 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 280 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 281 } 282 283 @Test testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b()284 public void testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b() { 285 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 286 "testDeviceSelect"); 287 TestActionTimer actionTimer = new TestActionTimer(); 288 TestCallback callback = new TestCallback(); 289 DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback, 290 /*isCec20=*/false); 291 action.start(); 292 mTestLooper.dispatchAll(); 293 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 294 mNativeWrapper.clearResultMessages(); 295 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 296 action.processCommand(REPORT_POWER_STATUS_STANDBY); 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.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON); 301 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 302 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 303 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 304 action.handleTimerEvent(STATE_WAIT_FOR_REPORT_POWER_STATUS); 305 // Give up getting power status, and just send <Set Stream Path> 306 mTestLooper.dispatchAll(); 307 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 308 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 309 } 310 311 @Test testDeviceSelect_DeviceInPowerOnStatus_Cec20()312 public void testDeviceSelect_DeviceInPowerOnStatus_Cec20() { 313 mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, 314 HdmiControlManager.POWER_STATUS_ON); 315 TestActionTimer actionTimer = new TestActionTimer(); 316 TestCallback callback = new TestCallback(); 317 DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback, 318 /*isCec20=*/true); 319 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 320 "testDeviceSelect"); 321 action.start(); 322 mTestLooper.dispatchAll(); 323 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 324 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 325 } 326 327 @Test testDeviceSelect_DeviceInPowerUnknownStatus_Cec20()328 public void testDeviceSelect_DeviceInPowerUnknownStatus_Cec20() { 329 mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, 330 HdmiControlManager.POWER_STATUS_UNKNOWN); 331 TestActionTimer actionTimer = new TestActionTimer(); 332 TestCallback callback = new TestCallback(); 333 DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback, 334 /*isCec20=*/true); 335 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 336 "testDeviceSelect"); 337 action.start(); 338 mTestLooper.dispatchAll(); 339 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 340 mNativeWrapper.clearResultMessages(); 341 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 342 action.processCommand(REPORT_POWER_STATUS_ON); 343 mTestLooper.dispatchAll(); 344 assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); 345 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 346 } 347 348 @Test testDeviceSelect_DeviceInStandbyStatus_Cec20()349 public void testDeviceSelect_DeviceInStandbyStatus_Cec20() { 350 mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_1, 351 HdmiControlManager.POWER_STATUS_STANDBY); 352 mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, 353 "testDeviceSelect"); 354 TestActionTimer actionTimer = new TestActionTimer(); 355 TestCallback callback = new TestCallback(); 356 DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback, 357 /*isCec20=*/true); 358 action.start(); 359 mTestLooper.dispatchAll(); 360 assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); 361 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); 362 action.processCommand(REPORT_POWER_STATUS_STANDBY); 363 mTestLooper.dispatchAll(); 364 HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( 365 ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); 366 assertThat(mNativeWrapper.getResultMessages()).doesNotContain(userControlPressed); 367 mNativeWrapper.clearResultMessages(); 368 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); 369 action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON); 370 action.processCommand(REPORT_POWER_STATUS_ON); 371 mTestLooper.dispatchAll(); 372 assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); 373 assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); 374 } 375 } 376