| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.hdmi; |
| |
| import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM; |
| |
| import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP; |
| import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_BOOT_UP; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.TruthJUnit.assume; |
| |
| import static org.mockito.AdditionalMatchers.not; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.Mockito.atLeastOnce; |
| import static org.mockito.Mockito.clearInvocations; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.eq; |
| import static org.mockito.Mockito.inOrder; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.verify; |
| |
| import android.content.Context; |
| import android.content.ContextWrapper; |
| import android.hardware.hdmi.DeviceFeatures; |
| import android.hardware.hdmi.HdmiControlManager; |
| import android.hardware.hdmi.HdmiDeviceInfo; |
| import android.hardware.hdmi.HdmiPortInfo; |
| import android.hardware.tv.cec.V1_0.SendMessageResult; |
| import android.media.AudioDeviceAttributes; |
| import android.media.AudioDeviceVolumeManager; |
| import android.media.AudioManager; |
| import android.media.VolumeInfo; |
| import android.os.Looper; |
| import android.os.RemoteException; |
| import android.os.test.TestLooper; |
| |
| import androidx.test.InstrumentationRegistry; |
| |
| import com.android.server.SystemService; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.mockito.InOrder; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| |
| /** |
| * Tests that absolute volume behavior (AVB) is enabled and disabled correctly, and that |
| * the device responds correctly to incoming <Report Audio Status> messages and API calls |
| * from AudioService when AVB is active. |
| * |
| * This is an abstract base class. Concrete subclasses specify the type of the local device, and the |
| * type of the System Audio device. This allows each test to be run for multiple setups. |
| * |
| * We test the following pairs of (local device, System Audio device): |
| * (Playback, TV): {@link PlaybackDeviceToTvAvbTest} |
| * (Playback, Audio System): {@link PlaybackDeviceToAudioSystemAvbTest} |
| * (TV, Audio System): {@link TvToAudioSystemAvbTest} |
| */ |
| public abstract class BaseAbsoluteVolumeBehaviorTest { |
| private HdmiControlService mHdmiControlService; |
| private HdmiCecController mHdmiCecController; |
| private HdmiCecLocalDevice mHdmiCecLocalDevice; |
| private FakeHdmiCecConfig mHdmiCecConfig; |
| private FakePowerManagerWrapper mPowerManager; |
| private Looper mLooper; |
| private Context mContextSpy; |
| private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); |
| |
| @Mock protected AudioManager mAudioManager; |
| protected FakeAudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager; |
| |
| protected TestLooper mTestLooper = new TestLooper(); |
| protected FakeNativeWrapper mNativeWrapper; |
| |
| // Audio Status given by the System Audio device in its initial <Report Audio Status> that |
| // triggers AVB being enabled |
| private static final AudioStatus INITIAL_SYSTEM_AUDIO_DEVICE_STATUS = |
| new AudioStatus(50, false); |
| |
| // VolumeInfo passed to AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior to enable AVB |
| private static final VolumeInfo ENABLE_AVB_VOLUME_INFO = |
| new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) |
| .setMuted(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute()) |
| .setVolumeIndex(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume()) |
| .setMaxVolumeIndex(AudioStatus.MAX_VOLUME) |
| .setMinVolumeIndex(AudioStatus.MIN_VOLUME) |
| .build(); |
| |
| protected abstract HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService); |
| |
| protected abstract int getPhysicalAddress(); |
| protected abstract int getDeviceType(); |
| protected abstract AudioDeviceAttributes getAudioOutputDevice(); |
| |
| protected abstract int getSystemAudioDeviceLogicalAddress(); |
| protected abstract int getSystemAudioDeviceType(); |
| |
| @Before |
| public void setUp() throws RemoteException { |
| MockitoAnnotations.initMocks(this); |
| |
| mContextSpy = spy(new ContextWrapper( |
| InstrumentationRegistry.getInstrumentation().getTargetContext())); |
| |
| mAudioDeviceVolumeManager = spy(new FakeAudioDeviceVolumeManagerWrapper()); |
| |
| mHdmiControlService = |
| new HdmiControlService(InstrumentationRegistry.getTargetContext(), |
| Collections.singletonList(getDeviceType()), |
| mAudioDeviceVolumeManager) { |
| @Override |
| AudioManager getAudioManager() { |
| return mAudioManager; |
| } |
| |
| @Override |
| protected void writeStringSystemProperty(String key, String value) { |
| // do nothing |
| } |
| }; |
| |
| mLooper = mTestLooper.getLooper(); |
| mHdmiControlService.setIoLooper(mLooper); |
| |
| mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy); |
| mHdmiCecConfig.setIntValue( |
| HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, |
| HdmiControlManager.HDMI_CEC_VERSION_2_0); |
| mHdmiCecConfig.setIntValue( |
| HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, |
| HdmiControlManager.VOLUME_CONTROL_DISABLED); |
| mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig); |
| mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper()); |
| |
| mNativeWrapper = new FakeNativeWrapper(); |
| mNativeWrapper.setPhysicalAddress(getPhysicalAddress()); |
| mNativeWrapper.setPollAddressResponse(Constants.ADDR_TV, SendMessageResult.SUCCESS); |
| |
| mHdmiCecController = HdmiCecController.createWithNativeWrapper( |
| mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); |
| mHdmiControlService.setCecController(mHdmiCecController); |
| mHdmiControlService.setHdmiMhlController( |
| HdmiMhlControllerStub.create(mHdmiControlService)); |
| mHdmiControlService.initService(); |
| mPowerManager = new FakePowerManagerWrapper(mContextSpy); |
| mHdmiControlService.setPowerManager(mPowerManager); |
| |
| mHdmiCecLocalDevice = createLocalDevice(mHdmiControlService); |
| mHdmiCecLocalDevice.init(); |
| mLocalDevices.add(mHdmiCecLocalDevice); |
| |
| HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1]; |
| hdmiPortInfos[0] = |
| new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000) |
| .setCecSupported(true) |
| .setMhlSupported(false) |
| .setArcSupported(false) |
| .build(); |
| mNativeWrapper.setPortInfo(hdmiPortInfos); |
| mNativeWrapper.setPortConnectionStatus(1, true); |
| |
| mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_BOOT_UP); |
| mTestLooper.dispatchAll(); |
| |
| // Simulate AudioManager's behavior and response when setDeviceVolumeBehavior is called |
| doAnswer(invocation -> { |
| setDeviceVolumeBehavior(invocation.getArgument(0), invocation.getArgument(1)); |
| return null; |
| }).when(mAudioManager).setDeviceVolumeBehavior(any(), anyInt()); |
| |
| // Set starting volume behavior |
| doReturn(AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE) |
| .when(mAudioManager).getDeviceVolumeBehavior(eq(getAudioOutputDevice())); |
| |
| // Audio service always plays STREAM_MUSIC on the device we need |
| doReturn(Collections.singletonList(getAudioOutputDevice())).when(mAudioManager) |
| .getDevicesForAttributes(HdmiControlService.STREAM_MUSIC_ATTRIBUTES); |
| |
| // Max volume of STREAM_MUSIC |
| doReturn(25).when(mAudioManager).getStreamMaxVolume(AudioManager.STREAM_MUSIC); |
| |
| // Receive messages from devices to make sure they're registered in HdmiCecNetwork |
| mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus( |
| Constants.ADDR_TV, getLogicalAddress())); |
| if (getSystemAudioDeviceType() == DEVICE_AUDIO_SYSTEM) { |
| mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus( |
| Constants.ADDR_AUDIO_SYSTEM, getLogicalAddress())); |
| } |
| |
| mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); |
| mHdmiControlService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); |
| mTestLooper.dispatchAll(); |
| } |
| |
| protected int getLogicalAddress() { |
| return mHdmiCecLocalDevice.getDeviceInfo().getLogicalAddress(); |
| } |
| |
| /** |
| * Simulates the volume behavior of {@code device} being set to {@code behavior}. |
| */ |
| protected void setDeviceVolumeBehavior(AudioDeviceAttributes device, |
| @AudioManager.DeviceVolumeBehavior int behavior) { |
| doReturn(behavior).when(mAudioManager).getDeviceVolumeBehavior(eq(device)); |
| mHdmiControlService.onDeviceVolumeBehaviorChanged(device, behavior); |
| mTestLooper.dispatchAll(); |
| } |
| |
| /** |
| * Changes the setting for CEC volume. |
| */ |
| protected void setCecVolumeControlSetting(@HdmiControlManager.VolumeControl int setting) { |
| mHdmiControlService.getHdmiCecConfig().setIntValue( |
| HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, setting); |
| mTestLooper.dispatchAll(); |
| } |
| |
| /** |
| * Has the device receive a <Report Features> message from the System Audio device specifying |
| * whether <Set Audio Volume Level> is supported or not. |
| */ |
| protected void receiveSetAudioVolumeLevelSupport( |
| @DeviceFeatures.FeatureSupportStatus int featureSupportStatus) { |
| // <Report Features> can't specify an unknown feature support status |
| if (featureSupportStatus != DeviceFeatures.FEATURE_SUPPORT_UNKNOWN) { |
| mNativeWrapper.onCecMessage(ReportFeaturesMessage.build( |
| getSystemAudioDeviceLogicalAddress(), HdmiControlManager.HDMI_CEC_VERSION_2_0, |
| Arrays.asList(getSystemAudioDeviceType()), Constants.RC_PROFILE_SOURCE, |
| Collections.emptyList(), |
| DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder() |
| .setSetAudioVolumeLevelSupport(featureSupportStatus) |
| .build())); |
| mTestLooper.dispatchAll(); |
| } |
| } |
| |
| /** |
| * Enables System Audio mode if the System Audio device is an Audio System. |
| */ |
| protected void enableSystemAudioModeIfNeeded() { |
| if (getSystemAudioDeviceType() == DEVICE_AUDIO_SYSTEM) { |
| receiveSetSystemAudioMode(true); |
| } |
| } |
| |
| /** |
| * Sets System Audio Mode by having the device receive <Set System Audio Mode> |
| * from the Audio System. |
| */ |
| protected void receiveSetSystemAudioMode(boolean status) { |
| mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildSetSystemAudioMode( |
| Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, status)); |
| mTestLooper.dispatchAll(); |
| } |
| |
| /** |
| * Has the device receive a <Report Audio Status> reporting the status in |
| * {@link #INITIAL_SYSTEM_AUDIO_DEVICE_STATUS} |
| */ |
| protected void receiveInitialReportAudioStatus() { |
| receiveReportAudioStatus( |
| INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(), |
| INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute()); |
| } |
| |
| /** |
| * Has the device receive a <Report Audio Status> message from the System Audio Device. |
| */ |
| protected void receiveReportAudioStatus(int volume, boolean mute) { |
| mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportAudioStatus( |
| getSystemAudioDeviceLogicalAddress(), |
| getLogicalAddress(), |
| volume, |
| mute)); |
| mTestLooper.dispatchAll(); |
| } |
| |
| /** |
| * Triggers all the conditions required to enable absolute volume behavior. |
| */ |
| protected void enableAbsoluteVolumeBehavior() { |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED); |
| enableSystemAudioModeIfNeeded(); |
| receiveInitialReportAudioStatus(); |
| |
| verifyAbsoluteVolumeEnabled(); |
| } |
| |
| /** |
| * Verifies that the audio output device's volume behavior was last set to |
| * absolute volume behavior. |
| */ |
| protected void verifyAbsoluteVolumeEnabled() { |
| InOrder inOrder = inOrder(mAudioManager, mAudioDeviceVolumeManager); |
| inOrder.verify(mAudioDeviceVolumeManager, atLeastOnce()).setDeviceAbsoluteVolumeBehavior( |
| eq(getAudioOutputDevice()), any(), any(), any(), anyBoolean()); |
| inOrder.verify(mAudioManager, never()).setDeviceVolumeBehavior( |
| eq(getAudioOutputDevice()), not(eq(AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE))); |
| } |
| |
| /** |
| * Verifies that the audio output device's volume behavior was last set to something other than |
| * absolute volume behavior. |
| */ |
| protected void verifyAbsoluteVolumeDisabled() { |
| InOrder inOrder = inOrder(mAudioManager, mAudioDeviceVolumeManager); |
| inOrder.verify(mAudioManager, atLeastOnce()).setDeviceVolumeBehavior( |
| eq(getAudioOutputDevice()), not(eq(AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE))); |
| inOrder.verify(mAudioDeviceVolumeManager, never()).setDeviceAbsoluteVolumeBehavior( |
| eq(getAudioOutputDevice()), any(), any(), any(), anyBoolean()); |
| } |
| |
| protected void verifyGiveAudioStatusNeverSent() { |
| assertThat(mNativeWrapper.getResultMessages()).doesNotContain( |
| HdmiCecMessageBuilder.buildGiveAudioStatus( |
| getLogicalAddress(), getSystemAudioDeviceLogicalAddress())); |
| } |
| |
| protected void verifyGiveAudioStatusSent() { |
| assertThat(mNativeWrapper.getResultMessages()).contains( |
| HdmiCecMessageBuilder.buildGiveAudioStatus( |
| getLogicalAddress(), getSystemAudioDeviceLogicalAddress())); |
| } |
| |
| @Test |
| public void allConditionsExceptSavlSupportMet_sendsSetAudioVolumeLevelAndGiveFeatures() { |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| enableSystemAudioModeIfNeeded(); |
| |
| assertThat(mNativeWrapper.getResultMessages()).contains( |
| SetAudioVolumeLevelMessage.build( |
| getLogicalAddress(), getSystemAudioDeviceLogicalAddress(), |
| Constants.AUDIO_VOLUME_STATUS_UNKNOWN)); |
| assertThat(mNativeWrapper.getResultMessages()).contains( |
| HdmiCecMessageBuilder.buildGiveFeatures( |
| getLogicalAddress(), getSystemAudioDeviceLogicalAddress())); |
| } |
| |
| @Test |
| public void allConditionsMet_savlSupportLast_reportFeatures_giveAudioStatusSent() { |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| enableSystemAudioModeIfNeeded(); |
| verifyGiveAudioStatusNeverSent(); |
| |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED); |
| verifyGiveAudioStatusSent(); |
| } |
| |
| @Test |
| public void allConditionsMet_savlSupportLast_noFeatureAbort_giveAudioStatusSent() { |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| enableSystemAudioModeIfNeeded(); |
| verifyGiveAudioStatusNeverSent(); |
| |
| mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); |
| mTestLooper.dispatchAll(); |
| verifyGiveAudioStatusSent(); |
| } |
| |
| @Test |
| public void allConditionsMet_cecVolumeEnabledLast_giveAudioStatusSent() { |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| enableSystemAudioModeIfNeeded(); |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED); |
| verifyGiveAudioStatusNeverSent(); |
| |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| verifyGiveAudioStatusSent(); |
| } |
| |
| @Test |
| public void allConditionsMet_fullVolumeBehaviorLast_giveAudioStatusSent() { |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| enableSystemAudioModeIfNeeded(); |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED); |
| verifyGiveAudioStatusNeverSent(); |
| |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| verifyGiveAudioStatusSent(); |
| } |
| |
| @Test |
| public void allConditionsMet_systemAudioModeEnabledLast_giveAudioStatusSent() { |
| // Only run when the System Audio device is an Audio System. |
| assume().that(getSystemAudioDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); |
| |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED); |
| verifyGiveAudioStatusNeverSent(); |
| |
| receiveSetSystemAudioMode(true); |
| verifyGiveAudioStatusSent(); |
| } |
| |
| @Test |
| public void giveAudioStatusSent_systemAudioDeviceSendsReportAudioStatus_avbEnabled() { |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| enableSystemAudioModeIfNeeded(); |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED); |
| setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| |
| // Verify that AVB was never enabled |
| verify(mAudioDeviceVolumeManager, never()).setDeviceAbsoluteVolumeBehavior( |
| eq(getAudioOutputDevice()), any(), any(), any(), anyBoolean()); |
| receiveInitialReportAudioStatus(); |
| |
| verifyAbsoluteVolumeEnabled(); |
| } |
| |
| @Test |
| public void giveAudioStatusSent_reportAudioStatusVolumeOutOfBounds_avbNotEnabled() { |
| mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(), |
| AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); |
| enableSystemAudioModeIfNeeded(); |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED); |
| |
| assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( |
| AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| receiveReportAudioStatus(127, false); |
| assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( |
| AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); |
| } |
| |
| @Test |
| public void avbEnabled_cecVolumeDisabled_avbDisabled() { |
| enableAbsoluteVolumeBehavior(); |
| |
| setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_DISABLED); |
| verifyAbsoluteVolumeDisabled(); |
| } |
| |
| @Test |
| public void avbEnabled_setAudioVolumeLevelNotSupported_avbDisabled() { |
| enableAbsoluteVolumeBehavior(); |
| |
| receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED); |
| verifyAbsoluteVolumeDisabled(); |
| } |
| |
| @Test |
| public void avbEnabled_setAudioVolumeLevelFeatureAborted_avbDisabled() { |
| enableAbsoluteVolumeBehavior(); |
| |
| mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand( |
| getSystemAudioDeviceLogicalAddress(), getLogicalAddress(), |
| Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE)); |
| mTestLooper.dispatchAll(); |
| verifyAbsoluteVolumeDisabled(); |
| } |
| |
| @Test |
| public void avbEnabled_systemAudioModeDisabled_avbDisabled() { |
| // Only run when the System Audio device is an Audio System. |
| assume().that(getSystemAudioDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); |
| |
| enableAbsoluteVolumeBehavior(); |
| |
| receiveSetSystemAudioMode(false); |
| verifyAbsoluteVolumeDisabled(); |
| } |
| |
| @Test |
| public void avbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() { |
| // Initial <Report Audio Status> has volume=50 and mute=false |
| enableAbsoluteVolumeBehavior(); |
| |
| // New volume and mute status: sets both |
| receiveReportAudioStatus(20, true); |
| verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5), |
| anyInt()); |
| verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), |
| eq(AudioManager.ADJUST_MUTE), anyInt()); |
| clearInvocations(mAudioManager); |
| |
| // New volume only: sets volume only |
| receiveReportAudioStatus(32, true); |
| verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8), |
| anyInt()); |
| verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), |
| eq(AudioManager.ADJUST_MUTE), anyInt()); |
| clearInvocations(mAudioManager); |
| |
| // New mute status only: sets mute only |
| receiveReportAudioStatus(32, false); |
| verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8), |
| anyInt()); |
| verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), |
| eq(AudioManager.ADJUST_UNMUTE), anyInt()); |
| clearInvocations(mAudioManager); |
| |
| // Repeat of earlier message: sets neither volume nor mute |
| receiveReportAudioStatus(32, false); |
| verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8), |
| anyInt()); |
| verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), |
| eq(AudioManager.ADJUST_UNMUTE), anyInt()); |
| clearInvocations(mAudioManager); |
| |
| // Volume not within range [0, 100]: sets neither volume nor mute |
| receiveReportAudioStatus(127, true); |
| verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(), |
| anyInt()); |
| verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(), |
| anyInt()); |
| clearInvocations(mAudioManager); |
| |
| // If AudioService causes us to send <Set Audio Volume Level>, the System Audio device's |
| // volume changes. Afterward, a duplicate of an earlier <Report Audio Status> should |
| // still cause us to call setStreamVolume() |
| mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged( |
| getAudioOutputDevice(), |
| new VolumeInfo.Builder(ENABLE_AVB_VOLUME_INFO) |
| .setVolumeIndex(20) |
| .build() |
| ); |
| mTestLooper.dispatchAll(); |
| receiveReportAudioStatus(32, false); |
| verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8), |
| anyInt()); |
| verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), |
| eq(AudioManager.ADJUST_UNMUTE), anyInt()); |
| } |
| |
| @Test |
| public void avbEnabled_audioDeviceVolumeAdjusted_sendsUserControlPressedAndGiveAudioStatus() { |
| enableAbsoluteVolumeBehavior(); |
| mNativeWrapper.clearResultMessages(); |
| |
| mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted( |
| getAudioOutputDevice(), |
| ENABLE_AVB_VOLUME_INFO, |
| AudioManager.ADJUST_RAISE, |
| AudioDeviceVolumeManager.ADJUST_MODE_NORMAL |
| ); |
| mTestLooper.dispatchAll(); |
| |
| assertThat(mNativeWrapper.getResultMessages()).contains( |
| HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(), |
| getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP)); |
| assertThat(mNativeWrapper.getResultMessages()).contains( |
| HdmiCecMessageBuilder.buildUserControlReleased(getLogicalAddress(), |
| getSystemAudioDeviceLogicalAddress())); |
| assertThat(mNativeWrapper.getResultMessages()).contains( |
| HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(), |
| getSystemAudioDeviceLogicalAddress())); |
| } |
| |
| @Test |
| public void avbEnabled_audioDeviceVolumeChanged_sendsSetAudioVolumeLevel() { |
| enableAbsoluteVolumeBehavior(); |
| mNativeWrapper.clearResultMessages(); |
| |
| mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged( |
| getAudioOutputDevice(), |
| new VolumeInfo.Builder(ENABLE_AVB_VOLUME_INFO) |
| .setVolumeIndex(20) |
| .build() |
| ); |
| mTestLooper.dispatchAll(); |
| |
| assertThat(mNativeWrapper.getResultMessages()).contains( |
| SetAudioVolumeLevelMessage.build(getLogicalAddress(), |
| getSystemAudioDeviceLogicalAddress(), 20)); |
| } |
| } |