Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting the advertisting_data field of the Advertisement struct generates a parse error in BlueZ #49

Closed
potto216 opened this issue Sep 25, 2022 · 14 comments · Fixed by #55
Labels
bug Something isn't working

Comments

@potto216
Copy link
Contributor

I cannot set the advertisting_data field of the Advertisement struct to a valid value.
Every value I have tried for advertisting_data: BTreeMap<u8, Vec<u8>> results in a BlueR error of

Advertising on Bluetooth adapter hci0 with address 00:E0:42:AB:3D:03
Advertisement { advertisement_type: Peripheral, service_uuids: {}, manufacturer_data: {}, solicit_uuids: {}, service_data: {}, advertisting_data: {1: [6]}, discoverable: Some(true), discoverable_timeout: None, system_includes: {}, local_name: Some("le_advertise"), appearance: None, duration: None, timeout: None, secondary_channel: None, min_interval: None, max_interval: None, tx_power: None, _non_exhaustive: () }
Error: Error { kind: Failed, message: "Failed to parse advertisement." }

which results from the BlueZ bluetoothd which has the following debug information indicating an error parsing the Data property

Sep 25 12:37:17 debian rsyslogd: [origin software="rsyslogd" swVersion="8.2102.0" x-pid="551" x-info="https://www.rsyslog.com"] rsyslogd was HUPed
Sep 25 12:38:54 debian bluetoothd[541]: src/advertising.c:register_advertisement() RegisterAdvertisement
Sep 25 12:38:54 debian bluetoothd[541]: src/advertising.c:client_create() Adding proxy for /org/bluez/bluer/advertising/2fdf9eaa36a544799667bac89cbaec17
Sep 25 12:38:54 debian bluetoothd[541]: src/advertising.c:register_advertisement() Registered advertisement at path /org/bluez/bluer/advertising/2fdf9eaa36a544799667bac89cbaec17
Sep 25 12:38:54 debian bluetoothd[541]: src/advertising.c:parse_advertisement() Error parsing Data property

I have looked in BlueZ's advertising.c, but it is unclear why it isn't happy with what BlueR is sending to it over D-Bus.

I have followed Silicon Lab's recommendation for sending advertising data.

For the advertising data type (the u8) I am following Bluetooth Document Generic Access Profile Revision Date: 2021-07-13 Assigned numbers and GAP.

I am assuming the advertising data is the data being placed in the AdvData field of ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND, AUX_ADV_IND, and AUX_CHAIN_IND PDUs as defined in
Bluetooth Core Specification 5.3:Vol. 3, Part C section 11

For the below examples test_data is defined as let mut test_data = BTreeMap::<u8, Vec<u8>>::new(); is being passed to the advertisement:

    let le_advertisement = Advertisement {
        advertisement_type: bluer::adv::Type::Peripheral,
        advertisting_data: test_data,
        discoverable: Some(true),
        local_name: Some("le_advertise".to_string()),
        ..Default::default()
    };

The following all generate the same parse error.
I set flag data type, 0x01, and I send data 0x06 test_data.insert(0x01, vec![0x06]);

I set the Complete List of 16-bit Service Class UUIDs data type, 0x03, with test_data.insert(0x01, vec![0x09, 0x18]); which is Health Thermometer service 0x1809

I set the Complete Local Name data type, 0x09, with test_data.insert(0x09, vec![0x54, 0x68, 0x65, 0x72, 0x6D, 0x6F, 0x6d, 0x65, 074, 0x65, 0x72, 0x20, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65]); which is "Thermometer Example"

Any thoughts on what the problem is?

@ln-12
Copy link

ln-12 commented Sep 28, 2022

I think that I am having a similar issue. I want to insert more than 6 bytes as manufacturer data which fails.

let mut manufacturer_data = BTreeMap::new();
manufacturer_data.insert(0xffff, vec![0; 12]);  // <--- here, more than 6 is not possible

let le_advertisement = Advertisement {
    service_uuids: vec![config::SERVICE_UUID].into_iter().collect(),
    manufacturer_data,
    discoverable: Some(true),
    local_name: Some("Test".to_string()),
    ..Default::default()
};
let adv_handle = adapter.advertise(le_advertisement).await.unwrap();

The error is the following:
thread ‘tokio-runtime-worker’ panicked at ‘called Result::unwrap() on an Err value: Error { kind: Failed, message: “Failed to register advertisement” }’, src/[bluetooth.rs:115](http://bluetooth.rs:115/):64 note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

@potto216
Copy link
Contributor Author

Interesting. I wonder if the failure to register advertisement error for manufacturer_data is related to the error I am seeing. I found that any advertising data with the Manufacturer Specific Data type (0xFF) I use results in the same parse error I showed initally.

For example:

advertisting_data.insert(0xff, vec![0; 6]);

    let le_advertisement = Advertisement {
        advertisting_data,
        ..Default::default()
    };

results in

Advertising on Bluetooth adapter hci0 with address 00:E0:42:AB:3D:03
Advertisement { advertisement_type: Peripheral, service_uuids: {}, manufacturer_data: {}, solicit_uuids: {}, service_data: {}, advertisting_data: {255: [0, 0, 0, 0, 0, 0]}, discoverable: None, discoverable_timeout: None, system_includes: {}, local_name: None, appearance: None, duration: None, timeout: None, secondary_channel: None, min_interval: None, max_interval: None, tx_power: None, _non_exhaustive: () }
Error: Error { kind: Failed, message: "Failed to parse advertisement." }

@surban
Copy link
Collaborator

surban commented Oct 1, 2022

I've never tried myself to get advertising data to work. For my purposes manufacturer data and service data were enough. It makes sense though that the size of these fields is limited, since all data must be packed into one BLE packet which is constrained in size. If you need to transmit more data using advertisements the only way is to periodically change your advertisement and break up your message into chunks.

For finding out the exact rules what can be put into the advertising data field I recommend stepping through bluetoothd with gdb and see where it fails. Or ask in the BlueZ slack ;)

@potto216
Copy link
Contributor Author

potto216 commented Oct 2, 2022

Although I compiled BlueZ I have not debugged kernel level modules before, so I'll come up to speed on that.

In the meantime I did more debugging by capturing dbus messages and looking at them in Wireshark to compare what messages are sent by BlueR to BlueZ when the advertisements work and do not work. One difference I saw is the array length for the advertisement data property (Data).

Advertising works when advertisting_data is not specified.
BlueR produces a D-Bus message entry for Data of

Dict Entry, Data
    String: Data
    Variant
        Variant Signature: a{yay}
        Array (Dict)
            Array Length: 0

BlueZ has a "Failed to parse advertisement." when advertisting_data.insert(0xff, vec![0; 0]);
And BlueR produces a D-Bus message entry for Data of

Dict Entry, Data
    String: Data
    Variant
        Variant Signature: a{yay}
        Array (Dict)
            Array Length: 8
            Dict Entry, 255
                Byte: 255 (0xff)
                Array
                    Array Length: 0

BlueZ has a "Failed to parse advertisement." when advertisting_data.insert(0xff, vec![0; 1]);
And BlueR produces a D-Bus message entry for Data of

Dict Entry, Data
    String: Data
    Variant
        Variant Signature: a{yay}
        Array (Dict)
            Array Length: 9
            Dict Entry, 255
                Byte: 255 (0xff)
                Array
                    Array Length: 1
                    Byte: 0 (0x00)

@surban Does the array length of 8 and 9 in the above example look correct to you?

@surban
Copy link
Collaborator

surban commented Oct 2, 2022

I can't really tell if this is correct or not, since I am just a user of the D-Bus library and have no knowledge about the underlying protocol. However, I don't see why wrong D-Bus messages should be generated for advertising data but not for manufacturer data.

You don't need to debug the kernel. The parse error is most likely coming from bluetoothd which is a regular user-space process, which can be debugged using gdb.

@potto216
Copy link
Contributor Author

potto216 commented Oct 2, 2022

Oh cool I didn't realize bluetoothd is a user level process. I'll debug that and see what I find.

@potto216
Copy link
Contributor Author

potto216 commented Oct 7, 2022

I believe I found root cause and it is that BlueR is not sending the advertising data with the dbus wrapper expected by bluetoothd.

I demonstrated this by looking in bluetoothd's advertising.c

on line 654 it expects a DBUS_TYPE_VARIANT "v", but receives an array type "a" from BlueR. That caused the fail I was seeing. I found that if I
comment out that check on line 654 and also line 657 and add the statement
value = entry; that it successfully called bt_ad_add_data on line 671 and ran.

I demonstrated this by creating an advertisement in BlueR with the line
advertisting_data.insert(0x0B, vec![0x09, 0x18]);
and below I was able to see the advertisement with an over the air collect.
advertise_data

I found you are not able to send data types that are registered in Bluetooth's
Generic Access Profile. This is is because in

bt_ad_add_data which is at line 1005 in ad.c
There is a reject list on line 1015 which is defined on line 962 and those types are defined in ad.h

This is why I used 0x0B.

@surban would the next step be for me to modify BlueR's dbus wrapper to match what bluetoothd is expecting and file a PR?

@surban
Copy link
Collaborator

surban commented Oct 7, 2022

Yes. You could also extend the documentation with your findings about forbidden data types.

@potto216
Copy link
Contributor Author

potto216 commented Oct 7, 2022 via email

@potto216
Copy link
Contributor Author

potto216 commented Oct 15, 2022

So what is working for me is modifying adv.rs and changing

cr_property!(ib, "Data", la => {
                Some(la.advertisting_data.clone().into_iter().collect::<HashMap<_, _>>())                
            });

to

        cr_property!(ib, "Data", la => {
            Some(la.advertisting_data.iter().map(|(k, v)| (k.clone(), Variant(v.clone()))).collect::<HashMap<_, _>>())
        });

This is satisfying D-Bus's variant requirement and the advertisements include the type and data in advertisting_data. I'll put together a PR with documentation updates for review.

@potto216
Copy link
Contributor Author

This is the file I used to test (renaming .txt to .rs)
le_advertise_data_test.txt

surban pushed a commit that referenced this issue Oct 21, 2022
@ln-12
Copy link

ln-12 commented Oct 27, 2022

Hey @potto216, I tested your suggested fix (which did not work for me) and noticed that the manufacturer data already uses that access pattern (see here). Do you by chance have an idea how I could solve my issue?

@potto216
Copy link
Contributor Author

@ln-12 this is an interesting problem. The issue appears to be that the maximum payload size of the advertising packet (37 bytes) was reached. When I turned on debugging in bluetoothd I got the error message:
Oct 27 19:49:33 debian bluetoothd[33670]: src/advertising.c:refresh_legacy_adv() Advertising data too long or couldn't be generated.
Also an over the air capture showed the maximum payload size with 6 bytes of data
manufacturer_data.insert(0xffff, vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05);
I found you can send another byte of manufacturer data by turning off discoverability so the discoverable flag byte is not sent.

   manufacturer_data.insert(0xffff, vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);  
   
   let le_advertisement = Advertisement {
       service_uuids: vec![SERVICE_UUID].into_iter().collect(),
       manufacturer_data,
       discoverable: Some(false),
       local_name: Some("Test".to_string()),
       ..Default::default()
   };

@surban do you think it is possible to have BlueR return a more descriptive error message?

@surban
Copy link
Collaborator

surban commented Nov 30, 2022

@surban do you think it is possible to have BlueR return a more descriptive error message?

Generally Error::message contains the error message text received from bluetoothd over D-Bus. If this is empty, we don't have more error information.

otaviojr added a commit to otaviojr/bluer that referenced this issue May 10, 2023
# This is the 1st commit message:

BLE Passive Scanning

# This is the commit message #2:

monitor

# This is the commit message bluez#3:

monitor

# This is the commit message bluez#4:

monitor

# This is the commit message bluez#5:

monitor

# This is the commit message bluez#6:

monitor

# This is the commit message bluez#7:

monitor

# This is the commit message bluez#8:

monitor

# This is the commit message bluez#9:

monitor

# This is the commit message bluez#10:

monitor

# This is the commit message bluez#11:

monitor

# This is the commit message bluez#12:

monitor

# This is the commit message bluez#13:

monitor

# This is the commit message bluez#14:

monitor

# This is the commit message bluez#15:

monitor

# This is the commit message bluez#16:

monitor

# This is the commit message bluez#17:

monitor

# This is the commit message bluez#18:

monitor

# This is the commit message bluez#19:

monitor

# This is the commit message bluez#20:

monitor

# This is the commit message bluez#21:

monitor

# This is the commit message bluez#22:

monitor

# This is the commit message bluez#23:

monitor

# This is the commit message bluez#24:

monitor

# This is the commit message bluez#25:

monitor

# This is the commit message bluez#26:

monitor

# This is the commit message bluez#27:

monitor

# This is the commit message bluez#28:

monitor

# This is the commit message bluez#29:

monitor

# This is the commit message bluez#30:

monitor

# This is the commit message bluez#31:

monitor

# This is the commit message bluez#32:

monitor

# This is the commit message bluez#33:

monitor

# This is the commit message bluez#34:

monitor

# This is the commit message bluez#35:

monitor

# This is the commit message bluez#36:

monitor

# This is the commit message bluez#37:

monitor

# This is the commit message bluez#38:

monitor

# This is the commit message bluez#39:

monitor

# This is the commit message bluez#40:

monitor

# This is the commit message bluez#41:

monitor

# This is the commit message bluez#42:

monitor

# This is the commit message bluez#43:

monitor

# This is the commit message bluez#44:

monitor

# This is the commit message bluez#45:

monitor

# This is the commit message bluez#46:

monitor

# This is the commit message bluez#47:

monitor

# This is the commit message bluez#48:

monitor

# This is the commit message bluez#49:

monitor

# This is the commit message bluez#50:

monitor

# This is the commit message bluez#51:

monitor

# This is the commit message bluez#52:

monitor

# This is the commit message bluez#53:

monitor

# This is the commit message bluez#54:

monitor

# This is the commit message bluez#55:

monitor

# This is the commit message bluez#56:

monitor

# This is the commit message bluez#57:

monitor

# This is the commit message bluez#58:

monitor

# This is the commit message bluez#59:

monitor

# This is the commit message bluez#60:

monitor

# This is the commit message bluez#61:

monitor

# This is the commit message bluez#62:

monitor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants