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 com.android.server.hdmi;
18 
19 import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.ArgumentMatchers.anyBoolean;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.clearInvocations;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.verify;
30 
31 import android.hardware.hdmi.DeviceFeatures;
32 import android.hardware.hdmi.HdmiControlManager;
33 import android.hardware.hdmi.HdmiDeviceInfo;
34 import android.media.AudioDeviceAttributes;
35 import android.media.AudioDeviceVolumeManager;
36 import android.media.AudioManager;
37 import android.media.VolumeInfo;
38 import android.platform.test.annotations.Presubmit;
39 
40 import androidx.test.filters.SmallTest;
41 
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.junit.runners.JUnit4;
45 
46 /**
47  * Tests for absolute volume behavior where the local device is a TV and the System Audio device
48  * is an Audio System. Assumes that the TV uses ARC (rather than eARC).
49  */
50 @SmallTest
51 @Presubmit
52 @RunWith(JUnit4.class)
53 public class TvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehaviorTest {
54 
55     @Override
createLocalDevice(HdmiControlService hdmiControlService)56     protected HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService) {
57         return new HdmiCecLocalDeviceTv(hdmiControlService);
58     }
59 
60     @Override
getPhysicalAddress()61     protected int getPhysicalAddress() {
62         return 0x0000;
63     }
64 
65     @Override
getDeviceType()66     protected int getDeviceType() {
67         return HdmiDeviceInfo.DEVICE_TV;
68     }
69 
70     @Override
getAudioOutputDevice()71     protected AudioDeviceAttributes getAudioOutputDevice() {
72         return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_ARC;
73     }
74 
75     @Override
getSystemAudioDeviceLogicalAddress()76     protected int getSystemAudioDeviceLogicalAddress() {
77         return Constants.ADDR_AUDIO_SYSTEM;
78     }
79 
80     @Override
getSystemAudioDeviceType()81     protected int getSystemAudioDeviceType() {
82         return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
83     }
84 
85     /**
86      * TVs start the process for adopting adjust-only AVB if the System Audio device doesn't
87      * support <Set Audio Volume Level>
88      */
89     @Test
savlNotSupported_allOtherConditionsMet_giveAudioStatusSent()90     public void savlNotSupported_allOtherConditionsMet_giveAudioStatusSent() {
91         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
92                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
93         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
94         enableSystemAudioModeIfNeeded();
95         verifyGiveAudioStatusNeverSent();
96 
97         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
98         verifyGiveAudioStatusSent();
99     }
100 
101     @Test
savlNotSupported_systemAudioDeviceSendsReportAudioStatus_adjustOnlyAvbEnabled()102     public void savlNotSupported_systemAudioDeviceSendsReportAudioStatus_adjustOnlyAvbEnabled() {
103         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
104                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
105         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
106         enableSystemAudioModeIfNeeded();
107         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
108 
109         // Adjust-only AVB should not be enabled before receiving <Report Audio Status>
110         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
111                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
112 
113         receiveReportAudioStatus(20, false);
114 
115         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
116                 AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
117 
118         verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
119                 eq(getAudioOutputDevice()),
120                 eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
121                         .setVolumeIndex(20)
122                         .setMuted(false)
123                         .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
124                         .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
125                         .build()),
126                 any(), any(), anyBoolean());
127     }
128 
129 
130     @Test
avbEnabled_savlNotSupported_receiveReportAudioStatus_switchToAdjustOnlyAvb()131     public void avbEnabled_savlNotSupported_receiveReportAudioStatus_switchToAdjustOnlyAvb() {
132         enableAbsoluteVolumeBehavior();
133 
134         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
135 
136         receiveReportAudioStatus(40, true);
137 
138         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
139                 AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
140 
141         verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
142                 eq(getAudioOutputDevice()),
143                 eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
144                         .setVolumeIndex(40)
145                         .setMuted(true)
146                         .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
147                         .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
148                         .build()),
149                 any(), any(), anyBoolean());
150     }
151 
152     @Test
avbEnabled_savlFeatureAborted_receiveReportAudioStatus_switchToAdjustOnlyAvb()153     public void avbEnabled_savlFeatureAborted_receiveReportAudioStatus_switchToAdjustOnlyAvb() {
154         enableAbsoluteVolumeBehavior();
155 
156         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
157                 getSystemAudioDeviceLogicalAddress(), getLogicalAddress(),
158                 Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE));
159         mTestLooper.dispatchAll();
160 
161         receiveReportAudioStatus(40, true);
162 
163         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
164                 AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
165 
166         verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
167                 eq(getAudioOutputDevice()),
168                 eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
169                         .setVolumeIndex(40)
170                         .setMuted(true)
171                         .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
172                         .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
173                         .build()),
174                 any(), any(), anyBoolean());
175     }
176 
177     @Test
adjustOnlyAvbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges()178     public void adjustOnlyAvbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() {
179         enableAdjustOnlyAbsoluteVolumeBehavior();
180 
181         // New volume and mute status: sets both
182         receiveReportAudioStatus(20, true);
183         verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5),
184                 anyInt());
185         verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
186                 eq(AudioManager.ADJUST_MUTE), anyInt());
187         clearInvocations(mAudioManager);
188 
189         // New volume only: sets volume only
190         receiveReportAudioStatus(32, true);
191         verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
192                 anyInt());
193         verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
194                 eq(AudioManager.ADJUST_MUTE), anyInt());
195         clearInvocations(mAudioManager);
196 
197         // New mute status only: sets mute only
198         receiveReportAudioStatus(32, false);
199         verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
200                 anyInt());
201         verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
202                 eq(AudioManager.ADJUST_UNMUTE), anyInt());
203         clearInvocations(mAudioManager);
204 
205         // Repeat of earlier message: sets neither volume nor mute
206         receiveReportAudioStatus(32, false);
207         verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
208                 anyInt());
209         verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
210                 eq(AudioManager.ADJUST_UNMUTE), anyInt());
211 
212         // Volume not within range [0, 100]: sets neither volume nor mute
213         receiveReportAudioStatus(127, true);
214         verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
215                 anyInt());
216         verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
217                 anyInt());
218     }
219 
220     @Test
adjustOnlyAvbEnabled_audioDeviceVolumeAdjusted_sendsUcpAndGiveAudioStatus()221     public void adjustOnlyAvbEnabled_audioDeviceVolumeAdjusted_sendsUcpAndGiveAudioStatus() {
222         enableAdjustOnlyAbsoluteVolumeBehavior();
223         mNativeWrapper.clearResultMessages();
224 
225         mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted(
226                 getAudioOutputDevice(),
227                 new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
228                         .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
229                         .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
230                         .build(),
231                 AudioManager.ADJUST_RAISE,
232                 AudioDeviceVolumeManager.ADJUST_MODE_NORMAL
233         );
234         mTestLooper.dispatchAll();
235 
236         assertThat(mNativeWrapper.getResultMessages()).contains(
237                 HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(),
238                         getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP));
239         assertThat(mNativeWrapper.getResultMessages()).contains(
240                 HdmiCecMessageBuilder.buildUserControlReleased(getLogicalAddress(),
241                         getSystemAudioDeviceLogicalAddress()));
242         assertThat(mNativeWrapper.getResultMessages()).contains(
243                 HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(),
244                         getSystemAudioDeviceLogicalAddress()));
245     }
246 
247     @Test
adjustOnlyAvbEnabled_audioDeviceVolumeChanged_doesNotSendSetAudioVolumeLevel()248     public void adjustOnlyAvbEnabled_audioDeviceVolumeChanged_doesNotSendSetAudioVolumeLevel() {
249         enableAdjustOnlyAbsoluteVolumeBehavior();
250 
251         mNativeWrapper.clearResultMessages();
252 
253         mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged(
254                 getAudioOutputDevice(),
255                 new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
256                         .setVolumeIndex(20)
257                         .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
258                         .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
259                         .build()
260         );
261         mTestLooper.dispatchAll();
262 
263         assertThat(mNativeWrapper.getResultMessages()).isEmpty();
264     }
265 }
266