diff options
author | 2025-03-13 21:15:32 -0700 | |
---|---|---|
committer | 2025-03-13 21:15:32 -0700 | |
commit | 15717f41d0b11a9a26b0e5288336a060d1854221 (patch) | |
tree | 308a02adc4bb79649b0294f35dad3f2d8fef9a27 | |
parent | 2353e035d49b5965ce987ed9a8e6354549f46c51 (diff) | |
parent | f022c09b3f5a150a2a1b365cad132b9bdb81f463 (diff) |
Merge "LE Audio: do not hardcode TMAP role mask" into main
-rw-r--r-- | android/app/src/com/android/bluetooth/le_audio/LeAudioService.java | 69 | ||||
-rw-r--r-- | android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java | 100 |
2 files changed, 142 insertions, 27 deletions
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java index cebf4270e5..de152f0ae1 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -27,6 +27,7 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.bass_client.BassConstants.INVALID_BROADCAST_ID; +import static com.android.bluetooth.flags.Flags.doNotHardcodeTmapRoleMask; import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState; import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiManagePrimaryGroup; import static com.android.bluetooth.flags.Flags.leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator; @@ -261,24 +262,49 @@ public class LeAudioService extends ProfileService { mStateMachinesThread.start(); // Initialize Broadcast native interface - if ((mAdapterService.getSupportedProfilesBitMask() - & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) - != 0) { - Log.i(TAG, "Init Le Audio broadcaster"); - LeAudioBroadcasterNativeInterface broadcastNativeInterface = - requireNonNull(LeAudioBroadcasterNativeInterface.getInstance()); - broadcastNativeInterface.init(); - mLeAudioBroadcasterNativeInterface = Optional.of(broadcastNativeInterface); - mTmapRoleMask = - LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG - | LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS - | LeAudioTmapGattServer.TMAP_ROLE_FLAG_BMS; + if (doNotHardcodeTmapRoleMask()) { + int mask = 0; + if (isProfileSupported(BluetoothProfile.LE_CALL_CONTROL)) { + // Table 3.5 of TMAP v1.0: CCP Server is mandatory for the TMAP CG role. + mask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG; + } + if (isProfileSupported(BluetoothProfile.MCP_SERVER)) { + // Table 3.5 of TMAP v1.0: MCP Server is mandatory for the TMAP UMS role. + mask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS; + } + if (isProfileSupported(BluetoothProfile.LE_AUDIO_BROADCAST)) { + Log.i(TAG, "Init Le Audio broadcaster"); + LeAudioBroadcasterNativeInterface broadcastNativeInterface = + requireNonNull(LeAudioBroadcasterNativeInterface.getInstance()); + broadcastNativeInterface.init(); + mLeAudioBroadcasterNativeInterface = Optional.of(broadcastNativeInterface); + + mask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_BMS; + } else { + mLeAudioBroadcasterNativeInterface = Optional.empty(); + Log.w(TAG, "Le Audio Broadcasts not supported."); + } + mTmapRoleMask = mask; } else { - mTmapRoleMask = - LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG - | LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS; - mLeAudioBroadcasterNativeInterface = Optional.empty(); - Log.w(TAG, "Le Audio Broadcasts not supported."); + if ((mAdapterService.getSupportedProfilesBitMask() + & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) + != 0) { + Log.i(TAG, "Init Le Audio broadcaster"); + LeAudioBroadcasterNativeInterface broadcastNativeInterface = + requireNonNull(LeAudioBroadcasterNativeInterface.getInstance()); + broadcastNativeInterface.init(); + mLeAudioBroadcasterNativeInterface = Optional.of(broadcastNativeInterface); + mTmapRoleMask = + LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG + | LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS + | LeAudioTmapGattServer.TMAP_ROLE_FLAG_BMS; + } else { + mTmapRoleMask = + LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG + | LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS; + mLeAudioBroadcasterNativeInterface = Optional.empty(); + Log.w(TAG, "Le Audio Broadcasts not supported."); + } } mTmapStarted = registerTmap(); @@ -302,6 +328,15 @@ public class LeAudioService extends ProfileService { } } + private boolean isProfileSupported(int profile) { + return (mAdapterService.getSupportedProfilesBitMask() & (1 << profile)) != 0; + } + + @VisibleForTesting + int getTmapRoleMask() { + return mTmapRoleMask; + } + private class LeAudioGroupDescriptor { LeAudioGroupDescriptor(int groupId, boolean isInbandRingtoneEnabled) { mGroupId = groupId; diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java index f0f2037358..cf836ddc3c 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java @@ -76,12 +76,12 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.sysprop.BluetoothProperties; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; import com.android.bluetooth.bass_client.BassClientService; @@ -113,15 +113,20 @@ import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.hamcrest.MockitoHamcrest; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; @MediumTest -@RunWith(AndroidJUnit4.class) +@RunWith(ParameterizedAndroidJunit4.class) public class LeAudioServiceTest { - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule public final SetFlagsRule mSetFlagsRule; @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private AdapterService mAdapterService; @@ -201,6 +206,20 @@ public class LeAudioServiceTest { private InOrder mInOrder; + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.progressionOf( + Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION, + Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX, + Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS, + Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP, + Flags.FLAG_DO_NOT_HARDCODE_TMAP_ROLE_MASK); + } + + public LeAudioServiceTest(FlagsParameterization flags) { + mSetFlagsRule = new SetFlagsRule(flags); + } + @Before public void setUp() throws Exception { mInOrder = inOrder(mAdapterService); @@ -211,11 +230,9 @@ public class LeAudioServiceTest { doReturn(mTargetContext.getContentResolver()).when(mAdapterService).getContentResolver(); doReturn(MAX_LE_AUDIO_CONNECTIONS).when(mAdapterService).getMaxConnectedAudioDevices(); - doReturn( - (long) (1 << BluetoothProfile.LE_AUDIO_BROADCAST) - | (1 << BluetoothProfile.LE_AUDIO)) - .when(mAdapterService) - .getSupportedProfilesBitMask(); + injectSupportedProfilesBitMask( + Set.of(BluetoothProfile.LE_AUDIO_BROADCAST, BluetoothProfile.LE_AUDIO)); + doReturn(new ParcelUuid[] {BluetoothUuid.LE_AUDIO}) .when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); @@ -280,6 +297,43 @@ public class LeAudioServiceTest { assertThat(LeAudioService.getLeAudioService()).isNull(); } + @Test + @EnableFlags(Flags.FLAG_DO_NOT_HARDCODE_TMAP_ROLE_MASK) + public void testTmapRoleMask() { + List<Set<Integer>> powerSet = + List.of( + Set.of(BluetoothProfile.LE_CALL_CONTROL), + Set.of(BluetoothProfile.MCP_SERVER), + Set.of(BluetoothProfile.LE_CALL_CONTROL, BluetoothProfile.MCP_SERVER), + Set.of(BluetoothProfile.LE_AUDIO_BROADCAST), + Set.of( + BluetoothProfile.LE_AUDIO_BROADCAST, + BluetoothProfile.LE_CALL_CONTROL), + Set.of(BluetoothProfile.LE_AUDIO_BROADCAST, BluetoothProfile.MCP_SERVER), + Set.of( + BluetoothProfile.LE_AUDIO_BROADCAST, + BluetoothProfile.LE_CALL_CONTROL, + BluetoothProfile.MCP_SERVER)); + + List<Integer> tmapMasks = + powerSet.stream() + .map( + set -> { + injectSupportedProfilesBitMask(set); + LeAudioService service = + new LeAudioService(mAdapterService, mNativeInterface); + return service.getTmapRoleMask(); + }) + .collect(Collectors.toList()); + + List<Integer> expectedMasks = + powerSet.stream() + .map(LeAudioServiceTest::constructTmapRoleMask) + .collect(Collectors.toList()); + + assertThat(tmapMasks).containsExactly(expectedMasks.toArray()).inOrder(); + } + /** Test getting LeAudio Service: getLeAudioService() */ @Test public void testGetLeAudioService() { @@ -1788,8 +1842,8 @@ public class LeAudioServiceTest { /** Test native interface group status message handling */ @Test + @EnableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX) public void testMessageFromNativeGroupCodecConfigChangedNonActiveDevice() { - mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX); onGroupCodecConfChangedCallbackCalled = false; injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); @@ -1874,8 +1928,8 @@ public class LeAudioServiceTest { /** Test native interface group status message handling */ @Test + @EnableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX) public void testMessageFromNativeGroupCodecConfigChangedActiveDevice_DifferentConfiguration() { - mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX); onGroupCodecConfChangedCallbackCalled = false; injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); @@ -3216,4 +3270,30 @@ public class LeAudioServiceTest { .sendBroadcastAsUser( MockitoHamcrest.argThat(AllOf.allOf(matchers)), any(), any(), any()); } + + private void injectSupportedProfilesBitMask(Set<Integer> profiles) { + long mask = 0; + for (int profile : profiles) { + mask |= (long) (1 << profile); + } + doReturn(mask).when(mAdapterService).getSupportedProfilesBitMask(); + } + + private static int constructTmapRoleMask(Set<Integer> profiles) { + int mask = 0; + for (int profile : profiles) { + switch (profile) { + case BluetoothProfile.LE_CALL_CONTROL: + mask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG; + break; + case BluetoothProfile.MCP_SERVER: + mask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS; + break; + case BluetoothProfile.LE_AUDIO_BROADCAST: + mask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_BMS; + break; + } + } + return mask; + } } |