From: Luiz Augusto von Dentz <[email protected]>
This changes adds command to manage advertisement data to bluetootctl.
Note: This also fix the format of ManufacturerData and ServiceData that
has been documented as being dictionaries but the implemention and sample
has it more like a struct using fixed types instead of variant container
so this would probably break any application using these properties.
Luiz Augusto von Dentz (7):
core/advertising: Fix not parsing data properly
test/example-advertisement: Fix data format of properties
client: Add advertise command
client: Add set-advertise-uuids command
client: Add set-advertise-service command
client: Add set-advertise-manufacturer command
client: Add set-advertise-tx-power command
Makefile.tools | 2 +
client/advertising.c | 452 +++++++++++++++++++++++++++++++++++++++++++++
client/main.c | 139 ++++++++++++++
src/advertising.c | 24 ++-
test/example-advertisement | 12 +-
5 files changed, 613 insertions(+), 16 deletions(-)
create mode 100644 client/advertising.c
--
2.7.4
Hi,
On Thu, Aug 11, 2016 at 3:14 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This changes adds command to manage advertisement data to bluetootctl.
>
> Note: This also fix the format of ManufacturerData and ServiceData that
> has been documented as being dictionaries but the implemention and sample
> has it more like a struct using fixed types instead of variant container
> so this would probably break any application using these properties.
>
> Luiz Augusto von Dentz (7):
> core/advertising: Fix not parsing data properly
> test/example-advertisement: Fix data format of properties
> client: Add advertise command
> client: Add set-advertise-uuids command
> client: Add set-advertise-service command
> client: Add set-advertise-manufacturer command
> client: Add set-advertise-tx-power command
>
> Makefile.tools | 2 +
> client/advertising.c | 452 +++++++++++++++++++++++++++++++++++++++++++++
> client/main.c | 139 ++++++++++++++
> src/advertising.c | 24 ++-
> test/example-advertisement | 12 +-
> 5 files changed, 613 insertions(+), 16 deletions(-)
> create mode 100644 client/advertising.c
>
> --
> 2.7.4
Applied.
--
Luiz Augusto von Dentz
Hi,
Luiz Augusto von Dentz <[email protected]> writes:
> From: Luiz Augusto von Dentz <[email protected]>
>
> D-Bus dictionaries always except the values to be of variant type.
nitpick: expect
> ---
> test/example-advertisement | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/test/example-advertisement b/test/example-advertisement
> index ce6e40f..7da0c7b 100755
> --- a/test/example-advertisement
> +++ b/test/example-advertisement
> @@ -65,10 +65,10 @@ class Advertisement(dbus.service.Object):
> signature='s')
> if self.manufacturer_data is not None:
> properties['ManufacturerData'] = dbus.Dictionary(
> - self.manufacturer_data, signature='qay')
> + self.manufacturer_data, signature='qv')
> if self.service_data is not None:
> properties['ServiceData'] = dbus.Dictionary(self.service_data,
> - signature='say')
> + signature='sv')
> if self.include_tx_power is not None:
> properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
> return {LE_ADVERTISEMENT_IFACE: properties}
> @@ -88,13 +88,13 @@ class Advertisement(dbus.service.Object):
>
> def add_manufacturer_data(self, manuf_code, data):
> if not self.manufacturer_data:
> - self.manufacturer_data = dict()
> - self.manufacturer_data[manuf_code] = data
> + self.manufacturer_data = dbus.Dictionary({}, signature='qv')
> + self.manufacturer_data[manuf_code] = dbus.Array(data, signature='y')
>
> def add_service_data(self, uuid, data):
> if not self.service_data:
> - self.service_data = dict()
> - self.service_data[uuid] = data
> + self.service_data = dbus.Dictionary({}, signature='sv')
> + self.service_data[uuid] = dbus.Array(data, signature='y')
>
> @dbus.service.method(DBUS_PROP_IFACE,
> in_signature='s',
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Cheers,
--
Vinicius
From: Luiz Augusto von Dentz <[email protected]>
This adds set-advertise-tx-power which can be used include TX power
in the advertisement data:
set-advertise-manufacturer <on/off>
[bluetooth]# set-advertise-tx-power on
[bluetooth]# advertise on
@ Advertising Added: 1
< HCI Command: LE Set Aidvertising Data (0x08|0x0008) plen 32
Length: 6
Flags: 0x02
LE General Discoverable Mode
TX power: 9 dBm
---
client/advertising.c | 21 +++++++++++++++++++++
client/main.c | 25 ++++++++++++++++++++++++-
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/client/advertising.c b/client/advertising.c
index 5176bc5..62201d5 100644
--- a/client/advertising.c
+++ b/client/advertising.c
@@ -48,6 +48,7 @@ static uint8_t ad_service_data_len = 0;
static uint16_t ad_manufacturer_id;
static uint8_t ad_manufacturer_data[25];
static uint8_t ad_manufacturer_data_len = 0;
+static gboolean ad_tx_power = FALSE;
static void ad_release(DBusConnection *conn)
{
@@ -238,12 +239,26 @@ static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean tx_power_exists(const GDBusPropertyTable *property, void *data)
+{
+ return ad_tx_power;
+}
+
+static gboolean get_tx_power(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &ad_tx_power);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable ad_props[] = {
{ "Type", "s", get_type },
{ "ServiceUUIDs", "as", get_uuids, NULL, uuids_exists },
{ "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
{ "ManufacturerData", "a{qv}", get_manufacturer_data, NULL,
manufacturer_data_exists },
+ { "IncludeTxPower", "b", get_tx_power, NULL, tx_power_exists },
{ }
};
@@ -429,3 +444,9 @@ void ad_advertise_manufacturer(const char *arg)
done:
wordfree(&w);
}
+
+
+void ad_advertise_tx_power(gboolean value)
+{
+ ad_tx_power = value;
+}
diff --git a/client/main.c b/client/main.c
index 41a0af3..1ceddb0 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1885,7 +1885,27 @@ static void cmd_set_advertise_service(const char *arg)
static void cmd_set_advertise_manufacturer(const char *arg)
{
- ad_advertise_service(arg);
+ ad_advertise_manufacturer(arg);
+}
+
+static void cmd_set_advertise_tx_power(const char *arg)
+{
+ if (arg == NULL || strlen(arg) == 0) {
+ rl_printf("Missing on/off argument\n");
+ return;
+ }
+
+ if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+ ad_advertise_tx_power(TRUE);
+ return;
+ }
+
+ if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+ ad_advertise_tx_power(FALSE);
+ return;
+ }
+
+ rl_printf("Invalid argument\n");
}
static const struct {
@@ -1927,6 +1947,9 @@ static const struct {
{ "set-advertise-manufacturer", "[id][data=[xx xx ...]",
cmd_set_advertise_manufacturer,
"Set advertise manufacturer data" },
+ { "set-advertise-tx-power", "<on/off>",
+ cmd_set_advertise_tx_power,
+ "Enable/disable TX power to be advertised" },
{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
cmd_set_scan_filter_uuids, "Set scan filter uuids" },
{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
--
2.7.4
From: Luiz Augusto von Dentz <[email protected]>
This adds set-advertise-manufacturer which can be used set manfacturer
spefic data to be advertised:
set-advertise-manufacturer [id][data=[xx xx ...]
[bluetooth]# set-advertise-manufacturer 0xffff 0x00 0x01 0x02 0x03
[bluetooth]# advertise on
@ Advertising Added: 1
< HCI Command: LE Set Advertising Data (0x08|0x0008) plen 32
Length: 11
Flags: 0x02
LE General Discoverable Mode
Company: internal use (65535)
Data: 00010203
---
client/advertising.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++
client/main.c | 8 ++++++
2 files changed, 89 insertions(+)
diff --git a/client/advertising.c b/client/advertising.c
index 3e4e10c..5176bc5 100644
--- a/client/advertising.c
+++ b/client/advertising.c
@@ -45,6 +45,9 @@ static size_t ad_uuids_len = 0;
static char *ad_service_uuid = NULL;
static uint8_t ad_service_data[25];
static uint8_t ad_service_data_len = 0;
+static uint16_t ad_manufacturer_id;
+static uint8_t ad_manufacturer_data[25];
+static uint8_t ad_manufacturer_data_len = 0;
static void ad_release(DBusConnection *conn)
{
@@ -212,10 +215,35 @@ static gboolean get_service_data(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean manufacturer_data_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ return ad_manufacturer_id != 0;
+}
+
+static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const uint8_t *data = ad_manufacturer_data;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict);
+
+ dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad_manufacturer_id,
+ DBUS_TYPE_BYTE, &data,
+ ad_manufacturer_data_len);
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable ad_props[] = {
{ "Type", "s", get_type },
{ "ServiceUUIDs", "as", get_uuids, NULL, uuids_exists },
{ "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
+ { "ManufacturerData", "a{qv}", get_manufacturer_data, NULL,
+ manufacturer_data_exists },
{ }
};
@@ -348,3 +376,56 @@ void ad_advertise_service(const char *arg)
done:
wordfree(&w);
}
+
+static void ad_clear_manufacturer(void)
+{
+ ad_manufacturer_id = 0;
+ memset(ad_manufacturer_data, 0, sizeof(ad_manufacturer_data));
+ ad_manufacturer_data_len = 0;
+}
+
+void ad_advertise_manufacturer(const char *arg)
+{
+ wordexp_t w;
+ unsigned int i;
+ char *endptr = NULL;
+ long int val;
+
+ if (wordexp(arg, &w, WRDE_NOCMD)) {
+ rl_printf("Invalid argument\n");
+ return;
+ }
+
+ ad_clear_manufacturer();
+
+ if (w.we_wordc == 0)
+ goto done;
+
+ val = strtol(w.we_wordv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT16_MAX) {
+ rl_printf("Invalid manufacture id\n");
+ goto done;
+ }
+
+ ad_manufacturer_id = val;
+
+ for (i = 1; i < w.we_wordc; i++) {
+ if (i >= G_N_ELEMENTS(ad_service_data)) {
+ rl_printf("Too much data\n");
+ goto done;
+ }
+
+ val = strtol(w.we_wordv[i], &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+ rl_printf("Invalid value at index %d\n", i);
+ ad_clear_service();
+ goto done;
+ }
+
+ ad_manufacturer_data[ad_manufacturer_data_len] = val;
+ ad_manufacturer_data_len++;
+ }
+
+done:
+ wordfree(&w);
+}
diff --git a/client/main.c b/client/main.c
index 72c2cd9..41a0af3 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1883,6 +1883,11 @@ static void cmd_set_advertise_service(const char *arg)
ad_advertise_service(arg);
}
+static void cmd_set_advertise_manufacturer(const char *arg)
+{
+ ad_advertise_service(arg);
+}
+
static const struct {
const char *cmd;
const char *arg;
@@ -1919,6 +1924,9 @@ static const struct {
{ "set-advertise-service", "[uuid][data=[xx xx ...]",
cmd_set_advertise_service,
"Set advertise service data" },
+ { "set-advertise-manufacturer", "[id][data=[xx xx ...]",
+ cmd_set_advertise_manufacturer,
+ "Set advertise manufacturer data" },
{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
cmd_set_scan_filter_uuids, "Set scan filter uuids" },
{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
--
2.7.4
From: Luiz Augusto von Dentz <[email protected]>
This adds set-advertise-service which can be used set UUID data to be
advertised:
set-advertise-service [uuid][data=[xx xx ...]
[bluetooth]# set-advertise-service 180D 0xff 0xff
[bluetooth]# advertise on
@ Advertising Added: 1
< HCI Command: LE Set Advertising Data (0x08|0x0008) plen 32
Length: 9
Flags: 0x02
LE General Discoverable Mode
Service Data (UUID 0x180d): ffff
---
client/advertising.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++
client/main.c | 8 ++++
2 files changed, 136 insertions(+)
diff --git a/client/advertising.c b/client/advertising.c
index e74221f..3e4e10c 100644
--- a/client/advertising.c
+++ b/client/advertising.c
@@ -42,6 +42,9 @@ static gboolean registered = FALSE;
static char *ad_type = NULL;
static char **ad_uuids = NULL;
static size_t ad_uuids_len = 0;
+static char *ad_service_uuid = NULL;
+static uint8_t ad_service_data[25];
+static uint8_t ad_service_data_len = 0;
static void ad_release(DBusConnection *conn)
{
@@ -134,9 +137,85 @@ static gboolean get_uuids(const GDBusPropertyTable *property,
return TRUE;
}
+static void append_array_variant(DBusMessageIter *iter, int type, void *val,
+ int n_elements)
+{
+ DBusMessageIter variant, array;
+ char type_sig[2] = { type, '\0' };
+ char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE) {
+ dbus_message_iter_append_fixed_array(&array, type, val,
+ n_elements);
+ } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ const char ***str_array = val;
+ int i;
+
+ for (i = 0; i < n_elements; i++)
+ dbus_message_iter_append_basic(&array, type,
+ &((*str_array)[i]));
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+
+ dbus_message_iter_close_container(iter, &variant);
+}
+
+static void dict_append_basic_array(DBusMessageIter *dict, int key_type,
+ const void *key, int type, void *val,
+ int n_elements)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, key_type, key);
+
+ append_array_variant(&entry, type, val, n_elements);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+ void *val, int n_elements)
+{
+ dict_append_basic_array(dict, DBUS_TYPE_STRING, &key, type, val,
+ n_elements);
+}
+
+static gboolean service_data_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ return ad_service_uuid != NULL;
+}
+
+static gboolean get_service_data(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const uint8_t *data = ad_service_data;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+ dict_append_array(&dict, ad_service_uuid, DBUS_TYPE_BYTE, &data,
+ ad_service_data_len);
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable ad_props[] = {
{ "Type", "s", get_type },
{ "ServiceUUIDs", "as", get_uuids, NULL, uuids_exists },
+ { "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
{ }
};
@@ -220,3 +299,52 @@ void ad_advertise_uuids(const char *arg)
ad_uuids_len = g_strv_length(ad_uuids);
}
+
+static void ad_clear_service(void)
+{
+ g_free(ad_service_uuid);
+ ad_service_uuid = NULL;
+ memset(ad_service_data, 0, sizeof(ad_service_data));
+ ad_service_data_len = 0;
+}
+
+void ad_advertise_service(const char *arg)
+{
+ wordexp_t w;
+ unsigned int i;
+
+ if (wordexp(arg, &w, WRDE_NOCMD)) {
+ rl_printf("Invalid argument\n");
+ return;
+ }
+
+ ad_clear_service();
+
+ if (w.we_wordc == 0)
+ goto done;
+
+ ad_service_uuid = g_strdup(w.we_wordv[0]);
+
+ for (i = 1; i < w.we_wordc; i++) {
+ long int val;
+ char *endptr = NULL;
+
+ if (i >= G_N_ELEMENTS(ad_service_data)) {
+ rl_printf("Too much data\n");
+ goto done;
+ }
+
+ val = strtol(w.we_wordv[i], &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+ rl_printf("Invalid value at index %d\n", i);
+ ad_clear_service();
+ goto done;
+ }
+
+ ad_service_data[ad_service_data_len] = val;
+ ad_service_data_len++;
+ }
+
+done:
+ wordfree(&w);
+}
diff --git a/client/main.c b/client/main.c
index d6b52c4..72c2cd9 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1878,6 +1878,11 @@ static void cmd_set_advertise_uuids(const char *arg)
ad_advertise_uuids(arg);
}
+static void cmd_set_advertise_service(const char *arg)
+{
+ ad_advertise_service(arg);
+}
+
static const struct {
const char *cmd;
const char *arg;
@@ -1911,6 +1916,9 @@ static const struct {
ad_generator},
{ "set-advertise-uuids", "[uuid1 uuid2 ...]",
cmd_set_advertise_uuids, "Set advertise uuids" },
+ { "set-advertise-service", "[uuid][data=[xx xx ...]",
+ cmd_set_advertise_service,
+ "Set advertise service data" },
{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
cmd_set_scan_filter_uuids, "Set scan filter uuids" },
{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
--
2.7.4
From: Luiz Augusto von Dentz <[email protected]>
This adds set-advertise-uuids which can be used set UUIDs to be
advertised:
set-advertise-uuids [uuid1 uuid2...]
[bluetooth]# set-advertise-uuids 180D 180F
[bluetooth]# advertise on
@ Advertising Added: 1
< HCI Command: LE Set Advertising Data (0x08|0x0008) plen 32
Length: 9
Flags: 0x02
LE General Discoverable Mode
16-bit Service UUIDs (complete): 2 entries
Heart Rate (0x180d)
Battery Service (0x180f)
---
client/advertising.c | 43 +++++++++++++++++++++++++++++++++++++++++++
client/main.c | 7 +++++++
2 files changed, 50 insertions(+)
diff --git a/client/advertising.c b/client/advertising.c
index 34e3f59..e74221f 100644
--- a/client/advertising.c
+++ b/client/advertising.c
@@ -40,6 +40,8 @@
static gboolean registered = FALSE;
static char *ad_type = NULL;
+static char **ad_uuids = NULL;
+static size_t ad_uuids_len = 0;
static void ad_release(DBusConnection *conn)
{
@@ -110,8 +112,31 @@ static gboolean get_type(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean uuids_exists(const GDBusPropertyTable *property, void *data)
+{
+ return ad_uuids_len != 0;
+}
+
+static gboolean get_uuids(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter array;
+ size_t i;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
+
+ for (i = 0; i < ad_uuids_len; i++)
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &ad_uuids[i]);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable ad_props[] = {
{ "Type", "s", get_type },
+ { "ServiceUUIDs", "as", get_uuids, NULL, uuids_exists },
{ }
};
@@ -177,3 +202,21 @@ void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
return;
}
}
+
+void ad_advertise_uuids(const char *arg)
+{
+ g_strfreev(ad_uuids);
+ ad_uuids = NULL;
+ ad_uuids_len = 0;
+
+ if (!arg || !strlen(arg))
+ return;
+
+ ad_uuids = g_strsplit(arg, " ", -1);
+ if (!ad_uuids) {
+ rl_printf("Failed to parse input\n");
+ return;
+ }
+
+ ad_uuids_len = g_strv_length(ad_uuids);
+}
diff --git a/client/main.c b/client/main.c
index 471879f..d6b52c4 100644
--- a/client/main.c
+++ b/client/main.c
@@ -1873,6 +1873,11 @@ static char *ad_generator(const char *text, int state)
return NULL;
}
+static void cmd_set_advertise_uuids(const char *arg)
+{
+ ad_advertise_uuids(arg);
+}
+
static const struct {
const char *cmd;
const char *arg;
@@ -1904,6 +1909,8 @@ static const struct {
{ "advertise", "<on/off/type>", cmd_advertise,
"Enable/disable advertising with given type",
ad_generator},
+ { "set-advertise-uuids", "[uuid1 uuid2 ...]",
+ cmd_set_advertise_uuids, "Set advertise uuids" },
{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
cmd_set_scan_filter_uuids, "Set scan filter uuids" },
{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
--
2.7.4
From: Luiz Augusto von Dentz <[email protected]>
This adds advertise command which uses org.bluez.LEAdvertisingManager1 to
add an advertising instance:
[bluetooth]# advertise
broadcast off on peripheral
[bluetooth]# advertise on
Advertising object registered
@ Advertising Added: 1
< HCI Command: LE Set Advertising Data (0x08|0x0008) plen 32
Length: 3
Flags: 0x02
LE General Discoverable Mode
[bluetooth]# advertise off
Advertising object unregistered
@ Advertising Removed: 1
< HCI Command: LE Set Advertise Enable (0x08|0x000a) plen 1
Advertising: Disabled (0x00)
---
Makefile.tools | 2 +
client/advertising.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++
client/main.c | 93 ++++++++++++++++++++++++++
3 files changed, 274 insertions(+)
create mode 100644 client/advertising.c
diff --git a/Makefile.tools b/Makefile.tools
index 0d5f143..7706dc7 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -5,6 +5,8 @@ bin_PROGRAMS += client/bluetoothctl
client_bluetoothctl_SOURCES = client/main.c \
client/display.h client/display.c \
client/agent.h client/agent.c \
+ client/advertising.h \
+ client/advertising.c \
client/gatt.h client/gatt.c \
monitor/uuid.h monitor/uuid.c
client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
diff --git a/client/advertising.c b/client/advertising.c
new file mode 100644
index 0000000..34e3f59
--- /dev/null
+++ b/client/advertising.c
@@ -0,0 +1,179 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <readline/readline.h>
+#include <wordexp.h>
+
+#include "gdbus/gdbus.h"
+#include "display.h"
+#include "advertising.h"
+
+#define AD_PATH "/org/bluez/advertising"
+#define AD_IFACE "org.bluez.LEAdvertisement1"
+
+static gboolean registered = FALSE;
+static char *ad_type = NULL;
+
+static void ad_release(DBusConnection *conn)
+{
+ registered = FALSE;
+
+ g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE);
+}
+
+static DBusMessage *release_advertising(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ rl_printf("Advertising released\n");
+
+ ad_release(conn);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable ad_methods[] = {
+ { GDBUS_METHOD("Release", NULL, NULL, release_advertising) },
+ { }
+};
+
+static void register_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const char *path = AD_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void register_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == FALSE) {
+ registered = TRUE;
+ rl_printf("Advertising object registered\n");
+ } else {
+ rl_printf("Failed to register advertisement: %s\n", error.name);
+ dbus_error_free(&error);
+
+ if (g_dbus_unregister_interface(conn, AD_PATH,
+ AD_IFACE) == FALSE)
+ rl_printf("Failed to unregister advertising object\n");
+ }
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *type = "peripheral";
+
+ if (!ad_type || strlen(ad_type) > 0)
+ type = ad_type;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable ad_props[] = {
+ { "Type", "s", get_type },
+ { }
+};
+
+void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type)
+{
+ if (registered == TRUE) {
+ rl_printf("Advertisement is already registered\n");
+ return;
+ }
+
+ ad_type = g_strdup(type);
+
+ if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods,
+ NULL, ad_props, NULL, NULL) == FALSE) {
+ rl_printf("Failed to register advertising object\n");
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement",
+ register_setup, register_reply,
+ conn, NULL) == FALSE) {
+ rl_printf("Failed to register advertising\n");
+ return;
+ }
+}
+
+static void unregister_setup(DBusMessageIter *iter, void *user_data)
+{
+ const char *path = AD_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void unregister_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == FALSE) {
+ registered = FALSE;
+ rl_printf("Advertising object unregistered\n");
+ if (g_dbus_unregister_interface(conn, AD_PATH,
+ AD_IFACE) == FALSE)
+ rl_printf("Failed to unregister advertising object\n");
+ } else {
+ rl_printf("Failed to unregister advertisement: %s\n",
+ error.name);
+ dbus_error_free(&error);
+ }
+}
+
+void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
+{
+ if (!manager)
+ ad_release(conn);
+
+ if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement",
+ unregister_setup, unregister_reply,
+ conn, NULL) == FALSE) {
+ rl_printf("Failed to unregister advertisement method\n");
+ return;
+ }
+}
diff --git a/client/main.c b/client/main.c
index 056331f..471879f 100644
--- a/client/main.c
+++ b/client/main.c
@@ -43,6 +43,7 @@
#include "agent.h"
#include "display.h"
#include "gatt.h"
+#include "advertising.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
@@ -58,6 +59,8 @@ static DBusConnection *dbus_conn;
static GDBusProxy *agent_manager;
static char *auto_register_agent = NULL;
+static GDBusProxy *ad_manager;
+
static GDBusProxy *default_ctrl;
static GDBusProxy *default_dev;
static GDBusProxy *default_attr;
@@ -77,6 +80,14 @@ static const char * const agent_arguments[] = {
NULL
};
+static const char * const ad_arguments[] = {
+ "on",
+ "off",
+ "peripheral",
+ "broadcast",
+ NULL
+};
+
static void proxy_leak(gpointer data)
{
printf("Leaking proxy %p\n", data);
@@ -445,6 +456,8 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
gatt_add_descriptor(proxy);
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_add_manager(proxy);
+ } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+ ad_manager = proxy;
}
}
@@ -509,6 +522,11 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
set_default_attribute(NULL);
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_remove_manager(proxy);
+ } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+ if (ad_manager == proxy) {
+ agent_manager = NULL;
+ ad_unregister(dbus_conn, NULL);
+ }
}
}
@@ -1783,6 +1801,78 @@ static char *capability_generator(const char *text, int state)
return NULL;
}
+static gboolean parse_argument_advertise(const char *arg, dbus_bool_t *value,
+ const char **type)
+{
+ const char * const *opt;
+
+ if (arg == NULL || strlen(arg) == 0) {
+ rl_printf("Missing on/off/type argument\n");
+ return FALSE;
+ }
+
+ if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+ *value = TRUE;
+ *type = "";
+ return TRUE;
+ }
+
+ if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+ *value = FALSE;
+ return TRUE;
+ }
+
+ for (opt = ad_arguments; *opt; opt++) {
+ if (strcmp(arg, *opt) == 0) {
+ *value = TRUE;
+ *type = *opt;
+ return TRUE;
+ }
+ }
+
+ rl_printf("Invalid argument %s\n", arg);
+ return FALSE;
+}
+
+static void cmd_advertise(const char *arg)
+{
+ dbus_bool_t enable;
+ const char *type;
+
+ if (parse_argument_advertise(arg, &enable, &type) == FALSE)
+ return;
+
+ if (!ad_manager) {
+ rl_printf("LEAdvertisingManager not found\n");
+ return;
+ }
+
+ if (enable == TRUE)
+ ad_register(dbus_conn, ad_manager, type);
+ else
+ ad_unregister(dbus_conn, ad_manager);
+}
+
+static char *ad_generator(const char *text, int state)
+{
+ static int index, len;
+ const char *arg;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ while ((arg = ad_arguments[index])) {
+ index++;
+
+ if (!strncmp(arg, text, len))
+ return strdup(arg);
+ }
+
+ return NULL;
+}
+
static const struct {
const char *cmd;
const char *arg;
@@ -1811,6 +1901,9 @@ static const struct {
capability_generator},
{ "default-agent",NULL, cmd_default_agent,
"Set agent as the default one" },
+ { "advertise", "<on/off/type>", cmd_advertise,
+ "Enable/disable advertising with given type",
+ ad_generator},
{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
cmd_set_scan_filter_uuids, "Set scan filter uuids" },
{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
--
2.7.4
From: Luiz Augusto von Dentz <[email protected]>
D-Bus dictionaries always except the values to be of variant type.
---
test/example-advertisement | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/test/example-advertisement b/test/example-advertisement
index ce6e40f..7da0c7b 100755
--- a/test/example-advertisement
+++ b/test/example-advertisement
@@ -65,10 +65,10 @@ class Advertisement(dbus.service.Object):
signature='s')
if self.manufacturer_data is not None:
properties['ManufacturerData'] = dbus.Dictionary(
- self.manufacturer_data, signature='qay')
+ self.manufacturer_data, signature='qv')
if self.service_data is not None:
properties['ServiceData'] = dbus.Dictionary(self.service_data,
- signature='say')
+ signature='sv')
if self.include_tx_power is not None:
properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
return {LE_ADVERTISEMENT_IFACE: properties}
@@ -88,13 +88,13 @@ class Advertisement(dbus.service.Object):
def add_manufacturer_data(self, manuf_code, data):
if not self.manufacturer_data:
- self.manufacturer_data = dict()
- self.manufacturer_data[manuf_code] = data
+ self.manufacturer_data = dbus.Dictionary({}, signature='qv')
+ self.manufacturer_data[manuf_code] = dbus.Array(data, signature='y')
def add_service_data(self, uuid, data):
if not self.service_data:
- self.service_data = dict()
- self.service_data[uuid] = data
+ self.service_data = dbus.Dictionary({}, signature='sv')
+ self.service_data[uuid] = dbus.Array(data, signature='y')
@dbus.service.method(DBUS_PROP_IFACE,
in_signature='s',
--
2.7.4
From: Luiz Augusto von Dentz <[email protected]>
Dictionary entries should contain their value component as variant
container which requires dbus_message_iter_recurse to access.
Note: This will break the application that were using this API up to
now, but since this was experimental it should still be possible to fix
it without having to bump the interface revision.
---
src/advertising.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/advertising.c b/src/advertising.c
index d2019de..e5cd31c 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -288,7 +288,7 @@ static bool parse_advertising_manufacturer_data(GDBusProxy *proxy,
while (dbus_message_iter_get_arg_type(&entries)
== DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter value, entry;
+ DBusMessageIter value, entry, array;
uint16_t manuf_id;
uint8_t *manuf_data;
int len;
@@ -297,15 +297,17 @@ static bool parse_advertising_manufacturer_data(GDBusProxy *proxy,
dbus_message_iter_get_basic(&entry, &manuf_id);
dbus_message_iter_next(&entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
goto fail;
- dbus_message_iter_recurse(&entry, &value);
+ dbus_message_iter_recurse(&value, &array);
- if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
goto fail;
- dbus_message_iter_get_fixed_array(&value, &manuf_data, &len);
+ dbus_message_iter_get_fixed_array(&array, &manuf_data, &len);
DBG("Adding ManufacturerData for %04x", manuf_id);
@@ -340,7 +342,7 @@ static bool parse_advertising_service_data(GDBusProxy *proxy,
while (dbus_message_iter_get_arg_type(&entries)
== DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter value, entry;
+ DBusMessageIter value, entry, array;
const char *uuid_str;
bt_uuid_t uuid;
uint8_t *service_data;
@@ -353,15 +355,17 @@ static bool parse_advertising_service_data(GDBusProxy *proxy,
goto fail;
dbus_message_iter_next(&entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
goto fail;
- dbus_message_iter_recurse(&entry, &value);
+ dbus_message_iter_recurse(&value, &array);
- if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
goto fail;
- dbus_message_iter_get_fixed_array(&value, &service_data, &len);
+ dbus_message_iter_get_fixed_array(&array, &service_data, &len);
DBG("Adding ServiceData for %s", uuid_str);
--
2.7.4