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

Commit 31057b09 authored by Jakub Pawłowski's avatar Jakub Pawłowski Committed by Gerrit Code Review
Browse files

Merge "Synchronize group-related variables"

parents 6fef8540 518a0f66
Loading
Loading
Loading
Loading
+146 −111
Original line number Diff line number Diff line
@@ -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;

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

@@ -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();
@@ -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()");
@@ -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();
@@ -269,6 +283,9 @@ public class LeAudioService extends ProfileService {
                    break;
                }
            }
            mDeviceGroupIdMap.clear();
            mGroupDescriptors.clear();
        }

        // Cleanup native interfaces
        mLeAudioNativeInterface.cleanup();
@@ -292,9 +309,7 @@ public class LeAudioService extends ProfileService {
            mStateMachines.clear();
        }

        mDeviceGroupIdMap.clear();
        mDeviceAudioLocationMap.clear();
        mGroupDescriptors.clear();

        if (mBroadcastCallbacks != null) {
            mBroadcastCallbacks.kill();
@@ -323,7 +338,6 @@ public class LeAudioService extends ProfileService {
            }
        }

        mAudioManager = null;
        mAdapterService = null;
        mAudioManager = null;

@@ -418,7 +432,7 @@ public class LeAudioService extends ProfileService {
        }
    }

    public BluetoothDevice getConnectedGroupLeadDevice(int groupId) {
    BluetoothDevice getConnectedGroupLeadDevice(int groupId) {
        return getFirstDeviceFromGroup(groupId);
    }

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

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

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

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

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

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

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

@@ -954,7 +972,6 @@ public class LeAudioService extends ProfileService {
                 sm.sendMessage(LeAudioStateMachine.CONNECT);
            }
        }

    }

    // Suppressed since this is part of a local process
@@ -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;
@@ -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;
@@ -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,
@@ -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;
                }
@@ -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) {
@@ -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
@@ -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;
@@ -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
@@ -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();
@@ -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();
@@ -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;
        }
@@ -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;
@@ -1788,7 +1824,6 @@ public class LeAudioService extends ProfileService {
                                inputCodecConfig, outputCodecConfig);
    }


    /**
     * Binder object: must be a static class or memory leak may occur
     */