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

Commit 3f43e93a authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "le_audio: LeAudioService: Fix handling initial zero available contexts"...

Merge "le_audio: LeAudioService: Fix handling initial zero available contexts" into main am: e1d4b23f

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/3201538



Change-Id: Icb007ac8af91ebcdb0e442a82a525aacaa7fb444
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 7cd772ed e1d4b23f
Loading
Loading
Loading
Loading
+20 −8
Original line number Diff line number Diff line
@@ -249,7 +249,7 @@ public class LeAudioService extends ProfileService {
            mLostLeadDeviceWhileStreaming = null;
            mCurrentLeadDevice = null;
            mInbandRingtoneEnabled = isInbandRingtonEnabled;
            mAvailableContexts = 0;
            mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0;
            mInputSelectableConfig = new ArrayList<>();
            mOutputSelectableConfig = new ArrayList<>();
            mInactivatedDueToContextType = false;
@@ -2952,9 +2952,13 @@ public class LeAudioService extends ProfileService {
                return;
            }

            boolean ringtoneContextAvailable =
                    ((groupDescriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE)
                            != 0);
            boolean ringtoneContextAvailable;
            if (groupDescriptor.mAvailableContexts != null) {
                ringtoneContextAvailable = ((groupDescriptor.mAvailableContexts &
                                            BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0);
            } else {
                ringtoneContextAvailable = false;
            }

            Log.d(
                    TAG,
@@ -3492,9 +3496,10 @@ public class LeAudioService extends ProfileService {
                                                    BluetoothLeAudio.GROUP_STATUS_INACTIVE));
                        }
                    }

                    boolean isInitial = descriptor.mAvailableContexts == null;
                    boolean availableContextChanged =
                            Integer.bitCount(descriptor.mAvailableContexts)
                                    != Integer.bitCount(available_contexts);
                            isInitial ? true : descriptor.mAvailableContexts != available_contexts;

                    descriptor.mDirection = direction;
                    descriptor.mAvailableContexts = available_contexts;
@@ -3519,6 +3524,13 @@ public class LeAudioService extends ProfileService {
                                            + " due to unavailable context types");
                            descriptor.mInactivatedDueToContextType = true;
                            setActiveGroupWithDevice(null, false);
                        } else if (isInitial) {
                            Log.i(
                                    TAG,
                                    " New group "
                                            + groupId
                                            + " with no context types available");
                            descriptor.mInactivatedDueToContextType = true;
                        }
                        return;
                    }
@@ -4144,7 +4156,7 @@ public class LeAudioService extends ProfileService {

            if (getConnectedPeerDevices(groupId).isEmpty()) {
                descriptor.mIsConnected = false;
                descriptor.mInactivatedDueToContextType = false;
                descriptor.mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0;
                if (descriptor.isActive()) {
                    /* Notify Native layer */
                    removeActiveDevice(hasFallbackDevice);
@@ -4495,7 +4507,7 @@ public class LeAudioService extends ProfileService {
                Log.e(TAG, "getGroupId: No valid descriptor for groupId: " + groupId);
                return false;
            }
            return descriptor.mAvailableContexts != 0;
            return descriptor.mAvailableContexts != null && descriptor.mAvailableContexts != 0;
        } finally {
            mGroupReadLock.unlock();
        }
+312 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import android.media.BluetoothProfileConnectionInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.sysprop.BluetoothProperties;

@@ -3109,6 +3110,317 @@ public class LeAudioServiceTest {
                        any(BluetoothProfileConnectionInfo.class));
    }

    /**
     * Test the group is activated once the available contexts are back.
     *
     * Scenario:
     *  1. Have a group of 2 devices that initially does not expose any available contexts.
     *     The group shall be inactive at this point.
     *  2. Once the available contexts are updated with non-zero value,
     *     the group shall become active.
     *  3. The available contexts are changed to zero. Group becomes inactive.
     *  4. The available contexts are back again. Group becomes active.
     */
    @Test
    @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS)
    public void testActivateGroupWhenAvailableContextAreBack_Scenario1() {
        int groupId = 1;
        /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
        int direction = 1;
        int snkAudioLocation = 3;
        int srcAudioLocation = 4;
        int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;

        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mLeftDevice, groupId);
        connectTestDevice(mRightDevice, groupId);

        // Checks group device lists for groupId 1
        List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);

        assertThat(groupDevicesById.size()).isEqualTo(2);
        assertThat(groupDevicesById.contains(mLeftDevice)).isTrue();
        assertThat(groupDevicesById.contains(mRightDevice)).isTrue();

        // Add location support
        LeAudioStackEvent audioConfChangedEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
        audioConfChangedEvent.valueInt1 = direction;
        audioConfChangedEvent.valueInt2 = groupId;
        audioConfChangedEvent.valueInt3 = snkAudioLocation;
        audioConfChangedEvent.valueInt4 = srcAudioLocation;
        audioConfChangedEvent.valueInt5 = 0;
        mService.messageFromNative(audioConfChangedEvent);

        assertThat(mService.setActiveDevice(mLeftDevice)).isFalse();
        verify(mNativeInterface, times(0)).groupSetActive(groupId);

        // Expect device to be active
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        verify(mNativeInterface, times(1)).groupSetActive(groupId);

        // Set group and device as active.
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        any(BluetoothDevice.class),
                        eq(null),
                        any(BluetoothProfileConnectionInfo.class));

        reset(mAudioManager);
        reset(mNativeInterface);

        // Expect device to be inactive
        audioConfChangedEvent.valueInt5 = 0;
        mService.messageFromNative(audioConfChangedEvent);

        verify(mNativeInterface, times(1)).groupSetActive(-1);
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);

        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        eq(null),
                        any(BluetoothDevice.class),
                        any(BluetoothProfileConnectionInfo.class));

        reset(mNativeInterface);
        reset(mAudioManager);

        // Expect device to be active
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        verify(mNativeInterface, times(1)).groupSetActive(groupId);
        reset(mNativeInterface);

        // Set group and device as active.
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        any(BluetoothDevice.class),
                        eq(null),
                        any(BluetoothProfileConnectionInfo.class));
    }

    /**
     * Test the group is activated once the available contexts are back.
     *
     * Scenario:
     *  1. Have a group of 2 devices. The available contexts are non-zero.
     *     The group shall be active at this point.
     *  2. Once the available contexts are updated with zero value,
     *     the group shall become inactive.
     *  3. All group devices are disconnected.
     *  4. Group devices are reconnected. The available contexts are still zero.
     *  4. The available contexts are updated with non-zero value. Group becomes active.
     */
    @Test
    @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS)
    public void testActivateDeviceWhenAvailableContextAreBack_Scenario2() {
        int groupId = 1;
        /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
        int direction = 1;
        int snkAudioLocation = 3;
        int srcAudioLocation = 4;
        int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;

        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mLeftDevice, groupId);
        connectTestDevice(mRightDevice, groupId);

        // Checks group device lists for groupId 1
        List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);

        assertThat(groupDevicesById.size()).isEqualTo(2);
        assertThat(groupDevicesById.contains(mLeftDevice)).isTrue();
        assertThat(groupDevicesById.contains(mRightDevice)).isTrue();

        // Add location support
        LeAudioStackEvent audioConfChangedEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
        audioConfChangedEvent.valueInt1 = direction;
        audioConfChangedEvent.valueInt2 = groupId;
        audioConfChangedEvent.valueInt3 = snkAudioLocation;
        audioConfChangedEvent.valueInt4 = srcAudioLocation;
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
        verify(mNativeInterface, times(1)).groupSetActive(groupId);

        // Set group and device as active.
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        any(BluetoothDevice.class),
                        eq(null),
                        any(BluetoothProfileConnectionInfo.class));

        reset(mAudioManager);
        reset(mNativeInterface);

        // Expect device to be inactive
        audioConfChangedEvent.valueInt5 = 0;
        mService.messageFromNative(audioConfChangedEvent);

        verify(mNativeInterface, times(1)).groupSetActive(-1);
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);

        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        eq(null),
                        any(BluetoothDevice.class),
                        any(BluetoothProfileConnectionInfo.class));

        reset(mNativeInterface);
        reset(mAudioManager);

        // Send a message to trigger disconnection completed to the left device
        injectAndVerifyDeviceDisconnected(mLeftDevice);

        // Send a message to trigger disconnection completed to the right device
        injectAndVerifyDeviceDisconnected(mRightDevice);

        // Verify the list of connected devices
        assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isFalse();
        assertThat(mService.getConnectedDevices().contains(mRightDevice)).isFalse();

        // Expect device to be inactive
        audioConfChangedEvent.valueInt5 = 0;
        mService.messageFromNative(audioConfChangedEvent);

        generateConnectionMessageFromNative(
                mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mService.getConnectionState(mLeftDevice))
                .isEqualTo(BluetoothProfile.STATE_CONNECTED);
        assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue();

        // Expect device to be inactive
        audioConfChangedEvent.valueInt5 = 0;
        mService.messageFromNative(audioConfChangedEvent);

        generateConnectionMessageFromNative(
                mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mService.getConnectionState(mRightDevice))
                .isEqualTo(BluetoothProfile.STATE_CONNECTED);
        assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue();

        // Expect device to be active
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        verify(mNativeInterface, times(1)).groupSetActive(groupId);

        // Set group and device as active.
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        any(BluetoothDevice.class),
                        eq(null),
                        any(BluetoothProfileConnectionInfo.class));
    }

    /**
     * Test the group is activated once the available contexts are back.
     *
     * Scenario:
     *  1. Have a group of 2 devices. The available contexts are non-zero.
     *     The group shall be active at this point.
     *  2. All group devices are disconnected.
     *  3. Group devices are reconnected. The available contexts are zero.
     *  4. The available contexts are updated with non-zero value. Group becomes active.
     */
    @Test
    @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS)
    public void testActivateDeviceWhenAvailableContextAreBack_Scenario3() {
        int groupId = 1;
        /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
        int direction = 1;
        int snkAudioLocation = 3;
        int srcAudioLocation = 4;
        int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;

        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mLeftDevice, groupId);
        connectTestDevice(mRightDevice, groupId);

        // Checks group device lists for groupId 1
        List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);

        assertThat(groupDevicesById.size()).isEqualTo(2);
        assertThat(groupDevicesById.contains(mLeftDevice)).isTrue();
        assertThat(groupDevicesById.contains(mRightDevice)).isTrue();

        // Add location support
        LeAudioStackEvent audioConfChangedEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
        audioConfChangedEvent.valueInt1 = direction;
        audioConfChangedEvent.valueInt2 = groupId;
        audioConfChangedEvent.valueInt3 = snkAudioLocation;
        audioConfChangedEvent.valueInt4 = srcAudioLocation;
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
        verify(mNativeInterface, times(1)).groupSetActive(groupId);

        // Set group and device as active.
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        any(BluetoothDevice.class),
                        eq(null),
                        any(BluetoothProfileConnectionInfo.class));

        reset(mNativeInterface);
        reset(mAudioManager);

        // Send a message to trigger disconnection completed to the right device
        injectAndVerifyDeviceDisconnected(mRightDevice);

        // Send a message to trigger disconnection completed to the left device
        injectAndVerifyDeviceDisconnected(mLeftDevice);

        reset(mNativeInterface);
        reset(mAudioManager);

        // Expect device to be inactive
        audioConfChangedEvent.valueInt5 = 0;
        mService.messageFromNative(audioConfChangedEvent);

        generateConnectionMessageFromNative(
                mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mService.getConnectionState(mLeftDevice))
                .isEqualTo(BluetoothProfile.STATE_CONNECTED);
        assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue();

        // Expect device to be inactive
        audioConfChangedEvent.valueInt5 = 0;
        mService.messageFromNative(audioConfChangedEvent);

        generateConnectionMessageFromNative(
                mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mService.getConnectionState(mRightDevice))
                .isEqualTo(BluetoothProfile.STATE_CONNECTED);
        assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue();

        // Expect device to be active
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        verify(mNativeInterface, times(1)).groupSetActive(groupId);

        // Set group and device as active.
        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        any(BluetoothDevice.class),
                        eq(null),
                        any(BluetoothProfileConnectionInfo.class));
    }

    /** Test setting allowed contexts for active group */
    @Test
    public void testSetAllowedContextsForActiveGroup() {