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

Commit d79e82df authored by Jakub Tyszkowski's avatar Jakub Tyszkowski
Browse files

Csip: Add more means to get grouped devices

This change adds more convenient ways to get group members by group
identifier to ease the CSIP integration in HAP and possibly other
services, eg. BASS Client.

It also makes the grouped device list sorted from the lowest to
the highest rank, which is the order of CSIP Ordered Access
procedure.

Bug: 150670922
Bug: 230559809
Tag: #feature
Sponsor: jpawlowski@
Test: atest BluetoothInstrumentationTests
Change-Id: I765f4a82b5f3ff8d7644a86030846d79af8b6397
parent a82ebbcd
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -96,7 +96,8 @@ class CsisClientCallbacksImpl : public CsisClientCallbacks {
  }

  void OnDeviceAvailable(const RawAddress& bd_addr, int group_id,
                         int group_size, const bluetooth::Uuid& uuid) override {
                         int group_size, int rank,
                         const bluetooth::Uuid& uuid) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
@@ -112,7 +113,7 @@ class CsisClientCallbacksImpl : public CsisClientCallbacks {

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable,
                                 addr.get(), (jint)group_id, (jint)group_size,
                                 UUID_PARAMS(uuid));
                                 (jint)rank, UUID_PARAMS(uuid));
  }

  void OnSetMemberAvailable(const RawAddress& bd_addr, int group_id) override {
@@ -157,7 +158,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
      env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");

  method_onDeviceAvailable =
      env->GetMethodID(clazz, "onDeviceAvailable", "([BIIJJ)V");
      env->GetMethodID(clazz, "onDeviceAvailable", "([BIIIJJ)V");

  method_onSetMemberAvailable =
      env->GetMethodID(clazz, "onSetMemberAvailable", "([BI)V");
+2 −1
Original line number Diff line number Diff line
@@ -149,13 +149,14 @@ public class CsipSetCoordinatorNativeInterface {
    /** Device availability */
    @VisibleForTesting
    public void onDeviceAvailable(
            byte[] address, int groupId, int groupSize, long uuidLsb, long uuidMsb) {
            byte[] address, int groupId, int groupSize, int rank, long uuidLsb, long uuidMsb) {
        UUID uuid = new UUID(uuidMsb, uuidLsb);
        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
                CsipSetCoordinatorStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
        event.device = getDevice(address);
        event.valueInt1 = groupId;
        event.valueInt2 = groupSize;
        event.valueInt3 = rank;
        event.valueUuid1 = uuid;

        if (DBG) {
+36 −13
Original line number Diff line number Diff line
@@ -52,11 +52,9 @@ import com.android.modules.utils.SynchronousResultReceiver;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -87,7 +85,8 @@ public class CsipSetCoordinatorService extends ProfileService {
            new HashMap<>();

    private final Map<Integer, ParcelUuid> mGroupIdToUuidMap = new HashMap<>();
    private final Map<BluetoothDevice, Set<Integer>> mDeviceGroupIdMap = new ConcurrentHashMap<>();
    private final Map<BluetoothDevice, Map<Integer, Integer>> mDeviceGroupIdRankMap =
            new ConcurrentHashMap<>();
    private final Map<Integer, Integer> mGroupIdToGroupSize = new HashMap<>();
    private final Map<ParcelUuid, Map<Executor, IBluetoothCsipSetCoordinatorCallback>> mCallbacks =
            new HashMap<>();
@@ -201,7 +200,7 @@ public class CsipSetCoordinatorService extends ProfileService {
            }
        }

        mDeviceGroupIdMap.clear();
        mDeviceGroupIdRankMap.clear();
        mCallbacks.clear();
        mGroupIdToGroupSize.clear();
        mGroupIdToUuidMap.clear();
@@ -573,13 +572,37 @@ public class CsipSetCoordinatorService extends ProfileService {
     * @return map of group id and related uuids.
     */
    public Map<Integer, ParcelUuid> getGroupUuidMapByDevice(BluetoothDevice device) {
        Set<Integer> device_groups = mDeviceGroupIdMap.getOrDefault(device, new HashSet<>());
        Map<Integer, Integer> device_groups =
                mDeviceGroupIdRankMap.getOrDefault(device, new HashMap<>());
        return mGroupIdToUuidMap.entrySet()
                .stream()
                .filter(e -> device_groups.contains(e.getKey()))
                .filter(e -> device_groups.containsKey(e.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    /**
     * Get grouped devices
     * @param groupId group ID
     * @return related list of devices sorted from the lowest to the highest rank value.
     */
    public @NonNull List<BluetoothDevice> getGroupDevicesOrdered(int groupId) {
        final Map<BluetoothDevice, Integer> deviceRankMap = new HashMap();
        for (Map.Entry<BluetoothDevice, ?> entry : mDeviceGroupIdRankMap.entrySet()) {
            Map<Integer, Integer> rankMap = (Map<Integer, Integer>) entry.getValue();
            BluetoothDevice device = entry.getKey();
            if (rankMap.containsKey(groupId)) {
                deviceRankMap.put(device, rankMap.get(groupId));
            }
        }

        // Return device list sorted by descending rank order
        return deviceRankMap.entrySet()
                .stream()
                .sorted(Map.Entry.comparingByValue())
                .map(e -> e.getKey())
                .collect(Collectors.toList());
    }

    /**
     * Get group desired size
     * @param groupId group ID
@@ -590,18 +613,18 @@ public class CsipSetCoordinatorService extends ProfileService {
                IBluetoothCsipSetCoordinator.CSIS_GROUP_SIZE_UNKNOWN);
    }

    private void handleDeviceAvailable(BluetoothDevice device, int groupId, UUID uuid) {
    private void handleDeviceAvailable(BluetoothDevice device, int groupId, int rank, UUID uuid) {
        ParcelUuid parcel_uuid = new ParcelUuid(uuid);
        if (!getAllGroupIds(parcel_uuid).contains(groupId)) {
            mGroupIdToUuidMap.put(groupId, parcel_uuid);
        }

        if (!mDeviceGroupIdMap.containsKey(device)) {
            mDeviceGroupIdMap.put(device, new HashSet<Integer>());
        if (!mDeviceGroupIdRankMap.containsKey(device)) {
            mDeviceGroupIdRankMap.put(device, new HashMap<Integer, Integer>());
        }

        Set<Integer> all_device_groups = mDeviceGroupIdMap.get(device);
        all_device_groups.add(groupId);
        Map<Integer, Integer> all_device_groups = mDeviceGroupIdRankMap.get(device);
        all_device_groups.put(groupId, rank);
    }

    private void executeCallback(Executor exec, IBluetoothCsipSetCoordinatorCallback callback,
@@ -700,7 +723,7 @@ public class CsipSetCoordinatorService extends ProfileService {
            intent.putExtra(
                    BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_TYPE_UUID, stackEvent.valueUuid1);

            handleDeviceAvailable(device, groupId, stackEvent.valueUuid1);
            handleDeviceAvailable(device, groupId, stackEvent.valueInt3, stackEvent.valueUuid1);
        } else if (stackEvent.type
                == CsipSetCoordinatorStackEvent.EVENT_TYPE_SET_MEMBER_AVAILABLE) {
            Objects.requireNonNull(device, "Device should never be null, event: " + stackEvent);
@@ -808,7 +831,7 @@ public class CsipSetCoordinatorService extends ProfileService {
            return;
        }

        mDeviceGroupIdMap.remove(device);
        mDeviceGroupIdRankMap.remove(device);

        synchronized (mStateMachines) {
            CsipSetCoordinatorStateMachine sm = mStateMachines.get(device);
+11 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ public class CsipSetCoordinatorStackEvent {
    public BluetoothDevice device;
    public int valueInt1 = 0;
    public int valueInt2 = 0;
    public int valueInt3 = 0;
    public UUID valueUuid1;

    public boolean valueBool1 = false;
@@ -67,6 +68,7 @@ public class CsipSetCoordinatorStackEvent {
        result.append(", device:" + device);
        result.append(", " + eventTypeValueInt1ToString(type, valueInt1));
        result.append(", " + eventTypeValueInt2ToString(type, valueInt2));
        result.append(", " + eventTypeValueInt3ToString(type, valueInt3));
        result.append(", " + eventTypeValueBool1ToString(type, valueBool1));
        result.append(", " + eventTypeValueUuid1ToString(type, valueUuid1));
        result.append("}");
@@ -147,6 +149,15 @@ public class CsipSetCoordinatorStackEvent {
        }
    }

    private static String eventTypeValueInt3ToString(int evType, int value) {
        switch (evType) {
            case EVENT_TYPE_DEVICE_AVAILABLE:
                return "rank: " + value;
            default:
                return "<unused>";
        }
    }

    private static String csipLockStatusToString(int state) {
        switch (state) {
            case LOCK_STATUS_SUCCESS:
+3 −5
Original line number Diff line number Diff line
@@ -50,7 +50,6 @@ import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;

@@ -1047,11 +1046,10 @@ public class HapClientService extends ProfileService {
    private List<BluetoothDevice> getGroupDevices(int groupId) {
        List<BluetoothDevice> devices = new ArrayList<>();

        // TODO: Fix missing CSIS service API to decouple from LeAudioService
        LeAudioService le_audio_service = mFactory.getLeAudioService();
        if (le_audio_service != null) {
        CsipSetCoordinatorService csipClient = mFactory.getCsipSetCoordinatorService();
        if (csipClient != null) {
            if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
                devices = le_audio_service.getGroupDevices(groupId);
                devices = csipClient.getGroupDevicesOrdered(groupId);
            }
        }
        return devices;
Loading