Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +146 −111 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.mcp.McpService; import com.android.bluetooth.vc.VolumeControlService; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.SynchronousResultReceiver; Loading Loading @@ -109,9 +110,10 @@ public class LeAudioService extends ProfileService { private AdapterService mAdapterService; private DatabaseManager mDatabaseManager; private HandlerThread mStateMachinesThread; private BluetoothDevice mActiveAudioOutDevice; private BluetoothDevice mActiveAudioInDevice; private volatile BluetoothDevice mActiveAudioOutDevice; private volatile BluetoothDevice mActiveAudioInDevice; private LeAudioCodecConfig mLeAudioCodecConfig; private Object mGroupLock = new Object(); ServiceFactory mServiceFactory = new ServiceFactory(); LeAudioNativeInterface mLeAudioNativeInterface; Loading Loading @@ -142,9 +144,11 @@ public class LeAudioService extends ProfileService { List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); List<BluetoothLeAudioCodecConfig> mOutputLocalCodecCapabilities = new ArrayList<>(); @GuardedBy("mGroupLock") private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptors = new LinkedHashMap<>(); private final Map<BluetoothDevice, LeAudioStateMachine> mStateMachines = new LinkedHashMap<>(); @GuardedBy("mGroupLock") private final Map<BluetoothDevice, Integer> mDeviceGroupIdMap = new ConcurrentHashMap<>(); private final Map<BluetoothDevice, Integer> mDeviceAudioLocationMap = new ConcurrentHashMap<>(); Loading Loading @@ -205,13 +209,15 @@ public class LeAudioService extends ProfileService { mStateMachinesThread = new HandlerThread("LeAudioService.StateMachines"); mStateMachinesThread.start(); mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mBroadcastStateMap.clear(); mBroadcastMetadataList.clear(); mBroadcastsPlaybackMap.clear(); synchronized (mGroupLock) { mDeviceGroupIdMap.clear(); mGroupDescriptors.clear(); } // Setup broadcast receivers IntentFilter filter = new IntentFilter(); Loading Loading @@ -242,12 +248,19 @@ public class LeAudioService extends ProfileService { // Delay the call to init by posting it. This ensures TBS and MCS are fully initialized // before we start accepting connections mHandler.post(() -> mLeAudioNativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading())); mHandler.post(this::init); return true; } private void init() { LeAudioNativeInterface nativeInterface = mLeAudioNativeInterface; if (nativeInterface == null) { Log.w(TAG, "the service is stopped. ignore init()"); } nativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading()); } @Override protected boolean stop() { Log.i(TAG, "stop()"); Loading @@ -259,6 +272,7 @@ public class LeAudioService extends ProfileService { setActiveDevice(null); //Don't wait for async call with INACTIVE group status, clean active //device for active group. synchronized (mGroupLock) { for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor descriptor = entry.getValue(); Integer group_id = entry.getKey(); Loading @@ -269,6 +283,9 @@ public class LeAudioService extends ProfileService { break; } } mDeviceGroupIdMap.clear(); mGroupDescriptors.clear(); } // Cleanup native interfaces mLeAudioNativeInterface.cleanup(); Loading @@ -292,9 +309,7 @@ public class LeAudioService extends ProfileService { mStateMachines.clear(); } mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mGroupDescriptors.clear(); if (mBroadcastCallbacks != null) { mBroadcastCallbacks.kill(); Loading Loading @@ -323,7 +338,6 @@ public class LeAudioService extends ProfileService { } } mAudioManager = null; mAdapterService = null; mAudioManager = null; Loading Loading @@ -418,7 +432,7 @@ public class LeAudioService extends ProfileService { } } public BluetoothDevice getConnectedGroupLeadDevice(int groupId) { BluetoothDevice getConnectedGroupLeadDevice(int groupId) { return getFirstDeviceFromGroup(groupId); } Loading Loading @@ -494,7 +508,7 @@ public class LeAudioService extends ProfileService { * @param device the active device * @return true on success, otherwise false */ public boolean groupAddNode(int groupId, BluetoothDevice device) { boolean groupAddNode(int groupId, BluetoothDevice device) { return mLeAudioNativeInterface.groupAddNode(groupId, device); } Loading @@ -504,7 +518,7 @@ public class LeAudioService extends ProfileService { * @param device the active device * @return true on success, otherwise false */ public boolean groupRemoveNode(int groupId, BluetoothDevice device) { boolean groupRemoveNode(int groupId, BluetoothDevice device) { return mLeAudioNativeInterface.groupRemoveNode(groupId, device); } Loading @@ -514,23 +528,25 @@ public class LeAudioService extends ProfileService { * @return true given group exists, otherwise false */ public boolean isValidDeviceGroup(int group_id) { return (group_id != LE_AUDIO_GROUP_ID_INVALID) ? mDeviceGroupIdMap.containsValue(group_id) : false; return group_id != LE_AUDIO_GROUP_ID_INVALID && mDeviceGroupIdMap.containsValue(group_id); } /** * Get all the devices within a given group. * @param group_id group Id to verify * @param groupId group id to get devices * @return all devices within a given group or empty list */ public List<BluetoothDevice> getGroupDevices(int group_id) { public List<BluetoothDevice> getGroupDevices(int groupId) { List<BluetoothDevice> result = new ArrayList<>(); if (group_id != LE_AUDIO_GROUP_ID_INVALID) { for (BluetoothDevice storedDevice : mDeviceGroupIdMap.keySet()) { if (getGroupId(storedDevice) == group_id) { result.add(storedDevice); if (groupId == LE_AUDIO_GROUP_ID_INVALID) { return result; } synchronized (mGroupLock) { for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceGroupIdMap.entrySet()) { if (entry.getValue() == groupId) { result.add(entry.getKey()); } } } Loading @@ -540,11 +556,11 @@ public class LeAudioService extends ProfileService { /** * Get supported group audio direction from available context. * * @param activeContext bitset of active context to be matched with possible audio direction * @param activeContexts bitset of active context to be matched with possible audio direction * support. * @return matched possible audio direction support masked bitset * {@link AUDIO_DIRECTION_INPUT_BIT} if input audio is supported * {@link AUDIO_DIRECTION_OUTPUT_BIT} if output audio is supported * {@link #AUDIO_DIRECTION_INPUT_BIT} if input audio is supported * {@link #AUDIO_DIRECTION_OUTPUT_BIT} if output audio is supported */ private Integer getAudioDirectionsFromActiveContextsMap(Integer activeContexts) { Integer supportedAudioDirections = 0; Loading @@ -560,12 +576,14 @@ public class LeAudioService extends ProfileService { } private Integer getActiveGroupId() { synchronized (mGroupLock) { for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor descriptor = entry.getValue(); if (descriptor.mIsActive) { return entry.getKey(); } } } return LE_AUDIO_GROUP_ID_INVALID; } Loading Loading @@ -664,21 +682,21 @@ public class LeAudioService extends ProfileService { } private BluetoothDevice getFirstDeviceFromGroup(Integer groupId) { if (groupId != LE_AUDIO_GROUP_ID_INVALID) { if (groupId == LE_AUDIO_GROUP_ID_INVALID) { return null; } synchronized (mGroupLock) { for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceGroupIdMap.entrySet()) { if (entry.getValue() != groupId) { continue; } LeAudioStateMachine sm = mStateMachines.get(entry.getKey()); if (sm == null || sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { continue; } return entry.getKey(); } } return null; } Loading Loading @@ -715,10 +733,12 @@ public class LeAudioService extends ProfileService { } } else if (previousGroupId != LE_AUDIO_GROUP_ID_INVALID) { /* Mark old group as no active */ LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(previousGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(previousGroupId); if (descriptor != null) { descriptor.mIsActive = false; } } } BluetoothDevice previousInDevice = mActiveAudioInDevice; Loading Loading @@ -779,10 +799,12 @@ public class LeAudioService extends ProfileService { } else if (previousGroupId != LE_AUDIO_GROUP_ID_INVALID) { Log.i(TAG, " Switching active group from " + previousGroupId + " to " + groupId); /* Mark old group as no active */ LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(previousGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(previousGroupId); if (descriptor != null) { descriptor.mIsActive = false; } } } BluetoothDevice previousOutDevice = mActiveAudioOutDevice; Loading Loading @@ -846,9 +868,8 @@ public class LeAudioService extends ProfileService { /** * Set the active device group. * @param groupId group Id to set active */ private void setActiveDeviceGroup(BluetoothDevice device) { private void setActiveGroupWithDevice(BluetoothDevice device) { int groupId = LE_AUDIO_GROUP_ID_INVALID; if (device != null) { Loading @@ -857,9 +878,9 @@ public class LeAudioService extends ProfileService { int currentlyActiveGroupId = getActiveGroupId(); if (DBG) { Log.d(TAG, "setActiveDeviceGroup = " + groupId + ", currentlyActiveGroupId = " + currentlyActiveGroupId + ", device: " + device); Log.d(TAG, "setActiveGroupWithDevice = " + groupId + ", currentlyActiveGroupId = " + currentlyActiveGroupId + ", device: " + device); } if (groupId == currentlyActiveGroupId) { Loading @@ -877,24 +898,22 @@ public class LeAudioService extends ProfileService { * @return true on success, otherwise false */ public boolean setActiveDevice(BluetoothDevice device) { synchronized (mStateMachines) { /* Clear active group */ if (device == null) { setActiveDeviceGroup(device); setActiveGroupWithDevice(device); return true; } if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { Log.e(TAG, "setActiveDevice(" + device + "): failed because group device is not " + "connected"); Log.e(TAG, "setActiveDevice(" + device + "): failed because group device is not " + "connected"); return false; } setActiveDeviceGroup(device); setActiveGroupWithDevice(device); return true; } } /** * Get the active LE audio device. * Get the active LE audio devices. * * Note: When LE audio group is active, one of the Bluetooth device address * which belongs to the group, represents the active LE audio group. Loading @@ -907,17 +926,16 @@ public class LeAudioService extends ProfileService { if (DBG) { Log.d(TAG, "getActiveDevices"); } ArrayList<BluetoothDevice> activeDevices = new ArrayList<>(); ArrayList<BluetoothDevice> activeDevices = new ArrayList<>(2); activeDevices.add(null); activeDevices.add(null); synchronized (mStateMachines) { int currentlyActiveGroupId = getActiveGroupId(); if (currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID) { return activeDevices; } activeDevices.add(0, mActiveAudioOutDevice); activeDevices.add(1, mActiveAudioInDevice); } activeDevices.set(0, mActiveAudioOutDevice); activeDevices.set(1, mActiveAudioInDevice); return activeDevices; } Loading Loading @@ -954,7 +972,6 @@ public class LeAudioService extends ProfileService { sm.sendMessage(LeAudioStateMachine.CONNECT); } } } // Suppressed since this is part of a local process Loading Loading @@ -990,27 +1007,18 @@ public class LeAudioService extends ProfileService { return; } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED) { int group_id = stackEvent.valueInt1; int node_status = stackEvent.valueInt2; int groupId = stackEvent.valueInt1; int nodeStatus = stackEvent.valueInt2; Objects.requireNonNull(stackEvent.device, "Device should never be null, event: " + stackEvent); switch (node_status) { switch (nodeStatus) { case LeAudioStackEvent.GROUP_NODE_ADDED: mDeviceGroupIdMap.put(device, group_id); LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); if (descriptor == null) { mGroupDescriptors.put(group_id, new LeAudioGroupDescriptor()); } notifyGroupNodeAdded(device, group_id); handleGroupNodeAdded(device, groupId); break; case LeAudioStackEvent.GROUP_NODE_REMOVED: mDeviceGroupIdMap.remove(device); if (mDeviceGroupIdMap.containsValue(group_id) == false) { mGroupDescriptors.remove(group_id); } notifyGroupNodeRemoved(device, group_id); handleGroupNodeRemoved(device, groupId); break; default: break; Loading @@ -1022,7 +1030,7 @@ public class LeAudioService extends ProfileService { } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED) { int groupId = stackEvent.valueInt1; LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null) { Log.e(TAG, " Group not found " + groupId); return; Loading @@ -1045,27 +1053,26 @@ public class LeAudioService extends ProfileService { descriptor.mCodecStatus = status; notifyUnicastCodecConfigChanged(groupId, status); } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { int direction = stackEvent.valueInt1; int group_id = stackEvent.valueInt2; int groupId = stackEvent.valueInt2; int snk_audio_location = stackEvent.valueInt3; int src_audio_location = stackEvent.valueInt4; int available_contexts = stackEvent.valueInt5; LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { if (descriptor.mIsActive) { descriptor.mIsActive = updateActiveDevices(group_id, descriptor.mActiveContexts, updateActiveDevices(groupId, descriptor.mActiveContexts, available_contexts, descriptor.mIsActive); if (!descriptor.mIsActive) { notifyGroupStatusChanged(group_id, BluetoothLeAudio.GROUP_STATUS_INACTIVE); notifyGroupStatusChanged(groupId, BluetoothLeAudio.GROUP_STATUS_INACTIVE); } } descriptor.mActiveContexts = available_contexts; } else { Log.e(TAG, "no descriptors for group: " + group_id); Log.e(TAG, "no descriptors for group: " + groupId); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE) { Objects.requireNonNull(stackEvent.device, Loading @@ -1079,36 +1086,36 @@ public class LeAudioService extends ProfileService { + " audio location:" + sink_audio_location); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) { int group_id = stackEvent.valueInt1; int group_status = stackEvent.valueInt2; int groupId = stackEvent.valueInt1; int groupStatus = stackEvent.valueInt2; boolean notifyGroupStatus = false; switch (group_status) { switch (groupStatus) { case LeAudioStackEvent.GROUP_STATUS_ACTIVE: { LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { if (!descriptor.mIsActive) { descriptor.mIsActive = updateActiveDevices(group_id, descriptor.mIsActive = updateActiveDevices(groupId, ACTIVE_CONTEXTS_NONE, descriptor.mActiveContexts, true); notifyGroupStatus = descriptor.mIsActive; } } else { Log.e(TAG, "no descriptors for group: " + group_id); Log.e(TAG, "no descriptors for group: " + groupId); } break; } case LeAudioStackEvent.GROUP_STATUS_INACTIVE: { LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { if (descriptor.mIsActive) { descriptor.mIsActive = false; updateActiveDevices(group_id, descriptor.mActiveContexts, updateActiveDevices(groupId, descriptor.mActiveContexts, ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); notifyGroupStatus = true; } } else { Log.e(TAG, "no descriptors for group: " + group_id); Log.e(TAG, "no descriptors for group: " + groupId); } break; } Loading @@ -1117,7 +1124,7 @@ public class LeAudioService extends ProfileService { } if (notifyGroupStatus) { notifyGroupStatusChanged(group_id, group_status); notifyGroupStatusChanged(groupId, groupStatus); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED) { Loading Loading @@ -1335,7 +1342,7 @@ public class LeAudioService extends ProfileService { // BluetoothMetricsProto.ProfileId.LE_AUDIO); } LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(myGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); if (descriptor != null) { descriptor.mIsConnected = true; /* HearingAid activates device after connection Loading Loading @@ -1367,7 +1374,7 @@ public class LeAudioService extends ProfileService { } int myGroupId = getGroupId(device); LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(myGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); if (descriptor == null) { Log.e(TAG, "no descriptors for group: " + myGroupId); return; Loading Loading @@ -1515,8 +1522,10 @@ public class LeAudioService extends ProfileService { if (device == null) { return LE_AUDIO_GROUP_ID_INVALID; } synchronized (mGroupLock) { return mDeviceGroupIdMap.getOrDefault(device, LE_AUDIO_GROUP_ID_INVALID); } } /** * Set volume for streaming devices Loading @@ -1539,6 +1548,23 @@ public class LeAudioService extends ProfileService { } } private LeAudioGroupDescriptor getGroupDescriptor(int groupId) { synchronized (mGroupLock) { return mGroupDescriptors.get(groupId); } } private void handleGroupNodeAdded(BluetoothDevice device, int groupId) { synchronized (mGroupLock) { mDeviceGroupIdMap.put(device, groupId); LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); if (descriptor == null) { mGroupDescriptors.put(groupId, new LeAudioGroupDescriptor()); } notifyGroupNodeAdded(device, groupId); } } private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) { if (mLeAudioCallbacks != null) { int n = mLeAudioCallbacks.beginBroadcast(); Loading @@ -1553,6 +1579,16 @@ public class LeAudioService extends ProfileService { } } private void handleGroupNodeRemoved(BluetoothDevice device, int groupId) { synchronized (mGroupLock) { mDeviceGroupIdMap.remove(device); if (!mDeviceGroupIdMap.containsValue(groupId)) { mGroupDescriptors.remove(groupId); } notifyGroupNodeRemoved(device, groupId); } } private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { if (mLeAudioCallbacks != null) { int n = mLeAudioCallbacks.beginBroadcast(); Loading Loading @@ -1736,7 +1772,7 @@ public class LeAudioService extends ProfileService { if (DBG) { Log.d(TAG, "getCodecStatus(" + groupId + ")"); } LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { return descriptor.mCodecStatus; } Loading @@ -1759,7 +1795,7 @@ public class LeAudioService extends ProfileService { + Objects.toString(inputCodecConfig) + Objects.toString(outputCodecConfig)); } LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null) { Log.e(TAG, "setCodecConfigPreference: Invalid groupId, " + groupId); return; Loading Loading @@ -1788,7 +1824,6 @@ public class LeAudioService extends ProfileService { inputCodecConfig, outputCodecConfig); } /** * Binder object: must be a static class or memory leak may occur */ Loading Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +146 −111 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.mcp.McpService; import com.android.bluetooth.vc.VolumeControlService; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.SynchronousResultReceiver; Loading Loading @@ -109,9 +110,10 @@ public class LeAudioService extends ProfileService { private AdapterService mAdapterService; private DatabaseManager mDatabaseManager; private HandlerThread mStateMachinesThread; private BluetoothDevice mActiveAudioOutDevice; private BluetoothDevice mActiveAudioInDevice; private volatile BluetoothDevice mActiveAudioOutDevice; private volatile BluetoothDevice mActiveAudioInDevice; private LeAudioCodecConfig mLeAudioCodecConfig; private Object mGroupLock = new Object(); ServiceFactory mServiceFactory = new ServiceFactory(); LeAudioNativeInterface mLeAudioNativeInterface; Loading Loading @@ -142,9 +144,11 @@ public class LeAudioService extends ProfileService { List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); List<BluetoothLeAudioCodecConfig> mOutputLocalCodecCapabilities = new ArrayList<>(); @GuardedBy("mGroupLock") private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptors = new LinkedHashMap<>(); private final Map<BluetoothDevice, LeAudioStateMachine> mStateMachines = new LinkedHashMap<>(); @GuardedBy("mGroupLock") private final Map<BluetoothDevice, Integer> mDeviceGroupIdMap = new ConcurrentHashMap<>(); private final Map<BluetoothDevice, Integer> mDeviceAudioLocationMap = new ConcurrentHashMap<>(); Loading Loading @@ -205,13 +209,15 @@ public class LeAudioService extends ProfileService { mStateMachinesThread = new HandlerThread("LeAudioService.StateMachines"); mStateMachinesThread.start(); mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mBroadcastStateMap.clear(); mBroadcastMetadataList.clear(); mBroadcastsPlaybackMap.clear(); synchronized (mGroupLock) { mDeviceGroupIdMap.clear(); mGroupDescriptors.clear(); } // Setup broadcast receivers IntentFilter filter = new IntentFilter(); Loading Loading @@ -242,12 +248,19 @@ public class LeAudioService extends ProfileService { // Delay the call to init by posting it. This ensures TBS and MCS are fully initialized // before we start accepting connections mHandler.post(() -> mLeAudioNativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading())); mHandler.post(this::init); return true; } private void init() { LeAudioNativeInterface nativeInterface = mLeAudioNativeInterface; if (nativeInterface == null) { Log.w(TAG, "the service is stopped. ignore init()"); } nativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading()); } @Override protected boolean stop() { Log.i(TAG, "stop()"); Loading @@ -259,6 +272,7 @@ public class LeAudioService extends ProfileService { setActiveDevice(null); //Don't wait for async call with INACTIVE group status, clean active //device for active group. synchronized (mGroupLock) { for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor descriptor = entry.getValue(); Integer group_id = entry.getKey(); Loading @@ -269,6 +283,9 @@ public class LeAudioService extends ProfileService { break; } } mDeviceGroupIdMap.clear(); mGroupDescriptors.clear(); } // Cleanup native interfaces mLeAudioNativeInterface.cleanup(); Loading @@ -292,9 +309,7 @@ public class LeAudioService extends ProfileService { mStateMachines.clear(); } mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mGroupDescriptors.clear(); if (mBroadcastCallbacks != null) { mBroadcastCallbacks.kill(); Loading Loading @@ -323,7 +338,6 @@ public class LeAudioService extends ProfileService { } } mAudioManager = null; mAdapterService = null; mAudioManager = null; Loading Loading @@ -418,7 +432,7 @@ public class LeAudioService extends ProfileService { } } public BluetoothDevice getConnectedGroupLeadDevice(int groupId) { BluetoothDevice getConnectedGroupLeadDevice(int groupId) { return getFirstDeviceFromGroup(groupId); } Loading Loading @@ -494,7 +508,7 @@ public class LeAudioService extends ProfileService { * @param device the active device * @return true on success, otherwise false */ public boolean groupAddNode(int groupId, BluetoothDevice device) { boolean groupAddNode(int groupId, BluetoothDevice device) { return mLeAudioNativeInterface.groupAddNode(groupId, device); } Loading @@ -504,7 +518,7 @@ public class LeAudioService extends ProfileService { * @param device the active device * @return true on success, otherwise false */ public boolean groupRemoveNode(int groupId, BluetoothDevice device) { boolean groupRemoveNode(int groupId, BluetoothDevice device) { return mLeAudioNativeInterface.groupRemoveNode(groupId, device); } Loading @@ -514,23 +528,25 @@ public class LeAudioService extends ProfileService { * @return true given group exists, otherwise false */ public boolean isValidDeviceGroup(int group_id) { return (group_id != LE_AUDIO_GROUP_ID_INVALID) ? mDeviceGroupIdMap.containsValue(group_id) : false; return group_id != LE_AUDIO_GROUP_ID_INVALID && mDeviceGroupIdMap.containsValue(group_id); } /** * Get all the devices within a given group. * @param group_id group Id to verify * @param groupId group id to get devices * @return all devices within a given group or empty list */ public List<BluetoothDevice> getGroupDevices(int group_id) { public List<BluetoothDevice> getGroupDevices(int groupId) { List<BluetoothDevice> result = new ArrayList<>(); if (group_id != LE_AUDIO_GROUP_ID_INVALID) { for (BluetoothDevice storedDevice : mDeviceGroupIdMap.keySet()) { if (getGroupId(storedDevice) == group_id) { result.add(storedDevice); if (groupId == LE_AUDIO_GROUP_ID_INVALID) { return result; } synchronized (mGroupLock) { for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceGroupIdMap.entrySet()) { if (entry.getValue() == groupId) { result.add(entry.getKey()); } } } Loading @@ -540,11 +556,11 @@ public class LeAudioService extends ProfileService { /** * Get supported group audio direction from available context. * * @param activeContext bitset of active context to be matched with possible audio direction * @param activeContexts bitset of active context to be matched with possible audio direction * support. * @return matched possible audio direction support masked bitset * {@link AUDIO_DIRECTION_INPUT_BIT} if input audio is supported * {@link AUDIO_DIRECTION_OUTPUT_BIT} if output audio is supported * {@link #AUDIO_DIRECTION_INPUT_BIT} if input audio is supported * {@link #AUDIO_DIRECTION_OUTPUT_BIT} if output audio is supported */ private Integer getAudioDirectionsFromActiveContextsMap(Integer activeContexts) { Integer supportedAudioDirections = 0; Loading @@ -560,12 +576,14 @@ public class LeAudioService extends ProfileService { } private Integer getActiveGroupId() { synchronized (mGroupLock) { for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor descriptor = entry.getValue(); if (descriptor.mIsActive) { return entry.getKey(); } } } return LE_AUDIO_GROUP_ID_INVALID; } Loading Loading @@ -664,21 +682,21 @@ public class LeAudioService extends ProfileService { } private BluetoothDevice getFirstDeviceFromGroup(Integer groupId) { if (groupId != LE_AUDIO_GROUP_ID_INVALID) { if (groupId == LE_AUDIO_GROUP_ID_INVALID) { return null; } synchronized (mGroupLock) { for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceGroupIdMap.entrySet()) { if (entry.getValue() != groupId) { continue; } LeAudioStateMachine sm = mStateMachines.get(entry.getKey()); if (sm == null || sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { continue; } return entry.getKey(); } } return null; } Loading Loading @@ -715,10 +733,12 @@ public class LeAudioService extends ProfileService { } } else if (previousGroupId != LE_AUDIO_GROUP_ID_INVALID) { /* Mark old group as no active */ LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(previousGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(previousGroupId); if (descriptor != null) { descriptor.mIsActive = false; } } } BluetoothDevice previousInDevice = mActiveAudioInDevice; Loading Loading @@ -779,10 +799,12 @@ public class LeAudioService extends ProfileService { } else if (previousGroupId != LE_AUDIO_GROUP_ID_INVALID) { Log.i(TAG, " Switching active group from " + previousGroupId + " to " + groupId); /* Mark old group as no active */ LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(previousGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(previousGroupId); if (descriptor != null) { descriptor.mIsActive = false; } } } BluetoothDevice previousOutDevice = mActiveAudioOutDevice; Loading Loading @@ -846,9 +868,8 @@ public class LeAudioService extends ProfileService { /** * Set the active device group. * @param groupId group Id to set active */ private void setActiveDeviceGroup(BluetoothDevice device) { private void setActiveGroupWithDevice(BluetoothDevice device) { int groupId = LE_AUDIO_GROUP_ID_INVALID; if (device != null) { Loading @@ -857,9 +878,9 @@ public class LeAudioService extends ProfileService { int currentlyActiveGroupId = getActiveGroupId(); if (DBG) { Log.d(TAG, "setActiveDeviceGroup = " + groupId + ", currentlyActiveGroupId = " + currentlyActiveGroupId + ", device: " + device); Log.d(TAG, "setActiveGroupWithDevice = " + groupId + ", currentlyActiveGroupId = " + currentlyActiveGroupId + ", device: " + device); } if (groupId == currentlyActiveGroupId) { Loading @@ -877,24 +898,22 @@ public class LeAudioService extends ProfileService { * @return true on success, otherwise false */ public boolean setActiveDevice(BluetoothDevice device) { synchronized (mStateMachines) { /* Clear active group */ if (device == null) { setActiveDeviceGroup(device); setActiveGroupWithDevice(device); return true; } if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { Log.e(TAG, "setActiveDevice(" + device + "): failed because group device is not " + "connected"); Log.e(TAG, "setActiveDevice(" + device + "): failed because group device is not " + "connected"); return false; } setActiveDeviceGroup(device); setActiveGroupWithDevice(device); return true; } } /** * Get the active LE audio device. * Get the active LE audio devices. * * Note: When LE audio group is active, one of the Bluetooth device address * which belongs to the group, represents the active LE audio group. Loading @@ -907,17 +926,16 @@ public class LeAudioService extends ProfileService { if (DBG) { Log.d(TAG, "getActiveDevices"); } ArrayList<BluetoothDevice> activeDevices = new ArrayList<>(); ArrayList<BluetoothDevice> activeDevices = new ArrayList<>(2); activeDevices.add(null); activeDevices.add(null); synchronized (mStateMachines) { int currentlyActiveGroupId = getActiveGroupId(); if (currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID) { return activeDevices; } activeDevices.add(0, mActiveAudioOutDevice); activeDevices.add(1, mActiveAudioInDevice); } activeDevices.set(0, mActiveAudioOutDevice); activeDevices.set(1, mActiveAudioInDevice); return activeDevices; } Loading Loading @@ -954,7 +972,6 @@ public class LeAudioService extends ProfileService { sm.sendMessage(LeAudioStateMachine.CONNECT); } } } // Suppressed since this is part of a local process Loading Loading @@ -990,27 +1007,18 @@ public class LeAudioService extends ProfileService { return; } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED) { int group_id = stackEvent.valueInt1; int node_status = stackEvent.valueInt2; int groupId = stackEvent.valueInt1; int nodeStatus = stackEvent.valueInt2; Objects.requireNonNull(stackEvent.device, "Device should never be null, event: " + stackEvent); switch (node_status) { switch (nodeStatus) { case LeAudioStackEvent.GROUP_NODE_ADDED: mDeviceGroupIdMap.put(device, group_id); LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); if (descriptor == null) { mGroupDescriptors.put(group_id, new LeAudioGroupDescriptor()); } notifyGroupNodeAdded(device, group_id); handleGroupNodeAdded(device, groupId); break; case LeAudioStackEvent.GROUP_NODE_REMOVED: mDeviceGroupIdMap.remove(device); if (mDeviceGroupIdMap.containsValue(group_id) == false) { mGroupDescriptors.remove(group_id); } notifyGroupNodeRemoved(device, group_id); handleGroupNodeRemoved(device, groupId); break; default: break; Loading @@ -1022,7 +1030,7 @@ public class LeAudioService extends ProfileService { } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED) { int groupId = stackEvent.valueInt1; LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null) { Log.e(TAG, " Group not found " + groupId); return; Loading @@ -1045,27 +1053,26 @@ public class LeAudioService extends ProfileService { descriptor.mCodecStatus = status; notifyUnicastCodecConfigChanged(groupId, status); } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { int direction = stackEvent.valueInt1; int group_id = stackEvent.valueInt2; int groupId = stackEvent.valueInt2; int snk_audio_location = stackEvent.valueInt3; int src_audio_location = stackEvent.valueInt4; int available_contexts = stackEvent.valueInt5; LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { if (descriptor.mIsActive) { descriptor.mIsActive = updateActiveDevices(group_id, descriptor.mActiveContexts, updateActiveDevices(groupId, descriptor.mActiveContexts, available_contexts, descriptor.mIsActive); if (!descriptor.mIsActive) { notifyGroupStatusChanged(group_id, BluetoothLeAudio.GROUP_STATUS_INACTIVE); notifyGroupStatusChanged(groupId, BluetoothLeAudio.GROUP_STATUS_INACTIVE); } } descriptor.mActiveContexts = available_contexts; } else { Log.e(TAG, "no descriptors for group: " + group_id); Log.e(TAG, "no descriptors for group: " + groupId); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE) { Objects.requireNonNull(stackEvent.device, Loading @@ -1079,36 +1086,36 @@ public class LeAudioService extends ProfileService { + " audio location:" + sink_audio_location); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) { int group_id = stackEvent.valueInt1; int group_status = stackEvent.valueInt2; int groupId = stackEvent.valueInt1; int groupStatus = stackEvent.valueInt2; boolean notifyGroupStatus = false; switch (group_status) { switch (groupStatus) { case LeAudioStackEvent.GROUP_STATUS_ACTIVE: { LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { if (!descriptor.mIsActive) { descriptor.mIsActive = updateActiveDevices(group_id, descriptor.mIsActive = updateActiveDevices(groupId, ACTIVE_CONTEXTS_NONE, descriptor.mActiveContexts, true); notifyGroupStatus = descriptor.mIsActive; } } else { Log.e(TAG, "no descriptors for group: " + group_id); Log.e(TAG, "no descriptors for group: " + groupId); } break; } case LeAudioStackEvent.GROUP_STATUS_INACTIVE: { LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(group_id); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { if (descriptor.mIsActive) { descriptor.mIsActive = false; updateActiveDevices(group_id, descriptor.mActiveContexts, updateActiveDevices(groupId, descriptor.mActiveContexts, ACTIVE_CONTEXTS_NONE, descriptor.mIsActive); notifyGroupStatus = true; } } else { Log.e(TAG, "no descriptors for group: " + group_id); Log.e(TAG, "no descriptors for group: " + groupId); } break; } Loading @@ -1117,7 +1124,7 @@ public class LeAudioService extends ProfileService { } if (notifyGroupStatus) { notifyGroupStatusChanged(group_id, group_status); notifyGroupStatusChanged(groupId, groupStatus); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED) { Loading Loading @@ -1335,7 +1342,7 @@ public class LeAudioService extends ProfileService { // BluetoothMetricsProto.ProfileId.LE_AUDIO); } LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(myGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); if (descriptor != null) { descriptor.mIsConnected = true; /* HearingAid activates device after connection Loading Loading @@ -1367,7 +1374,7 @@ public class LeAudioService extends ProfileService { } int myGroupId = getGroupId(device); LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(myGroupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); if (descriptor == null) { Log.e(TAG, "no descriptors for group: " + myGroupId); return; Loading Loading @@ -1515,8 +1522,10 @@ public class LeAudioService extends ProfileService { if (device == null) { return LE_AUDIO_GROUP_ID_INVALID; } synchronized (mGroupLock) { return mDeviceGroupIdMap.getOrDefault(device, LE_AUDIO_GROUP_ID_INVALID); } } /** * Set volume for streaming devices Loading @@ -1539,6 +1548,23 @@ public class LeAudioService extends ProfileService { } } private LeAudioGroupDescriptor getGroupDescriptor(int groupId) { synchronized (mGroupLock) { return mGroupDescriptors.get(groupId); } } private void handleGroupNodeAdded(BluetoothDevice device, int groupId) { synchronized (mGroupLock) { mDeviceGroupIdMap.put(device, groupId); LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); if (descriptor == null) { mGroupDescriptors.put(groupId, new LeAudioGroupDescriptor()); } notifyGroupNodeAdded(device, groupId); } } private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) { if (mLeAudioCallbacks != null) { int n = mLeAudioCallbacks.beginBroadcast(); Loading @@ -1553,6 +1579,16 @@ public class LeAudioService extends ProfileService { } } private void handleGroupNodeRemoved(BluetoothDevice device, int groupId) { synchronized (mGroupLock) { mDeviceGroupIdMap.remove(device); if (!mDeviceGroupIdMap.containsValue(groupId)) { mGroupDescriptors.remove(groupId); } notifyGroupNodeRemoved(device, groupId); } } private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { if (mLeAudioCallbacks != null) { int n = mLeAudioCallbacks.beginBroadcast(); Loading Loading @@ -1736,7 +1772,7 @@ public class LeAudioService extends ProfileService { if (DBG) { Log.d(TAG, "getCodecStatus(" + groupId + ")"); } LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { return descriptor.mCodecStatus; } Loading @@ -1759,7 +1795,7 @@ public class LeAudioService extends ProfileService { + Objects.toString(inputCodecConfig) + Objects.toString(outputCodecConfig)); } LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null) { Log.e(TAG, "setCodecConfigPreference: Invalid groupId, " + groupId); return; Loading Loading @@ -1788,7 +1824,6 @@ public class LeAudioService extends ProfileService { inputCodecConfig, outputCodecConfig); } /** * Binder object: must be a static class or memory leak may occur */ Loading