Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +62 −5 Original line number Original line Diff line number Diff line Loading @@ -135,12 +135,15 @@ public class LeAudioService extends ProfileService { mIsActive = false; mIsActive = false; mActiveContexts = ACTIVE_CONTEXTS_NONE; mActiveContexts = ACTIVE_CONTEXTS_NONE; mCodecStatus = null; mCodecStatus = null; mLostDevicesWhileStreaming = new ArrayList<>(); } } public Boolean mIsConnected; public Boolean mIsConnected; public Boolean mIsActive; public Boolean mIsActive; public Integer mActiveContexts; public Integer mActiveContexts; public BluetoothLeAudioCodecStatus mCodecStatus; public BluetoothLeAudioCodecStatus mCodecStatus; /* This can be non empty only for the streaming time */ List<BluetoothDevice> mLostDevicesWhileStreaming; } } List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); Loading Loading @@ -1023,6 +1026,21 @@ public class LeAudioService extends ProfileService { } } } } 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 // Suppressed since this is part of a local process @SuppressLint("AndroidFrameworkRequiresPermission") @SuppressLint("AndroidFrameworkRequiresPermission") void messageFromNative(LeAudioStackEvent stackEvent) { void messageFromNative(LeAudioStackEvent stackEvent) { Loading @@ -1034,7 +1052,41 @@ public class LeAudioService extends ProfileService { // Some events require device state machine // Some events require device state machine synchronized (mStateMachines) { synchronized (mStateMachines) { LeAudioStateMachine sm = mStateMachines.get(device); 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) { switch (stackEvent.valueInt1) { case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: Loading @@ -1045,12 +1097,12 @@ public class LeAudioService extends ProfileService { default: default: break; break; } } } if (sm == null) { if (sm == null) { Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); return; return; } } } sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); return; return; Loading Loading @@ -1162,6 +1214,8 @@ public class LeAudioService extends ProfileService { updateActiveDevices(groupId, descriptor.mActiveContexts, updateActiveDevices(groupId, descriptor.mActiveContexts, ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); notifyGroupStatus = true; notifyGroupStatus = true; /* Clear lost devices */ clearLostDevicesWhileStreaming(descriptor); } } } else { } else { Log.e(TAG, "no descriptors for group: " + groupId); Log.e(TAG, "no descriptors for group: " + groupId); Loading Loading @@ -2407,6 +2461,9 @@ public class LeAudioService extends ProfileService { ProfileService.println(sb, " mActiveContexts: " + descriptor.mActiveContexts); ProfileService.println(sb, " mActiveContexts: " + descriptor.mActiveContexts); ProfileService.println(sb, " group lead: " + getConnectedGroupLeadDevice(groupId)); ProfileService.println(sb, " group lead: " + getConnectedGroupLeadDevice(groupId)); ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId)); ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId)); for (BluetoothDevice dev : descriptor.mLostDevicesWhileStreaming) { ProfileService.println(sb, " lost device: " + dev); } } } } } } } framework/java/android/bluetooth/BluetoothLeAudio.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -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. * 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: 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. * @param groupId The group id. * @return group lead device. * @return group lead device. Loading Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +62 −5 Original line number Original line Diff line number Diff line Loading @@ -135,12 +135,15 @@ public class LeAudioService extends ProfileService { mIsActive = false; mIsActive = false; mActiveContexts = ACTIVE_CONTEXTS_NONE; mActiveContexts = ACTIVE_CONTEXTS_NONE; mCodecStatus = null; mCodecStatus = null; mLostDevicesWhileStreaming = new ArrayList<>(); } } public Boolean mIsConnected; public Boolean mIsConnected; public Boolean mIsActive; public Boolean mIsActive; public Integer mActiveContexts; public Integer mActiveContexts; public BluetoothLeAudioCodecStatus mCodecStatus; public BluetoothLeAudioCodecStatus mCodecStatus; /* This can be non empty only for the streaming time */ List<BluetoothDevice> mLostDevicesWhileStreaming; } } List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); Loading Loading @@ -1023,6 +1026,21 @@ public class LeAudioService extends ProfileService { } } } } 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 // Suppressed since this is part of a local process @SuppressLint("AndroidFrameworkRequiresPermission") @SuppressLint("AndroidFrameworkRequiresPermission") void messageFromNative(LeAudioStackEvent stackEvent) { void messageFromNative(LeAudioStackEvent stackEvent) { Loading @@ -1034,7 +1052,41 @@ public class LeAudioService extends ProfileService { // Some events require device state machine // Some events require device state machine synchronized (mStateMachines) { synchronized (mStateMachines) { LeAudioStateMachine sm = mStateMachines.get(device); 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) { switch (stackEvent.valueInt1) { case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: Loading @@ -1045,12 +1097,12 @@ public class LeAudioService extends ProfileService { default: default: break; break; } } } if (sm == null) { if (sm == null) { Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); return; return; } } } sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); return; return; Loading Loading @@ -1162,6 +1214,8 @@ public class LeAudioService extends ProfileService { updateActiveDevices(groupId, descriptor.mActiveContexts, updateActiveDevices(groupId, descriptor.mActiveContexts, ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); notifyGroupStatus = true; notifyGroupStatus = true; /* Clear lost devices */ clearLostDevicesWhileStreaming(descriptor); } } } else { } else { Log.e(TAG, "no descriptors for group: " + groupId); Log.e(TAG, "no descriptors for group: " + groupId); Loading Loading @@ -2407,6 +2461,9 @@ public class LeAudioService extends ProfileService { ProfileService.println(sb, " mActiveContexts: " + descriptor.mActiveContexts); ProfileService.println(sb, " mActiveContexts: " + descriptor.mActiveContexts); ProfileService.println(sb, " group lead: " + getConnectedGroupLeadDevice(groupId)); ProfileService.println(sb, " group lead: " + getConnectedGroupLeadDevice(groupId)); ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId)); ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId)); for (BluetoothDevice dev : descriptor.mLostDevicesWhileStreaming) { ProfileService.println(sb, " lost device: " + dev); } } } } } } }
framework/java/android/bluetooth/BluetoothLeAudio.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -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. * 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: 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. * @param groupId The group id. * @return group lead device. * @return group lead device. Loading