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

Commit 2cba79fd authored by Grzegorz Kolodziejczyk's avatar Grzegorz Kolodziejczyk Committed by Automerger Merge Worker
Browse files

Merge "BassClientStateMachine: Add support for Long Writes" into main am: 4476c870

parents 484a8ec0 4476c870
Loading
Loading
Loading
Loading
+37 −14
Original line number Diff line number Diff line
@@ -113,6 +113,8 @@ public class BassClientStateMachine extends StateMachine {
    static final int ARGTYPE_METADATA = 1;
    static final int ARGTYPE_RCVSTATE = 2;

    static final int ATT_WRITE_CMD_HDR_LEN = 3;

    /*key is combination of sourceId, Address and advSid for this hashmap*/
    private final Map<Integer, BluetoothLeBroadcastReceiveState>
            mBluetoothLeBroadcastReceiveStates =
@@ -169,6 +171,7 @@ public class BassClientStateMachine extends StateMachine {
    @VisibleForTesting
    BluetoothGattTestableWrapper mBluetoothGatt = null;
    BluetoothGattCallback mGattCallback = null;
    int mMaxSingleAttributeWriteValueLen = 0;

    BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper,
            int connectTimeoutMs) {
@@ -1037,6 +1040,11 @@ public class BassClientStateMachine extends StateMachine {
                log("onMtuChanged is remote initiated trigger, mBluetoothGatt:"
                        + mBluetoothGatt);
            }

            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, "mtu: " + mtu);
                mMaxSingleAttributeWriteValueLen = mtu - ATT_WRITE_CMD_HDR_LEN;
            }
        }

        @Override
@@ -1110,8 +1118,16 @@ public class BassClientStateMachine extends StateMachine {
        log("Total number of chars" + numOfChars);
        for (int i = 0; i < allChars.size(); i++) {
            if (allChars.get(i).getUuid().equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
                int properties = allChars.get(i).getProperties();

                if (((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0)
                        || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0)) {
                    Log.w(TAG, "Broadcast Audio Scan Control Point characteristic has invalid "
                            + "properties!");
                } else {
                    mBroadcastScanControlPoint = allChars.get(i);
                    log("Index of ScanCtrlPoint:" + i);
                }
            } else {
                log("Reading " + i + "th ReceiverState");
                mBroadcastCharacteristics.add(allChars.get(i));
@@ -1505,6 +1521,19 @@ public class BassClientStateMachine extends StateMachine {
            mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
        }

        private void writeBassControlPoint(byte[] value) {
            if (value.length > mMaxSingleAttributeWriteValueLen) {
                mBroadcastScanControlPoint.setWriteType(
                        BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
            } else {
                mBroadcastScanControlPoint.setWriteType(
                        BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
            }

            mBroadcastScanControlPoint.setValue(value);
            mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
        }

        @Override
        public boolean processMessage(Message message) {
            log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what));
@@ -1550,8 +1579,7 @@ public class BassClientStateMachine extends StateMachine {
                    break;
                case START_SCAN_OFFLOAD:
                    if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
                        mBroadcastScanControlPoint.setValue(REMOTE_SCAN_START);
                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
                        writeBassControlPoint(REMOTE_SCAN_START);
                        mPendingOperation = message.what;
                        transitionTo(mConnectedProcessing);
                    } else {
@@ -1560,8 +1588,7 @@ public class BassClientStateMachine extends StateMachine {
                    break;
                case STOP_SCAN_OFFLOAD:
                    if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
                        mBroadcastScanControlPoint.setValue(REMOTE_SCAN_STOP);
                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
                        writeBassControlPoint(REMOTE_SCAN_STOP);
                        mPendingOperation = message.what;
                        transitionTo(mConnectedProcessing);
                    } else {
@@ -1593,8 +1620,7 @@ public class BassClientStateMachine extends StateMachine {
                        break;
                    }
                    if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
                        mBroadcastScanControlPoint.setValue(addSourceInfo);
                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
                        writeBassControlPoint(addSourceInfo);
                        mPendingOperation = message.what;
                        mPendingMetadata = metaData;
                        if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) {
@@ -1620,8 +1646,7 @@ public class BassClientStateMachine extends StateMachine {
                        break;
                    }
                    if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
                        mBroadcastScanControlPoint.setValue(updateSourceInfo);
                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
                        writeBassControlPoint(updateSourceInfo);
                        mPendingOperation = message.what;
                        mPendingSourceId = (byte) sourceId;
                        if (paSync == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE) {
@@ -1665,8 +1690,7 @@ public class BassClientStateMachine extends StateMachine {
                        break;
                    }
                    if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
                        mBroadcastScanControlPoint.setValue(setBroadcastPINcmd);
                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
                        writeBassControlPoint(setBroadcastPINcmd);
                        mPendingOperation = message.what;
                        mPendingSourceId = (byte) recvState.getSourceId();
                        transitionTo(mConnectedProcessing);
@@ -1684,8 +1708,7 @@ public class BassClientStateMachine extends StateMachine {
                            setPendingRemove((int) sid, false);
                        }

                        mBroadcastScanControlPoint.setValue(removeSourceInfo);
                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
                        writeBassControlPoint(removeSourceInfo);
                        mPendingOperation = message.what;
                        mPendingSourceId = sid;
                        transitionTo(mConnectedProcessing);
+68 −0
Original line number Diff line number Diff line
@@ -1593,6 +1593,74 @@ public class BassClientStateMachineTest {
        mBassClientStateMachine.dump(new StringBuilder());
    }

    @Test
    public void sendAddBcastSourceMessage_NoResponseWrite() {
        mBassClientStateMachine.connectGatt(true);
        BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback;
        cb.onMtuChanged(null, 250, GATT_SUCCESS);
        initToConnectedState();

        BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class);
        when(mBassClientService.getCallbacks()).thenReturn(callbacks);

        BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata();
        // verify local broadcast doesn't require active synced source
        when(mBassClientService.isLocalBroadcast(any())).thenReturn(true);
        mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        verify(mBassClientService).getCallbacks();
        verify(callbacks).notifySourceAddFailed(any(), any(), anyInt());

        BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
                Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
        mBassClientStateMachine.mBluetoothGatt = btGatt;
        BluetoothGattCharacteristic scanControlPoint =
                Mockito.mock(BluetoothGattCharacteristic.class);
        mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint;

        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata),
                BassClientStateMachine.ConnectedProcessing.class);
        verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
        verify(scanControlPoint).setValue(any(byte[].class));
        verify(btGatt).writeCharacteristic(any());
    }

    @Test
    public void sendAddBcastSourceMessage_LongWrite() {
        mBassClientStateMachine.connectGatt(true);
        BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback;
        cb.onMtuChanged(null, 23, GATT_SUCCESS);
        initToConnectedState();

        BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class);
        when(mBassClientService.getCallbacks()).thenReturn(callbacks);

        BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata();
        // verify local broadcast doesn't require active synced source
        when(mBassClientService.isLocalBroadcast(any())).thenReturn(true);
        mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        verify(mBassClientService).getCallbacks();
        verify(callbacks).notifySourceAddFailed(any(), any(), anyInt());

        BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
                Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
        mBassClientStateMachine.mBluetoothGatt = btGatt;
        BluetoothGattCharacteristic scanControlPoint =
                Mockito.mock(BluetoothGattCharacteristic.class);
        mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint;

        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata),
                BassClientStateMachine.ConnectedProcessing.class);
        verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        verify(scanControlPoint).setValue(any(byte[].class));
        verify(btGatt).writeCharacteristic(any());
    }

    private void initToDisconnectedState() {
        allowConnection(true);
        allowConnectGatt(true);