Loading android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +5 −0 Original line number Diff line number Diff line Loading @@ -422,6 +422,11 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac return; } if (!leAudioService.isGroupAvailableForStream(leAudioService.getGroupId(device))) { Log.i(TAG, "LE Audio device is not available for streaming now." + device); return; } if (mHearingAidActiveDevices.isEmpty() && mLeHearingAidActiveDevice == null && mPendingLeHearingAidActiveDevice.isEmpty()) { Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +40 −10 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; import static com.android.bluetooth.flags.Flags.leaudioApiSynchronizedBlockFix; import static com.android.bluetooth.flags.Flags.leaudioGettingActiveStateSupport; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.modules.utils.build.SdkLevel.isAtLeastU; Loading Loading @@ -1972,20 +1971,30 @@ public class LeAudioService extends ProfileService { /** * Set the active device group. * * @param hasFallbackDevice hasFallbackDevice whether any fallback device exists when * {@code device} is null. * @param hasFallbackDevice hasFallbackDevice whether any fallback device exists when {@code * device} is null. */ private void setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice) { private boolean setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice) { int groupId = LE_AUDIO_GROUP_ID_INVALID; if (device != null) { LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); if (descriptor == null) { Log.e(TAG, "setActiveGroupWithDevice: No valid descriptor for device: " + device); return; return false; } groupId = descriptor.mGroupId; if (!isGroupAvailableForStream(groupId)) { Log.e( TAG, "setActiveGroupWithDevice: groupId " + groupId + " is not available for streaming"); return false; } clearInactiveDueToContextTypeFlags(); } Loading @@ -2010,7 +2019,7 @@ public class LeAudioService extends ProfileService { // If broadcast is ongoing and need to update unicast fallback active group // we need to update the cached group id and skip changing the active device updateFallbackUnicastGroupIdForBroadcast(groupId); return; return true; } LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(currentlyActiveGroupId); Loading @@ -2029,7 +2038,7 @@ public class LeAudioService extends ProfileService { + mExposedActiveDevice); sentActiveDeviceChangeIntent(mExposedActiveDevice); } return; return true; } if (currentlyActiveGroupId != LE_AUDIO_GROUP_ID_INVALID Loading @@ -2040,7 +2049,7 @@ public class LeAudioService extends ProfileService { if (!mLeAudioNativeIsInitialized) { Log.e(TAG, "Le Audio not initialized properly."); return; return false; } if (Flags.leaudioGettingActiveStateSupport()) { Loading @@ -2063,6 +2072,7 @@ public class LeAudioService extends ProfileService { */ handleGroupTransitToInactive(currentlyActiveGroupId); } return true; } /** Loading Loading @@ -2113,8 +2123,7 @@ public class LeAudioService extends ProfileService { } } } setActiveGroupWithDevice(device, false); return true; return setActiveGroupWithDevice(device, false); } /** Loading Loading @@ -3766,6 +3775,27 @@ public class LeAudioService extends ProfileService { } } /** * Check if group is available for streaming. If there is no available context types then group * is not available for streaming. * * @param groupId groupid * @return true if available, false otherwise */ public boolean isGroupAvailableForStream(int groupId) { mGroupReadLock.lock(); try { LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null) { Log.e(TAG, "getGroupId: No valid descriptor for groupId: " + groupId); return false; } return descriptor.mAvailableContexts != 0; } finally { mGroupReadLock.unlock(); } } /** * Set the user application ccid along with used context type * @param userUuid user uuid Loading android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.bluetooth.btservice; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.after; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; Loading Loading @@ -156,6 +157,7 @@ public class ActiveDeviceManagerTest { when(mHeadsetService.setActiveDevice(any())).thenReturn(true); when(mHearingAidService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mLeAudioService.removeActiveDevice(anyBoolean())).thenReturn(true); when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice); Loading Loading @@ -689,15 +691,25 @@ public class ActiveDeviceManagerTest { */ @Test public void onlyLeAudioConnected_setHeadsetActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); } /** LE Audio is connected but is not ready for stream (no available context types). */ @Test public void leAudioConnected_notReadyForStream() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(false); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); } /** * Two LE Audio are connected. Should set the second one active. */ @Test public void secondLeAudioConnected_setSecondLeAudioActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -710,6 +722,7 @@ public class ActiveDeviceManagerTest { */ @Test public void lastLeAudioDisconnected_clearLeAudioActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -722,6 +735,7 @@ public class ActiveDeviceManagerTest { */ @Test public void leAudioActiveDeviceSelected_setActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -842,6 +856,7 @@ public class ActiveDeviceManagerTest { @Test public void leAudioSetConnectedThenNotActiveOneDisconnected_noFallback() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -866,6 +881,7 @@ public class ActiveDeviceManagerTest { @Test public void leAudioSetConnectedThenActiveOneDisconnected_noFallback() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -893,6 +909,7 @@ public class ActiveDeviceManagerTest { @Test public void leAudioSetConnectedThenActiveOneDisconnected_hasFallback() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -914,6 +931,8 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); leAudioConnected(mLeAudioDevice); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -948,6 +967,8 @@ public class ActiveDeviceManagerTest { a2dpConnected(mA2dpDevice, false); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -990,6 +1011,7 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mA2dpService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mHearingAidService.setActiveDevice(any())).thenReturn(true); when(mHearingAidService.removeActiveDevice(anyBoolean())).thenReturn(true); Loading Loading @@ -1019,6 +1041,9 @@ public class ActiveDeviceManagerTest { public void onlyLeHearingAidConnected_setLeAudioActive() { leHearingAidConnected(mLeHearingAidDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); verify(mLeAudioService, never()).setActiveDevice(mLeHearingAidDevice); leAudioConnected(mLeHearingAidDevice); Loading @@ -1032,6 +1057,9 @@ public class ActiveDeviceManagerTest { @Test public void leAudioConnectedAfterLeHearingAid_setLeAudioActiveShouldNotBeCalled() { leHearingAidConnected(mLeHearingAidDevice); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeHearingAidDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice); Loading @@ -1049,6 +1077,7 @@ public class ActiveDeviceManagerTest { public void activeDeviceChange_withHearingAidLeHearingAidAndA2dpDevices() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mHearingAidService.removeActiveDevice(anyBoolean())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); hearingAidConnected(mHearingAidDevice); verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice); Loading Loading @@ -1079,6 +1108,7 @@ public class ActiveDeviceManagerTest { public void dualModeAudioDeviceConnected_withDualModeFeatureDisabled() { // Turn off the dual mode audio flag Utils.setDualModeAudioStateForTesting(false); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); // Ensure we remove the LEA active device when classic audio profiles are made active a2dpConnected(mDualModeAudioDevice, true); Loading Loading @@ -1108,6 +1138,8 @@ public class ActiveDeviceManagerTest { // Turn on the dual mode audio flag Utils.setDualModeAudioStateForTesting(true); reset(mLeAudioService); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mAdapterService.isAllSupportedClassicAudioProfilesActive(mDualModeAudioDevice)) .thenReturn(false); Loading @@ -1120,6 +1152,7 @@ public class ActiveDeviceManagerTest { Assert.assertNull(mActiveDeviceManager.getHfpActiveDevice()); when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mLeAudioService.removeActiveDevice(anyBoolean())).thenReturn(true); when(mLeAudioService.getLeadDevice(mDualModeAudioDevice)).thenReturn(mDualModeAudioDevice); Loading Loading @@ -1161,6 +1194,7 @@ public class ActiveDeviceManagerTest { when(mA2dpService.setActiveDevice(any())).thenReturn(false); when(mHearingAidService.setActiveDevice(any())).thenReturn(false); when(mLeAudioService.setActiveDevice(any())).thenReturn(false); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mDualModeAudioDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Loading Loading @@ -1274,6 +1308,7 @@ public class ActiveDeviceManagerTest { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES); final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class); when(mLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leHearingAidConnected(mLeHearingAidDevice); verify(mLeAudioService, never()).setActiveDevice(any()); } Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +47 −5 Original line number Diff line number Diff line Loading @@ -1348,6 +1348,38 @@ public class LeAudioServiceTest { .isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); } /** Test setting active device group with not available contexts */ @Test public void testSetActiveDeviceGroupWithNoContextTypes() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 0; // Not connected device assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Connected device doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.device = mSingleDevice; audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); verify(mNativeInterface, times(0)).groupSetActive(groupId); } /** Test switching active groups */ @Test public void testSwitchActiveGroups() { Loading Loading @@ -1580,7 +1612,7 @@ public class LeAudioServiceTest { nodeStatusChangedEvent.valueInt2 = nodeStatus; mService.messageFromNative(nodeStatusChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -1593,6 +1625,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); // Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading Loading @@ -2043,7 +2077,7 @@ public class LeAudioServiceTest { memberDevice = mRightDevice; } assertThat(mService.setActiveDevice(leadDevice)).isTrue(); assertThat(mService.setActiveDevice(leadDevice)).isFalse(); //Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -2055,6 +2089,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(leadDevice)).isTrue(); //Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading Loading @@ -2114,7 +2150,7 @@ public class LeAudioServiceTest { memberDevice = mRightDevice; } assertThat(mService.setActiveDevice(leadDevice)).isTrue(); assertThat(mService.setActiveDevice(leadDevice)).isFalse(); //Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -2126,6 +2162,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(leadDevice)).isTrue(); //Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading Loading @@ -2179,7 +2217,7 @@ public class LeAudioServiceTest { connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); assertThat(mService.setActiveDevice(mLeftDevice)).isFalse(); ArgumentCaptor<BluetoothProfileConnectionInfo> profileInfo = ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class); Loading @@ -2187,6 +2225,8 @@ public class LeAudioServiceTest { //Add location support. injectAudioConfChanged(groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); doReturn(-1).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId); //Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); Loading Loading @@ -2471,7 +2511,7 @@ public class LeAudioServiceTest { nodeStatusChangedEvent.valueInt2 = nodeStatus; mService.messageFromNative(nodeStatusChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -2484,6 +2524,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); // Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading system/bta/le_audio/client.cc +23 −14 Original line number Diff line number Diff line Loading @@ -3257,8 +3257,6 @@ class LeAudioClientImpl : public LeAudioClient { L2CA_LockBleConnParamsForProfileConnection(leAudioDevice->address_, false); } callbacks_->OnConnectionState(ConnectionState::CONNECTED, leAudioDevice->address_); if (leAudioDevice->GetConnectionState() == DeviceConnectState::CONNECTED_BY_USER_GETTING_READY && Loading @@ -3273,11 +3271,23 @@ class LeAudioClientImpl : public LeAudioClient { ConnectionState::CONNECTED, bluetooth::le_audio::ConnectionStatus::SUCCESS); if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) { if (leAudioDevice->group_id_ == bluetooth::groups::kGroupUnknown) { log::warn(" LeAudio device {} connected with no group", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); callbacks_->OnConnectionState(ConnectionState::CONNECTED, leAudioDevice->address_); return; } LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_); if (group) { UpdateLocationsAndContextsAvailability(group); } /* Notify connected after contexts are notified */ callbacks_->OnConnectionState(ConnectionState::CONNECTED, leAudioDevice->address_); AttachToStreamingGroupIfNeeded(leAudioDevice); if (reconnection_mode_ == BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS) { Loading @@ -3287,7 +3297,6 @@ class LeAudioClientImpl : public LeAudioClient { group->AddToAllowListNotConnectedGroupMembers(gatt_if_); } } } bool IsAseAcceptingAudioData(struct ase* ase) { if (ase == nullptr) return false; Loading Loading
android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +5 −0 Original line number Diff line number Diff line Loading @@ -422,6 +422,11 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac return; } if (!leAudioService.isGroupAvailableForStream(leAudioService.getGroupId(device))) { Log.i(TAG, "LE Audio device is not available for streaming now." + device); return; } if (mHearingAidActiveDevices.isEmpty() && mLeHearingAidActiveDevice == null && mPendingLeHearingAidActiveDevice.isEmpty()) { Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +40 −10 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; import static com.android.bluetooth.flags.Flags.leaudioApiSynchronizedBlockFix; import static com.android.bluetooth.flags.Flags.leaudioGettingActiveStateSupport; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.modules.utils.build.SdkLevel.isAtLeastU; Loading Loading @@ -1972,20 +1971,30 @@ public class LeAudioService extends ProfileService { /** * Set the active device group. * * @param hasFallbackDevice hasFallbackDevice whether any fallback device exists when * {@code device} is null. * @param hasFallbackDevice hasFallbackDevice whether any fallback device exists when {@code * device} is null. */ private void setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice) { private boolean setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice) { int groupId = LE_AUDIO_GROUP_ID_INVALID; if (device != null) { LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); if (descriptor == null) { Log.e(TAG, "setActiveGroupWithDevice: No valid descriptor for device: " + device); return; return false; } groupId = descriptor.mGroupId; if (!isGroupAvailableForStream(groupId)) { Log.e( TAG, "setActiveGroupWithDevice: groupId " + groupId + " is not available for streaming"); return false; } clearInactiveDueToContextTypeFlags(); } Loading @@ -2010,7 +2019,7 @@ public class LeAudioService extends ProfileService { // If broadcast is ongoing and need to update unicast fallback active group // we need to update the cached group id and skip changing the active device updateFallbackUnicastGroupIdForBroadcast(groupId); return; return true; } LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(currentlyActiveGroupId); Loading @@ -2029,7 +2038,7 @@ public class LeAudioService extends ProfileService { + mExposedActiveDevice); sentActiveDeviceChangeIntent(mExposedActiveDevice); } return; return true; } if (currentlyActiveGroupId != LE_AUDIO_GROUP_ID_INVALID Loading @@ -2040,7 +2049,7 @@ public class LeAudioService extends ProfileService { if (!mLeAudioNativeIsInitialized) { Log.e(TAG, "Le Audio not initialized properly."); return; return false; } if (Flags.leaudioGettingActiveStateSupport()) { Loading @@ -2063,6 +2072,7 @@ public class LeAudioService extends ProfileService { */ handleGroupTransitToInactive(currentlyActiveGroupId); } return true; } /** Loading Loading @@ -2113,8 +2123,7 @@ public class LeAudioService extends ProfileService { } } } setActiveGroupWithDevice(device, false); return true; return setActiveGroupWithDevice(device, false); } /** Loading Loading @@ -3766,6 +3775,27 @@ public class LeAudioService extends ProfileService { } } /** * Check if group is available for streaming. If there is no available context types then group * is not available for streaming. * * @param groupId groupid * @return true if available, false otherwise */ public boolean isGroupAvailableForStream(int groupId) { mGroupReadLock.lock(); try { LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null) { Log.e(TAG, "getGroupId: No valid descriptor for groupId: " + groupId); return false; } return descriptor.mAvailableContexts != 0; } finally { mGroupReadLock.unlock(); } } /** * Set the user application ccid along with used context type * @param userUuid user uuid Loading
android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.bluetooth.btservice; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.after; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; Loading Loading @@ -156,6 +157,7 @@ public class ActiveDeviceManagerTest { when(mHeadsetService.setActiveDevice(any())).thenReturn(true); when(mHearingAidService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mLeAudioService.removeActiveDevice(anyBoolean())).thenReturn(true); when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice); Loading Loading @@ -689,15 +691,25 @@ public class ActiveDeviceManagerTest { */ @Test public void onlyLeAudioConnected_setHeadsetActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); } /** LE Audio is connected but is not ready for stream (no available context types). */ @Test public void leAudioConnected_notReadyForStream() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(false); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); } /** * Two LE Audio are connected. Should set the second one active. */ @Test public void secondLeAudioConnected_setSecondLeAudioActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -710,6 +722,7 @@ public class ActiveDeviceManagerTest { */ @Test public void lastLeAudioDisconnected_clearLeAudioActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -722,6 +735,7 @@ public class ActiveDeviceManagerTest { */ @Test public void leAudioActiveDeviceSelected_setActive() { when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -842,6 +856,7 @@ public class ActiveDeviceManagerTest { @Test public void leAudioSetConnectedThenNotActiveOneDisconnected_noFallback() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -866,6 +881,7 @@ public class ActiveDeviceManagerTest { @Test public void leAudioSetConnectedThenActiveOneDisconnected_noFallback() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -893,6 +909,7 @@ public class ActiveDeviceManagerTest { @Test public void leAudioSetConnectedThenActiveOneDisconnected_hasFallback() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading @@ -914,6 +931,8 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); leAudioConnected(mLeAudioDevice); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -948,6 +967,8 @@ public class ActiveDeviceManagerTest { a2dpConnected(mA2dpDevice, false); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Loading Loading @@ -990,6 +1011,7 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mA2dpService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mHearingAidService.setActiveDevice(any())).thenReturn(true); when(mHearingAidService.removeActiveDevice(anyBoolean())).thenReturn(true); Loading Loading @@ -1019,6 +1041,9 @@ public class ActiveDeviceManagerTest { public void onlyLeHearingAidConnected_setLeAudioActive() { leHearingAidConnected(mLeHearingAidDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); verify(mLeAudioService, never()).setActiveDevice(mLeHearingAidDevice); leAudioConnected(mLeHearingAidDevice); Loading @@ -1032,6 +1057,9 @@ public class ActiveDeviceManagerTest { @Test public void leAudioConnectedAfterLeHearingAid_setLeAudioActiveShouldNotBeCalled() { leHearingAidConnected(mLeHearingAidDevice); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mLeHearingAidDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice); Loading @@ -1049,6 +1077,7 @@ public class ActiveDeviceManagerTest { public void activeDeviceChange_withHearingAidLeHearingAidAndA2dpDevices() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); when(mHearingAidService.removeActiveDevice(anyBoolean())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); hearingAidConnected(mHearingAidDevice); verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice); Loading Loading @@ -1079,6 +1108,7 @@ public class ActiveDeviceManagerTest { public void dualModeAudioDeviceConnected_withDualModeFeatureDisabled() { // Turn off the dual mode audio flag Utils.setDualModeAudioStateForTesting(false); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); // Ensure we remove the LEA active device when classic audio profiles are made active a2dpConnected(mDualModeAudioDevice, true); Loading Loading @@ -1108,6 +1138,8 @@ public class ActiveDeviceManagerTest { // Turn on the dual mode audio flag Utils.setDualModeAudioStateForTesting(true); reset(mLeAudioService); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mAdapterService.isAllSupportedClassicAudioProfilesActive(mDualModeAudioDevice)) .thenReturn(false); Loading @@ -1120,6 +1152,7 @@ public class ActiveDeviceManagerTest { Assert.assertNull(mActiveDeviceManager.getHfpActiveDevice()); when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); when(mLeAudioService.removeActiveDevice(anyBoolean())).thenReturn(true); when(mLeAudioService.getLeadDevice(mDualModeAudioDevice)).thenReturn(mDualModeAudioDevice); Loading Loading @@ -1161,6 +1194,7 @@ public class ActiveDeviceManagerTest { when(mA2dpService.setActiveDevice(any())).thenReturn(false); when(mHearingAidService.setActiveDevice(any())).thenReturn(false); when(mLeAudioService.setActiveDevice(any())).thenReturn(false); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leAudioConnected(mDualModeAudioDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Loading Loading @@ -1274,6 +1308,7 @@ public class ActiveDeviceManagerTest { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES); final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class); when(mLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList); when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(true); leHearingAidConnected(mLeHearingAidDevice); verify(mLeAudioService, never()).setActiveDevice(any()); } Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +47 −5 Original line number Diff line number Diff line Loading @@ -1348,6 +1348,38 @@ public class LeAudioServiceTest { .isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); } /** Test setting active device group with not available contexts */ @Test public void testSetActiveDeviceGroupWithNoContextTypes() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 0; // Not connected device assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Connected device doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.device = mSingleDevice; audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); verify(mNativeInterface, times(0)).groupSetActive(groupId); } /** Test switching active groups */ @Test public void testSwitchActiveGroups() { Loading Loading @@ -1580,7 +1612,7 @@ public class LeAudioServiceTest { nodeStatusChangedEvent.valueInt2 = nodeStatus; mService.messageFromNative(nodeStatusChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -1593,6 +1625,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); // Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading Loading @@ -2043,7 +2077,7 @@ public class LeAudioServiceTest { memberDevice = mRightDevice; } assertThat(mService.setActiveDevice(leadDevice)).isTrue(); assertThat(mService.setActiveDevice(leadDevice)).isFalse(); //Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -2055,6 +2089,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(leadDevice)).isTrue(); //Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading Loading @@ -2114,7 +2150,7 @@ public class LeAudioServiceTest { memberDevice = mRightDevice; } assertThat(mService.setActiveDevice(leadDevice)).isTrue(); assertThat(mService.setActiveDevice(leadDevice)).isFalse(); //Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -2126,6 +2162,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(leadDevice)).isTrue(); //Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading Loading @@ -2179,7 +2217,7 @@ public class LeAudioServiceTest { connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); assertThat(mService.setActiveDevice(mLeftDevice)).isFalse(); ArgumentCaptor<BluetoothProfileConnectionInfo> profileInfo = ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class); Loading @@ -2187,6 +2225,8 @@ public class LeAudioServiceTest { //Add location support. injectAudioConfChanged(groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); doReturn(-1).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId); //Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); Loading Loading @@ -2471,7 +2511,7 @@ public class LeAudioServiceTest { nodeStatusChangedEvent.valueInt2 = nodeStatus; mService.messageFromNative(nodeStatusChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Add location support LeAudioStackEvent audioConfChangedEvent = Loading @@ -2484,6 +2524,8 @@ public class LeAudioServiceTest { audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); // Set group and device as active LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); Loading
system/bta/le_audio/client.cc +23 −14 Original line number Diff line number Diff line Loading @@ -3257,8 +3257,6 @@ class LeAudioClientImpl : public LeAudioClient { L2CA_LockBleConnParamsForProfileConnection(leAudioDevice->address_, false); } callbacks_->OnConnectionState(ConnectionState::CONNECTED, leAudioDevice->address_); if (leAudioDevice->GetConnectionState() == DeviceConnectState::CONNECTED_BY_USER_GETTING_READY && Loading @@ -3273,11 +3271,23 @@ class LeAudioClientImpl : public LeAudioClient { ConnectionState::CONNECTED, bluetooth::le_audio::ConnectionStatus::SUCCESS); if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) { if (leAudioDevice->group_id_ == bluetooth::groups::kGroupUnknown) { log::warn(" LeAudio device {} connected with no group", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); callbacks_->OnConnectionState(ConnectionState::CONNECTED, leAudioDevice->address_); return; } LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_); if (group) { UpdateLocationsAndContextsAvailability(group); } /* Notify connected after contexts are notified */ callbacks_->OnConnectionState(ConnectionState::CONNECTED, leAudioDevice->address_); AttachToStreamingGroupIfNeeded(leAudioDevice); if (reconnection_mode_ == BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS) { Loading @@ -3287,7 +3297,6 @@ class LeAudioClientImpl : public LeAudioClient { group->AddToAllowListNotConnectedGroupMembers(gatt_if_); } } } bool IsAseAcceptingAudioData(struct ase* ase) { if (ase == nullptr) return false; Loading