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

Commit b25ffe89 authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Gerrit Code Review
Browse files

Merge "[BassClient] Fix metadata/codecconfig length parsing" into main

parents d02ef554 e0524b14
Loading
Loading
Loading
Loading
+13 −16
Original line number Diff line number Diff line
@@ -67,9 +67,9 @@ class BaseData {
    public static class BaseInformation {
        public byte[] presentationDelay = new byte[3];
        public byte[] codecId = new byte[5];
        public byte codecConfigLength;
        public int codecConfigLength;
        public byte[] codecConfigInfo;
        public byte metaDataLength;
        public int metaDataLength;
        public byte[] metaData;
        public byte numSubGroups;
        public byte[] bisIndices;
@@ -126,12 +126,12 @@ class BaseData {
                log("codecConfigLength: " + codecConfigLength);
                log("subGroupId: " + subGroupId);
            }
            if (codecConfigLength != (byte) 0) {
            if (codecConfigLength != 0) {
                log("codecConfigInfo: " + Arrays.toString(codecConfigInfo));
            }
            if (level == 2) {
                log("metaDataLength: " + metaDataLength);
                if (metaDataLength != (byte) 0) {
                if (metaDataLength != 0) {
                    log("metaData: " + Arrays.toString(metaData));
                }
                if (level == 1 || level == 2) {
@@ -250,18 +250,16 @@ class BaseData {
                    0, METADATA_CODEC_LENGTH);
            offset += METADATA_CODEC_LENGTH;
        }
        node.codecConfigLength = serviceData[offset++];
        node.codecConfigLength = serviceData[offset++] & 0xff;
        if (node.codecConfigLength != 0) {
            node.codecConfigInfo = new byte[(int) node.codecConfigLength];
            System.arraycopy(serviceData, offset, node.codecConfigInfo,
                    0, (int) node.codecConfigLength);
            node.codecConfigInfo = new byte[node.codecConfigLength];
            System.arraycopy(serviceData, offset, node.codecConfigInfo, 0, node.codecConfigLength);
            offset += node.codecConfigLength;
        }
        node.metaDataLength = serviceData[offset++];
        node.metaDataLength = serviceData[offset++] & 0xff;
        if (node.metaDataLength != 0) {
            node.metaData = new byte[(int) node.metaDataLength];
            System.arraycopy(serviceData, offset,
                    node.metaData, 0, (int) node.metaDataLength);
            node.metaData = new byte[node.metaDataLength];
            System.arraycopy(serviceData, offset, node.metaData, 0, node.metaDataLength);
            offset += node.metaDataLength;
        }
        return new Pair<BaseInformation, Integer>(node, offset);
@@ -273,11 +271,10 @@ class BaseData {
        BaseInformation node = new BaseInformation();
        node.level = METADATA_LEVEL3;
        node.index = serviceData[offset++];
        node.codecConfigLength = serviceData[offset++];
        node.codecConfigLength = serviceData[offset++] & 0xff;
        if (node.codecConfigLength != 0) {
            node.codecConfigInfo = new byte[(int) node.codecConfigLength];
            System.arraycopy(serviceData, offset,
                    node.codecConfigInfo, 0, (int) node.codecConfigLength);
            node.codecConfigInfo = new byte[node.codecConfigLength];
            System.arraycopy(serviceData, offset, node.codecConfigInfo, 0, node.codecConfigLength);
            offset += node.codecConfigLength;
        }
        return new Pair<BaseInformation, Integer>(node, offset);
+1 −1
Original line number Diff line number Diff line
@@ -826,7 +826,7 @@ public class BassClientStateMachine extends StateMachine {
                offset += BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE;
                bisSyncState.add((long) Utils.byteArrayToInt(bisSyncIndex));

                byte metaDataLength = receiverState[offset++];
                int metaDataLength = receiverState[offset++] & 0xFF;
                if (metaDataLength > 0) {
                    log("metadata of length: " + metaDataLength + "is available");
                    byte[] metaData = new byte[metaDataLength];
+10 −6
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ class PublicBroadcastData {
    private final PublicBroadcastInfo mPublicBroadcastInfo;

    public static class PublicBroadcastInfo {
        public byte metaDataLength;
        public int metaDataLength;
        public byte[] metaData;
        public boolean isEncrypted;
        public int audioConfigQuality;
@@ -53,7 +53,7 @@ class PublicBroadcastData {
            log("encrypted: " + isEncrypted);
            log("audio config quality: " + audioConfigQuality);
            log("metaDataLength: " + metaDataLength);
            if (metaDataLength != (byte) 0) {
            if (metaDataLength != 0) {
                log("metaData: " + Arrays.toString(metaData));
            }
            log("**END: Public Broadcast Information****");
@@ -90,16 +90,20 @@ class PublicBroadcastData {
        }

        // Parse Public broadcast announcement metadata
        publicBroadcastInfo.metaDataLength = serviceData[offset++];
        publicBroadcastInfo.metaDataLength = serviceData[offset++] & 0xff;
        if (serviceData.length
                != (publicBroadcastInfo.metaDataLength + PUBLIC_BROADCAST_SERVICE_DATA_LEN_MIN)) {
            Log.w(TAG, "Invalid meta data length for PublicBroadcastData construction");
            return null;
        }
        if (publicBroadcastInfo.metaDataLength != 0) {
            publicBroadcastInfo.metaData = new byte[(int) publicBroadcastInfo.metaDataLength];
            System.arraycopy(serviceData, offset,
                    publicBroadcastInfo.metaData, 0, (int) publicBroadcastInfo.metaDataLength);
            publicBroadcastInfo.metaData = new byte[publicBroadcastInfo.metaDataLength];
            System.arraycopy(
                    serviceData,
                    offset,
                    publicBroadcastInfo.metaData,
                    0,
                    publicBroadcastInfo.metaDataLength);
            offset += publicBroadcastInfo.metaDataLength;
        }
        publicBroadcastInfo.print();
+78 −0
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@ import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertThrows;

import com.google.common.primitives.Bytes;

import java.util.Random;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -114,4 +118,78 @@ public class BaseDataTest {
        assertThat(level.index).isEqualTo(4);
        assertThat(level.codecConfigLength).isEqualTo(3);
    }

    @Test
    public void parseBaseData_longMetaData() {
        assertThrows(IllegalArgumentException.class, () -> BaseData.parseBaseData(null));

        int metaDataLength = 142;

        byte[] serviceDataLevel1 =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01 // numSubGroups
                };

        byte[] serviceDataLevel2 =
                new byte[] {
                    // LEVEL 2
                    (byte) 0x01, // numSubGroups
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00, // UNKNOWN_CODEC
                    (byte) 0x02, // codecConfigLength
                    (byte) 0x01,
                    (byte) 'A', // codecConfigInfo
                    (byte) metaDataLength, // metaDataLength 142
                };

        byte[] metadataHeader =
                new byte[] {
                    (byte) (metaDataLength - 1), // length 141
                    (byte) 0xFF
                };

        byte[] metadataPayload = new byte[140];
        new Random().nextBytes(metadataPayload);

        byte[] serviceDataLevel3 =
                new byte[] {
                    // LEVEL 3
                    (byte) 0x04, // index
                    (byte) 0x03, // codecConfigLength
                    (byte) 0x02,
                    (byte) 'B',
                    (byte) 'C' // codecConfigInfo
                };

        BaseData data =
                BaseData.parseBaseData(
                        Bytes.concat(
                                serviceDataLevel1,
                                Bytes.concat(serviceDataLevel2, metadataHeader, metadataPayload),
                                serviceDataLevel3));
        BaseData.BaseInformation level = data.getLevelOne();
        assertThat(level.presentationDelay).isEqualTo(new byte[] {0x01, 0x02, 0x03});
        assertThat(level.numSubGroups).isEqualTo(1);

        assertThat(data.getLevelTwo().size()).isEqualTo(1);
        level = data.getLevelTwo().get(0);

        assertThat(level.numSubGroups).isEqualTo(1);
        assertThat(level.codecId).isEqualTo(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00});
        assertThat(level.codecConfigLength).isEqualTo(2);
        assertThat(level.metaDataLength).isEqualTo(metaDataLength);
        assertThat(level.metaData).isEqualTo(Bytes.concat(metadataHeader, metadataPayload));

        assertThat(data.getLevelThree().size()).isEqualTo(1);
        level = data.getLevelThree().get(0);
        assertThat(level.index).isEqualTo(4);
        assertThat(level.codecConfigLength).isEqualTo(3);
    }
}
+83 −0
Original line number Diff line number Diff line
@@ -94,6 +94,8 @@ import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.flags.Flags;

import com.google.common.primitives.Bytes;

import org.hamcrest.core.IsInstanceOf;
import org.junit.After;
import org.junit.Assert;
@@ -111,6 +113,7 @@ import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.UUID;

@MediumTest
@@ -1517,6 +1520,86 @@ public class BassClientStateMachineTest {
        verify(scanControlPoint).setValue(any(byte[].class));
    }

    @Test
    public void receiveSinkReceiveState_inConnectedState() {
        initToConnectedState();
        mBassClientStateMachine.connectGatt(true);
        mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2;
        BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class);
        when(mBassClientService.getCallbacks()).thenReturn(callbacks);

        // Prepare mBluetoothLeBroadcastReceiveStates with metadata for test
        int sourceId = 1;
        int metaDataLength = 142;
        byte[] value =
                new byte[] {
                    (byte) sourceId, // sourceId
                    (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType
                    Utils.getByteAddress(mSourceTestDevice)[5],
                    Utils.getByteAddress(mSourceTestDevice)[4],
                    Utils.getByteAddress(mSourceTestDevice)[3],
                    Utils.getByteAddress(mSourceTestDevice)[2],
                    Utils.getByteAddress(mSourceTestDevice)[1],
                    Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress
                    0x00, // sourceAdvSid
                    0x00,
                    0x00,
                    0x00, // broadcastIdBytes
                    (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
                    (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED,
                    // no badBroadcastCode
                    0x01, // numSubGroups 1
                    0x00,
                    0x00,
                    0x00,
                    0x00, // audioSyncIndex
                    (byte) metaDataLength, // metaDataLength
                };

        byte[] metadataHeader =
                new byte[] {
                    (byte) (metaDataLength - 1), // length 141
                    (byte) 0xFF
                };

        byte[] metadataPayload = new byte[140];
        new Random().nextBytes(metadataPayload);

        BluetoothGattCharacteristic characteristic =
                Mockito.mock(BluetoothGattCharacteristic.class);
        when(characteristic.getValue())
                .thenReturn(Bytes.concat(value, metadataHeader, metadataPayload));
        when(characteristic.getInstanceId()).thenReturn(sourceId);
        when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE);

        mBassClientStateMachine.mGattCallback.onCharacteristicRead(
                null, characteristic, GATT_SUCCESS);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        assertThat(mBassClientStateMachine.getAllSources().size()).isEqualTo(1);
        BluetoothLeBroadcastReceiveState recvState = mBassClientStateMachine.getAllSources().get(0);

        assertThat(recvState.getSourceId()).isEqualTo(sourceId);
        assertThat(recvState.getSourceAddressType()).isEqualTo(mSourceTestDevice.getAddressType());
        assertThat(recvState.getSourceDevice()).isEqualTo(mSourceTestDevice);
        assertThat(recvState.getSourceAdvertisingSid()).isEqualTo(0x00);
        assertThat(recvState.getBroadcastId()).isEqualTo(0x00);
        assertThat(recvState.getPaSyncState())
                .isEqualTo(BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE);
        assertThat(recvState.getBigEncryptionState())
                .isEqualTo(BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED);
        assertThat(recvState.getNumSubgroups()).isEqualTo(1);

        assertThat(recvState.getBisSyncState().size()).isEqualTo(1);
        assertThat(recvState.getBisSyncState().get(0)).isEqualTo(0x00);

        assertThat(recvState.getSubgroupMetadata().size()).isEqualTo(1);
        BluetoothLeAudioContentMetadata metaData = recvState.getSubgroupMetadata().get(0);
        assertThat(metaData.getRawMetadata().length).isEqualTo(metaDataLength);
        assertThat(metaData.getRawMetadata())
                .isEqualTo(Bytes.concat(metadataHeader, metadataPayload));
    }

    @Test
    public void sendRemoveBcastSourceMessage_inConnectedState() {
        initToConnectedState();
Loading