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

Commit 185d7fbe authored by Michal Belusiak's avatar Michal Belusiak
Browse files

Bass: Modify source on duplicate addition

That's allow to sync and resume receivers source synchronization
by modify source instead of remove and add source one again.

Another problem was that if there was room for source addition,
then duplicate addition ends with no action on sink and error callback.

Bug: 375125837
Bug: 363168099
Test: atest BassClientServiceTest BassClientStateMachineTest
Change-Id: Id18778c4791eee510b547104413aee9bff30841d
parent 522ce877
Loading
Loading
Loading
Loading
+147 −121
Original line number Diff line number Diff line
@@ -1297,9 +1297,9 @@ public class BassClientService extends ProfileService {
        return devices;
    }

    private boolean isValidBroadcastSourceAddition(
    private int checkDuplicateSourceAdditionAndGetSourceId(
            BluetoothDevice device, BluetoothLeBroadcastMetadata metaData) {
        boolean retval = true;
        int sourceId = BassConstants.INVALID_SOURCE_ID;
        List<BluetoothLeBroadcastReceiveState> currentAllSources = getAllSources(device);
        for (int i = 0; i < currentAllSources.size(); i++) {
            BluetoothLeBroadcastReceiveState state = currentAllSources.get(i);
@@ -1307,17 +1307,12 @@ public class BassClientService extends ProfileService {
                    && metaData.getSourceAddressType() == state.getSourceAddressType()
                    && metaData.getSourceAdvertisingSid() == state.getSourceAdvertisingSid()
                    && metaData.getBroadcastId() == state.getBroadcastId()) {
                retval = false;
                Log.e(
                        TAG,
                        "isValidBroadcastSourceAddition: fail for "
                                + device
                                + " metaData: "
                                + metaData);
                sourceId = state.getSourceId();
                log("DuplicatedSourceAddition: for " + device + " metaData: " + metaData);
                break;
            }
        }
        return retval;
        return sourceId;
    }

    private boolean hasRoomForBroadcastSourceAddition(BluetoothDevice device) {
@@ -1512,45 +1507,77 @@ public class BassClientService extends ProfileService {
        }
    }

    private int areValidParametersToModifySource(
            BluetoothLeBroadcastMetadata updatedMetadata,
            BassClientStateMachine stateMachine,
            Integer deviceSourceId,
            BluetoothDevice device) {
        if (updatedMetadata == null || stateMachine == null) {
            log(
                    "areValidParametersToModifySource: Error bad parameters: sourceId = "
                            + deviceSourceId
                            + " updatedMetadata = "
                            + updatedMetadata);
    private int validateParametersForSourceOperation(
            BassClientStateMachine stateMachine, BluetoothDevice device) {
        if (stateMachine == null) {
            log("validateParameters: stateMachine is null for device: " + device);
            return BluetoothStatusCodes.ERROR_BAD_PARAMETERS;
        }
        if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) {
            log("areValidParametersToModifySource: no such sourceId for device: " + device);
            return BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID;
        }

        if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
            log("areValidParametersToModifySource: device is not connected");
            log("validateParameters: device is not connected, device: " + device);
            return BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR;
        }
        byte[] code = updatedMetadata.getBroadcastCode();

        return BluetoothStatusCodes.SUCCESS;
    }

    private int validateParametersForSourceOperation(
            BassClientStateMachine stateMachine,
            BluetoothDevice device,
            BluetoothLeBroadcastMetadata metadata) {
        int status = validateParametersForSourceOperation(stateMachine, device);
        if (status != BluetoothStatusCodes.SUCCESS) {
            return status;
        }

        if (metadata == null) {
            log("validateParameters: metadata is null for device: " + device);
            return BluetoothStatusCodes.ERROR_BAD_PARAMETERS;
        }

        byte[] code = metadata.getBroadcastCode();
        if ((code != null) && (code.length != 0)) {
            if ((code.length > 16) || (code.length < 4)) {
                log(
                        "areValidParametersToModifySource: Invalid broadcast code length: "
                        "validateParameters: Invalid broadcast code length: "
                                + code.length
                                + ", should be between 4 and 16 octets");
                return BluetoothStatusCodes.ERROR_BAD_PARAMETERS;
            }
        }
        if (stateMachine.hasPendingSourceOperation()) {
            Log.w(
                    TAG,
                    "modifySource: source operation already pending, device: "
                            + device
                            + ", broadcastId: "
                            + updatedMetadata.getBroadcastId());
            return BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE;

        return BluetoothStatusCodes.SUCCESS;
    }

    private int validateParametersForSourceOperation(
            BassClientStateMachine stateMachine, BluetoothDevice device, Integer sourceId) {
        int status = validateParametersForSourceOperation(stateMachine, device);
        if (status != BluetoothStatusCodes.SUCCESS) {
            return status;
        }

        if (sourceId == BassConstants.INVALID_SOURCE_ID) {
            log("validateParameters: no such sourceId for device: " + device);
            return BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID;
        }

        return BluetoothStatusCodes.SUCCESS;
    }

    private int validateParametersForSourceOperation(
            BassClientStateMachine stateMachine,
            BluetoothDevice device,
            BluetoothLeBroadcastMetadata metadata,
            Integer sourceId) {
        int status = validateParametersForSourceOperation(stateMachine, device, metadata);
        if (status != BluetoothStatusCodes.SUCCESS) {
            return status;
        }

        if (sourceId == BassConstants.INVALID_SOURCE_ID) {
            log("validateParameters: no such sourceId for device: " + device);
            return BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID;
        }

        return BluetoothStatusCodes.SUCCESS;
@@ -2942,16 +2969,10 @@ public class BassClientService extends ProfileService {
        byte[] code = sourceMetadata.getBroadcastCode();
        for (BluetoothDevice device : devices) {
            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
            if (stateMachine == null) {
                log("addSource: Error bad parameter: no state machine for " + device);
                mCallbacks.notifySourceAddFailed(
                        device, sourceMetadata, BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
                continue;
            }
            if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
                log("addSource: device is not connected");
                mCallbacks.notifySourceAddFailed(
                        device, sourceMetadata, BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
            int statusCode =
                    validateParametersForSourceOperation(stateMachine, device, sourceMetadata);
            if (statusCode != BluetoothStatusCodes.SUCCESS) {
                mCallbacks.notifySourceAddFailed(device, sourceMetadata, statusCode);
                continue;
            }
            if (stateMachine.hasPendingSourceOperation()) {
@@ -2965,12 +2986,19 @@ public class BassClientService extends ProfileService {
                        device, sourceMetadata, BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE);
                continue;
            }
            if (leaudioBroadcastResyncHelper()) {
                int sourceId = checkDuplicateSourceAdditionAndGetSourceId(device, sourceMetadata);
                if (sourceId != BassConstants.INVALID_SOURCE_ID) {
                    updateSourceToResumeBroadcast(device, sourceId, sourceMetadata);
                    continue;
                }
            }
            if (!hasRoomForBroadcastSourceAddition(device)) {
                log("addSource: device has no room");
                Integer sourceId = getSourceIdToRemove(device);
                if (sourceId != BassConstants.INVALID_SOURCE_ID) {
                Integer sourceIdToRemove = getSourceIdToRemove(device);
                if (sourceIdToRemove != BassConstants.INVALID_SOURCE_ID) {
                    BluetoothLeBroadcastMetadata metaData =
                            stateMachine.getCurrentBroadcastMetadata(sourceId);
                            stateMachine.getCurrentBroadcastMetadata(sourceIdToRemove);
                    if (metaData != null) {
                        // Add host intentional pause if previous broadcast is different than
                        // current
@@ -2983,7 +3011,7 @@ public class BassClientService extends ProfileService {
                            TAG,
                            "Switch Broadcast Source: "
                                    + ("device: " + device)
                                    + (", old SourceId: " + sourceId)
                                    + (", old SourceId: " + sourceIdToRemove)
                                    + (", new broadcastId: " + sourceMetadata.getBroadcastId())
                                    + (", new broadcastName: "
                                            + sourceMetadata.getBroadcastName()));
@@ -2993,7 +3021,9 @@ public class BassClientService extends ProfileService {
                        // mark group op for both remove and add source
                        // so setSourceGroupManaged will be updated accordingly in callbacks
                        enqueueSourceGroupOp(
                                device, BassClientStateMachine.REMOVE_BCAST_SOURCE, sourceId);
                                device,
                                BassClientStateMachine.REMOVE_BCAST_SOURCE,
                                sourceIdToRemove);
                        enqueueSourceGroupOp(
                                device, BassClientStateMachine.ADD_BCAST_SOURCE, sourceMetadata);
                    }
@@ -3004,7 +3034,7 @@ public class BassClientService extends ProfileService {
                    Message message =
                            stateMachine.obtainMessage(BassClientStateMachine.SWITCH_BCAST_SOURCE);
                    message.obj = sourceMetadata;
                    message.arg1 = sourceId;
                    message.arg1 = sourceIdToRemove;
                    stateMachine.sendMessage(message);
                } else {
                    mCallbacks.notifySourceAddFailed(
@@ -3014,7 +3044,9 @@ public class BassClientService extends ProfileService {
                }
                continue;
            }
            if (!isValidBroadcastSourceAddition(device, sourceMetadata)) {
            if (!leaudioBroadcastResyncHelper()) {
                int sourceId = checkDuplicateSourceAdditionAndGetSourceId(device, sourceMetadata);
                if (sourceId != BassConstants.INVALID_SOURCE_ID) {
                    log("addSource: not a valid broadcast source addition");
                    mCallbacks.notifySourceAddFailed(
                            device,
@@ -3022,16 +3054,6 @@ public class BassClientService extends ProfileService {
                            BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_DUPLICATE_ADDITION);
                    continue;
                }
            if ((code != null) && (code.length != 0)) {
                if ((code.length > 16) || (code.length < 4)) {
                    log(
                            "Invalid broadcast code length: "
                                    + code.length
                                    + ", should be between 4 and 16 octets");
                    mCallbacks.notifySourceAddFailed(
                            device, sourceMetadata, BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
                    continue;
                }
            }

            /* Store metadata for sink device */
@@ -3106,14 +3128,24 @@ public class BassClientService extends ProfileService {
            BluetoothDevice device = deviceSourceIdPair.getKey();
            Integer deviceSourceId = deviceSourceIdPair.getValue();
            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);

            int statusCode =
                    areValidParametersToModifySource(
                            updatedMetadata, stateMachine, deviceSourceId, device);
                    validateParametersForSourceOperation(
                            stateMachine, device, updatedMetadata, deviceSourceId);
            if (statusCode != BluetoothStatusCodes.SUCCESS) {
                mCallbacks.notifySourceModifyFailed(device, sourceId, statusCode);
                continue;
            }
            if (stateMachine.hasPendingSourceOperation()) {
                Log.w(
                        TAG,
                        "modifySource: source operation already pending, device: "
                                + device
                                + ", broadcastId: "
                                + updatedMetadata.getBroadcastId());
                mCallbacks.notifySourceModifyFailed(
                        device, sourceId, BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE);
                continue;
            }

            sEventLogger.logd(
                    TAG,
@@ -3158,32 +3190,17 @@ public class BassClientService extends ProfileService {
        Map<BluetoothDevice, Integer> devices = getGroupManagedDeviceSources(sink, sourceId).second;
        for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) {
            BluetoothDevice device = deviceSourceIdPair.getKey();
            Integer deviceSourceId = deviceSourceIdPair.getValue();
            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);

            /* Removes metadata for sink device if not paused */
            if (!mPausedBroadcastSinks.contains(device)) {
                mBroadcastMetadataMap.remove(device);
            }

            if (stateMachine == null) {
                log("removeSource: Error bad parameters: device = " + device);
                mCallbacks.notifySourceRemoveFailed(
                        device, sourceId, BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
                continue;
            }
            if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) {
                log("removeSource: no such sourceId for device: " + device);
                mCallbacks.notifySourceRemoveFailed(
                        device,
                        sourceId,
                        BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID);
                continue;
            }
            if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
                log("removeSource: device is not connected");
                mCallbacks.notifySourceRemoveFailed(
                        device, sourceId, BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
            Integer deviceSourceId = deviceSourceIdPair.getValue();
            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
            int statusCode =
                    validateParametersForSourceOperation(stateMachine, device, deviceSourceId);
            if (statusCode != BluetoothStatusCodes.SUCCESS) {
                mCallbacks.notifySourceRemoveFailed(device, sourceId, statusCode);
                continue;
            }

@@ -3796,15 +3813,45 @@ public class BassClientService extends ProfileService {
                                || activeSyncedSrc.contains(
                                        getSyncHandleForBroadcastId(metadata.getBroadcastId())))) {
                    int sourceId = receiveState.get().getSourceId();
                    int statusCode =
                            areValidParametersToModifySource(
                                    metadata, stateMachine, sourceId, sink);

                    if (statusCode != BluetoothStatusCodes.SUCCESS) {
                        mCallbacks.notifySourceModifyFailed(sink, sourceId, statusCode);
                    updateSourceToResumeBroadcast(sink, sourceId, metadata);
                } else {
                    addSource(sink, metadata, false);
                }
            } else {
                if (metadata != null) {
                    mPausedBroadcastIds.remove(metadata.getBroadcastId());
                    addSource(sink, metadata, false);
                } else {
                    Log.w(
                            TAG,
                            "resumeReceiversSourceSynchronization: failed to get metadata to"
                                    + " resume sink: "
                                    + sink);
                }
            }
            // remove the device from mPausedBroadcastSinks
            iterator.remove();
                        continue;
        }

        logPausedBroadcastsAndSinks();
    }

    private void updateSourceToResumeBroadcast(
            BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata metadata) {
        BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
        int statusCode =
                validateParametersForSourceOperation(stateMachine, sink, metadata, sourceId);
        if (statusCode != BluetoothStatusCodes.SUCCESS) {
            return;
        }
        if (stateMachine.hasPendingSourceOperation()) {
            Log.w(
                    TAG,
                    "updateSourceToResumeBroadcast: source operation already pending, device: "
                            + sink
                            + ", broadcastId: "
                            + metadata.getBroadcastId());
            return;
        }

        sEventLogger.logd(
@@ -3814,8 +3861,7 @@ public class BassClientService extends ProfileService {
                        + (", sourceId: " + sourceId)
                        + (", updatedBroadcastId: " + metadata.getBroadcastId())
                        + (", updatedBroadcastName: " + metadata.getBroadcastName()));
                    Message message =
                            stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
        Message message = stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
        message.arg1 = sourceId;
        message.arg2 =
                DeviceConfig.getBoolean(
@@ -3826,26 +3872,6 @@ public class BassClientService extends ProfileService {
                        : BassConstants.PA_SYNC_PAST_AVAILABLE;
        message.obj = metadata;
        stateMachine.sendMessage(message);
                } else {
                    addSource(sink, metadata, false);
                }
            } else {
                if (metadata != null) {
                    mPausedBroadcastIds.remove(metadata.getBroadcastId());
                    addSource(sink, metadata, false);
                } else {
                    Log.w(
                            TAG,
                            "resumeReceiversSourceSynchronization: failed to get metadata to"
                                    + " resume sink: "
                                    + sink);
                }
            }
            // remove the device from mPausedBroadcastSinks
            iterator.remove();
        }

        logPausedBroadcastsAndSinks();
    }

    /** Handle Unicast source stream status change */
+3 −1
Original line number Diff line number Diff line
@@ -1916,7 +1916,7 @@ class BassClientStateMachine extends StateMachine {
        res[offset++] = (byte) numSubGroups;

        for (int i = 0; i < numSubGroups; i++) {
            int bisIndexValue = existingState.getBisSyncState().get(i).intValue();
            int bisIndexValue = 0xFFFFFFFF;
            if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
                bisIndexValue = 0;
            } else if (metaData != null
@@ -1929,6 +1929,8 @@ class BassClientStateMachine extends StateMachine {
                if (bisIndexValue == 0) {
                    bisIndexValue = 0xFFFFFFFF;
                }
            } else if (i < existingState.getBisSyncState().size()) {
                bisIndexValue = existingState.getBisSyncState().get(i).intValue();
            }
            log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
            // BIS_Sync