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

Commit df6335d8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Bass: Fix parsing BASE structure" into main

parents 3af158ea e7407a3f
Loading
Loading
Loading
Loading
+74 −11
Original line number Diff line number Diff line
@@ -141,26 +141,36 @@ class BaseData {
        levelOne.print();
        log("levelOne subgroups" + levelOne.numSubGroups);
        for (int i = 0; i < (int) levelOne.numSubGroups; i++) {
            if (offset >= serviceData.length) {
                Log.e(TAG, "Error: parsing Level 2");
                return null;
            }

            Pair<BaseInformation, Integer> pair1 = parseLevelTwo(serviceData, i, offset);
            BaseInformation node2 = pair1.first;
            if (node2 == null) {
            if (pair1 == null) {
                Log.e(TAG, "Error: parsing Level 2");
                return null;
            }
            BaseInformation node2 = pair1.first;
            numOfBISIndices += node2.numSubGroups;
            levelTwo.add(node2);
            node2.print();
            offset = pair1.second;
            for (int k = 0; k < node2.numSubGroups; k++) {
                if (offset >= serviceData.length) {
                    Log.e(TAG, "Error: parsing Level 3");
                    return null;
                }

                Pair<BaseInformation, Integer> pair2 = parseLevelThree(serviceData, offset);
                BaseInformation node3 = pair2.first;
                offset = pair2.second;
                if (node3 == null) {
                if (pair2 == null) {
                    Log.e(TAG, "Error: parsing Level 3");
                    return null;
                }
                BaseInformation node3 = pair2.first;
                levelThree.add(node3);
                node3.print();
                offset = pair2.second;
            }
        }
        consolidateBaseofLevelTwo(levelTwo, levelThree);
@@ -173,17 +183,52 @@ class BaseData {
        BaseInformation node = new BaseInformation();
        node.level = METADATA_LEVEL2;
        node.subGroupId = groupIndex;
        int bufferLengthLeft = (serviceData.length - offset);

        // Min. length expected is: codecID (5) + numBis (1) + codecSpecCfgLen (1) + metadataLen (1)
        final int minNodeBufferLen = METADATA_CODEC_LENGTH + 3;
        if (bufferLengthLeft < minNodeBufferLen) {
            Log.e(TAG, "Error: Invalid Lvl2 buffer length.");
            return null;
        }

        node.numSubGroups = serviceData[offset++]; // NumBis
        System.arraycopy(serviceData, offset, node.codecId, 0, METADATA_CODEC_LENGTH);
        offset += METADATA_CODEC_LENGTH;
        node.codecConfigLength = serviceData[offset++] & 0xff;
        if (node.codecConfigLength != 0) {

        // Declared codec specific data length
        int declaredLength = serviceData[offset++] & 0xff;

        bufferLengthLeft = (serviceData.length - offset);
        if (declaredLength < 0 || declaredLength > bufferLengthLeft) {
            Log.e(TAG, "Error: Invalid codec config length or codec config truncated.");
            return null;
        }

        if (declaredLength != 0) {
            node.codecConfigLength = declaredLength;
            node.codecConfigInfo = new byte[node.codecConfigLength];
            System.arraycopy(serviceData, offset, node.codecConfigInfo, 0, node.codecConfigLength);
            offset += node.codecConfigLength;
        }
        node.metaDataLength = serviceData[offset++] & 0xff;
        if (node.metaDataLength != 0) {

        // Verify the buffer size left
        bufferLengthLeft = (serviceData.length - offset);
        if (bufferLengthLeft < 1) {
            Log.e(TAG, "Error: Invalid Lvl2 buffer length.");
            return null;
        }

        // Declared metadata length
        declaredLength = serviceData[offset++] & 0xff;
        --bufferLengthLeft;
        if (declaredLength < 0 || declaredLength > bufferLengthLeft) {
            Log.e(TAG, "Error: Invalid metadata length or metadata truncated.");
            return null;
        }

        if (declaredLength != 0) {
            node.metaDataLength = declaredLength;
            node.metaData = new byte[node.metaDataLength];
            System.arraycopy(serviceData, offset, node.metaData, 0, node.metaDataLength);
            offset += node.metaDataLength;
@@ -195,9 +240,27 @@ class BaseData {
        log("Parsing Level 3");
        BaseInformation node = new BaseInformation();
        node.level = METADATA_LEVEL3;
        int bufferLengthLeft = (serviceData.length - offset);

        // Min. length expected is: bisIdx (1) + codecSpecCfgLen (1)
        final int minNodeBufferLen = 2;
        if (bufferLengthLeft < minNodeBufferLen) {
            Log.e(TAG, "Error: Invalid Lvl2 buffer length.");
            return null;
        }
        node.index = serviceData[offset++];
        node.codecConfigLength = serviceData[offset++] & 0xff;
        if (node.codecConfigLength != 0) {

        // Verify the buffer size left
        int declaredLength = serviceData[offset++] & 0xff;

        bufferLengthLeft = (serviceData.length - offset);
        if (declaredLength < 0 || declaredLength > bufferLengthLeft) {
            Log.e(TAG, "Error: Invalid metadata length or metadata truncated.");
            return null;
        }

        if (declaredLength != 0) {
            node.codecConfigLength = declaredLength;
            node.codecConfigInfo = new byte[node.codecConfigLength];
            System.arraycopy(serviceData, offset, node.codecConfigInfo, 0, node.codecConfigLength);
            offset += node.codecConfigLength;
+277 −0
Original line number Diff line number Diff line
@@ -119,6 +119,283 @@ public class BaseDataTest {
        assertThat(level.codecConfigLength).isEqualTo(3);
    }

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

        byte[] serviceData =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01, // numSubGroups
                    // LEVEL 2
                    (byte) 0x01, // numBIS
                    (byte) 0x06,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00, // Lc3
                    (byte) 0x03, // codecConfigLength
                    (byte) 0x01,
                    (byte) 'A', // codecConfigInfo
                };

        BaseData data = BaseData.parseBaseData(serviceData);
        assertThat(data).isEqualTo(null);
    }

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

        byte[] serviceData =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01, // numSubGroups
                    // LEVEL 2
                    (byte) 0x01, // numBIS
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00, // UNKNOWN_CODEC
                    (byte) 0x02, // codecConfigLength
                    (byte) 0x01,
                    (byte) 'A', // codecConfigInfo
                    (byte) 0x04, // metaDataLength
                    (byte) 0x06,
                    (byte) 0x07,
                    (byte) 0x08, // metaData
                };

        BaseData data = BaseData.parseBaseData(serviceData);
        assertThat(data).isEqualTo(null);
    }

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

        byte[] serviceData =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01, // numSubGroups
                    // LEVEL 2
                    (byte) 0x01, // numBIS
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00, // UNKNOWN_CODEC
                    (byte) 0x02, // codecConfigLength
                    (byte) 0x01,
                    (byte) 'A', // codecConfigInfo
                    (byte) 0x03, // metaDataLength
                    (byte) 0x06,
                    (byte) 0x07,
                    (byte) 0x08, // metaData
                    // LEVEL 3
                    (byte) 0x04, // index
                    (byte) 0x04, // codecConfigLength
                    (byte) 0x02,
                    (byte) 'B',
                    (byte) 'C' // codecConfigInfo
                };

        BaseData data = BaseData.parseBaseData(serviceData);
        assertThat(data).isEqualTo(null);
    }

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

        byte[] serviceData =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01, // numSubGroups
                    // LEVEL 2
                    (byte) 0x01, // numBIS
                    (byte) 0x06,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00,
                    (byte) 0x00, // LC3
                    (byte) 0x02, // codecConfigLength
                    (byte) 0x04,
                    (byte) 'A', // codecConfigInfo
                    (byte) 0x03, // metaDataLength
                    (byte) 0x06,
                    (byte) 0x07,
                    (byte) 0x08, // metaData
                    // LEVEL 3
                    (byte) 0x04, // index
                    (byte) 0x03, // codecConfigLength
                    (byte) 0x03,
                    (byte) 'B',
                    (byte) 'C' // codecConfigInfo
                };

        BaseData data = BaseData.parseBaseData(serviceData);
        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[] {0x06, 0x00, 0x00, 0x00, 0x00});
        assertThat(level.codecConfigLength).isEqualTo(2);
        assertThat(level.metaDataLength).isEqualTo(3);

        assertThat(data.getLevelThree().size()).isEqualTo(1);
        level = data.getLevelThree().get(0);
        assertThat(level.index).isEqualTo(4);

        // Got the whole config, without interpreting it as LTV
        assertThat(level.codecConfigLength).isEqualTo(3);
    }

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

        byte[] serviceData =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01, // numSubGroups
                    // LEVEL 2
                    (byte) 0x01, // numBIS
                    (byte) 0xFF, // VENDOR_CODEC
                    (byte) 0x0A,
                    (byte) 0xAB,
                    (byte) 0xBC,
                    (byte) 0xCD,
                    (byte) 0x04, // codecConfigLength
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03,
                    (byte) 0x04, // opaque vendor data
                    (byte) 0x03, // metaDataLength
                    (byte) 0x06,
                    (byte) 0x07,
                    (byte) 0x08, // metaData
                    // LEVEL 3
                    (byte) 0x04, // index
                    (byte) 0x03, // codecConfigLength
                    (byte) 0x03,
                    (byte) 0x02,
                    (byte) 0x01 // opaque vendor data
                };

        BaseData data = BaseData.parseBaseData(serviceData);
        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[] {
                            (byte) 0xFF, (byte) 0x0A, (byte) 0xAB, (byte) 0xBC, (byte) 0xCD
                        });
        assertThat(level.codecConfigLength).isEqualTo(4);
        assertThat(level.metaDataLength).isEqualTo(3);

        assertThat(data.getLevelThree().size()).isEqualTo(1);
        level = data.getLevelThree().get(0);
        assertThat(level.index).isEqualTo(4);
        assertThat(level.codecConfigLength).isEqualTo(3);
    }

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

        byte[] serviceData =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01, // numSubGroups
                    // LEVEL 2
                    (byte) 0x01, // numBIS
                    (byte) 0xFF, // VENDOR_CODEC
                    (byte) 0x0A,
                    (byte) 0xAB,
                    (byte) 0xBC,
                    (byte) 0xCD,
                    (byte) 0x00, // codecConfigLength
                    (byte) 0x00, // metaDataLength
                    // LEVEL 3
                    (byte) 0x04, // index
                    (byte) 0x00, // codecConfigLength
                };

        BaseData data = BaseData.parseBaseData(serviceData);
        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[] {
                            (byte) 0xFF, (byte) 0x0A, (byte) 0xAB, (byte) 0xBC, (byte) 0xCD
                        });
        assertThat(level.codecConfigLength).isEqualTo(0);
        assertThat(level.metaDataLength).isEqualTo(0);

        assertThat(data.getLevelThree().size()).isEqualTo(1);
        level = data.getLevelThree().get(0);
        assertThat(level.index).isEqualTo(4);
        assertThat(level.codecConfigLength).isEqualTo(0);
    }

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

        byte[] serviceData =
                new byte[] {
                    // LEVEL 1
                    (byte) 0x01,
                    (byte) 0x02,
                    (byte) 0x03, // presentationDelay
                    (byte) 0x01, // numSubGroups
                    // LEVEL 2
                    (byte) 0x00, // numBIS invalid value
                    (byte) 0xFE, // UNKNOWN CODEC
                    (byte) 0x00, // codecConfigLength
                    (byte) 0x00, // metaDataLength
                };

        BaseData data = BaseData.parseBaseData(serviceData);
        assertThat(data).isEqualTo(null);
    }

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