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

Commit 16d4fe3a authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Jack He
Browse files

BluetoothLeAudio: Improve handling lead device disconnection

To improve scenario when lead Le Audio device is disconnected for the
streaming group, while there are still other devices streaming,
LeAudioService will not notify audio framework or other users about
Le Audio lead device disconnection. Instead we try to reconnect under the hood
and keep using lead device as a audio device indetifier in the audio framework
in order to not stop the stream.

Bug: 150670922
Bug: 228809876
Test: atest BluetoothInstrumentationTests
Sponsor: @jpawlowski
Change-Id: I02b960c6a5a966b6650dc3485c5d3f6735e4566b
Merged-In: I02b960c6a5a966b6650dc3485c5d3f6735e4566b
(cherry picked from commit eaf4f412)
parent 742c3ea4
Loading
Loading
Loading
Loading
+62 −5
Original line number Diff line number Diff line
@@ -135,12 +135,15 @@ public class LeAudioService extends ProfileService {
            mIsActive = false;
            mActiveContexts = ACTIVE_CONTEXTS_NONE;
            mCodecStatus = null;
            mLostDevicesWhileStreaming = new ArrayList<>();
        }

        public Boolean mIsConnected;
        public Boolean mIsActive;
        public Integer mActiveContexts;
        public BluetoothLeAudioCodecStatus mCodecStatus;
        /* This can be non empty only for the streaming time */
        List<BluetoothDevice> mLostDevicesWhileStreaming;
    }

    List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>();
@@ -1014,6 +1017,21 @@ public class LeAudioService extends ProfileService {
        return BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel);
    }

    private void clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor) {
        for (BluetoothDevice dev : descriptor.mLostDevicesWhileStreaming) {
            LeAudioStateMachine sm = mStateMachines.get(dev);
            if (sm == null) {
                continue;
            }

            LeAudioStackEvent stackEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
            stackEvent.device = dev;
            stackEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED;
            sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent);
        }
    }

    // Suppressed since this is part of a local process
    @SuppressLint("AndroidFrameworkRequiresPermission")
    void messageFromNative(LeAudioStackEvent stackEvent) {
@@ -1025,7 +1043,41 @@ public class LeAudioService extends ProfileService {
        // Some events require device state machine
            synchronized (mStateMachines) {
                LeAudioStateMachine sm = mStateMachines.get(device);
                if (sm == null) {
                if (sm != null) {
                    /*
                    * To improve scenario when lead Le Audio device is disconnected for the
                    * streaming group, while there are still other devices streaming,
                    * LeAudioService will not notify audio framework or other users about
                    * Le Audio lead device disconnection. Instead we try to reconnect under the hood
                    * and keep using lead device as a audio device indetifier in the audio framework
                    * in order to not stop the stream.
                    */
                    int groupId = getGroupId(device);
                    synchronized (mGroupLock) {
                        LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId);
                        switch (stackEvent.valueInt1) {
                            case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTING:
                            case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED:
                                if (descriptor != null && (Objects.equals(device,
                                        mActiveAudioOutDevice)
                                        || Objects.equals(device, mActiveAudioInDevice))
                                        && (getConnectedPeerDevices(groupId).size() > 1)) {
                                    descriptor.mLostDevicesWhileStreaming.add(device);
                                    return;
                                }
                                break;
                            case LeAudioStackEvent.CONNECTION_STATE_CONNECTED:
                            case LeAudioStackEvent.CONNECTION_STATE_CONNECTING:
                                if (descriptor != null) {
                                    descriptor.mLostDevicesWhileStreaming.remove(device);
                                    /* Try to connect other devices from the group */
                                    connectSet(device);
                                }
                                break;
                        }
                    }
                } else {
                    /* state machine does not exist yet */
                    switch (stackEvent.valueInt1) {
                        case LeAudioStackEvent.CONNECTION_STATE_CONNECTED:
                        case LeAudioStackEvent.CONNECTION_STATE_CONNECTING:
@@ -1036,12 +1088,12 @@ public class LeAudioService extends ProfileService {
                        default:
                            break;
                    }
                }

                    if (sm == null) {
                        Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
                        return;
                    }
                }

                sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent);
                return;
@@ -1153,6 +1205,8 @@ public class LeAudioService extends ProfileService {
                            updateActiveDevices(groupId, descriptor.mActiveContexts,
                                    ACTIVE_CONTEXTS_NONE, descriptor.mIsActive);
                            notifyGroupStatus = true;
                            /* Clear lost devices */
                            clearLostDevicesWhileStreaming(descriptor);
                        }
                    } else {
                        Log.e(TAG, "no descriptors for group: " + groupId);
@@ -2396,6 +2450,9 @@ public class LeAudioService extends ProfileService {
            ProfileService.println(sb, "    mActiveContexts: " + descriptor.mActiveContexts);
            ProfileService.println(sb, "    group lead: " + getConnectedGroupLeadDevice(groupId));
            ProfileService.println(sb, "    first device: " + getFirstDeviceFromGroup(groupId));
            for (BluetoothDevice dev : descriptor.mLostDevicesWhileStreaming) {
                ProfileService.println(sb, "        lost device: " + dev);
            }
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -803,7 +803,9 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
     * of the system, which wants to set to active a particular Le Audio group.
     *
     * Note: getActiveDevice() returns the Lead device for the currently active LE Audio group.
     * Note: When lead device gets disconnected, there will be new lead device for the group.
     * Note: When Lead device gets disconnected while Le Audio group is active and has more devices
     * in the group, then Lead device will not change. If Lead device gets disconnected, for the
     * Le Audio group which is not active, a new Lead device will be chosen
     *
     * @param groupId The group id.
     * @return group lead device.