1 /* 2 * Copyright (C) 2022 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; 18 19 import static com.google.common.truth.Truth.assertWithMessage; 20 21 import static org.junit.Assume.assumeTrue; 22 23 import org.junit.Before; 24 25 /** 26 * <pre> 27 * Abstract base class for tests for absolute volume behavior (AVB). Subclasses must call 28 * this class's constructor to specify the device type of the DUT and the System Audio device. 29 * 30 * The three valid pairs of (DUT type, System Audio device type) for AVB are as follows: 31 * (Playback, TV); (Playback, Audio System); (TV, Audio System). 32 * 33 * Unfortunately, it is infeasible to test cases where the System Audio device is an Audio System, 34 * because the CEC adapter responds <Feature Abort> to <Set Audio Volume Level> when it is started 35 * as an Audio System. 36 * 37 * However, there is a special case for the (TV, Audio System) pair that can be tested: 38 * When the DUT is a TV, it may adopt *adjust-only* absolute volume behavior if all of the 39 * conditions for absolute volume behavior are met, except the Audio System does not support 40 * incoming <Set Audio Volume Level> messages. 41 * 42 * To summarize, these are all of the valid the (DUT, System Audio device, volume behavior) triplets 43 * and how/whether they can be tested: 44 * 45 * - (Playback, TV, absolute volume behavior): 46 * Tested in {@link android.hdmicec.cts.playback.HdmiCecAvbToTvTest} 47 * 48 * - (Playback, Audio System, absolute volume behavior): 49 * Infeasible to test because CEC adapter feature aborts <Set Audio Volume Level> 50 * 51 * - (TV, Audio System, absolute volume behavior): 52 * Infeasible to test because CEC adapter feature aborts <Set Audio Volume Level> 53 * 54 * - (TV, Audio System, adjust-only absolute volume behavior): 55 * Tested in {@link android.hdmicec.cts.tv.HdmiCecAvbToAudioSystemTest} 56 * </pre> 57 */ 58 public abstract class BaseHdmiCecAbsoluteVolumeBehaviorTest extends BaseHdmiCecCtsTest { 59 60 /** 61 * Constructor. The test device type and client params (determining the client's device type) 62 * passed in here determine the behavior of the tests. 63 */ BaseHdmiCecAbsoluteVolumeBehaviorTest(@dmiCecConstants.CecDeviceType int testDeviceType, String... clientParams)64 public BaseHdmiCecAbsoluteVolumeBehaviorTest(@HdmiCecConstants.CecDeviceType int testDeviceType, 65 String... clientParams) { 66 super(testDeviceType, clientParams); 67 } 68 69 /** 70 * Returns the audio output device being used. 71 */ getAudioOutputDevice()72 public int getAudioOutputDevice() { 73 if (mTestDeviceType == HdmiCecConstants.CEC_DEVICE_TYPE_TV) { 74 return HdmiCecConstants.DEVICE_OUT_HDMI_ARC; 75 } else { 76 return HdmiCecConstants.DEVICE_OUT_HDMI; 77 } 78 } 79 80 @Before 81 @Override setUp()82 public void setUp() throws Exception { 83 super.setUp(); 84 85 // This setting must be enabled to use AVB. Start with it disabled to ensure that we can 86 // control when the AVB initiation process starts. 87 setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED, 88 HdmiCecConstants.VOLUME_CONTROL_DISABLED); 89 90 // Disable and enable CEC on the DUT to clear its knowledge of device feature support. 91 // If the DUT isn't a TV, simulate a connected sink as well. 92 if (mTestDeviceType == HdmiCecConstants.CEC_DEVICE_TYPE_TV) { 93 getDevice().executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0"); 94 waitForCondition(() -> !isCecEnabled(getDevice()), "Could not disable CEC"); 95 getDevice().executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1"); 96 waitForCondition(() -> isCecEnabled(getDevice()), "Could not enable CEC"); 97 } else { 98 simulateCecSinkConnected(getDevice(), getTargetLogicalAddress()); 99 } 100 101 // Full volume behavior is a prerequisite for AVB. However, we cannot control this 102 // condition from CTS tests or shell due to missing permissions. Therefore, we run these 103 // tests only if it is already true. 104 assumeTrue(isFullVolumeDevice(getAudioOutputDevice())); 105 } 106 107 /** 108 * Enables System Audio Mode if the System Audio device is an Audio System. 109 */ enableSystemAudioModeIfApplicable()110 protected void enableSystemAudioModeIfApplicable() throws Exception { 111 if (hdmiCecClient.getSelfDevice() == LogicalAddress.AUDIO_SYSTEM) { 112 broadcastSystemAudioModeMessage(true); 113 } 114 } 115 116 /** 117 * Has the CEC client broadcast a message enabling or disabling System Audio Mode. 118 */ broadcastSystemAudioModeMessage(boolean val)119 protected void broadcastSystemAudioModeMessage(boolean val) throws Exception { 120 hdmiCecClient.sendCecMessage( 121 hdmiCecClient.getSelfDevice(), 122 LogicalAddress.BROADCAST, 123 CecOperand.SET_SYSTEM_AUDIO_MODE, 124 CecMessage.formatParams(val ? 1 : 0)); 125 } 126 127 /** 128 * Has the CEC client send a <Report Features> message expressing support or lack of support for 129 * <Set Audio Volume Level>. 130 */ sendReportFeatures(boolean setAudioVolumeLevelSupport)131 protected void sendReportFeatures(boolean setAudioVolumeLevelSupport) throws Exception { 132 String deviceTypeNibble = hdmiCecClient.getSelfDevice() == LogicalAddress.TV 133 ? "80" : "08"; 134 String featureSupportNibble = setAudioVolumeLevelSupport ? "01" : "00"; 135 hdmiCecClient.sendCecMessage( 136 hdmiCecClient.getSelfDevice(), 137 LogicalAddress.BROADCAST, 138 CecOperand.REPORT_FEATURES, 139 CecMessage.formatParams("06" + deviceTypeNibble + "00" + featureSupportNibble)); 140 } 141 assertAbsoluteVolumeBehavior()142 protected void assertAbsoluteVolumeBehavior() throws Exception { 143 waitForCondition(() -> isAbsoluteVolumeDevice(getAudioOutputDevice()), 144 "Not using absolute volume behavior"); 145 } 146 assertAdjustOnlyAbsoluteVolumeBehavior()147 protected void assertAdjustOnlyAbsoluteVolumeBehavior() throws Exception { 148 waitForCondition(() -> isAdjustOnlyAbsoluteVolumeDevice(getAudioOutputDevice()), 149 "Not using adjust-only absolute volume behavior"); 150 } 151 assertFullVolumeBehavior()152 protected void assertFullVolumeBehavior() throws Exception { 153 waitForCondition(() -> isFullVolumeDevice(getAudioOutputDevice()), 154 "Not using full absolute volume behavior"); 155 } 156 157 /** 158 * Asserts that the DUT's volume (scale: [0, 100]) is within 5 points of an expected volume. 159 * This accounts for small differences due to rounding when converting between volume scales. 160 * Also asserts that the DUT's mute status is equal to {@code expectedMute}. 161 * 162 * Asserting both volume and mute at the same time saves a shell command because both are 163 * conveyed in a single log message. 164 */ assertApproximateDeviceVolumeAndMute(int expectedVolume, boolean expectedMute)165 protected void assertApproximateDeviceVolumeAndMute(int expectedVolume, boolean expectedMute) 166 throws Exception { 167 // Raw output is equal to volume out of 100, plus 128 if muted 168 // In practice, if the stream is muted, volume equals 0, so this will be at most 128 169 int rawOutput = AudioManagerHelper.getDutAudioVolume(getDevice()); 170 171 int actualVolume = rawOutput % 128; 172 assertWithMessage("Expected DUT to have volume " + expectedVolume 173 + " but was actually " + actualVolume) 174 .that(Math.abs(expectedVolume - actualVolume) <= 5) 175 .isTrue(); 176 177 boolean actualMute = rawOutput >= 128; 178 String expectedMuteString = expectedMute ? "muted" : "unmuted"; 179 String actualMuteString = actualMute ? "muted" : "unmuted"; 180 assertWithMessage("Expected DUT to be " + expectedMuteString 181 + "but was actually " + actualMuteString) 182 .that(expectedMute) 183 .isEqualTo(actualMute); 184 } 185 } 186