Ticket Created
over 4 years ago

WERETECH-9123

Connect-IQ Ble advertising layer parsing bug, prevent many devices to function properly. (paired or not paired) Toybox::BluetoothLowEnergy::ScanResult:getRawData(), getServiceData , getServiceUuids are broken

Connect-IQ Ble advertising layer parsing bug, prevent many devices to function properly.
Reproduced on simulator, fenix 6x (9.0), fenix 6s.

Used bluetooth sniffer to confirm that the advertising bytes were correct, and different than what connect IQ returns.

When not paired :

Toybox::BluetoothLowEnergy::ScanResult:getRawData(), getServiceData , getServiceUuids
 are not receiving the proper data bytes from the network layer when **to be confirmed !!**the last advertising data type are :
 Complete List of 16-bit Service Class UUIDs

which is very common !
if the order is changed, and something else is set at the end, there is no problem at all.

In order to demonstrate this, I'm parsing the content of getRaw, using 2 stryd devices (ble indeed), one hardware v4 (last advertising data, is Shortened local name), which is working perfectly.

The parser, read all data, and decode them, now problem on this trace, it is complete, and perfectly good and valid.

devicename:Stryd kl
raw Adverting Data:[2, 1, 6, 7, 3, 24, 24, 20, 24, 10, 24, 9, 255, 170, 170, 210, 91, 0, 81, 82, 41, 9, 8, 83, 116, 114, 121, 100, 32, 107, 108]
--[AD Dump:
----[Record Dump:
    AD Len:2
    AD Type:Flags
    AD Type Integer:1
    AD Data Integer:[6]
    AD Data Hex:06
----[Record Dump:
    AD Len:7
    AD Type:Complete List of 16-bit Service Class UUIDs
    AD Type Integer:3
    AD Data Integer:[24, 24, 20, 24, 10, 24]
    AD Data Hex:181814180a18
    AD Data 16bits Service Class UUIDs:
        UUID: 0x1818
        UUID: 0x1418
        UUID: 0x0A18
----[Record Dump:
    AD Len:9
    AD Type:Manufacturer Specific Data
    AD Type Integer:255
    AD Data Integer:[170, 170, 210, 91, 0, 81, 82, 41]
    AD Data Hex:aaaad25b00515229
    AD Data String:ªªÒ[QR)
----[Record Dump:
    AD Len:9
    AD Type:Shortened Local Name
    AD Type Integer:8
    AD Data Integer:[83, 116, 114, 121, 100, 32, 107, 108]
    AD Data Hex:5374727964206b6c
    AD Data String:Stryd kl
--end of AD Dump]


Now, another generation of stryd (v3), where by lack of lack, advertising data last bytes are ...Complete List of 16-bit Service Class UUIDs

In this case, you can see that the raw data, is not correct, and stop before those advertising data.

If it was only the raw data, it won't be a problem, but getServiceUuids looks to use those bytes... so return none of those services.

devicename:Stryd
raw Adverting Data:[2, 1, 6, 9, 255, 170, 170, 115, 84, 0, 80, 82, 166, 6, 9, 83, 116, 114, 121, 100]
--[AD Dump:
----[Record Dump:
    AD Len:2
    AD Type:Flags
    AD Type Integer:1
    AD Data Integer:[6]
    AD Data Hex:06
----[Record Dump:
    AD Len:9
    AD Type:Manufacturer Specific Data
    AD Type Integer:255
    AD Data Integer:[170, 170, 115, 84, 0, 80, 82, 166]
    AD Data Hex:aaaa7354005052a6
    AD Data String:ªªsTPR¦
----[Record Dump:
    AD Len:6
    AD Type:Complete Local Name
    AD Type Integer:9
    AD Data Integer:[83, 116, 114, 121, 100]
    AD Data Hex:5374727964
    AD Data String:Stryd
--end of AD Dump]


When paired :

Class: Toybox::BluetoothLowEnergy::Device getService() and getServices()

are also affected !

This was confirmed with :
- nrf connect on android
- nrf connect desktop, with the nrf52 dongle I'm using for the simulator (confirmed into the simulator indeed)

Now, if it was only stryd, it would be okay, but my elliptical bike(FTMS profile), also adverstise services at the end... so they are not visible to the watch.

It is the same for many devices I checked, all that was availlable to me.

Is reporting to this forum enough to get it tracked by garmin ? anything else to do ?

Kind Regards.

Klick

  • On one device where I don't see a UUID, the number of bytes changes from 21-27-30.  I'm wondering if some things may be hidden due to GDPR or something, and there's a bug there.

    The screen shot is for a thingy52 (named "thing17") where the name and UUID are both available.

  • great, I have to learn a bit more on the UI side I admit Slight smile

    Maybe you can still look into the code, actually, it's clearer than raw bytes for users , so you can see what's in there, I'll likely write decoders for the flags, etc , a bit later.

    Could you also check if you have an android phone with nrf connect, if you see the same advertising bytes on your devices ? because I was surprised to see so much truncated.

    Regards.

    Xavier.

  • I ended up adding my own display before I saw this and the store has the updated version of my app.  Pressing select switches between the summary (what was there before) and a dump of the raw data.

  •  currentByte=currentByte+currentSize+1;
            
            }        
            System.println("--end of AD Dump]");

    }

    was missing at the end :)

  • Hi Jim,

    thanks for the reply.

    The point here, is that for those devices, the uuid will never be shown, at any stages. Those devices are forever not useable until garmin fix the advertising bytes parsing, which I believe occurs before the profile ;)

    If you want to do raw parsing, feel free to use this, clunky code, but did on the corner of a table, just my first attempt at monkey c Slight smile

    It takes rawdata from the scanresults as argument :

      function rawdataDecoder(rawBytes){
        
            var currentByte=0;
            var currentSize=0;
            var currentCode=0;
            var currentDataString;
            var currentDataHex;
            var currentDataRaw;
            
            
            var keyConvertOptionsHex = {
                        :fromRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY,
                        :toRepresentation => StringUtil.REPRESENTATION_STRING_HEX
                        
                    };
                    
                    
            System.println("raw Adverting Data:"+rawBytes);
            System.println("--[AD Dump:");
            while(currentByte<rawBytes.size()){
            
                currentSize=rawBytes[currentByte];
                currentCode=rawBytes[currentByte+1];
                
                
                System.println("----[Record Dump:");
                System.println("\tAD Len:"+currentSize);
                System.println("\tAD Type:"+EIRCodeToString[currentCode]);
                System.println("\tAD Type Integer:"+currentCode);
                
                
                if(currentSize>1){
                    currentDataRaw=rawBytes.slice(currentByte+2,currentByte+1+currentSize);
                    currentDataHex        = StringUtil.convertEncodedString(currentDataRaw, keyConvertOptionsHex);
                    System.println("\tAD Data Integer:"+currentDataRaw);
                    System.println("\tAD Data Hex:"+currentDataHex);
                    switch(currentCode){
                        case 1:
                            break;
                        case 3:
                            System.println("\tAD Data 16bits Service Class UUIDs:");
                            for( var i = 0; i < currentDataRaw.size(); i=i+2 ) {
                            //var uuid=StringUtil.convertEncodedString(currentDataRaw.slice(i+1,i+1), keyConvertOptionsHex);
                            //uuid=StringUtil.convertEncodedString(currentDataRaw.slice(i,i), keyConvertOptionsHex);
                             var uuid=Lang.format("0x$2$$1$",[currentDataRaw[i+1].format("%02X"), currentDataRaw[i].format("%02X")]);
                             
                             System.println("\t\tUUID: "+uuid);
                             }
                            break;
                        default:
                            
                            currentDataString="";
                            var sz=currentDataRaw.size();
                            for(var i=0;i<sz;i++) {currentDataString=currentDataString+currentDataRaw[i].toChar();}
                            
                            System.println("\tAD Data String:"+currentDataString);    
                            break;
                    }                
                            
                                
                }

    this is also what I'm using to resolve types , it's not complete, but the most common ones are here (extract from bluetooth sig)

     var EIRCodeToString =  [ "0", "Flags" ,"Incomplete List of 16-bit Service Class UUID" ,"Complete List of 16-bit Service Class UUIDs" ,"Incomplete List of 32-bit Service Class UUIDs" ,"Complete List of 32-bit Service Class UUIDs" ,"Incomplete List of 128-bit Service Class UUIDs" ,"Complete List of 128-bit Service Class UUIDs" ,"Shortened Local Name" ,"Complete Local Name" ,"Tx Power Level", "0B", "0C", "Class of Device", "Simple Pairing Hash C", "Simple Pairing Randomizer R",
        "Device ID","Security Manager Out of Band Flags","Slave Connection Interval Range","13","List of 16-bit Service Solicitation UUIDs","List of 128-bit Service Solicitation UUIDs","Service Data","Public Target Address","Random Target Address","Appearance","Advertising Interval","LE Bluetooth Device Address","LE Role","Simple Pairing Hash C-256","Simple Pairing Randomizer R-256","List of 32-bit Service Solicitation UUID",
        "Service Data - 32-bit UUID","Service Data - 128-bit UUID","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","2F",
        "30","31","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","3F",
        "40","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","4F",
        "50","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","5F",
        "60","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","6F",
        "70","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","7F",
        "80","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","8F",
        "90","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","9F",
        "A0","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","AF",
        "B0","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","BF",
        "C0","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","CF",
        "D0","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","DF",
        "E0","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","EF",
        "F0","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","Manufacturer Specific Data"
        ];

    Kind Regards.

    Klick