diff options
| author | 2023-12-04 17:17:18 -0800 | |
|---|---|---|
| committer | 2023-12-06 09:03:12 -0800 | |
| commit | a1e0582e5df25be78e4955a7e905ca28e1ae7b76 (patch) | |
| tree | 55aaa87aed98f930b2ab9b0e87ff1e83ee4ae3e1 | |
| parent | 39917599b42aefea9136824fcb5b669baea5a198 (diff) | |
Add tests for the automatic audio device categorization
Testing the forwarded AudioDeviceBroker methods calls that query and
modify the internal AdiDeviceState objects which store the audio device
categorization information
Test: adb shell device_config put media_audio android.media.audio.automatic_bt_device_type true
Test: atest AudioDeviceBrokerTest
Bug: 302323921
Change-Id: I6f0b3a50fd6ad2c16ad4b5bedd0319414d28a417
4 files changed, 141 insertions, 11 deletions
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java index b91e633bd3de..ffdab7dfbfa4 100644 --- a/services/core/java/com/android/server/audio/AdiDeviceState.java +++ b/services/core/java/com/android/server/audio/AdiDeviceState.java @@ -21,6 +21,8 @@ import static android.media.AudioSystem.DEVICE_NONE; import static android.media.AudioSystem.isBluetoothDevice; import static android.media.audio.Flags.automaticBtDeviceType; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.media.AudioDeviceAttributes; @@ -31,13 +33,16 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.VisibleForTesting; + import java.util.Objects; /** * Class representing all devices that were previously or are currently connected. Data is * persisted in {@link android.provider.Settings.Secure} */ -/*package*/ final class AdiDeviceState { +@VisibleForTesting(visibility = PACKAGE) +public final class AdiDeviceState { private static final String TAG = "AS.AdiDeviceState"; private static final String SETTING_FIELD_SEPARATOR = ","; diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 61ec04b755ce..46e9b2afe655 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -31,6 +31,8 @@ import static android.media.AudioSystem.isBluetoothScoOutDevice; import static android.media.audio.Flags.automaticBtDeviceType; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; @@ -347,7 +349,8 @@ public class AudioDeviceInventory { * @return the found {@link AdiDeviceState} or {@code null} otherwise. */ @Nullable - AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) { + @VisibleForTesting(visibility = PACKAGE) + public AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) { Set<Integer> deviceSet; if (isBluetoothA2dpOutDevice(deviceType)) { deviceSet = DEVICE_OUT_ALL_A2DP_SET; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 8cec24d1bbb5..f7b7aaa60a35 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -39,6 +39,7 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.media.audio.Flags.alarmMinVolumeZero; import static com.android.media.audio.Flags.bluetoothMacAddressAnonymization; import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume; @@ -6785,7 +6786,8 @@ public class AudioService extends IAudioService.Stub return mContentResolver; } - /*package*/ SettingsAdapter getSettings() { + @VisibleForTesting(visibility = PACKAGE) + public SettingsAdapter getSettings() { return mSettings; } @@ -11250,7 +11252,9 @@ public class AudioService extends IAudioService.Stub return mDeviceBroker.isBluetoothAudioDeviceCategoryFixed(address); } - /*package*/void onUpdatedAdiDeviceState(AdiDeviceState deviceState) { + /** Update the sound dose and spatializer state based on the new AdiDeviceState. */ + @VisibleForTesting(visibility = PACKAGE) + public void onUpdatedAdiDeviceState(AdiDeviceState deviceState) { if (deviceState == null) { return; } diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 0d58542ab040..cc3c880b8927 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -15,14 +15,28 @@ */ package com.android.server.audio; +import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_DEFAULT; +import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_HEADSET; +import static android.media.audio.Flags.automaticBtDeviceType; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; @@ -40,10 +54,10 @@ import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Spy; @@ -70,6 +84,9 @@ public class AudioDeviceBrokerTest { Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); mMockAudioService = mock(AudioService.class); + SettingsAdapter mockAdapter = mock(SettingsAdapter.class); + when(mMockAudioService.getSettings()).thenReturn(mockAdapter); + when(mockAdapter.getSecureStringForUser(any(), any(), anyInt())).thenReturn(""); when(mMockAudioService.getBluetoothContextualVolumeStream()) .thenReturn(AudioSystem.STREAM_MUSIC); @@ -82,7 +99,6 @@ public class AudioDeviceBrokerTest { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); mFakeBtDevice = adapter.getRemoteDevice("00:01:02:03:04:05"); - Assert.assertNotNull("invalid null BT device", mFakeBtDevice); } @After @@ -100,15 +116,14 @@ public class AudioDeviceBrokerTest { @Test public void testPostA2dpDeviceConnectionChange() throws Exception { Log.i(TAG, "starting testPostA2dpDeviceConnectionChange"); - Assert.assertNotNull("invalid null BT device", mFakeBtDevice); + assertNotNull("invalid null BT device", mFakeBtDevice); mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource")); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice( - any(AudioDeviceBroker.BtDeviceInfo.class) - ); + any(AudioDeviceBroker.BtDeviceInfo.class)); // verify the connection was reported to AudioSystem checkSingleSystemConnection(mFakeBtDevice); @@ -212,7 +227,7 @@ public class AudioDeviceBrokerTest { AudioManager.DEVICE_OUT_SPEAKER, null); new AdiDeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, null); - Assert.fail(); + fail(); } catch (NullPointerException e) { } } @@ -228,11 +243,114 @@ public class AudioDeviceBrokerTest { final AdiDeviceState result = AdiDeviceState.fromPersistedString(persistString); Log.i(TAG, "original:" + devState); Log.i(TAG, "result :" + result); - Assert.assertEquals(devState, result); + assertEquals(devState, result); + } + + @Test + public void testIsBluetoothAudioDeviceCategoryFixed() throws Exception { + Log.i(TAG, "starting testIsBluetoothAudioDeviceCategoryFixed"); + + if (!automaticBtDeviceType()) { + Log.i(TAG, "Enable automaticBtDeviceType flag to run the test " + + "testIsBluetoothAudioDeviceCategoryFixed"); + return; + } + assertNotNull("invalid null BT device", mFakeBtDevice); + + final AdiDeviceState devState = new AdiDeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, + AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, mFakeBtDevice.getAddress()); + doReturn(devState).when(mSpyDevInventory).findBtDeviceStateForAddress( + mFakeBtDevice.getAddress(), AudioManager.DEVICE_OUT_BLUETOOTH_A2DP); + try { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .adoptShellPermissionIdentity(Manifest.permission.BLUETOOTH_PRIVILEGED); + + // no metadata set + assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE, + DEVICE_TYPE_DEFAULT.getBytes())); + assertFalse( + mAudioDeviceBroker.isBluetoothAudioDeviceCategoryFixed( + mFakeBtDevice.getAddress())); + + // metadata set + assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE, + DEVICE_TYPE_HEADSET.getBytes())); + assertTrue(mAudioDeviceBroker.isBluetoothAudioDeviceCategoryFixed( + mFakeBtDevice.getAddress())); + } finally { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .dropShellPermissionIdentity(); + } + } + + @Test + public void testGetAndUpdateBtAdiDeviceStateCategoryForAddress() throws Exception { + Log.i(TAG, "starting testGetAndUpdateBtAdiDeviceStateCategoryForAddress"); + + if (!automaticBtDeviceType()) { + Log.i(TAG, "Enable automaticBtDeviceType flag to run the test " + + "testGetAndUpdateBtAdiDeviceStateCategoryForAddress"); + return; + } + assertNotNull("invalid null BT device", mFakeBtDevice); + + final AdiDeviceState devState = new AdiDeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, + AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, mFakeBtDevice.getAddress()); + devState.setAudioDeviceCategory(AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER); + doReturn(devState).when(mSpyDevInventory).findBtDeviceStateForAddress( + eq(mFakeBtDevice.getAddress()), anyInt()); + try { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .adoptShellPermissionIdentity(Manifest.permission.BLUETOOTH_PRIVILEGED); + + // no metadata set + assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE, + DEVICE_TYPE_DEFAULT.getBytes())); + assertEquals(AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER, + mAudioDeviceBroker.getAndUpdateBtAdiDeviceStateCategoryForAddress( + mFakeBtDevice.getAddress())); + verify(mMockAudioService, + timeout(MAX_MESSAGE_HANDLING_DELAY_MS).times(0)).onUpdatedAdiDeviceState( + eq(devState)); + + // metadata set + assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE, + DEVICE_TYPE_HEADSET.getBytes())); + assertEquals(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES, + mAudioDeviceBroker.getAndUpdateBtAdiDeviceStateCategoryForAddress( + mFakeBtDevice.getAddress())); + verify(mMockAudioService, + timeout(MAX_MESSAGE_HANDLING_DELAY_MS)).onUpdatedAdiDeviceState( + any()); + } finally { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .dropShellPermissionIdentity(); + } + } + + @Test + public void testAddAudioDeviceWithCategoryInInventoryIfNeeded() throws Exception { + Log.i(TAG, "starting testAddAudioDeviceWithCategoryInInventoryIfNeeded"); + + if (!automaticBtDeviceType()) { + Log.i(TAG, "Enable automaticBtDeviceType flag to run the test " + + "testAddAudioDeviceWithCategoryInInventoryIfNeeded"); + return; + } + assertNotNull("invalid null BT device", mFakeBtDevice); + + mAudioDeviceBroker.addAudioDeviceWithCategoryInInventoryIfNeeded( + mFakeBtDevice.getAddress(), AudioManager.AUDIO_DEVICE_CATEGORY_OTHER); + + verify(mMockAudioService, + timeout(MAX_MESSAGE_HANDLING_DELAY_MS).atLeast(1)).onUpdatedAdiDeviceState( + ArgumentMatchers.argThat(devState -> devState.getAudioDeviceCategory() + == AudioManager.AUDIO_DEVICE_CATEGORY_OTHER)); } private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection, boolean mockMediaPlayback, boolean guaranteeSingleConnection) throws Exception { + assertNotNull("invalid null BT device", mFakeBtDevice); when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC)) .thenReturn(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); when(mMockAudioService.isInCommunication()).thenReturn(false); |