• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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