1 /*
2  * Copyright (C) 2021 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 android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
20 
21 import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
22 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_BOOT_UP;
23 
24 import static com.google.common.truth.Truth.assertThat;
25 import static com.google.common.truth.TruthJUnit.assume;
26 
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.anyBoolean;
29 import static org.mockito.ArgumentMatchers.anyInt;
30 import static org.mockito.Mockito.clearInvocations;
31 import static org.mockito.Mockito.eq;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.spy;
34 import static org.mockito.Mockito.verify;
35 
36 import android.content.Context;
37 import android.content.ContextWrapper;
38 import android.hardware.hdmi.DeviceFeatures;
39 import android.hardware.hdmi.HdmiControlManager;
40 import android.hardware.hdmi.HdmiDeviceInfo;
41 import android.hardware.hdmi.HdmiPortInfo;
42 import android.hardware.tv.cec.V1_0.SendMessageResult;
43 import android.media.AudioDeviceAttributes;
44 import android.media.AudioDeviceVolumeManager;
45 import android.media.AudioManager;
46 import android.media.VolumeInfo;
47 import android.os.Looper;
48 import android.os.RemoteException;
49 import android.os.test.TestLooper;
50 
51 import androidx.test.InstrumentationRegistry;
52 
53 import com.android.server.SystemService;
54 
55 import org.junit.Before;
56 import org.junit.Test;
57 import org.mockito.MockitoAnnotations;
58 
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.Collections;
62 
63 /**
64  * Tests that absolute volume behavior (AVB) is enabled and disabled correctly, and that
65  * the device responds correctly to incoming <Report Audio Status> messages and API calls
66  * from AudioService when AVB is active.
67  *
68  * This is an abstract base class. Concrete subclasses specify the type of the local device, and the
69  * type of the System Audio device. This allows each test to be run for multiple setups.
70  *
71  * We test the following pairs of (local device, System Audio device):
72  * (Playback, TV): {@link PlaybackDeviceToTvAvbTest}
73  * (Playback, Audio System): {@link PlaybackDeviceToAudioSystemAvbTest}
74  * (TV, Audio System): {@link TvToAudioSystemAvbTest}
75  */
76 public abstract class BaseAbsoluteVolumeBehaviorTest {
77     protected HdmiControlService mHdmiControlService;
78     private HdmiCecController mHdmiCecController;
79     private HdmiCecLocalDevice mHdmiCecLocalDevice;
80     private FakeHdmiCecConfig mHdmiCecConfig;
81     private FakePowerManagerWrapper mPowerManager;
82     private Looper mLooper;
83     private Context mContextSpy;
84     private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
85 
86     protected FakeAudioFramework mAudioFramework;
87     protected AudioManagerWrapper mAudioManager;
88     protected AudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager;
89 
90     protected TestLooper mTestLooper = new TestLooper();
91     protected FakeNativeWrapper mNativeWrapper;
92 
93     // Default Audio Status given by the System Audio device in its initial <Report Audio Status>
94     // that triggers AVB being enabled
95     private static final AudioStatus INITIAL_SYSTEM_AUDIO_DEVICE_STATUS =
96             new AudioStatus(50, false);
97 
98     // VolumeInfo passed to AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior to enable AVB
99     private static final VolumeInfo ENABLE_AVB_VOLUME_INFO =
100             new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
101                     .setMuted(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute())
102                     .setVolumeIndex(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume())
103                     .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
104                     .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
105                     .build();
106 
107     private static final int EMPTY_FLAGS = 0;
108 
createLocalDevice(HdmiControlService hdmiControlService)109     protected abstract HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService);
110 
getPhysicalAddress()111     protected abstract int getPhysicalAddress();
getDeviceType()112     protected abstract int getDeviceType();
getAudioOutputDevice()113     protected abstract AudioDeviceAttributes getAudioOutputDevice();
114 
getSystemAudioDeviceLogicalAddress()115     protected abstract int getSystemAudioDeviceLogicalAddress();
getSystemAudioDeviceType()116     protected abstract int getSystemAudioDeviceType();
117 
118     @Before
setUp()119     public void setUp() throws RemoteException {
120         MockitoAnnotations.initMocks(this);
121 
122         mContextSpy = spy(new ContextWrapper(
123                 InstrumentationRegistry.getInstrumentation().getTargetContext()));
124 
125         mAudioFramework = new FakeAudioFramework();
126         mAudioManager = spy(mAudioFramework.getAudioManager());
127         mAudioDeviceVolumeManager = spy(mAudioFramework.getAudioDeviceVolumeManager());
128 
129         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, EMPTY_FLAGS);
130         mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
131 
132         mHdmiControlService =
133                 new HdmiControlService(InstrumentationRegistry.getTargetContext(),
134                         Collections.singletonList(getDeviceType()),
135                         mAudioManager, mAudioDeviceVolumeManager) {
136                     @Override
137                     protected void writeStringSystemProperty(String key, String value) {
138                         // do nothing
139                     }
140                 };
141 
142         mLooper = mTestLooper.getLooper();
143         mHdmiControlService.setIoLooper(mLooper);
144 
145         mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
146         mHdmiCecConfig.setIntValue(
147                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
148                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
149         mHdmiCecConfig.setIntValue(
150                 HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
151                 HdmiControlManager.VOLUME_CONTROL_DISABLED);
152         mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
153         mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
154 
155         mNativeWrapper = new FakeNativeWrapper();
156         mNativeWrapper.setPhysicalAddress(getPhysicalAddress());
157         mNativeWrapper.setPollAddressResponse(Constants.ADDR_TV, SendMessageResult.SUCCESS);
158 
159         mHdmiCecController = HdmiCecController.createWithNativeWrapper(
160                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
161         mHdmiControlService.setCecController(mHdmiCecController);
162         mHdmiControlService.setHdmiMhlController(
163                 HdmiMhlControllerStub.create(mHdmiControlService));
164         mHdmiControlService.initService();
165         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
166         mHdmiControlService.setPowerManager(mPowerManager);
167 
168         mHdmiCecLocalDevice = createLocalDevice(mHdmiControlService);
169         mHdmiCecLocalDevice.init();
170         mLocalDevices.add(mHdmiCecLocalDevice);
171 
172         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
173         hdmiPortInfos[0] =
174                 new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
175                         .setCecSupported(true)
176                         .setMhlSupported(false)
177                         .setArcSupported(false)
178                         .build();
179         mNativeWrapper.setPortInfo(hdmiPortInfos);
180         mNativeWrapper.setPortConnectionStatus(1, true);
181 
182         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_BOOT_UP);
183         mTestLooper.dispatchAll();
184 
185         // Audio service always plays STREAM_MUSIC on the device we need
186         mAudioFramework.setDevicesForAttributes(HdmiControlService.STREAM_MUSIC_ATTRIBUTES,
187                 Collections.singletonList(getAudioOutputDevice()));
188 
189         // Max volume of STREAM_MUSIC
190         mAudioFramework.setStreamMaxVolume(AudioManager.STREAM_MUSIC, 25);
191 
192         // Receive messages from devices to make sure they're registered in HdmiCecNetwork
193         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
194                 Constants.ADDR_TV, getLogicalAddress()));
195         if (getSystemAudioDeviceType() == DEVICE_AUDIO_SYSTEM) {
196             mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
197                     Constants.ADDR_AUDIO_SYSTEM, getLogicalAddress()));
198         }
199 
200         mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
201         mHdmiControlService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
202         mTestLooper.dispatchAll();
203     }
204 
getLogicalAddress()205     protected int getLogicalAddress() {
206         return mHdmiCecLocalDevice.getDeviceInfo().getLogicalAddress();
207     }
208 
209     /**
210      * Changes the setting for CEC volume.
211      */
setCecVolumeControlSetting(@dmiControlManager.VolumeControl int setting)212     protected void setCecVolumeControlSetting(@HdmiControlManager.VolumeControl int setting) {
213         mHdmiControlService.getHdmiCecConfig().setIntValue(
214                 HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, setting);
215         mTestLooper.dispatchAll();
216     }
217 
218     /**
219      * Has the device receive a <Report Features> message from the System Audio device specifying
220      * whether <Set Audio Volume Level> is supported or not.
221      */
receiveSetAudioVolumeLevelSupport( @eviceFeatures.FeatureSupportStatus int featureSupportStatus)222     protected void receiveSetAudioVolumeLevelSupport(
223             @DeviceFeatures.FeatureSupportStatus int featureSupportStatus) {
224         // <Report Features> can't specify an unknown feature support status
225         if (featureSupportStatus != DeviceFeatures.FEATURE_SUPPORT_UNKNOWN) {
226             mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
227                     getSystemAudioDeviceLogicalAddress(), HdmiControlManager.HDMI_CEC_VERSION_2_0,
228                     Arrays.asList(getSystemAudioDeviceType()), Constants.RC_PROFILE_SOURCE,
229                     Collections.emptyList(),
230                     DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
231                             .setSetAudioVolumeLevelSupport(featureSupportStatus)
232                             .build()));
233             mTestLooper.dispatchAll();
234         }
235     }
236 
237     /**
238      * Enables System Audio mode if the System Audio device is an Audio System.
239      */
enableSystemAudioModeIfNeeded()240     protected void enableSystemAudioModeIfNeeded() {
241         if (getSystemAudioDeviceType() == DEVICE_AUDIO_SYSTEM) {
242             receiveSetSystemAudioMode(true);
243         }
244     }
245 
246     /**
247      * Sets System Audio Mode by having the device receive <Set System Audio Mode>
248      * from the Audio System.
249      */
receiveSetSystemAudioMode(boolean status)250     protected void receiveSetSystemAudioMode(boolean status) {
251         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildSetSystemAudioMode(
252                 Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, status));
253         mTestLooper.dispatchAll();
254     }
255 
256     /**
257      * Has the device receive a <Report Audio Status> message from the System Audio Device.
258      */
receiveReportAudioStatus(int volume, boolean mute)259     protected void receiveReportAudioStatus(int volume, boolean mute) {
260         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportAudioStatus(
261                 getSystemAudioDeviceLogicalAddress(),
262                 getLogicalAddress(),
263                 volume,
264                 mute));
265         mTestLooper.dispatchAll();
266     }
267 
268     /**
269      * Triggers all the conditions required to enable absolute volume behavior.
270      */
enableAbsoluteVolumeBehavior()271     protected void enableAbsoluteVolumeBehavior() {
272         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
273                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
274         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
275         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
276         enableSystemAudioModeIfNeeded();
277         receiveReportAudioStatus(
278                 INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
279                 INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
280 
281         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
282                 AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
283     }
284 
enableAdjustOnlyAbsoluteVolumeBehavior()285     protected void enableAdjustOnlyAbsoluteVolumeBehavior() {
286         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
287                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
288         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
289         enableSystemAudioModeIfNeeded();
290         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
291         receiveReportAudioStatus(
292                 INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
293                 INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
294 
295         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
296                 AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
297     }
298 
verifyGiveAudioStatusNeverSent()299     protected void verifyGiveAudioStatusNeverSent() {
300         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(
301                 HdmiCecMessageBuilder.buildGiveAudioStatus(
302                         getLogicalAddress(), getSystemAudioDeviceLogicalAddress()));
303     }
304 
verifyGiveAudioStatusSent()305     protected void verifyGiveAudioStatusSent() {
306         assertThat(mNativeWrapper.getResultMessages()).contains(
307                 HdmiCecMessageBuilder.buildGiveAudioStatus(
308                         getLogicalAddress(), getSystemAudioDeviceLogicalAddress()));
309     }
310 
311     @Test
allConditionsExceptSavlSupportMet_sendsSetAudioVolumeLevelAndGiveFeatures()312     public void allConditionsExceptSavlSupportMet_sendsSetAudioVolumeLevelAndGiveFeatures() {
313         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
314                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
315         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
316         enableSystemAudioModeIfNeeded();
317 
318         assertThat(mNativeWrapper.getResultMessages()).contains(
319                 SetAudioVolumeLevelMessage.build(
320                         getLogicalAddress(), getSystemAudioDeviceLogicalAddress(),
321                         Constants.AUDIO_VOLUME_STATUS_UNKNOWN));
322         assertThat(mNativeWrapper.getResultMessages()).contains(
323                 HdmiCecMessageBuilder.buildGiveFeatures(
324                         getLogicalAddress(), getSystemAudioDeviceLogicalAddress()));
325     }
326 
327     @Test
allConditionsMet_savlSupportLast_reportFeatures_giveAudioStatusSent()328     public void allConditionsMet_savlSupportLast_reportFeatures_giveAudioStatusSent() {
329         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
330                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
331         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
332         enableSystemAudioModeIfNeeded();
333         verifyGiveAudioStatusNeverSent();
334 
335         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
336         verifyGiveAudioStatusSent();
337     }
338 
339     @Test
allConditionsMet_savlSupportLast_noFeatureAbort_giveAudioStatusSent()340     public void allConditionsMet_savlSupportLast_noFeatureAbort_giveAudioStatusSent() {
341         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
342                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
343         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
344         enableSystemAudioModeIfNeeded();
345         verifyGiveAudioStatusNeverSent();
346 
347         mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
348         mTestLooper.dispatchAll();
349         verifyGiveAudioStatusSent();
350     }
351 
352     @Test
allConditionsMet_cecVolumeEnabledLast_giveAudioStatusSent()353     public void allConditionsMet_cecVolumeEnabledLast_giveAudioStatusSent() {
354         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
355                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
356         enableSystemAudioModeIfNeeded();
357         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
358         verifyGiveAudioStatusNeverSent();
359 
360         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
361         verifyGiveAudioStatusSent();
362     }
363 
364     @Test
allConditionsMet_fullVolumeBehaviorLast_giveAudioStatusSent()365     public void allConditionsMet_fullVolumeBehaviorLast_giveAudioStatusSent() {
366         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
367         enableSystemAudioModeIfNeeded();
368         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
369         verifyGiveAudioStatusNeverSent();
370 
371         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
372                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
373         mTestLooper.dispatchAll();
374         verifyGiveAudioStatusSent();
375     }
376 
377     @Test
allConditionsMet_systemAudioModeEnabledLast_giveAudioStatusSent()378     public void allConditionsMet_systemAudioModeEnabledLast_giveAudioStatusSent() {
379         // Only run when the System Audio device is an Audio System.
380         assume().that(getSystemAudioDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
381 
382         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
383                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
384         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
385         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
386         verifyGiveAudioStatusNeverSent();
387 
388         receiveSetSystemAudioMode(true);
389         verifyGiveAudioStatusSent();
390     }
391 
392     @Test
giveAudioStatusSent_systemAudioDeviceSendsReportAudioStatus_avbEnabled()393     public void giveAudioStatusSent_systemAudioDeviceSendsReportAudioStatus_avbEnabled() {
394         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
395                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
396         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
397         enableSystemAudioModeIfNeeded();
398         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
399 
400         // AVB should not be enabled before receiving <Report Audio Status>
401         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
402                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
403 
404         receiveReportAudioStatus(60, false);
405 
406         // Check that absolute volume behavior was the last one adopted
407         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
408                 AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
409 
410         // Check that the volume and mute status received were included when setting AVB
411         verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeBehavior(
412                 eq(getAudioOutputDevice()),
413                 eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
414                         .setVolumeIndex(60)
415                         .setMuted(false)
416                         .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
417                         .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
418                         .build()),
419                 any(), any(), anyBoolean());
420     }
421 
422     @Test
giveAudioStatusSent_reportAudioStatusVolumeOutOfBounds_avbNotEnabled()423     public void giveAudioStatusSent_reportAudioStatusVolumeOutOfBounds_avbNotEnabled() {
424         mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
425                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
426         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
427         enableSystemAudioModeIfNeeded();
428         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
429 
430         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
431                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
432         receiveReportAudioStatus(127, false);
433         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
434                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
435     }
436 
437     @Test
avbEnabled_cecVolumeDisabled_avbDisabled()438     public void avbEnabled_cecVolumeDisabled_avbDisabled() {
439         enableAbsoluteVolumeBehavior();
440 
441         setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_DISABLED);
442 
443         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
444                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
445     }
446 
447     @Test
avbEnabled_setAudioVolumeLevelNotSupported_avbDisabled()448     public void avbEnabled_setAudioVolumeLevelNotSupported_avbDisabled() {
449         enableAbsoluteVolumeBehavior();
450 
451         receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
452         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
453                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
454     }
455 
456     @Test
avbEnabled_setAudioVolumeLevelFeatureAborted_avbDisabled()457     public void avbEnabled_setAudioVolumeLevelFeatureAborted_avbDisabled() {
458         enableAbsoluteVolumeBehavior();
459 
460         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
461                 getSystemAudioDeviceLogicalAddress(), getLogicalAddress(),
462                 Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE));
463         mTestLooper.dispatchAll();
464         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
465                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
466     }
467 
468     @Test
avbEnabled_systemAudioModeDisabled_avbDisabled()469     public void avbEnabled_systemAudioModeDisabled_avbDisabled() {
470         // Only run when the System Audio device is an Audio System.
471         assume().that(getSystemAudioDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
472 
473         enableAbsoluteVolumeBehavior();
474 
475         receiveSetSystemAudioMode(false);
476         assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
477                 AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
478     }
479 
480     @Test
avbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges()481     public void avbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() {
482         // Initial <Report Audio Status> has volume=50 and mute=false
483         enableAbsoluteVolumeBehavior();
484 
485         // New volume and mute status: sets both
486         receiveReportAudioStatus(20, true);
487         verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5),
488                 anyInt());
489         verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
490                 eq(AudioManager.ADJUST_MUTE), anyInt());
491         clearInvocations(mAudioManager);
492 
493         // New volume only: sets volume only
494         receiveReportAudioStatus(32, true);
495         verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
496                 anyInt());
497         verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
498                 eq(AudioManager.ADJUST_MUTE), anyInt());
499         clearInvocations(mAudioManager);
500 
501         // New mute status only: sets mute only
502         receiveReportAudioStatus(32, false);
503         verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
504                 anyInt());
505         verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
506                 eq(AudioManager.ADJUST_UNMUTE), anyInt());
507         clearInvocations(mAudioManager);
508 
509         // Repeat of earlier message: sets neither volume nor mute
510         receiveReportAudioStatus(32, false);
511         verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
512                 anyInt());
513         verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
514                 eq(AudioManager.ADJUST_UNMUTE), anyInt());
515         clearInvocations(mAudioManager);
516 
517         // Volume not within range [0, 100]: sets neither volume nor mute
518         receiveReportAudioStatus(127, true);
519         verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
520                 anyInt());
521         verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
522                 anyInt());
523         clearInvocations(mAudioManager);
524 
525         // If AudioService causes us to send <Set Audio Volume Level>, the System Audio device's
526         // volume changes. Afterward, a duplicate of an earlier <Report Audio Status> should
527         // still cause us to call setStreamVolume()
528         mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged(
529                 getAudioOutputDevice(),
530                 new VolumeInfo.Builder(ENABLE_AVB_VOLUME_INFO)
531                         .setVolumeIndex(20)
532                         .build()
533         );
534         mTestLooper.dispatchAll();
535         receiveReportAudioStatus(32, false);
536         verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
537                 anyInt());
538         verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
539                 eq(AudioManager.ADJUST_UNMUTE), anyInt());
540     }
541 
542     @Test
avbEnabled_audioDeviceVolumeAdjusted_sendsUserControlPressedAndGiveAudioStatus()543     public void avbEnabled_audioDeviceVolumeAdjusted_sendsUserControlPressedAndGiveAudioStatus() {
544         enableAbsoluteVolumeBehavior();
545         mNativeWrapper.clearResultMessages();
546 
547         mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted(
548                 getAudioOutputDevice(),
549                 ENABLE_AVB_VOLUME_INFO,
550                 AudioManager.ADJUST_RAISE,
551                 AudioDeviceVolumeManager.ADJUST_MODE_NORMAL
552         );
553         mTestLooper.dispatchAll();
554 
555         assertThat(mNativeWrapper.getResultMessages()).contains(
556                 HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(),
557                         getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP));
558         assertThat(mNativeWrapper.getResultMessages()).contains(
559                 HdmiCecMessageBuilder.buildUserControlReleased(getLogicalAddress(),
560                         getSystemAudioDeviceLogicalAddress()));
561         assertThat(mNativeWrapper.getResultMessages()).contains(
562                 HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(),
563                         getSystemAudioDeviceLogicalAddress()));
564     }
565 
566     @Test
avbEnabled_audioDeviceVolumeChanged_sendsSetAudioVolumeLevel()567     public void avbEnabled_audioDeviceVolumeChanged_sendsSetAudioVolumeLevel() {
568         enableAbsoluteVolumeBehavior();
569         mNativeWrapper.clearResultMessages();
570 
571         mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged(
572                 getAudioOutputDevice(),
573                 new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
574                         .setVolumeIndex(20)
575                         .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
576                         .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
577                         .build()
578         );
579         mTestLooper.dispatchAll();
580 
581         assertThat(mNativeWrapper.getResultMessages()).contains(
582                 SetAudioVolumeLevelMessage.build(getLogicalAddress(),
583                         getSystemAudioDeviceLogicalAddress(), 20));
584     }
585 }
586