2015-03-26 22:08:30

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 00/13] Implement LE Advertisement D-Bus API

Implementation of the LE Advertisement D-Bus API.

Things that are missing still (and added to the TODO):
* MGMT API for modifying the LE Advertisements
* Semantics for multiple Advertisements (currently any more than one will fail)
* TX Power property parsing / interface

v1 -> v2:
* Cleanup based on comments from Arman
* Add g_dbus_proxy_set_read_watch function to avoid ObjectManager dependency
* Consolidate script updates into single test script

v2 -> v3:
* Change "ManufacturerSpecificData" to "ManufacturerData"
* Add advertisement-api.txt to EXTRA_DIST
* Add Release() callback to org.bluez.LEAdvertisement1
* Remove IncludePower from the LEAdvertisement1 API

v3 -> v4:
* Rename "g_dbus_proxy_set_read_watch" to "g_dbus_proxy_set_ready_watch"

v4 -> v5:
* Rebase
* Use g_dbus_client_set_proxy_handlers instead of g_dbus_proxy_set_ready_watch
* Add MGMT interface calls to actually advertise.

v5 -> v6:
* Minor patch cruft cleanup
* Rename btd_advertising_manager to btd_advertising and fix filename change.


Michael Janssen (13):
core: advertising: add LEAdvertisingManager stubs
core/advertising: Implement RegisterAdvertisement
tools: Python script to test Advertisement API
core/advertising: implement UnregisterAdvertisement
shared: add advertising-data
core/advertising: use advertising_data
core/advertising: Parse ServiceUUIDs
core/advertising: Parse SolicitUUIDs
core/advertising: Parse ManufacturerSpecificData
core/advertising: Parse ServiceData
Update TODO for LE Advertising
shared/advertising: implement advertising_data_generate
core/advertising: Add and Remove AD using MGMT

Makefile.am | 3 +
TODO | 20 +-
src/adapter.c | 19 ++
src/advertising.c | 665 ++++++++++++++++++++++++++++++++++++++++++
src/advertising.h | 25 ++
src/shared/advertising-data.c | 585 +++++++++++++++++++++++++++++++++++++
src/shared/advertising-data.h | 69 +++++
tools/advertisement-example | 170 +++++++++++
8 files changed, 1546 insertions(+), 10 deletions(-)
create mode 100644 src/advertising.c
create mode 100644 src/advertising.h
create mode 100644 src/shared/advertising-data.c
create mode 100644 src/shared/advertising-data.h
create mode 100644 tools/advertisement-example

--
2.2.0.rc0.207.ga3a616c



2015-03-27 15:39:41

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [BlueZ v6 00/13] Implement LE Advertisement D-Bus API

Hi Michael,

On Fri, Mar 27, 2015 at 12:08 AM, Michael Janssen <[email protected]> wrote:
> Implementation of the LE Advertisement D-Bus API.
>
> Things that are missing still (and added to the TODO):
> * MGMT API for modifying the LE Advertisements
> * Semantics for multiple Advertisements (currently any more than one will fail)
> * TX Power property parsing / interface
>
> v1 -> v2:
> * Cleanup based on comments from Arman
> * Add g_dbus_proxy_set_read_watch function to avoid ObjectManager dependency
> * Consolidate script updates into single test script
>
> v2 -> v3:
> * Change "ManufacturerSpecificData" to "ManufacturerData"
> * Add advertisement-api.txt to EXTRA_DIST
> * Add Release() callback to org.bluez.LEAdvertisement1
> * Remove IncludePower from the LEAdvertisement1 API
>
> v3 -> v4:
> * Rename "g_dbus_proxy_set_read_watch" to "g_dbus_proxy_set_ready_watch"
>
> v4 -> v5:
> * Rebase
> * Use g_dbus_client_set_proxy_handlers instead of g_dbus_proxy_set_ready_watch
> * Add MGMT interface calls to actually advertise.
>
> v5 -> v6:
> * Minor patch cruft cleanup
> * Rename btd_advertising_manager to btd_advertising and fix filename change.
>
>
> Michael Janssen (13):
> core: advertising: add LEAdvertisingManager stubs
> core/advertising: Implement RegisterAdvertisement
> tools: Python script to test Advertisement API
> core/advertising: implement UnregisterAdvertisement
> shared: add advertising-data
> core/advertising: use advertising_data
> core/advertising: Parse ServiceUUIDs
> core/advertising: Parse SolicitUUIDs
> core/advertising: Parse ManufacturerSpecificData
> core/advertising: Parse ServiceData
> Update TODO for LE Advertising
> shared/advertising: implement advertising_data_generate
> core/advertising: Add and Remove AD using MGMT
>
> Makefile.am | 3 +
> TODO | 20 +-
> src/adapter.c | 19 ++
> src/advertising.c | 665 ++++++++++++++++++++++++++++++++++++++++++
> src/advertising.h | 25 ++
> src/shared/advertising-data.c | 585 +++++++++++++++++++++++++++++++++++++
> src/shared/advertising-data.h | 69 +++++
> tools/advertisement-example | 170 +++++++++++
> 8 files changed, 1546 insertions(+), 10 deletions(-)
> create mode 100644 src/advertising.c
> create mode 100644 src/advertising.h
> create mode 100644 src/shared/advertising-data.c
> create mode 100644 src/shared/advertising-data.h
> create mode 100644 tools/advertisement-example
>
> --
> 2.2.0.rc0.207.ga3a616c

Ive applied patches 1-4, now for shared I think it is better to use
bt_ prefix, perhaps bt_advertisement or bt_ad if the former makes the
functions name too big.


--
Luiz Augusto von Dentz

2015-03-26 22:08:43

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 13/13] core/advertising: Add and Remove AD using MGMT

Updates the advertising manager to add and remove the LE Advertising Data
using MGMT interface commands.
---
src/advertising.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/src/advertising.c b/src/advertising.c
index 3705a22..7fd9c78 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -26,6 +26,7 @@
#include <gdbus/gdbus.h>

#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
#include "lib/sdp.h"

#include "adapter.h"
@@ -33,6 +34,7 @@
#include "error.h"
#include "log.h"
#include "src/shared/advertising-data.h"
+#include "src/shared/mgmt.h"
#include "src/shared/queue.h"
#include "src/shared/util.h"

@@ -41,6 +43,8 @@

struct btd_advertising {
struct btd_adapter *adapter;
+ struct mgmt *mgmt;
+ uint16_t mgmt_index;
struct queue *adverts;
};

@@ -55,6 +59,7 @@ struct advertisement {
GDBusProxy *proxy;
DBusMessage *reg;
uint8_t type; /* Advertising type */
+ uint8_t instance;
struct advertising_data *data;
};

@@ -123,10 +128,15 @@ static void advertisement_destroy(void *data)
static void advertisement_remove(void *data)
{
struct advertisement *ad = data;
+ struct mgmt_cp_remove_advertising cp;

g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);

- /* TODO: mgmt API call to remove advert */
+ cp.instance = ad->instance;
+
+ mgmt_send(ad->manager->mgmt, MGMT_OP_REMOVE_ADVERTISING,
+ ad->manager->mgmt_index, sizeof(cp), &cp, NULL, NULL,
+ NULL);

queue_remove(ad->manager->adverts, ad);

@@ -348,9 +358,44 @@ fail:
return false;
}

+static void add_advertising_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct advertisement *ad = user_data;
+ const struct mgmt_rp_add_advertising *rp = param;
+
+ if (status || !param) {
+ error("Failed to set advertising data!");
+ return;
+ }
+
+ ad->instance = rp->instance;
+}
+
static void refresh_advertisement(struct advertisement *ad)
{
+ struct mgmt_cp_add_advertising *cp;
+ uint8_t adv_data_len, param_len;
+ uint8_t *adv_data;
+
DBG("Refreshing advertisement: %s", ad->path);
+
+ adv_data = advertising_data_generate(ad->data, &adv_data_len);
+
+ param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len;
+
+ cp = malloc(param_len);
+
+ cp->instance = ad->instance;
+ cp->flags = 0;
+ cp->duration = 0;
+ cp->timeout = 0;
+ cp->adv_data_len = adv_data_len;
+ memcpy(cp->data, adv_data, adv_data_len);
+
+ mgmt_send(ad->manager->mgmt, MGMT_OP_ADD_ADVERTISING,
+ ad->manager->mgmt_index, param_len, cp,
+ add_advertising_callback, ad, NULL);
}

static bool parse_advertisement(struct advertisement *ad)
@@ -427,6 +472,8 @@ static struct advertisement *advertisement_create(DBusConnection *conn,
if (!ad)
return NULL;

+ ad->instance = 1;
+
ad->client = g_dbus_client_new_full(conn, sender, path, path);
if (!ad->client)
goto fail;
@@ -564,6 +611,16 @@ advertising_manager_create(struct btd_adapter *adapter)

manager->adapter = adapter;

+ manager->mgmt = mgmt_new_default();
+
+ if (!manager->mgmt) {
+ error("Failed to access management interface");
+ free(manager);
+ return NULL;
+ }
+
+ manager->mgmt_index = btd_adapter_get_index(adapter);
+
if (!g_dbus_register_interface(btd_get_dbus_connection(),
adapter_get_path(adapter),
LE_ADVERTISING_MGR_IFACE,
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:42

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 12/13] shared/advertising: implement advertising_data_generate

Implements the function to provide a raw version of the advertising data
packet for passing to the kernel.
---
src/shared/advertising-data.c | 240 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 238 insertions(+), 2 deletions(-)

diff --git a/src/shared/advertising-data.c b/src/shared/advertising-data.c
index 800856c..ccef2a4 100644
--- a/src/shared/advertising-data.c
+++ b/src/shared/advertising-data.c
@@ -22,6 +22,21 @@
#include "src/shared/queue.h"
#include "src/shared/util.h"

+#define MAX_ADV_DATA_LEN 31
+
+#define LE_AD_TYPE_UUID16_ALL 0x03 /* 16-bit service UUIDs, all */
+#define LE_AD_TYPE_UUID32_ALL 0x05 /* 16-bit service UUIDs, all */
+#define LE_AD_TYPE_UUID128_ALL 0x07 /* 16-bit service UUIDs, all */
+#define LE_AD_TYPE_SOLICIT16 0x14 /* 16-bit service solic. UUIDs */
+#define LE_AD_TYPE_SOLICIT32 0x1F /* 32-bit service solic. UUIDs */
+#define LE_AD_TYPE_SOLICIT128 0x15 /* 128-bit service solic. UUIDs */
+#define LE_AD_TYPE_SVC_DATA16 0x16 /* Service Data - 16-bit UUID */
+#define LE_AD_TYPE_SVC_DATA32 0x20 /* Service Data - 32-bit UUID */
+#define LE_AD_TYPE_SVC_DATA128 0x21 /* Service Data - 128-bit UUID */
+#define LE_AD_TYPE_PUB_TRGT_ADDR 0x17 /* Public Target Address */
+#define LE_AD_TYPE_RND_TRGT_ADDR 0x18 /* Random Target Address */
+#define LE_AD_TYPE_MANUF_DATA 0xFF /* Manufacturer Specific Data */
+
struct advertising_data {
int ref_count;
struct queue *service_uuids;
@@ -136,10 +151,231 @@ void advertising_data_unref(struct advertising_data *ad)
free(ad);
}

+static uint8_t uuid_list_length(struct queue *uuid_queue)
+{
+ bool uuid16_included = false;
+ bool uuid32_included = false;
+ bool uuid128_included = false;
+ uint8_t length = 0;
+ const struct queue_entry *entry;
+
+ entry = queue_get_entries(uuid_queue);
+
+ while (entry) {
+ bt_uuid_t *uuid = entry->data;
+ uint8_t uuid_len = bt_uuid_len(uuid);
+
+ length += uuid_len;
+
+ if (uuid_len == 2)
+ uuid16_included = true;
+ else if (uuid_len == 4)
+ uuid32_included = true;
+ else
+ uuid128_included = true;
+
+ entry = entry->next;
+ }
+
+ if (uuid16_included)
+ length += 2;
+
+ if (uuid32_included)
+ length += 2;
+
+ if (uuid128_included)
+ length += 2;
+
+ return length;
+}
+
+static uint8_t mfg_data_length(struct queue *manuf_data)
+{
+ uint8_t length = 0;
+ const struct queue_entry *entry;
+
+ entry = queue_get_entries(manuf_data);
+
+ while (entry) {
+ struct manufacturer_tagged_data *data = entry->data;
+
+ length += 2 + sizeof(uint16_t) + data->len;
+
+ entry = entry->next;
+ }
+
+ return length;
+}
+
+static uint8_t uuid_data_length(struct queue *tagged_data)
+{
+ uint8_t length = 0;
+ const struct queue_entry *entry;
+
+ entry = queue_get_entries(tagged_data);
+
+ while (entry) {
+ struct uuid_tagged_data *data = entry->data;
+
+ length += 2 + bt_uuid_len(&data->uuid) + data->len;
+
+ entry = entry->next;
+ }
+
+ return length;
+}
+
+static uint8_t calculate_length(struct advertising_data *ad)
+{
+ uint8_t length = 0;
+
+ length += uuid_list_length(ad->service_uuids);
+
+ length += uuid_list_length(ad->solicit_uuids);
+
+ length += mfg_data_length(ad->manufacturer_data);
+
+ length += uuid_data_length(ad->service_data);
+
+ return length;
+}
+
+static void serialize_uuids(struct queue *uuids, uint8_t uuid_type,
+ uint8_t ad_type, uint8_t *buf,
+ uint8_t *pos)
+{
+ const struct queue_entry *entry = queue_get_entries(uuids);
+ bool added = false;
+ uint8_t length_pos = 0;
+
+ while (entry) {
+ bt_uuid_t *uuid = entry->data;
+
+ if (uuid->type == uuid_type) {
+ if (!added) {
+ length_pos = (*pos)++;
+ buf[(*pos)++] = ad_type;
+ added = true;
+ }
+
+ if (uuid_type != BT_UUID32)
+ bt_uuid_to_le(uuid, buf + *pos);
+ else
+ bt_put_le32(uuid->value.u32, buf + *pos);
+
+ *pos += bt_uuid_len(uuid);
+ }
+
+ entry = entry->next;
+ }
+
+ if (added)
+ buf[length_pos] = *pos - length_pos;
+}
+
+static void serialize_service_uuids(struct queue *uuids, uint8_t *buf,
+ uint8_t *pos)
+{
+ serialize_uuids(uuids, BT_UUID16, LE_AD_TYPE_UUID16_ALL, buf, pos);
+
+ serialize_uuids(uuids, BT_UUID32, LE_AD_TYPE_UUID32_ALL, buf, pos);
+
+ serialize_uuids(uuids, BT_UUID128, LE_AD_TYPE_UUID128_ALL, buf, pos);
+}
+
+static void serialize_solicit_uuids(struct queue *uuids, uint8_t *buf,
+ uint8_t *pos)
+{
+ serialize_uuids(uuids, BT_UUID16, LE_AD_TYPE_SOLICIT16, buf, pos);
+
+ serialize_uuids(uuids, BT_UUID32, LE_AD_TYPE_SOLICIT32, buf, pos);
+
+ serialize_uuids(uuids, BT_UUID128, LE_AD_TYPE_SOLICIT128, buf, pos);
+}
+
+static void serialize_manuf_data(struct queue *manuf_data, uint8_t *buf,
+ uint8_t *pos)
+{
+ const struct queue_entry *entry = queue_get_entries(manuf_data);
+
+ while (entry) {
+ struct manufacturer_tagged_data *data = entry->data;
+
+ buf[(*pos)++] = data->len + 2 + 1;
+
+ buf[(*pos)++] = LE_AD_TYPE_MANUF_DATA;
+
+ bt_put_le16(data->manufacturer_id, buf + (*pos));
+
+ *pos += 2;
+
+ memcpy(buf + *pos, data->data, data->len);
+
+ *pos += data->len;
+
+ entry = entry->next;
+ }
+}
+
+static void serialize_service_data(struct queue *service_data, uint8_t *buf,
+ uint8_t *pos)
+{
+ const struct queue_entry *entry = queue_get_entries(service_data);
+
+ while (entry) {
+ struct uuid_tagged_data *data = entry->data;
+ int uuid_len = bt_uuid_len(&data->uuid);
+
+ buf[(*pos)++] = uuid_len + data->len + 1;
+
+ switch (uuid_len) {
+ case 2:
+ buf[(*pos)++] = LE_AD_TYPE_SVC_DATA16;
+ break;
+ case 4:
+ buf[(*pos)++] = LE_AD_TYPE_SVC_DATA32;
+ break;
+ case 16:
+ buf[(*pos)++] = LE_AD_TYPE_SVC_DATA128;
+ break;
+ }
+
+ if (uuid_len != 4)
+ bt_uuid_to_le(&data->uuid, buf + *pos);
+ else
+ bt_put_le32(data->uuid.value.u32, buf + *pos);
+
+ *pos += uuid_len;
+
+ memcpy(buf + *pos, data->data, data->len);
+
+ *pos += data->len;
+
+ entry = entry->next;
+ }
+}
+
uint8_t *advertising_data_generate(struct advertising_data *ad, uint8_t *length)
{
- /* TODO: implement */
- return NULL;
+ uint8_t *adv_data;
+ uint8_t pos = 0;
+
+ *length = calculate_length(ad);
+
+ if (*length > MAX_ADV_DATA_LEN)
+ return NULL;
+
+ adv_data = malloc(*length);
+
+ serialize_service_uuids(ad->service_uuids, adv_data, &pos);
+
+ serialize_solicit_uuids(ad->solicit_uuids, adv_data, &pos);
+
+ serialize_manuf_data(ad->manufacturer_data, adv_data, &pos);
+
+ serialize_service_data(ad->service_data, adv_data, &pos);
+
+ return adv_data;
}

static bool queue_add_uuid(struct queue *queue, const bt_uuid_t *uuid)
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:39

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 09/13] core/advertising: Parse ManufacturerSpecificData

Parse the ManufacturerSpecificData property of the LEAdvertisement1
---
src/advertising.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)

diff --git a/src/advertising.c b/src/advertising.c
index b373740..5646e68 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -242,6 +242,57 @@ fail:
return false;
}

+static bool parse_advertising_manufacturer_data(GDBusProxy *proxy,
+ struct advertising_data *data)
+{
+ DBusMessageIter iter, entries;
+
+ if (!g_dbus_proxy_get_property(proxy, "ManufacturerData", &iter))
+ return true;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &entries);
+
+ advertising_data_clear_manufacturer_data(data);
+
+ while (dbus_message_iter_get_arg_type(&entries)
+ == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter value, entry;
+ uint16_t manuf_id;
+ uint8_t *manuf_data;
+ int len;
+
+ dbus_message_iter_recurse(&entries, &entry);
+ dbus_message_iter_get_basic(&entry, &manuf_id);
+
+ dbus_message_iter_next(&entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_fixed_array(&value, &manuf_data, &len);
+
+ DBG("Adding ManufacturerData for %04x", manuf_id);
+
+ advertising_data_add_manufacturer_data(data, manuf_id,
+ manuf_data, len);
+
+ dbus_message_iter_next(&entries);
+ }
+
+ return true;
+
+fail:
+ advertising_data_clear_manufacturer_data(data);
+ return false;
+}
+
static void refresh_advertisement(struct advertisement *ad)
{
DBG("Refreshing advertisement: %s", ad->path);
@@ -264,6 +315,11 @@ static bool parse_advertisement(struct advertisement *ad)
return false;
}

+ if (!parse_advertising_manufacturer_data(ad->proxy, ad->data)) {
+ error("Property \"ManufacturerData\" failed to parse");
+ return false;
+ }
+
/* TODO: parse the rest of the properties */

refresh_advertisement(ad);
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:34

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 04/13] core/advertising: implement UnregisterAdvertisement

Implement the UnregisterAdvertisement method of the
LEAdvertisingManager1 interface.
---
src/advertising.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/src/advertising.c b/src/advertising.c
index 2be4343..8ab1b76 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -123,6 +123,8 @@ static void advertisement_remove(void *data)

g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);

+ /* TODO: mgmt API call to remove advert */
+
queue_remove(ad->manager->adverts, ad);

g_idle_add(advertisement_free_idle_cb, ad);
@@ -297,10 +299,28 @@ static DBusMessage *unregister_advertisement(DBusConnection *conn,
DBusMessage *msg,
void *user_data)
{
+ struct btd_advertising *manager = user_data;
+ DBusMessageIter args;
+ const char *path;
+ struct advertisement *ad;
+
DBG("UnregisterAdvertisement");

- /* TODO */
- return NULL;
+ if (!dbus_message_iter_init(msg, &args))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&args, &path);
+
+ ad = queue_find(manager->adverts, match_advertisement_path, path);
+ if (!ad)
+ return btd_error_does_not_exist(msg);
+
+ advertisement_remove(ad);
+
+ return dbus_message_new_method_return(msg);
}

static const GDBusMethodTable methods[] = {
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:35

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 05/13] shared: add advertising-data

The advetising_data structure provides an abstraction for easy
translation of defined Advertisement Data fields into the resulting
raw bytes needed by the Bluetooth HCI LE Set Advertising Data command.
---
Makefile.am | 2 +
src/shared/advertising-data.c | 349 ++++++++++++++++++++++++++++++++++++++++++
src/shared/advertising-data.h | 69 +++++++++
3 files changed, 420 insertions(+)
create mode 100644 src/shared/advertising-data.c
create mode 100644 src/shared/advertising-data.h

diff --git a/Makefile.am b/Makefile.am
index 676d929..5c556d3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -111,6 +111,8 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/uhid.h src/shared/uhid.c \
src/shared/pcap.h src/shared/pcap.c \
src/shared/btsnoop.h src/shared/btsnoop.c \
+ src/shared/advertising-data.h \
+ src/shared/advertising-data.c \
src/shared/att-types.h \
src/shared/att.h src/shared/att.c \
src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
diff --git a/src/shared/advertising-data.c b/src/shared/advertising-data.c
new file mode 100644
index 0000000..800856c
--- /dev/null
+++ b/src/shared/advertising-data.c
@@ -0,0 +1,349 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * 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.
+ *
+ */
+
+#include "src/shared/advertising-data.h"
+
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+
+struct advertising_data {
+ int ref_count;
+ struct queue *service_uuids;
+ struct queue *manufacturer_data;
+ struct queue *solicit_uuids;
+ struct queue *service_data;
+};
+
+struct uuid_tagged_data {
+ bt_uuid_t uuid;
+ uint8_t *data;
+ size_t len;
+};
+
+struct manufacturer_tagged_data {
+ uint16_t manufacturer_id;
+ uint8_t *data;
+ size_t len;
+};
+
+struct advertising_data *advertising_data_new(void)
+{
+ struct advertising_data *ad;
+
+ ad = new0(struct advertising_data, 1);
+ if (!ad)
+ return NULL;
+
+ ad->service_uuids = queue_new();
+ if (!ad->service_uuids)
+ goto fail;
+
+ ad->manufacturer_data = queue_new();
+ if (!ad->manufacturer_data)
+ goto fail;
+
+ ad->solicit_uuids = queue_new();
+ if (!ad->solicit_uuids)
+ goto fail;
+
+ ad->service_data = queue_new();
+ if (!ad->service_data)
+ goto fail;
+
+ return advertising_data_ref(ad);
+
+fail:
+ queue_destroy(ad->service_uuids, NULL);
+ queue_destroy(ad->manufacturer_data, NULL);
+ queue_destroy(ad->solicit_uuids, NULL);
+ queue_destroy(ad->service_data, NULL);
+
+ free(ad);
+
+ return NULL;
+}
+
+struct advertising_data *advertising_data_ref(struct advertising_data *ad)
+{
+ ad->ref_count++;
+ return ad;
+}
+
+static void uuid_tagged_destroy(void *data)
+{
+ struct uuid_tagged_data *tagged = data;
+
+ free(tagged->data);
+ free(tagged);
+}
+
+static bool uuid_tagged_match(const void *data, const void *elem)
+{
+ const struct uuid_tagged_data *tagged = elem;
+ const bt_uuid_t *uuid = data;
+
+ return !bt_uuid_cmp(&tagged->uuid, uuid);
+}
+
+static void manuf_tagged_destroy(void *data)
+{
+ struct manufacturer_tagged_data *tagged = data;
+
+ free(tagged->data);
+ free(tagged);
+}
+
+static bool manuf_tagged_match(const void *data, const void *elem)
+{
+ const struct manufacturer_tagged_data *tagged = elem;
+ uint16_t manuf_id = PTR_TO_UINT(elem);
+
+ return tagged->manufacturer_id == manuf_id;
+}
+
+void advertising_data_unref(struct advertising_data *ad)
+{
+ if (!ad)
+ return;
+
+ if (__sync_sub_and_fetch(&ad->ref_count, 1))
+ return;
+
+ queue_destroy(ad->service_uuids, free);
+
+ queue_destroy(ad->manufacturer_data, manuf_tagged_destroy);
+
+ queue_destroy(ad->solicit_uuids, free);
+
+ queue_destroy(ad->service_data, uuid_tagged_destroy);
+
+ free(ad);
+}
+
+uint8_t *advertising_data_generate(struct advertising_data *ad, uint8_t *length)
+{
+ /* TODO: implement */
+ return NULL;
+}
+
+static bool queue_add_uuid(struct queue *queue, const bt_uuid_t *uuid)
+{
+ bt_uuid_t *new_uuid;
+
+ if (!queue)
+ return false;
+
+ new_uuid = new0(bt_uuid_t, 1);
+ if (!new_uuid)
+ return false;
+
+ bt_uuid_to_uuid128(uuid, new_uuid);
+
+ queue_push_tail(queue, new_uuid);
+
+ return true;
+}
+
+static bool uuid_match(const void *data, const void *elem)
+{
+ const bt_uuid_t *match_uuid = data;
+ const bt_uuid_t *uuid = elem;
+
+ return bt_uuid_cmp(match_uuid, uuid);
+}
+
+static bool queue_remove_uuid(struct queue *queue, bt_uuid_t *uuid)
+{
+ bt_uuid_t *removed;
+
+ if (!queue || !uuid)
+ return false;
+
+ removed = queue_remove_if(queue, uuid_match, uuid);
+
+ if (removed) {
+ free(removed);
+ return true;
+ }
+
+ return false;
+}
+
+bool advertising_data_add_service_uuid(struct advertising_data *ad,
+ const bt_uuid_t *uuid)
+{
+ if (!ad)
+ return false;
+
+ return queue_add_uuid(ad->service_uuids, uuid);
+}
+
+bool advertising_data_remove_service_uuid(struct advertising_data *ad,
+ bt_uuid_t *uuid)
+{
+ if (!ad)
+ return false;
+
+ return queue_remove_uuid(ad->service_uuids, uuid);
+}
+
+void advertising_data_clear_service_uuid(struct advertising_data *ad)
+{
+ queue_destroy(ad->service_uuids, free);
+
+ ad->service_uuids = queue_new();
+}
+
+bool advertising_data_add_manufacturer_data(struct advertising_data *ad,
+ uint16_t manufacturer_id,
+ void *data, size_t len)
+{
+ struct manufacturer_tagged_data *new_data;
+
+ if (!ad)
+ return false;
+
+ new_data = new0(struct manufacturer_tagged_data, 1);
+ if (!new_data)
+ return false;
+
+ new_data->manufacturer_id = manufacturer_id;
+
+ new_data->data = malloc(len);
+ if (!new_data->data) {
+ free(new_data);
+ return false;
+ }
+
+ memcpy(new_data->data, data, len);
+
+ if (queue_push_tail(ad->manufacturer_data, new_data))
+ return true;
+
+ manuf_tagged_destroy(new_data);
+
+ return false;
+}
+
+bool advertising_data_remove_manufacturer_data(struct advertising_data *ad,
+ uint16_t manufacturer_id)
+{
+ struct manufacturer_tagged_data *data;
+
+ if (!ad)
+ return false;
+
+ data = queue_remove_if(ad->manufacturer_data, manuf_tagged_match,
+ UINT_TO_PTR(manufacturer_id));
+
+ if (!data)
+ return false;
+
+ manuf_tagged_destroy(data);
+
+ return true;
+}
+
+void advertising_data_clear_manufacturer_data(struct advertising_data *ad)
+{
+ queue_destroy(ad->manufacturer_data, manuf_tagged_destroy);
+
+ ad->manufacturer_data = queue_new();
+}
+
+bool advertising_data_add_solicit_uuid(struct advertising_data *ad,
+ const bt_uuid_t *uuid)
+{
+ if (!ad)
+ return false;
+
+ return queue_add_uuid(ad->solicit_uuids, uuid);
+}
+
+bool advertising_data_remove_solicit_uuid(struct advertising_data *ad,
+ bt_uuid_t *uuid)
+{
+ if (!ad)
+ return false;
+
+ return queue_remove_uuid(ad->solicit_uuids, uuid);
+}
+
+void advertising_data_clear_solicit_uuid(struct advertising_data *ad)
+{
+ queue_destroy(ad->solicit_uuids, free);
+
+ ad->solicit_uuids = queue_new();
+}
+
+bool advertising_data_add_service_data(struct advertising_data *ad,
+ const bt_uuid_t *uuid,
+ void *data, size_t len)
+{
+ struct uuid_tagged_data *new_data;
+
+ if (!ad)
+ return false;
+
+ new_data = new0(struct uuid_tagged_data, 1);
+ if (!new_data)
+ return false;
+
+ bt_uuid_to_uuid128(uuid, &new_data->uuid);
+
+ new_data->data = malloc(len);
+ if (!new_data) {
+ free(new_data);
+ return false;
+ }
+
+ memcpy(new_data->data, data, len);
+
+ if (queue_push_tail(ad->service_data, new_data))
+ return true;
+
+ uuid_tagged_destroy(new_data);
+
+ return false;
+}
+
+bool advertising_data_remove_service_data(struct advertising_data *ad,
+ bt_uuid_t *uuid)
+{
+ struct uuid_tagged_data *data;
+
+ if (!ad)
+ return false;
+
+ data = queue_remove_if(ad->service_data, uuid_tagged_match, uuid);
+
+ if (!data)
+ return false;
+
+ uuid_tagged_destroy(data);
+
+ return true;
+}
+
+void advertising_data_clear_service_data(struct advertising_data *ad)
+{
+ queue_destroy(ad->service_data, uuid_tagged_destroy);
+
+ ad->service_data = queue_new();
+}
diff --git a/src/shared/advertising-data.h b/src/shared/advertising-data.h
new file mode 100644
index 0000000..05d2b51
--- /dev/null
+++ b/src/shared/advertising-data.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * 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.
+ *
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+struct advertising_data;
+
+struct advertising_data *advertising_data_new(void);
+
+struct advertising_data *advertising_data_ref(struct advertising_data *ad);
+
+void advertising_data_unref(struct advertising_data *ad);
+
+uint8_t *advertising_data_generate(struct advertising_data *ad,
+ uint8_t *length);
+
+bool advertising_data_add_service_uuid(struct advertising_data *ad,
+ const bt_uuid_t *uuid);
+
+bool advertising_data_remove_service_uuid(struct advertising_data *ad,
+ bt_uuid_t *uuid);
+
+void advertising_data_clear_service_uuid(struct advertising_data *ad);
+
+bool advertising_data_add_manufacturer_data(struct advertising_data *ad,
+ uint16_t manufacturer_data,
+ void *data, size_t len);
+
+bool advertising_data_remove_manufacturer_data(struct advertising_data *ad,
+ uint16_t manufacturer_id);
+
+void advertising_data_clear_manufacturer_data(struct advertising_data *ad);
+
+bool advertising_data_add_solicit_uuid(struct advertising_data *ad,
+ const bt_uuid_t *uuid);
+
+bool advertising_data_remove_solicit_uuid(struct advertising_data *ad,
+ bt_uuid_t *uuid);
+
+void advertising_data_clear_solicit_uuid(struct advertising_data *ad);
+
+bool advertising_data_add_service_data(struct advertising_data *ad,
+ const bt_uuid_t *uuid,
+ void *data, size_t len);
+
+bool advertising_data_remove_service_data(struct advertising_data *ad,
+ bt_uuid_t *uuid);
+
+void advertising_data_clear_service_data(struct advertising_data *ad);
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:38

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 08/13] core/advertising: Parse SolicitUUIDs

Parse the SolicitUUIDs property of the LEAdvertisement1 object.
---
src/advertising.c | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)

diff --git a/src/advertising.c b/src/advertising.c
index 6568dd0..b373740 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -204,6 +204,44 @@ fail:
return false;
}

+static bool parse_advertising_solicit_uuids(GDBusProxy *proxy,
+ struct advertising_data *data)
+{
+ DBusMessageIter iter, ariter;
+
+ if (!g_dbus_proxy_get_property(proxy, "SolicitUUIDs", &iter))
+ return true;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &ariter);
+
+ advertising_data_clear_solicit_uuid(data);
+
+ while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
+ const char *uuid_str;
+ bt_uuid_t uuid;
+
+ dbus_message_iter_get_basic(&ariter, &uuid_str);
+
+ DBG("Adding SolicitUUID: %s", uuid_str);
+
+ if (bt_string_to_uuid(&uuid, uuid_str) < 0)
+ goto fail;
+
+ advertising_data_add_solicit_uuid(data, &uuid);
+
+ dbus_message_iter_next(&ariter);
+ }
+
+ return true;
+
+fail:
+ advertising_data_clear_solicit_uuid(data);
+ return false;
+}
+
static void refresh_advertisement(struct advertisement *ad)
{
DBG("Refreshing advertisement: %s", ad->path);
@@ -221,6 +259,11 @@ static bool parse_advertisement(struct advertisement *ad)
return false;
}

+ if (!parse_advertising_solicit_uuids(ad->proxy, ad->data)) {
+ error("Property \"SolicitUUIDs\" failed to parse");
+ return false;
+ }
+
/* TODO: parse the rest of the properties */

refresh_advertisement(ad);
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:33

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 03/13] tools: Python script to test Advertisement API

This script provides an example of using the advertisement-data
D-Bus API from python.
---
tools/advertisement-example | 170 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 170 insertions(+)
create mode 100644 tools/advertisement-example

diff --git a/tools/advertisement-example b/tools/advertisement-example
new file mode 100644
index 0000000..98aeafa
--- /dev/null
+++ b/tools/advertisement-example
@@ -0,0 +1,170 @@
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
+
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+
+class NotSupportedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotSupported'
+
+
+class NotPermittedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotPermitted'
+
+
+class InvalidValueLengthException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
+
+
+class FailedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.Failed'
+
+
+class Advertisement(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/advertisement'
+
+ def __init__(self, bus, index, advertising_type):
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.ad_type = advertising_type
+ self.service_uuids = None
+ self.manufacturer_data = None
+ self.solicit_uuids = None
+ self.service_data = None
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ properties = dict()
+ properties['Type'] = self.ad_type
+ if self.service_uuids is not None:
+ properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
+ signature='s')
+ if self.solicit_uuids is not None:
+ properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
+ signature='s')
+ if self.manufacturer_data is not None:
+ properties['ManufacturerData'] = dbus.Dictionary(
+ self.manufacturer_data, signature='qay')
+ if self.service_data is not None:
+ properties['ServiceData'] = dbus.Dictionary(self.service_data,
+ signature='say')
+ return {LE_ADVERTISEMENT_IFACE: properties}
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_service_uuid(self, uuid):
+ if not self.service_uuids:
+ self.service_uuids = []
+ self.service_uuids.append(uuid)
+
+ def add_solicit_uuid(self, uuid):
+ if not self.solicit_uuids:
+ self.solicit_uuids = []
+ self.solicit_uuids.append(uuid)
+
+ def add_manufacturer_data(self, manuf_code, data):
+ if not self.manufacturer_data:
+ self.manufacturer_data = dict()
+ self.manufacturer_data[manuf_code] = data
+
+ def add_service_data(self, uuid, data):
+ if not self.service_data:
+ self.service_data = dict()
+ self.service_data[uuid] = data
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ print 'GetAll'
+ if interface != LE_ADVERTISEMENT_IFACE:
+ raise InvalidArgsException()
+ print 'returning props'
+ return self.get_properties()[LE_ADVERTISEMENT_IFACE]
+
+ @dbus.service.method(LE_ADVERTISEMENT_IFACE,
+ in_signature='',
+ out_signature='')
+ def Release(self):
+ print '%s: Released!' % self.path
+
+class TestAdvertisement(Advertisement):
+
+ def __init__(self, bus, index):
+ Advertisement.__init__(self, bus, index, 'broadcast')
+ self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB')
+ self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB')
+ self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
+ self.add_service_data('00009999-0000-1000-8000-00805F9B34FB',
+ [0x00, 0x01, 0x02, 0x03, 0x04])
+
+
+def register_ad_cb():
+ print 'Advertisement registered'
+
+
+def register_ad_error_cb(error):
+ print 'Failed to register advertisement: ' + str(error)
+ mainloop.quit()
+
+
+def find_adapter(bus):
+ remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
+ DBUS_OM_IFACE)
+ objects = remote_om.GetManagedObjects()
+
+ for o, props in objects.iteritems():
+ if LE_ADVERTISING_MANAGER_IFACE in props:
+ return o
+
+ return None
+
+
+def main():
+ global mainloop
+
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ adapter = find_adapter(bus)
+ if not adapter:
+ print 'LEAdvertisingManager1 interface not found'
+ return
+
+ ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+ LE_ADVERTISING_MANAGER_IFACE)
+
+ test_advertisement = TestAdvertisement(bus, 0)
+
+ mainloop = gobject.MainLoop()
+
+ ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
+ reply_handler=register_ad_cb,
+ error_handler=register_ad_error_cb)
+
+ mainloop.run()
+
+if __name__ == '__main__':
+ main()
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:37

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 07/13] core/advertising: Parse ServiceUUIDs

Parse the ServiceUUIDs property of the LEAdvertisement1 object.
---
src/advertising.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)

diff --git a/src/advertising.c b/src/advertising.c
index ff082ae..6568dd0 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -166,6 +166,44 @@ static bool parse_advertising_type(GDBusProxy *proxy, uint8_t *type)
return false;
}

+static bool parse_advertising_service_uuids(GDBusProxy *proxy,
+ struct advertising_data *data)
+{
+ DBusMessageIter iter, ariter;
+
+ if (!g_dbus_proxy_get_property(proxy, "ServiceUUIDs", &iter))
+ return true;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &ariter);
+
+ advertising_data_clear_service_uuid(data);
+
+ while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
+ const char *uuid_str;
+ bt_uuid_t uuid;
+
+ dbus_message_iter_get_basic(&ariter, &uuid_str);
+
+ DBG("Adding ServiceUUID: %s", uuid_str);
+
+ if (bt_string_to_uuid(&uuid, uuid_str) < 0)
+ goto fail;
+
+ advertising_data_add_service_uuid(data, &uuid);
+
+ dbus_message_iter_next(&ariter);
+ }
+
+ return true;
+
+fail:
+ advertising_data_clear_service_uuid(data);
+ return false;
+}
+
static void refresh_advertisement(struct advertisement *ad)
{
DBG("Refreshing advertisement: %s", ad->path);
@@ -178,7 +216,12 @@ static bool parse_advertisement(struct advertisement *ad)
return false;
}

- /* TODO: parse the remaining properties into a shared structure */
+ if (!parse_advertising_service_uuids(ad->proxy, ad->data)) {
+ error("Property \"ServiceUUIDs\" failed to parse");
+ return false;
+ }
+
+ /* TODO: parse the rest of the properties */

refresh_advertisement(ad);

--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:41

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 11/13] Update TODO for LE Advertising

---
TODO | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/TODO b/TODO
index 65e19ee..2945392 100644
--- a/TODO
+++ b/TODO
@@ -64,16 +64,16 @@ General
Low Energy
==========

-- Advertising management. Adapter interface needs to be changed to manage
- connection modes, adapter type and advertising policy. See Volume 3,
- Part C, section 9.3. If Attribute Server is enabled the LE capable
- adapter shall to start advertising. Further investigation is necessary
- to define which connectable mode needs to be supported: Non-connectable,
- directed connectable and undirected connectable. Basically, two connectable
- scenarios shall be addressed:
- 1. GATT client is disconnected, but intends to become a Peripheral to
- receive indications/notifications.
- 2. GATT server intends to accept connections.
+- Connection modes. Adapter interface needs to be changed to manage
+ connection modes and adapter type. See Volume 3, Part C, section 9.3.
+ 1. Mode management: Peripheral / Central
+
+ Priority: Medium
+ Complexity: C2
+
+- Advertising data. The D-Bus interface needs to be updated to enable setting
+ scan response data, and to read the advertising and scan response data which
+ has been broadcast from other LE devices.

Priority: Medium
Complexity: C2
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:40

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 10/13] core/advertising: Parse ServiceData

Parse the ServiceData property of LEAdvertisement1
---
src/advertising.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)

diff --git a/src/advertising.c b/src/advertising.c
index 5646e68..3705a22 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -293,6 +293,61 @@ fail:
return false;
}

+static bool parse_advertising_service_data(GDBusProxy *proxy,
+ struct advertising_data *data)
+{
+ DBusMessageIter iter, entries;
+
+ if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
+ return true;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &entries);
+
+ advertising_data_clear_manufacturer_data(data);
+
+ while (dbus_message_iter_get_arg_type(&entries)
+ == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter value, entry;
+ const char *uuid_str;
+ bt_uuid_t uuid;
+ uint8_t *service_data;
+ int len;
+
+ dbus_message_iter_recurse(&entries, &entry);
+ dbus_message_iter_get_basic(&entry, &uuid_str);
+
+ if (bt_string_to_uuid(&uuid, uuid_str) < 0)
+ goto fail;
+
+ dbus_message_iter_next(&entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_fixed_array(&value, &service_data, &len);
+
+ DBG("Adding ServiceData for %s", uuid_str);
+
+ advertising_data_add_service_data(data, &uuid,
+ service_data, len);
+
+ dbus_message_iter_next(&entries);
+ }
+
+ return true;
+
+fail:
+ advertising_data_clear_manufacturer_data(data);
+ return false;
+}
+
static void refresh_advertisement(struct advertisement *ad)
{
DBG("Refreshing advertisement: %s", ad->path);
@@ -320,6 +375,11 @@ static bool parse_advertisement(struct advertisement *ad)
return false;
}

+ if (!parse_advertising_service_data(ad->proxy, ad->data)) {
+ error("Property \"ServiceData\" failed to parse");
+ return false;
+ }
+
/* TODO: parse the rest of the properties */

refresh_advertisement(ad);
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:31

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 01/13] core: advertising: add LEAdvertisingManager stubs

Introducing src/advertising which will implement the
org.bluez.LEAdvertisingManager1 D-Bus interface defined in
doc/advertising-api.txt. Each LE-capable controller gets
an instance of the interface.
---
Makefile.am | 1 +
src/adapter.c | 19 ++++++++
src/advertising.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/advertising.h | 25 +++++++++++
4 files changed, 177 insertions(+)
create mode 100644 src/advertising.c
create mode 100644 src/advertising.h

diff --git a/Makefile.am b/Makefile.am
index af15e9e..676d929 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,6 +175,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/uinput.h \
src/plugin.h src/plugin.c \
src/storage.h src/storage.c \
+ src/advertising.h src/advertising.c \
src/agent.h src/agent.c \
src/error.h src/error.c \
src/adapter.h src/adapter.c \
diff --git a/src/adapter.c b/src/adapter.c
index 6eeb2f9..200ea94 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -74,6 +74,7 @@
#include "attrib/gatt.h"
#include "attrib-server.h"
#include "gatt-database.h"
+#include "advertising.h"
#include "eir.h"

#define ADAPTER_INTERFACE "org.bluez.Adapter1"
@@ -210,6 +211,7 @@ struct btd_adapter {
sdp_list_t *services; /* Services associated to adapter */

struct btd_gatt_database *database;
+ struct btd_advertising *adv_manager;

gboolean initialized;

@@ -4607,6 +4609,9 @@ static void adapter_remove(struct btd_adapter *adapter)
btd_gatt_database_destroy(adapter->database);
adapter->database = NULL;

+ btd_advertising_manager_destroy(adapter->adv_manager);
+ adapter->adv_manager = NULL;
+
g_slist_free(adapter->pin_callbacks);
adapter->pin_callbacks = NULL;

@@ -6671,6 +6676,20 @@ static int adapter_register(struct btd_adapter *adapter)
return -EINVAL;
}

+ /* Don't start advertising managers on non-LE controllers. */
+ if (adapter->supported_settings & MGMT_SETTING_LE) {
+ adapter->adv_manager = btd_advertising_manager_new(adapter);
+ if (!adapter->adv_manager) {
+ error("Failed to register LEAdvertisingManager1 "
+ "interface for adapter");
+ btd_gatt_database_destroy(adapter->database);
+ adapter->database = NULL;
+ return -EINVAL;
+ }
+ } else {
+ info("Not starting LEAdvertisingManager, LE not supported");
+ }
+
db = btd_gatt_database_get_db(adapter->database);
adapter->db_id = gatt_db_register(db, services_modified,
services_modified,
diff --git a/src/advertising.c b/src/advertising.c
new file mode 100644
index 0000000..2f1a09a
--- /dev/null
+++ b/src/advertising.c
@@ -0,0 +1,132 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * 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.
+ *
+ */
+
+#include "advertising.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "adapter.h"
+#include "dbus-common.h"
+#include "log.h"
+#include "src/shared/util.h"
+
+#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
+#define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"
+
+struct btd_advertising {
+ struct btd_adapter *adapter;
+};
+
+static DBusMessage *register_advertisement(DBusConnection *conn,
+ DBusMessage *msg,
+ void *user_data)
+{
+ DBG("RegisterAdvertisement");
+
+ /* TODO */
+ return NULL;
+}
+
+static DBusMessage *unregister_advertisement(DBusConnection *conn,
+ DBusMessage *msg,
+ void *user_data)
+{
+ DBG("UnregisterAdvertisement");
+
+ /* TODO */
+ return NULL;
+}
+
+static const GDBusMethodTable methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterAdvertisement",
+ GDBUS_ARGS({ "advertisement", "o" },
+ { "options", "a{sv}" }),
+ NULL, register_advertisement) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
+ GDBUS_ARGS({ "service", "o" }),
+ NULL,
+ unregister_advertisement) },
+ { }
+};
+
+static void advertising_manager_destroy(void *user_data)
+{
+ struct btd_advertising *manager = user_data;
+
+ free(manager);
+}
+
+static struct btd_advertising *
+advertising_manager_create(struct btd_adapter *adapter)
+{
+ struct btd_advertising *manager;
+
+ manager = new0(struct btd_advertising, 1);
+ if (!manager)
+ return NULL;
+
+ manager->adapter = adapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ LE_ADVERTISING_MGR_IFACE,
+ methods, NULL, NULL, manager,
+ advertising_manager_destroy)) {
+ error("Failed to register " LE_ADVERTISING_MGR_IFACE);
+ free(manager);
+ return NULL;
+ }
+
+ return manager;
+}
+
+struct btd_advertising *
+btd_advertising_manager_new(struct btd_adapter *adapter)
+{
+ struct btd_advertising *manager;
+
+ if (!adapter)
+ return NULL;
+
+ manager = advertising_manager_create(adapter);
+ if (!manager)
+ return NULL;
+
+ DBG("LE Advertising Manager created for adapter: %s",
+ adapter_get_path(adapter));
+
+ return manager;
+}
+
+void btd_advertising_manager_destroy(struct btd_advertising *manager)
+{
+ if (!manager)
+ return;
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(manager->adapter),
+ LE_ADVERTISING_MGR_IFACE);
+}
diff --git a/src/advertising.h b/src/advertising.h
new file mode 100644
index 0000000..a4b99ff
--- /dev/null
+++ b/src/advertising.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * 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.
+ *
+ */
+
+struct btd_adapter;
+struct btd_advertising;
+
+struct btd_advertising *btd_advertising_manager_new(
+ struct btd_adapter *adapter);
+void btd_advertising_manager_destroy(struct btd_advertising *manager);
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:36

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 06/13] core/advertising: use advertising_data

Start using the newly introduced struct advertising_data API.
---
src/advertising.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/advertising.c b/src/advertising.c
index 8ab1b76..ff082ae 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -32,6 +32,7 @@
#include "dbus-common.h"
#include "error.h"
#include "log.h"
+#include "src/shared/advertising-data.h"
#include "src/shared/queue.h"
#include "src/shared/util.h"

@@ -54,6 +55,7 @@ struct advertisement {
GDBusProxy *proxy;
DBusMessage *reg;
uint8_t type; /* Advertising type */
+ struct advertising_data *data;
};

static bool match_advertisement_path(const void *a, const void *b)
@@ -73,8 +75,9 @@ static void advertisement_free(void *data)
g_dbus_client_unref(ad->client);
}

- if (ad->proxy)
- g_dbus_proxy_unref(ad->proxy);
+ advertising_data_unref(ad->data);
+
+ g_dbus_proxy_unref(ad->proxy);

if (ad->owner)
g_free(ad->owner);
@@ -244,6 +247,10 @@ static struct advertisement *advertisement_create(DBusConnection *conn,

ad->reg = dbus_message_ref(msg);

+ ad->data = advertising_data_new();
+ if (!ad->data)
+ goto fail;
+
return ad;

fail:
--
2.2.0.rc0.207.ga3a616c


2015-03-26 22:08:32

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ v6 02/13] core/advertising: Implement RegisterAdvertisement

Initial implementation of the RegisterAdvertisement function of the
LEAdvertisingManager1 interface.
---
src/advertising.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 248 insertions(+), 1 deletion(-)

diff --git a/src/advertising.c b/src/advertising.c
index 2f1a09a..2be4343 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -30,7 +30,9 @@

#include "adapter.h"
#include "dbus-common.h"
+#include "error.h"
#include "log.h"
+#include "src/shared/queue.h"
#include "src/shared/util.h"

#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
@@ -38,15 +40,256 @@

struct btd_advertising {
struct btd_adapter *adapter;
+ struct queue *adverts;
};

+#define AD_TYPE_BROADCAST 0
+#define AD_TYPE_PERIPHERAL 1
+
+struct advertisement {
+ struct btd_advertising *manager;
+ char *owner;
+ char *path;
+ GDBusClient *client;
+ GDBusProxy *proxy;
+ DBusMessage *reg;
+ uint8_t type; /* Advertising type */
+};
+
+static bool match_advertisement_path(const void *a, const void *b)
+{
+ const struct advertisement *ad = a;
+ const char *path = b;
+
+ return g_strcmp0(ad->path, path);
+}
+
+static void advertisement_free(void *data)
+{
+ struct advertisement *ad = data;
+
+ if (ad->client) {
+ g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+ g_dbus_client_unref(ad->client);
+ }
+
+ if (ad->proxy)
+ g_dbus_proxy_unref(ad->proxy);
+
+ if (ad->owner)
+ g_free(ad->owner);
+
+ if (ad->path)
+ g_free(ad->path);
+
+ free(ad);
+}
+
+static gboolean advertisement_free_idle_cb(void *data)
+{
+ advertisement_free(data);
+
+ return FALSE;
+}
+
+static void advertisement_release(void *data)
+{
+ struct advertisement *ad = data;
+ DBusMessage *message;
+
+ DBG("Releasing advertisement %s, %s", ad->owner, ad->path);
+
+ message = dbus_message_new_method_call(ad->owner, ad->path,
+ LE_ADVERTISEMENT_IFACE,
+ "Release");
+
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ g_dbus_send_message(btd_get_dbus_connection(), message);
+}
+
+static void advertisement_destroy(void *data)
+{
+ advertisement_release(data);
+ advertisement_free(data);
+}
+
+static void advertisement_remove(void *data)
+{
+ struct advertisement *ad = data;
+
+ g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+
+ queue_remove(ad->manager->adverts, ad);
+
+ g_idle_add(advertisement_free_idle_cb, ad);
+}
+
+static void client_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+ DBG("Client disconnected");
+
+ advertisement_remove(user_data);
+}
+
+static bool parse_advertising_type(GDBusProxy *proxy, uint8_t *type)
+{
+ DBusMessageIter iter;
+ const char *msg_type;
+
+ if (!g_dbus_proxy_get_property(proxy, "Type", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &msg_type);
+
+ if (!g_strcmp0(msg_type, "broadcast")) {
+ *type = AD_TYPE_BROADCAST;
+ return true;
+ }
+
+ if (!g_strcmp0(msg_type, "peripheral")) {
+ *type = AD_TYPE_PERIPHERAL;
+ return true;
+ }
+
+ return false;
+}
+
+static void refresh_advertisement(struct advertisement *ad)
+{
+ DBG("Refreshing advertisement: %s", ad->path);
+}
+
+static bool parse_advertisement(struct advertisement *ad)
+{
+ if (!parse_advertising_type(ad->proxy, &ad->type)) {
+ error("Failed to read \"Type\" property of advertisement");
+ return false;
+ }
+
+ /* TODO: parse the remaining properties into a shared structure */
+
+ refresh_advertisement(ad);
+
+ return true;
+}
+
+static void advertisement_proxy_added(GDBusProxy *proxy, void *data)
+{
+ struct advertisement *ad = data;
+ DBusMessage *reply;
+
+ if (!parse_advertisement(ad)) {
+ error("Failed to parse advertisement");
+
+ reply = btd_error_failed(ad->reg,
+ "Failed to register advertisement");
+ goto done;
+ }
+
+ g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
+ ad);
+
+ reply = dbus_message_new_method_return(ad->reg);
+
+ DBG("Advertisement registered: %s", ad->path);
+
+done:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(ad->reg);
+ ad->reg = NULL;
+}
+
+static struct advertisement *advertisement_create(DBusConnection *conn,
+ DBusMessage *msg, const char *path)
+{
+ struct advertisement *ad;
+ const char *sender = dbus_message_get_sender(msg);
+
+ if (!path || !g_str_has_prefix(path, "/"))
+ return NULL;
+
+ ad = new0(struct advertisement, 1);
+ if (!ad)
+ return NULL;
+
+ ad->client = g_dbus_client_new_full(conn, sender, path, path);
+ if (!ad->client)
+ goto fail;
+
+ ad->owner = g_strdup(sender);
+ if (!ad->owner)
+ goto fail;
+
+ ad->path = g_strdup(path);
+ if (!ad->path)
+ goto fail;
+
+ DBG("Adding proxy for %s", path);
+ ad->proxy = g_dbus_proxy_new(ad->client, path, LE_ADVERTISEMENT_IFACE);
+ if (!ad->proxy)
+ goto fail;
+
+ g_dbus_client_set_proxy_handlers(ad->client, advertisement_proxy_added,
+ NULL, NULL, ad);
+
+ ad->reg = dbus_message_ref(msg);
+
+ return ad;
+
+fail:
+ advertisement_free(ad);
+ return NULL;
+}
+
static DBusMessage *register_advertisement(DBusConnection *conn,
DBusMessage *msg,
void *user_data)
{
+ struct btd_advertising *manager = user_data;
+ DBusMessageIter args;
+ const char *path;
+ struct advertisement *ad;
+
DBG("RegisterAdvertisement");

- /* TODO */
+ if (!dbus_message_iter_init(msg, &args))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&args, &path);
+
+ if (queue_find(manager->adverts, match_advertisement_path, path))
+ return btd_error_already_exists(msg);
+
+ /* TODO: support more than one advertisement */
+ if (!queue_isempty(manager->adverts))
+ return btd_error_failed(msg, "Already advertising");
+
+ dbus_message_iter_next(&args);
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+ return btd_error_invalid_args(msg);
+
+ ad = advertisement_create(conn, msg, path);
+ if (!ad)
+ return btd_error_failed(msg,
+ "Failed to register advertisement");
+
+ DBG("Registered advertisement at path %s", path);
+
+ ad->manager = manager;
+ queue_push_tail(manager->adverts, ad);
+
return NULL;
}

@@ -76,6 +319,8 @@ static void advertising_manager_destroy(void *user_data)
{
struct btd_advertising *manager = user_data;

+ queue_destroy(manager->adverts, advertisement_destroy);
+
free(manager);
}

@@ -100,6 +345,8 @@ advertising_manager_create(struct btd_adapter *adapter)
return NULL;
}

+ manager->adverts = queue_new();
+
return manager;
}

--
2.2.0.rc0.207.ga3a616c