1 /* 2 * Copyright (C) 2020 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 android.hdmicec.cts.common; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assume.assumeTrue; 22 23 import android.hdmicec.cts.BaseHdmiCecCtsTest; 24 import android.hdmicec.cts.CecMessage; 25 import android.hdmicec.cts.CecOperand; 26 import android.hdmicec.cts.HdmiCecConstants; 27 import android.hdmicec.cts.LogHelper; 28 import android.hdmicec.cts.LogicalAddress; 29 30 import com.android.tradefed.device.ITestDevice; 31 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 32 33 import org.junit.Before; 34 import org.junit.Rule; 35 import org.junit.Test; 36 import org.junit.rules.RuleChain; 37 import org.junit.runner.RunWith; 38 39 import java.util.concurrent.TimeUnit; 40 41 /** HDMI CEC test to verify that device ignores invalid messages (Section 12) */ 42 @RunWith(DeviceJUnit4ClassRunner.class) 43 public final class HdmiCecInvalidMessagesTest extends BaseHdmiCecCtsTest { 44 45 /** The package name of the APK. */ 46 private static final String PACKAGE = "android.hdmicec.app"; 47 48 /** The class name of the main activity in the APK. */ 49 private static final String CLASS = "HdmiCecKeyEventCapture"; 50 51 /** The command to launch the main activity. */ 52 private static final String START_COMMAND = 53 String.format( 54 "am start -W -a android.intent.action.MAIN -n %s/%s.%s", 55 PACKAGE, PACKAGE, CLASS); 56 57 /** The command to clear the main activity. */ 58 private static final String CLEAR_COMMAND = String.format("pm clear %s", PACKAGE); 59 60 private LogicalAddress source; 61 private LogicalAddress targetLogicalAddress; 62 private LogicalAddress mNonLocalPlaybackAddress; 63 64 @Rule 65 public RuleChain ruleChain = 66 RuleChain.outerRule(CecRules.requiresCec(this)) 67 .around(CecRules.requiresLeanback(this)) 68 .around(CecRules.requiresPhysicalDevice(this)) 69 .around(hdmiCecClient); 70 71 @Before setupLogicalAddresses()72 public void setupLogicalAddresses() throws Exception { 73 source = (hasDeviceType(HdmiCecConstants.CEC_DEVICE_TYPE_TV)) ? LogicalAddress.RECORDER_1 74 : LogicalAddress.TV; 75 targetLogicalAddress = getTargetLogicalAddress(); 76 mNonLocalPlaybackAddress = 77 (targetLogicalAddress == LogicalAddress.PLAYBACK_1) 78 ? LogicalAddress.PLAYBACK_2 79 : LogicalAddress.PLAYBACK_1; 80 } 81 getUnusedPhysicalAddress(int usedValue)82 private int getUnusedPhysicalAddress(int usedValue) { 83 return (usedValue == 0x2000) ? 0x3000 : 0x2000; 84 } 85 reportPhysicalAddress(LogicalAddress logicalAddress, int physicalAddress, int deviceType)86 private void reportPhysicalAddress(LogicalAddress logicalAddress, int physicalAddress, 87 int deviceType) throws Exception { 88 String formattedPhysicalAddress = CecMessage.formatParams(physicalAddress, 89 HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH); 90 String formattedDeviceType = CecMessage.formatParams(deviceType); 91 hdmiCecClient.sendCecMessage( 92 logicalAddress, 93 LogicalAddress.BROADCAST, 94 CecOperand.REPORT_PHYSICAL_ADDRESS, 95 formattedPhysicalAddress + formattedDeviceType 96 ); 97 } 98 99 /** 100 * Test 12-1 101 * 102 * <p>Tests that the device ignores every broadcast only message that is received as directly 103 * addressed. 104 */ 105 @Test cect_12_1_BroadcastReceivedAsDirectlyAddressed()106 public void cect_12_1_BroadcastReceivedAsDirectlyAddressed() throws Exception { 107 /* <Set Menu Language> */ 108 assumeTrue("Language should be editable for this test", isLanguageEditable()); 109 final String locale = getSystemLocale(); 110 final String originalLanguage = extractLanguage(locale); 111 final String language = originalLanguage.equals("spa") ? "eng" : "spa"; 112 try { 113 hdmiCecClient.sendCecMessage( 114 source, 115 CecOperand.SET_MENU_LANGUAGE, 116 CecMessage.convertStringToHexParams(language)); 117 assertThat(originalLanguage).isEqualTo(extractLanguage(getSystemLocale())); 118 } finally { 119 // If the language was incorrectly changed during the test, restore it. 120 setSystemLocale(locale); 121 } 122 } 123 124 /** 125 * Test 12-2 126 * 127 * <p>Tests that the device ignores directly addressed message {@code <GET_CEC_VERSION>} if 128 * received as a broadcast message 129 */ 130 @Test cect_12_2_DirectlyAddressedReceivedAsBroadcast_getCecVersion()131 public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_getCecVersion() throws Exception { 132 hdmiCecClient.sendCecMessage(source, LogicalAddress.BROADCAST, CecOperand.GET_CEC_VERSION); 133 hdmiCecClient.checkOutputDoesNotContainMessage(source, CecOperand.CEC_VERSION); 134 } 135 136 /** 137 * Test 12-2 138 * 139 * <p>Tests that the device ignores directly addressed message {@code <GIVE_PHYSICAL_ADDRESS>} 140 * if received as a broadcast message 141 */ 142 @Test cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePhysicalAddress()143 public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePhysicalAddress() 144 throws Exception { 145 hdmiCecClient.sendCecMessage( 146 source, LogicalAddress.BROADCAST, CecOperand.GIVE_PHYSICAL_ADDRESS); 147 hdmiCecClient.checkOutputDoesNotContainMessage( 148 LogicalAddress.BROADCAST, CecOperand.REPORT_PHYSICAL_ADDRESS); 149 } 150 151 /** 152 * Test 12-2 153 * 154 * <p>Tests that the device ignores directly addressed message {@code <GIVE_POWER_STATUS>} if 155 * received as a broadcast message 156 */ 157 @Test cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePowerStatus()158 public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePowerStatus() throws Exception { 159 hdmiCecClient.sendCecMessage( 160 source, LogicalAddress.BROADCAST, CecOperand.GIVE_POWER_STATUS); 161 hdmiCecClient.checkOutputDoesNotContainMessage(source, CecOperand.REPORT_POWER_STATUS); 162 } 163 164 /** 165 * Test 12-2 166 * 167 * <p>Tests that the device ignores directly addressed message {@code <GIVE_DEVICE_VENDOR_ID>} 168 * if received as a broadcast message 169 */ 170 @Test cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveDeviceVendorId()171 public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveDeviceVendorId() 172 throws Exception { 173 hdmiCecClient.sendCecMessage( 174 source, LogicalAddress.BROADCAST, CecOperand.GIVE_DEVICE_VENDOR_ID); 175 hdmiCecClient.checkOutputDoesNotContainMessage( 176 LogicalAddress.BROADCAST, CecOperand.DEVICE_VENDOR_ID); 177 } 178 179 /** 180 * Test 12-2 181 * 182 * <p>Tests that the device ignores directly addressed message {@code <GIVE_OSD_NAME>} if 183 * received as a broadcast message 184 */ 185 @Test cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveOsdName()186 public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveOsdName() throws Exception { 187 hdmiCecClient.sendCecMessage(source, LogicalAddress.BROADCAST, CecOperand.GIVE_OSD_NAME); 188 hdmiCecClient.checkOutputDoesNotContainMessage(source, CecOperand.SET_OSD_NAME); 189 } 190 191 /** 192 * Test 12-2 193 * 194 * <p>Tests that the device ignores directly addressed message {@code <USER_CONTROL_PRESSED>} if 195 * received as a broadcast message 196 */ 197 @Test cect_12_2_DirectlyAddressedReceivedAsBroadcast_userControlPressed()198 public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_userControlPressed() 199 throws Exception { 200 ITestDevice device = getDevice(); 201 // Clear activity 202 device.executeShellCommand(CLEAR_COMMAND); 203 // Clear logcat. 204 device.executeAdbCommand("logcat", "-c"); 205 // Start the APK and wait for it to complete. 206 device.executeShellCommand(START_COMMAND); 207 hdmiCecClient.sendUserControlPressAndRelease( 208 source, LogicalAddress.BROADCAST, HdmiCecConstants.CEC_KEYCODE_UP, false); 209 LogHelper.assertLogDoesNotContain(getDevice(), CLASS, "Short press KEYCODE_DPAD_UP"); 210 } 211 212 /** 213 * <p>Tests that the device ignores a directly addressed message {@code <GIVE_PHYSICAL_ADDRESS>} 214 * if received as a broadcast message and its source is the device's logical address 215 */ 216 @Test cect_IgnoreDirectlyAddressedFromSameSource()217 public void cect_IgnoreDirectlyAddressedFromSameSource() 218 throws Exception { 219 hdmiCecClient.sendCecMessage( 220 targetLogicalAddress, targetLogicalAddress, CecOperand.GIVE_PHYSICAL_ADDRESS); 221 hdmiCecClient.checkOutputDoesNotContainMessage( 222 targetLogicalAddress, CecOperand.REPORT_PHYSICAL_ADDRESS); 223 } 224 225 /** 226 * <p>Tests that the device ignores a broadcasted message {@code <REQUEST_ACTIVE_SOURCE>} if its 227 * source has the logical address equal to device's logical address 228 * Change the active source to another device (a new Playback) first. 229 */ 230 @Test cect_IgnoreBroadcastedFromSameSource()231 public void cect_IgnoreBroadcastedFromSameSource() 232 throws Exception { 233 String previousPowerStateChange = setPowerStateChangeOnActiveSourceLost( 234 HdmiCecConstants.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE); 235 try { 236 int dumpsysPhysicalAddress = getDumpsysPhysicalAddress(); 237 // Add a new playback device in the network. 238 int playbackPhysicalAddress = getUnusedPhysicalAddress(dumpsysPhysicalAddress); 239 reportPhysicalAddress( 240 mNonLocalPlaybackAddress, 241 playbackPhysicalAddress, 242 HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE); 243 // Make the new Playback the active source. 244 hdmiCecClient.broadcastActiveSource(mNonLocalPlaybackAddress, playbackPhysicalAddress); 245 // Wait for the <Active Source> message to be processed by the DUT. 246 TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS); 247 248 // Press Home key and the DUT shall broadcast an <Active Source> message. 249 ITestDevice device = getDevice(); 250 device.executeShellCommand("input keyevent KEYCODE_HOME"); 251 hdmiCecClient.checkExpectedOutput( 252 LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE); 253 // The DUT shouldn't send <Active Source> again. 254 hdmiCecClient.sendCecMessage( 255 targetLogicalAddress, LogicalAddress.BROADCAST, CecOperand.REQUEST_ACTIVE_SOURCE); 256 hdmiCecClient.checkOutputDoesNotContainMessage( 257 LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE); 258 } finally { 259 // Restore the previous power state change. 260 setPowerStateChangeOnActiveSourceLost(previousPowerStateChange); 261 } 262 } 263 264 /** 265 * <p>Tests that the device ignores a directly addressed message {@code <GIVE_POWER_STATUS>} if 266 * coming from the unregistered address F. This message should only be sent from a device with 267 * an allocated logical address 268 */ 269 @Test cect_IgnoreDirectlyAddressedFromUnknownAddress_giveDevicePowerStatus()270 public void cect_IgnoreDirectlyAddressedFromUnknownAddress_giveDevicePowerStatus() 271 throws Exception { 272 hdmiCecClient.sendCecMessage( 273 LogicalAddress.UNKNOWN, targetLogicalAddress, CecOperand.GIVE_POWER_STATUS); 274 hdmiCecClient.checkOutputDoesNotContainMessage( 275 LogicalAddress.UNKNOWN, CecOperand.REPORT_POWER_STATUS); 276 } 277 278 /** 279 * <p>Tests that the device process a directly addressed message {@code <GIVE_PHYSICAL_ADDRESS>} 280 * if coming from the unregistered address F 281 */ 282 @Test cect_ProcessAddressedFromUnknownAddress_givePhysicalAddress()283 public void cect_ProcessAddressedFromUnknownAddress_givePhysicalAddress() 284 throws Exception { 285 hdmiCecClient.sendCecMessage( 286 LogicalAddress.UNKNOWN, targetLogicalAddress, CecOperand.GIVE_PHYSICAL_ADDRESS); 287 hdmiCecClient.checkExpectedOutput( 288 LogicalAddress.UNKNOWN, CecOperand.REPORT_PHYSICAL_ADDRESS); 289 } 290 } 291