Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd222b3e authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Gerrit Code Review
Browse files

Merge "leaudio: Additional improvement to handle no available context types" into main

parents 8df39a27 c0b24b6b
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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()) {
+40 −10
Original line number Diff line number Diff line
@@ -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;

@@ -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();
        }

@@ -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);
@@ -2029,7 +2038,7 @@ public class LeAudioService extends ProfileService {
                                + mExposedActiveDevice);
                sentActiveDeviceChangeIntent(mExposedActiveDevice);
            }
            return;
            return true;
        }

        if (currentlyActiveGroupId != LE_AUDIO_GROUP_ID_INVALID
@@ -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()) {
@@ -2063,6 +2072,7 @@ public class LeAudioService extends ProfileService {
             */
            handleGroupTransitToInactive(currentlyActiveGroupId);
        }
        return true;
    }

    /**
@@ -2113,8 +2123,7 @@ public class LeAudioService extends ProfileService {
                }
            }
        }
        setActiveGroupWithDevice(device, false);
        return true;
        return setActiveGroupWithDevice(device, false);
    }

    /**
@@ -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
+35 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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);

@@ -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);

@@ -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);

@@ -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);
@@ -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);
@@ -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);
@@ -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);

@@ -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);

@@ -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);

@@ -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);
@@ -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);

@@ -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);
@@ -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);
@@ -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);

@@ -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);

@@ -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());
@@ -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());
    }
+47 −5
Original line number Diff line number Diff line
@@ -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() {
@@ -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 =
@@ -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);
@@ -2043,7 +2077,7 @@ public class LeAudioServiceTest {
                memberDevice = mRightDevice;
        }

        assertThat(mService.setActiveDevice(leadDevice)).isTrue();
        assertThat(mService.setActiveDevice(leadDevice)).isFalse();

        //Add location support
        LeAudioStackEvent audioConfChangedEvent =
@@ -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);
@@ -2114,7 +2150,7 @@ public class LeAudioServiceTest {
                memberDevice = mRightDevice;
        }

        assertThat(mService.setActiveDevice(leadDevice)).isTrue();
        assertThat(mService.setActiveDevice(leadDevice)).isFalse();

        //Add location support
        LeAudioStackEvent audioConfChangedEvent =
@@ -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);
@@ -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);
@@ -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);
@@ -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 =
@@ -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);
+23 −14
Original line number Diff line number Diff line
@@ -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 &&
@@ -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) {
@@ -3287,7 +3297,6 @@ class LeAudioClientImpl : public LeAudioClient {
      group->AddToAllowListNotConnectedGroupMembers(gatt_if_);
    }
  }
  }

  bool IsAseAcceptingAudioData(struct ase* ase) {
    if (ase == nullptr) return false;