1 /* 2 * Copyright 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 package com.android.server.audio; 17 18 import static org.mockito.ArgumentMatchers.any; 19 import static org.mockito.ArgumentMatchers.anyBoolean; 20 import static org.mockito.Mockito.doNothing; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.spy; 23 import static org.mockito.Mockito.when; 24 25 import android.media.AudioAttributes; 26 import android.media.AudioDeviceAttributes; 27 import android.media.AudioFormat; 28 import android.media.AudioSystem; 29 import android.util.Log; 30 31 import androidx.test.filters.MediumTest; 32 import androidx.test.platform.app.InstrumentationRegistry; 33 import androidx.test.runner.AndroidJUnit4; 34 35 import org.junit.Assert; 36 import org.junit.Before; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 import org.mockito.Mock; 40 import org.mockito.Spy; 41 42 import java.util.ArrayList; 43 import java.util.List; 44 45 @MediumTest 46 @RunWith(AndroidJUnit4.class) 47 public class SpatializerHelperTest { 48 49 private static final String TAG = "SpatializerHelperTest"; 50 51 // the actual class under test 52 private SpatializerHelper mSpatHelper; 53 54 @Mock private AudioService mMockAudioService; 55 @Spy private AudioSystemAdapter mSpyAudioSystem; 56 @Spy private AudioDeviceBroker mSpyDeviceBroker; 57 58 @Before setUp()59 public void setUp() throws Exception { 60 mMockAudioService = mock(AudioService.class); 61 62 mSpyAudioSystem = spy(new NoOpAudioSystemAdapter()); 63 mSpyDeviceBroker = spy( 64 new AudioDeviceBroker( 65 InstrumentationRegistry.getInstrumentation().getTargetContext(), 66 mMockAudioService, mSpyAudioSystem)); 67 mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem, 68 mSpyDeviceBroker, /*binauralEnabledDefault=*/true, /*transauralEnabledDefault=*/ 69 true, /*headTrackingEnabledDefault*/false); 70 } 71 72 @Test testAdiDeviceStateSettings()73 public void testAdiDeviceStateSettings() throws Exception { 74 Log.i(TAG, "starting testSADeviceSettings"); 75 final AudioDeviceAttributes dev1 = 76 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""); 77 final AudioDeviceAttributes dev2 = 78 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "C3:PO:beep"); 79 final AudioDeviceAttributes dev3 = 80 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop"); 81 82 doNothing().when(mSpyDeviceBroker).persistAudioDeviceSettings(); 83 mSpatHelper.initForTest(true /*binaural*/, true /*transaural*/); 84 85 // test with single device 86 mSpatHelper.addCompatibleAudioDevice(dev1); 87 checkAddSettings(); 88 89 // test with 2+ devices so separator character is used in list 90 mSpatHelper.addCompatibleAudioDevice(dev2); 91 Assert.assertTrue(mSpatHelper.isAvailableForDevice(dev2)); 92 checkAddSettings(); 93 Assert.assertTrue(mSpatHelper.isAvailableForDevice(dev2)); 94 mSpatHelper.addCompatibleAudioDevice(dev3); 95 checkAddSettings(); 96 97 // test adding a device twice in the list 98 mSpatHelper.addCompatibleAudioDevice(dev1); 99 checkAddSettings(); 100 101 // test removing a device 102 mSpatHelper.removeCompatibleAudioDevice(dev2); 103 // spatializer could still be run for dev2 (is available) but spatial audio 104 // is disabled for dev2 by removeCompatibleAudioDevice 105 Assert.assertTrue(mSpatHelper.isAvailableForDevice(dev2)); 106 List<AudioDeviceAttributes> compatDevices = mSpatHelper.getCompatibleAudioDevices(); 107 Assert.assertFalse(compatDevices.stream().anyMatch(dev -> dev.equalTypeAddress(dev2))); 108 checkAddSettings(); 109 } 110 111 /** 112 * Gets the string representing the current configuration of the devices, then clears it 113 * and restores the configuration. Verify the new string from the restored settings matches 114 * the original one. 115 */ checkAddSettings()116 private void checkAddSettings() throws Exception { 117 String settings = mSpyDeviceBroker.getDeviceSettings(); 118 Log.i(TAG, "device settings: " + settings); 119 mSpyDeviceBroker.clearDeviceInventory(); 120 mSpyDeviceBroker.setDeviceSettings(settings); 121 String settingsRestored = mSpyDeviceBroker.getDeviceSettings(); 122 Log.i(TAG, "device settingsRestored: " + settingsRestored); 123 Assert.assertEquals(settings, settingsRestored); 124 } 125 126 /** 127 * Test that null devices for routing do not break canBeSpatialized 128 * @throws Exception 129 */ 130 @Test testNoRoutingCanBeSpatialized()131 public void testNoRoutingCanBeSpatialized() throws Exception { 132 Log.i(TAG, "Starting testNoRoutingCanBeSpatialized"); 133 mSpatHelper.forceStateForTest(SpatializerHelper.STATE_ENABLED_AVAILABLE); 134 135 final ArrayList<AudioDeviceAttributes> emptyList = new ArrayList<>(0); 136 final ArrayList<AudioDeviceAttributes> listWithNull = new ArrayList<>(1); 137 listWithNull.add(null); 138 final AudioAttributes media = new AudioAttributes.Builder() 139 .setUsage(AudioAttributes.USAGE_MEDIA).build(); 140 final AudioFormat spatialFormat = new AudioFormat.Builder() 141 .setEncoding(AudioFormat.ENCODING_PCM_16BIT) 142 .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1).build(); 143 144 when(mSpyAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean())) 145 .thenReturn(emptyList); 146 Assert.assertFalse("can be spatialized on empty routing", 147 mSpatHelper.canBeSpatialized(media, spatialFormat)); 148 149 when(mSpyAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean())) 150 .thenReturn(listWithNull); 151 Assert.assertFalse("can be spatialized on null routing", 152 mSpatHelper.canBeSpatialized(media, spatialFormat)); 153 } 154 } 155