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 com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; 20 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; 21 import static com.android.server.hdmi.Constants.ADDR_BROADCAST; 22 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; 23 import static com.android.server.hdmi.Constants.ADDR_TUNER_1; 24 import static com.android.server.hdmi.Constants.ADDR_TV; 25 import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED; 26 import static com.android.server.hdmi.Constants.MESSAGE_ACTIVE_SOURCE; 27 import static com.android.server.hdmi.Constants.MESSAGE_ROUTING_INFORMATION; 28 import static com.android.server.hdmi.RoutingControlAction.STATE_WAIT_FOR_ROUTING_INFORMATION; 29 30 import static com.google.common.truth.Truth.assertThat; 31 32 import android.content.Context; 33 import android.hardware.hdmi.HdmiDeviceInfo; 34 import android.hardware.hdmi.HdmiPortInfo; 35 import android.hardware.hdmi.IHdmiControlCallback; 36 import android.os.Looper; 37 import android.os.test.TestLooper; 38 import android.platform.test.annotations.Presubmit; 39 40 import androidx.test.InstrumentationRegistry; 41 import androidx.test.filters.SmallTest; 42 43 import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer; 44 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.junit.runners.JUnit4; 49 50 import java.util.ArrayList; 51 import java.util.Collections; 52 import java.util.List; 53 54 @SmallTest 55 @Presubmit 56 @RunWith(JUnit4.class) 57 public class RoutingControlActionTest { 58 /* 59 * Example connection diagram used in tests. Double-lined paths indicate the currently active 60 * routes. 61 * 62 * 63 * +-----------+ 64 * | TV | 65 * | 0.0.0.0 | 66 * +---+-----+-+ 67 * | | 68 * <----------+ 1) AVR -> Switch 69 * +----------+ | | +-----------+ 70 * | AVR +---------+ +--+ Switch | 71 * | 1.0.0.0 | | 2.0.0.0 | 72 * +--+---++--+ +--++-----+-+ <-------+ 2) Recorder -> Blu-ray 73 * | || || | 74 * | || || +--------+ 75 * +-----------+ | || +----------+ +----++----+ | 76 * | XBox +--+ ++--+ Tuner | | Blueray | +-----+----+ 77 * | 1.1.0.0 | | 1.2.0.0 | | 2.1.0.0 | | Recorder | 78 * +-----------+ +----++----+ +----------+ | 2.2.0.0 | 79 * || +----------+ 80 * || 81 * +----++----+ 82 * | Player | 83 * | 1.2.1.0 | 84 * +----------+ 85 * 86 */ 87 88 private static final int PHYSICAL_ADDRESS_TV = 0x0000; 89 private static final int PHYSICAL_ADDRESS_AVR = 0x1000; 90 private static final int PHYSICAL_ADDRESS_SWITCH = 0x2000; 91 private static final int PHYSICAL_ADDRESS_TUNER = 0x1200; 92 private static final int PHYSICAL_ADDRESS_PLAYER = 0x1210; 93 private static final int PHYSICAL_ADDRESS_BLUERAY = 0x2100; 94 private static final int PHYSICAL_ADDRESS_RECORDER = 0x2200; 95 private static final int PORT_1 = 1; 96 private static final int PORT_2 = 2; 97 private static final int VENDOR_ID_AVR = 0x11233; 98 99 private static final byte[] TUNER_PARAM = 100 new byte[]{(PHYSICAL_ADDRESS_TUNER >> 8) & 0xFF, PHYSICAL_ADDRESS_TUNER & 0xFF}; 101 private static final byte[] PLAYER_PARAM = 102 new byte[]{(PHYSICAL_ADDRESS_PLAYER >> 8) & 0xFF, PHYSICAL_ADDRESS_PLAYER & 0xFF}; 103 104 private static final HdmiDeviceInfo DEVICE_INFO_AVR = HdmiDeviceInfo.cecDeviceBuilder() 105 .setLogicalAddress(ADDR_AUDIO_SYSTEM) 106 .setPhysicalAddress(PHYSICAL_ADDRESS_AVR) 107 .setPortId(PORT_1) 108 .setDeviceType(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) 109 .setVendorId(VENDOR_ID_AVR) 110 .setDisplayName("Audio") 111 .build(); 112 private static final HdmiDeviceInfo DEVICE_INFO_PLAYER = HdmiDeviceInfo.cecDeviceBuilder() 113 .setLogicalAddress(ADDR_PLAYBACK_1) 114 .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYER) 115 .setPortId(PORT_1) 116 .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK) 117 .setVendorId(VENDOR_ID_AVR) 118 .setDisplayName("Player") 119 .build(); 120 private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = HdmiCecMessage.build( 121 ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, TUNER_PARAM); 122 private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = HdmiCecMessage.build( 123 ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, PLAYER_PARAM); 124 private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = HdmiCecMessage.build( 125 ADDR_TUNER_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, TUNER_PARAM); 126 private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = HdmiCecMessage.build( 127 ADDR_PLAYBACK_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, PLAYER_PARAM); 128 129 private HdmiControlService mHdmiControlService; 130 private HdmiCecController mHdmiCecController; 131 private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv; 132 private FakeNativeWrapper mNativeWrapper; 133 private FakePowerManagerWrapper mPowerManager; 134 private Looper mMyLooper; 135 private TestLooper mTestLooper = new TestLooper(); 136 createRoutingControlAction(HdmiCecLocalDeviceTv localDevice, TestInputSelectCallback callback)137 private static RoutingControlAction createRoutingControlAction(HdmiCecLocalDeviceTv localDevice, 138 TestInputSelectCallback callback) { 139 return new RoutingControlAction(localDevice, PHYSICAL_ADDRESS_AVR, callback); 140 } 141 142 @Before setUp()143 public void setUp() { 144 Context context = InstrumentationRegistry.getTargetContext(); 145 mMyLooper = mTestLooper.getLooper(); 146 147 HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context); 148 149 FakeAudioFramework audioFramework = new FakeAudioFramework(); 150 151 mHdmiControlService = 152 new HdmiControlService(InstrumentationRegistry.getTargetContext(), 153 Collections.singletonList(HdmiDeviceInfo.DEVICE_TV), 154 audioFramework.getAudioManager(), 155 audioFramework.getAudioDeviceVolumeManager()) { 156 @Override 157 boolean isCecControlEnabled() { 158 return true; 159 } 160 161 @Override 162 protected void writeStringSystemProperty(String key, String value) { 163 // do nothing 164 } 165 166 @Override 167 boolean isPowerStandbyOrTransient() { 168 return false; 169 } 170 171 @Override 172 protected HdmiCecConfig getHdmiCecConfig() { 173 return hdmiCecConfig; 174 } 175 }; 176 177 mHdmiControlService.setIoLooper(mMyLooper); 178 mNativeWrapper = new FakeNativeWrapper(); 179 mHdmiCecController = HdmiCecController.createWithNativeWrapper( 180 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); 181 mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper()); 182 mHdmiControlService.setCecController(mHdmiCecController); 183 mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService)); 184 HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1]; 185 hdmiPortInfos[0] = 186 new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR) 187 .setCecSupported(true) 188 .setMhlSupported(false) 189 .setArcSupported(false) 190 .build(); 191 mNativeWrapper.setPortInfo(hdmiPortInfos); 192 mHdmiControlService.initService(); 193 mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY); 194 mPowerManager = new FakePowerManagerWrapper(context); 195 mHdmiControlService.setPowerManager(mPowerManager); 196 mNativeWrapper.setPhysicalAddress(0x0000); 197 mTestLooper.dispatchAll(); 198 mHdmiCecLocalDeviceTv = mHdmiControlService.tv(); 199 mNativeWrapper.clearResultMessages(); 200 mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR); 201 } 202 203 // Routing control succeeds against the device connected directly to the port. Action 204 // won't get any <Routing Information> in this case. It times out on <Routing Information>, 205 // regards the directly connected one as the new routing path to switch to. 206 @Test testRoutingControl_succeedForDirectlyConnectedDevice()207 public void testRoutingControl_succeedForDirectlyConnectedDevice() { 208 TestInputSelectCallback callback = new TestInputSelectCallback(); 209 TestActionTimer actionTimer = new TestActionTimer(); 210 mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR); 211 212 RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback); 213 action.setActionTimer(actionTimer); 214 action.start(); 215 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION); 216 217 action.handleTimerEvent(actionTimer.getState()); 218 mTestLooper.dispatchAll(); 219 HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath( 220 ADDR_TV, PHYSICAL_ADDRESS_AVR); 221 assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath); 222 } 223 224 // Succeeds by receiving a couple of <Routing Information> commands, followed by 225 // <Set Stream Path> going out in the end. 226 @Test testRoutingControl_succeedForDeviceBehindSwitch()227 public void testRoutingControl_succeedForDeviceBehindSwitch() { 228 TestInputSelectCallback callback = new TestInputSelectCallback(); 229 TestActionTimer actionTimer = new TestActionTimer(); 230 mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_PLAYER); 231 RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback); 232 action.setActionTimer(actionTimer); 233 action.start(); 234 235 assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION); 236 237 action.processCommand(ROUTING_INFORMATION_TUNER); 238 action.processCommand(ROUTING_INFORMATION_PLAYER); 239 240 action.handleTimerEvent(actionTimer.getState()); 241 mTestLooper.dispatchAll(); 242 HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath( 243 ADDR_TV, PHYSICAL_ADDRESS_PLAYER); 244 assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath); 245 } 246 247 private static class TestActionTimer implements ActionTimer { 248 private int mState; 249 250 @Override sendTimerMessage(int state, long delayMillis)251 public void sendTimerMessage(int state, long delayMillis) { 252 mState = state; 253 } 254 255 @Override clearTimerMessage()256 public void clearTimerMessage() { 257 } 258 getState()259 private int getState() { 260 return mState; 261 } 262 } 263 264 private static class TestInputSelectCallback extends IHdmiControlCallback.Stub { 265 private final List<Integer> mCallbackResult = new ArrayList<Integer>(); 266 267 @Override onComplete(int result)268 public void onComplete(int result) { 269 mCallbackResult.add(result); 270 } 271 getResult()272 private int getResult() { 273 assert (mCallbackResult.size() == 1); 274 return mCallbackResult.get(0); 275 } 276 } 277 } 278