2015-03-12 17:11:47

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 00/11] Introduce LE Advertisement D-Bus API

Introduces and starts implementation of a LE Advertisement API.

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

Michael Janssen (12):
doc: Add LE Advertisement D-Bus API documentation
core: advertising: add LEAdvertisingManager stubs
advertising-manager: Implement RegisterAdvertisement
tools: Python script to test Advertisement API
advertising-manager: implement UnregisterAdvertisement
core: add advertising-data
advertising-manager: use advertising_data
advertising-manager: Parse ServiceUUIDs
advertising-manager: Parse SolicitUUIDs
advertising-manager: Parse ManufacturerSpecificData
advertising-manager: Parse ServiceData
Update TODO for LE Advertising

Makefile.am | 3 +
TODO | 21 +-
doc/advertising-api.txt | 94 ++++++
src/adapter.c | 19 ++
src/advertising-manager.c | 646 ++++++++++++++++++++++++++++++++++++++++++
src/advertising-manager.h | 25 ++
src/shared/advertising-data.c | 350 +++++++++++++++++++++++
src/shared/advertising-data.h | 69 +++++
tools/advertisement-example | 174 ++++++++++++
9 files changed, 1391 insertions(+), 10 deletions(-)
create mode 100644 doc/advertising-api.txt
create mode 100644 src/advertising-manager.c
create mode 100644 src/advertising-manager.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-13 08:11:34

by Szymon Janc

[permalink] [raw]
Subject: Re: [BlueZ 01/12] doc: Add LE Advertisement D-Bus API documentation

Hi Michael,

On Thursday 12 of March 2015 10:11:48 Michael Janssen wrote:
> The LE Advertisement API allows external appications to define
> persistent LE Advertisement Data packets.
> ---
> doc/advertising-api.txt | 94
> +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94
> insertions(+)
> create mode 100644 doc/advertising-api.txt
>
> diff --git a/doc/advertising-api.txt b/doc/advertising-api.txt
> new file mode 100644
> index 0000000..6bbe485
> --- /dev/null
> +++ b/doc/advertising-api.txt
> @@ -0,0 +1,94 @@
> +BlueZ D-Bus LE Advertising API Description
> +******************************************
> +
> +Advertising packets are structured data which is broadcast on the LE
> Advertising +channels and available for all devices in range. Because of
> the limited space +available in LE Advertising packets (32 bytes) , each
> packet's contents must be +controllable.
> +
> +BlueZ acts as a store for the multiple sets of Advertisement Data which is
> meant +to be sent. It constructs the correct Advertisement Data from the
> structured +data and communicates the sets of data to the kernel, where it
> is +multiplexed.
> +
> +Advertisement Data objects are registered freely and then referenced by
> BlueZ +when constructing the data sent to the kernel.
> +
> +LE Advertisement Data hierarchy
> +===============================
> +
> +Specifies the Advertisement Data to be broadcast and some advertising
> +parameters. Properties which are not present will not be included in the
> +data. Required advertisement data types will always be included.
> +All UUIDs are 128-bit versions in the API, and 16 or 32-bit
> +versions of the same UUID will be used in the advertising data as
> appropriate. +
> +Service org.bluez
> +Interface org.bluez.LEAdvertisement1
> +Object path freely definable
> +
> +Properties string Type
> +
> + Determines the type of advertising packet requested.
> +
> + Possible values: "broadcast" or "peripheral"
> +
> + array{string} ServiceUUIDs
> +
> + List of UUIDs to include in the "Service UUID" field of
> + the Advertising Data.
> +
> + dict ManufacturerSpecificData
> +
> + Manufactuer Specific Data fields to include in
> + the Advertising Data. Keys are the Manufacturer ID
> + to associate with the data.
> +
> + bool IncludePower
> +
> + If present and true, the TX Power Level will be included
> + in the Advertising Data.
> +
> + array{string} SolicitUUIDs
> +
> + Array of UUIDs to include in "Service Solicitation"
> + Advertisement Data.
> +
> + dict ServiceData
> +
> + Service Data elements to include. The keys are the
> + UUID to associate with the data.
> +
> +
> +LE Advertising Manager hierarchy
> +================================
> +
> +The Advertising Manager allows external applications to register
> Advertisement +Data which should be broadcast to devices. Advertisement
> Data elements must +follow the API for LE Advertisement Data described
> above.
> +
> +Service org.bluez
> +Interface org.bluez.LEAdvertisingManager1 [Experimental]
> +Object path /org/bluez/{hci0,hci1,...}
> +
> +Methods RegisterAdvertisement(object advertisement, dict options)
> +
> + Registers an advertisement object to be sent over the LE
> + Advertising channel. The service must be exported
> + under interface LEAdvertisement1. InvalidArguments
> + indicates that the object has invalid or conflicting
> + properties. InvalidLength indicates that the data
> + provided generates a data packet which is too long.
> +
> + Possible errors: org.bluez.Error.InvalidArguments
> + org.bluez.Error.AlreadyExists
> + org.bluez.Error.InvalidLength
> +
> + UnregisterAdvertisement(object advertisement)
> +
> + This unregisters the advertisement that has been
> + prevously registered. The object path parameter must
> + match the same value that has been used on registration.
> +
> + Possible errors: org.bluez.Error.InvalidArguments
> + org.bluez.Error.DoesNotExist

If app is allowed to change props values then there should be some way (eg
Unregister(error)) to report InvalidArguments error similar to what
RegisterAdvertisement() is reporting.

Also there should be a note that props change should be reported all-at-once
since otherwise this could be racy.


--
BR
Szymon Janc

2015-03-12 20:16:52

by Arman Uguray

[permalink] [raw]
Subject: Re: [BlueZ 09/12] advertising-manager: Parse SolicitUUIDs

Hi Michael,

> On Thu, Mar 12, 2015 at 10:11 AM, Michael Janssen <[email protected]> wrote:
> Parse the SolicitUUIDs property of the LEAdvertisement1 object.
> ---
> src/advertising-manager.c | 43 +++++++++++++++++++++++++++++++++++++++++++
> tools/advertisement-example | 5 +++++

Patches that touch core and those that touch tools should go in their
separate patches.

> 2 files changed, 48 insertions(+)
>
> diff --git a/src/advertising-manager.c b/src/advertising-manager.c
> index 92e5cad..e7c24c6 100644
> --- a/src/advertising-manager.c
> +++ b/src/advertising-manager.c
> @@ -212,6 +212,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);
> @@ -232,6 +270,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 correctly");
> + return false;
> + }
> +
> /* TODO: parse the rest of the properties */
>
> refresh_advertisement(ad);
> diff --git a/tools/advertisement-example b/tools/advertisement-example
> index fb2bdde..2227009 100644
> --- a/tools/advertisement-example
> +++ b/tools/advertisement-example
> @@ -78,6 +78,11 @@ class Advertisement(dbus.service.Object):
> 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_specific_data:
> self.manufacturer_specific_data = dict()
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> 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

Thanks,
Arman

2015-03-12 20:15:23

by Arman Uguray

[permalink] [raw]
Subject: Re: [BlueZ 07/12] advertising-manager: use advertising_data

Hi Michael,

> On Thu, Mar 12, 2015 at 10:11 AM, Michael Janssen <[email protected]> wrote:
> Start using the newly introduced struct advertising_data API.
> ---
> src/advertising-manager.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/src/advertising-manager.c b/src/advertising-manager.c
> index 5045a06..3f53637 100644
> --- a/src/advertising-manager.c
> +++ b/src/advertising-manager.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"
>
> @@ -56,6 +57,7 @@ struct advertisement {
> uint8_t type; /* Advertising type */
> bool random;
> bool published;
> + struct advertising_data *data;
> };
>
> static bool match_advertisement_path(const void *a, const void *b)
> @@ -76,6 +78,9 @@ static void advertisement_free(struct advertisement *ad)
> g_dbus_client_unref(ad->client);
> }
>
> + if (ad->data)
> + advertising_data_unref(ad->data);
> +

The checks for NULL are probably not needed since unref should
silently fail if NULL is passed.

> if (ad->proxy)
> g_dbus_proxy_unref(ad->proxy);
>
> @@ -272,6 +277,10 @@ static struct advertisement *advertisement_create(DBusConnection *conn,
> if (!ad->path)
> goto fail;
>
> + ad->data = advertising_data_new();
> + if (!ad->data)
> + goto fail;
> +
> ad->reg = dbus_message_ref(msg);
>
> g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> 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

Arman

2015-03-12 20:13:16

by Arman Uguray

[permalink] [raw]
Subject: Re: [BlueZ 06/12] core: add advertising-data

Hi Michael,

> On Thu, Mar 12, 2015 at 10:11 AM, Michael Janssen <[email protected]> wrote:
> 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.

The subject should say "shared:" rather than "core:", since the latter
has been used for the desktop daemon's core sources only (e.g.
adapter, device, etc).

I'll let the others comment on this, I don't have any objections as
this seems like a useful container structure for AD.

> ---
> Makefile.am | 2 +
> src/shared/advertising-data.c | 350 ++++++++++++++++++++++++++++++++++++++++++
> src/shared/advertising-data.h | 69 +++++++++
> 3 files changed, 421 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 38f9420..bab1502 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..4f86a78
> --- /dev/null
> +++ b/src/shared/advertising-data.c
> @@ -0,0 +1,350 @@
> +/*
> + *
> + * 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
>
> --
> 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

Thanks,
Arman

2015-03-12 20:00:02

by Arman Uguray

[permalink] [raw]
Subject: Re: [BlueZ 04/12] tools: Python script to test Advertisement API

Hi Michael,

> On Thu, Mar 12, 2015 at 10:11 AM, Michael Janssen <[email protected]> wrote:
> This script provides an example of using the advertisement-data
> D-Bus API from python.
> ---
> tools/advertisement-example | 160 ++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 160 insertions(+)
> create mode 100644 tools/advertisement-example
>
> diff --git a/tools/advertisement-example b/tools/advertisement-example
> new file mode 100644
> index 0000000..dcd2ea2
> --- /dev/null
> +++ b/tools/advertisement-example
> @@ -0,0 +1,160 @@
> +#!/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_specific_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_specific_data is not None:
> + properties['ManufacturerSpecificData'] = dbus.Dictionary(
> + self.manufacturer_specific_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_manufacturer_data(self, manuf_code, data):
> + if not self.manufacturer_specific_data:
> + self.manufacturer_specific_data = dict()
> + self.manufacturer_specific_data[manuf_code] = 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(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
> + def GetManagedObjects(self):

Nowhere in the documentation do we mention that ObjectManager is
required for this API and frankly I don't think it's necessary, since
you only register one LEAdvertisement1 object at a time (whose path
you provide to RegisterAdvertisement already). ObjectManager was
necessary for the GATT API, since we needed to atomically fetch the
object paths and properties of an entire
Service/Characteristic/Descriptor tree, but in the LEAdvertisement1
case it's much simpler.

> + response = {}
> + print 'GetManagedObjects'
> +
> + print self.get_properties()
> + response[self.get_path()] = self.get_properties()
> + return response
> +
> +
> +class TestAdvertisement(Advertisement):
> +
> + def __init__(self, bus, index):
> + Advertisement.__init__(self, bus, index, 'broadcast')
> + self.add_manufacturer_data(0x00ff, 'testing')
> +
> +
> +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
>
> --
> 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

Thanks,
Arman

2015-03-12 19:55:46

by Arman Uguray

[permalink] [raw]
Subject: Re: [BlueZ 03/12] advertising-manager: Implement RegisterAdvertisement

Hi Michael,

> On Thu, Mar 12, 2015 at 10:11 AM, Michael Janssen <[email protected]> wrote:
> Implement the RegisterAdvertisement method of the LEAdvertisingManager1
> interface.
> ---
> src/advertising-manager.c | 297 +++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 295 insertions(+), 2 deletions(-)
>
> diff --git a/src/advertising-manager.c b/src/advertising-manager.c
> index 754b6ff..76dcad9 100644
> --- a/src/advertising-manager.c
> +++ b/src/advertising-manager.c
> @@ -19,27 +19,318 @@
>
> #include "advertising-manager.h"
>
> +#include <stdbool.h>
> +#include <stdint.h>
> +
> #include <dbus/dbus.h>
> #include <gdbus/gdbus.h>
>
> +#include <lib/bluetooth.h>
> +#include <lib/bluetooth/sdp.h>
> +

If you're including the Linux includes from the system include path,
then these should look like this:

#include <bluetooth/bluetooth.h>

Otherwise, if you're including headers in lib/ then they should be in quotes:

#include "lib/bluetooth.h"

I honestly don't know which one we prefer, so maybe somebody else can
comment on that. Also, why is the sdp.h header needed?

> #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"
> +#define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"
>
> struct btd_advertising_manager {
> struct btd_adapter *adapter;
> + struct queue *adverts;
> +};
> +
> +#define AD_TYPE_BROADCAST 0
> +#define AD_TYPE_PERIPHERAL 1
> +
> +struct advertisement {
> + struct btd_advertising_manager *manager;
> + char *owner;
> + char *path;
> + DBusMessage *reg;
> + GDBusClient *client;

Aren't we registering a single object? I don't think GDBusClient is
suitable here, which interfaces with DBus.ObjectManager (which then
has to be an API requirement), I think just fetching the properties
via GetAll might be sufficient, so just a GDBusProxy should be
sufficient.

> + GDBusProxy *proxy;
> + uint8_t type; /* Advertising type */
> + bool random;
> + bool published;
> };
>
> +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(struct advertisement *ad)
> +{
> + if (ad->client) {
> + g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
> + g_dbus_client_set_proxy_handlers(ad->client, NULL, NULL, NULL,
> + NULL);
> + g_dbus_client_set_ready_watch(ad->client, NULL, NULL);
> + g_dbus_client_unref(ad->client);
> + }
> +
> + if (ad->proxy)
> + g_dbus_proxy_unref(ad->proxy);
> +
> + if (ad->reg)
> + dbus_message_unref(ad->reg);
> +
> + 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_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 void proxy_added_cb(GDBusProxy *proxy, void *user_data)
> +{
> + struct advertisement *ad = user_data;
> + const char *iface, *path;
> +
> + iface = g_dbus_proxy_get_interface(proxy);
> + path = g_dbus_proxy_get_path(proxy);
> +
> + if (g_strcmp0(iface, LE_ADVERTISEMENT_IFACE))
> + return;
> +
> + DBG("Object added to advertisement - path: %s iface: %s", path, iface);
> +
> + ad->proxy = g_dbus_proxy_ref(proxy);
> +}
> +
> +static void proxy_removed_cb(GDBusProxy *proxy, void *user_data)
> +{
> + struct advertisement *ad = user_data;
> +
> + DBG("Proxy removed, removing ad: %s", ad->path);
> +
> + advertisement_remove(ad);
> +}
> +
> +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);
> + if (!ad->published) {
> + /* TODO: MGMT API call to update the advertisement */
> + }
> +}
> +
> +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 property_changed_cb(GDBusProxy *proxy, const char *name,
> + DBusMessageIter *iter, void *user_data)
> +{
> + struct advertisement *ad = user_data;
> +
> + if (!parse_advertisement(ad)) {
> + error("Failed to parse updated advertisement");
> + return;
> + }
> +}
> +
> +static void client_ready_cb(GDBusClient *client, void *user_data)
> +{
> + struct advertisement *ad = user_data;
> +
> + DBusMessage *reply;
> +
> + bool fail = false;
> +
> + if (!ad->proxy) {
> + error("No advertisement object!");
> + fail = true;
> + reply = btd_error_failed(ad->reg,
> + "No advertisement object found");
> + goto reply;
> + }
> +
> + if (!parse_advertisement(ad)) {
> + error("Failed to add advertisement to controller");
> + fail = true;
> + reply = btd_error_failed(ad->reg,
> + "Failed to add advertisement to controller");
> + goto reply;
> + }
> +
> + if (g_dbus_proxy_set_property_watch(ad->proxy, property_changed_cb, ad)
> + == FALSE) {
> + error("Failed to set up property watch for advertisement");
> + reply = btd_error_failed(ad->reg,
> + "Can't watch advertisement object");
> + goto reply;
> + }
> +
> + DBG("Advertisement registered: %s", ad->path);
> +
> + reply = dbus_message_new_method_return(ad->reg);
> +
> +reply:
> + g_dbus_send_message(btd_get_dbus_connection(), reply);
> + dbus_message_unref(ad->reg);
> + ad->reg = NULL;
> +
> + if (fail)
> + advertisement_remove(ad);
> +}
> +
> +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;
> +
> + ad->reg = dbus_message_ref(msg);
> +
> + g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
> + ad);
> +
> + g_dbus_client_set_proxy_handlers(ad->client, proxy_added_cb,
> + proxy_removed_cb, NULL, ad);
> +
> + g_dbus_client_set_ready_watch(ad->client, client_ready_cb, ad);
> +
> + ad->random = false;
> + ad->published = false;
> +
> + return ad;
> +
> +fail:
> + advertisement_free(ad);
> + return NULL;
> +}
> +
> static DBusMessage *register_advertisement(DBusConnection *conn,
> DBusMessage *msg,
> void *user_data)
> {
> + struct btd_advertising_manager *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;
> }
>
> @@ -79,13 +370,15 @@ advertising_manager_create(struct btd_adapter *adapter)
> if (!g_dbus_register_interface(btd_get_dbus_connection(),
> adapter_get_path(adapter),
> LE_ADVERTISING_MGR_IFACE,
> - methods, NULL, NULL, adapter,
> + methods, NULL, NULL, manager,
> NULL)) {
> error("Failed to register " LE_ADVERTISING_MGR_IFACE);
> free(manager);
> return NULL;
> }
>
> + manager->adverts = queue_new();
> +

You'll need to destroy manager->adverts in manager's destructor.

> return manager;
> }
>
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> 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

Thanks,
Arman

2015-03-12 19:42:11

by Arman Uguray

[permalink] [raw]
Subject: Re: [BlueZ 02/12] core: advertising: add LEAdvertisingManager stubs

Hi Michael,

> On Thu, Mar 12, 2015 at 10:11 AM, Michael Janssen <[email protected]> wrote:
> Introducing src/advertising-manager 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-manager.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++
> src/advertising-manager.h | 25 ++++++++++
> 4 files changed, 165 insertions(+)
> create mode 100644 src/advertising-manager.c
> create mode 100644 src/advertising-manager.h
>
> diff --git a/Makefile.am b/Makefile.am
> index d0da411..38f9420 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-manager.h src/advertising-manager.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 329e9b4..d4401ec 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-manager.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_manager *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;
>
> @@ -6679,6 +6684,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) {

Did you mean to do a bitwise-& here?

> + 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 {
> + error("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-manager.c b/src/advertising-manager.c
> new file mode 100644
> index 0000000..754b6ff
> --- /dev/null
> +++ b/src/advertising-manager.c
> @@ -0,0 +1,120 @@
> +/*
> + *
> + * 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-manager.h"
> +
> +#include <dbus/dbus.h>
> +#include <gdbus/gdbus.h>
> +
> +#include "adapter.h"
> +#include "dbus-common.h"
> +#include "log.h"
> +#include "src/shared/util.h"
> +
> +#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
> +
> +struct btd_advertising_manager {
> + 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"},

nit: I would pick a consistent style for static initializers and
spacing, i.e either {a, b} or { a, b } (not sure what the recommended
style is for BlueZ), you seem to have a mix of everything here.

> + { "options", "a{sv}" }),
> + NULL, register_advertisement) },
> + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
> + GDBUS_ARGS({"service", "o"}),
> + NULL,
> + unregister_advertisement) },
> + { }
> +};
> +
> +static struct btd_advertising_manager *
> +advertising_manager_create(struct btd_adapter *adapter)
> +{
> + struct btd_advertising_manager *manager;
> +
> + manager = new0(struct btd_advertising_manager, 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, adapter,

The user_data should be |manager| here instead of |adapter|, no?

> + NULL)) {
> + error("Failed to register " LE_ADVERTISING_MGR_IFACE);
> + free(manager);
> + return NULL;
> + }
> +
> + return manager;
> +}
> +
> +struct btd_advertising_manager *
> +btd_advertising_manager_new(struct btd_adapter *adapter)
> +{
> + struct btd_advertising_manager *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 *manager)
> +{
> + if (!manager)
> + return;
> +
> + g_dbus_unregister_interface(btd_get_dbus_connection(),
> + adapter_get_path(manager->adapter),
> + LE_ADVERTISING_MGR_IFACE);
> +
> + free(manager);

You can also pass "free" as the destroy function to
g_dbus_register_interface above, if you pass "manager" as the user
data.

> +}
> diff --git a/src/advertising-manager.h b/src/advertising-manager.h
> new file mode 100644
> index 0000000..4046013
> --- /dev/null
> +++ b/src/advertising-manager.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_manager;
> +
> +struct btd_advertising_manager *btd_advertising_manager_new(
> + struct btd_adapter *adapter);
> +void btd_advertising_manager_destroy(struct btd_advertising_manager *manager);
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> 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

Thanks,
Arman

2015-03-12 19:32:51

by Arman Uguray

[permalink] [raw]
Subject: Re: [BlueZ 01/12] doc: Add LE Advertisement D-Bus API documentation

Hi Michael,

> On Thu, Mar 12, 2015 at 10:11 AM, Michael Janssen <[email protected]> wrote:
> The LE Advertisement API allows external appications to define
> persistent LE Advertisement Data packets.
> ---
> doc/advertising-api.txt | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 94 insertions(+)
> create mode 100644 doc/advertising-api.txt
>
> diff --git a/doc/advertising-api.txt b/doc/advertising-api.txt
> new file mode 100644
> index 0000000..6bbe485
> --- /dev/null
> +++ b/doc/advertising-api.txt
> @@ -0,0 +1,94 @@
> +BlueZ D-Bus LE Advertising API Description
> +******************************************
> +
> +Advertising packets are structured data which is broadcast on the LE Advertising
> +channels and available for all devices in range. Because of the limited space
> +available in LE Advertising packets (32 bytes) , each packet's contents must be

nit: Remove space before comma (after "(32 bytes)").

> +controllable.
> +
> +BlueZ acts as a store for the multiple sets of Advertisement Data which is meant

nit: s/which is/which are/

> +to be sent. It constructs the correct Advertisement Data from the structured
> +data and communicates the sets of data to the kernel, where it is
> +multiplexed.

I think this description might be confusing for the application
developers. I wouldn't assume that the reader has prior knowledge of
what "kernel multiplexing the data" means. Instead, just explain here
how different registered advertisement data objects will look like to
the outside word, more in layman's terms.

> +
> +Advertisement Data objects are registered freely and then referenced by BlueZ
> +when constructing the data sent to the kernel.
> +
> +LE Advertisement Data hierarchy
> +===============================
> +
> +Specifies the Advertisement Data to be broadcast and some advertising
> +parameters. Properties which are not present will not be included in the
> +data. Required advertisement data types will always be included.
> +All UUIDs are 128-bit versions in the API, and 16 or 32-bit
> +versions of the same UUID will be used in the advertising data as appropriate.
> +
> +Service org.bluez
> +Interface org.bluez.LEAdvertisement1
> +Object path freely definable
> +
> +Properties string Type
> +
> + Determines the type of advertising packet requested.
> +
> + Possible values: "broadcast" or "peripheral"
> +
> + array{string} ServiceUUIDs
> +
> + List of UUIDs to include in the "Service UUID" field of
> + the Advertising Data.
> +
> + dict ManufacturerSpecificData
> +
> + Manufactuer Specific Data fields to include in
> + the Advertising Data. Keys are the Manufacturer ID
> + to associate with the data.
> +
> + bool IncludePower
> +
> + If present and true, the TX Power Level will be included
> + in the Advertising Data.
> +
> + array{string} SolicitUUIDs
> +
> + Array of UUIDs to include in "Service Solicitation"
> + Advertisement Data.
> +
> + dict ServiceData
> +
> + Service Data elements to include. The keys are the
> + UUID to associate with the data.
> +
> +
> +LE Advertising Manager hierarchy
> +================================
> +
> +The Advertising Manager allows external applications to register Advertisement
> +Data which should be broadcast to devices. Advertisement Data elements must
> +follow the API for LE Advertisement Data described above.
> +
> +Service org.bluez
> +Interface org.bluez.LEAdvertisingManager1 [Experimental]
> +Object path /org/bluez/{hci0,hci1,...}
> +
> +Methods RegisterAdvertisement(object advertisement, dict options)
> +
> + Registers an advertisement object to be sent over the LE
> + Advertising channel. The service must be exported
> + under interface LEAdvertisement1. InvalidArguments
> + indicates that the object has invalid or conflicting
> + properties. InvalidLength indicates that the data
> + provided generates a data packet which is too long.

Is an application allowed to register more then one advertisement
object at a time? If not, you should specify that here.

> +
> + Possible errors: org.bluez.Error.InvalidArguments
> + org.bluez.Error.AlreadyExists
> + org.bluez.Error.InvalidLength
> +
> + UnregisterAdvertisement(object advertisement)
> +
> + This unregisters the advertisement that has been
> + prevously registered. The object path parameter must
> + match the same value that has been used on registration.
> +
> + Possible errors: org.bluez.Error.InvalidArguments
> + org.bluez.Error.DoesNotExist
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> 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

Otherwise this should be ready to merge, as I think we all have
consensus on this now. Luiz, any objections?

Thanks,
Arman

2015-03-12 17:11:57

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 10/12] advertising-manager: Parse ManufacturerSpecificData

Parse the ManufacturerSpecificData property of the LEAdvertisement1
---
src/advertising-manager.c | 58 +++++++++++++++++++++++++++++++++++++++++++++
tools/advertisement-example | 1 +
2 files changed, 59 insertions(+)

diff --git a/src/advertising-manager.c b/src/advertising-manager.c
index e7c24c6..cb76482 100644
--- a/src/advertising-manager.c
+++ b/src/advertising-manager.c
@@ -250,6 +250,58 @@ 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, "ManufacturerSpecificData",
+ &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 ManufactuerSpecificData 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);
@@ -275,6 +327,12 @@ static bool parse_advertisement(struct advertisement *ad)
return false;
}

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

refresh_advertisement(ad);
diff --git a/tools/advertisement-example b/tools/advertisement-example
index 2227009..96554dd 100644
--- a/tools/advertisement-example
+++ b/tools/advertisement-example
@@ -114,6 +114,7 @@ class TestAdvertisement(Advertisement):
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])


def register_ad_cb():
--
2.2.0.rc0.207.ga3a616c


2015-03-12 17:11:56

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 09/12] advertising-manager: Parse SolicitUUIDs

Parse the SolicitUUIDs property of the LEAdvertisement1 object.
---
src/advertising-manager.c | 43 +++++++++++++++++++++++++++++++++++++++++++
tools/advertisement-example | 5 +++++
2 files changed, 48 insertions(+)

diff --git a/src/advertising-manager.c b/src/advertising-manager.c
index 92e5cad..e7c24c6 100644
--- a/src/advertising-manager.c
+++ b/src/advertising-manager.c
@@ -212,6 +212,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);
@@ -232,6 +270,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 correctly");
+ return false;
+ }
+
/* TODO: parse the rest of the properties */

refresh_advertisement(ad);
diff --git a/tools/advertisement-example b/tools/advertisement-example
index fb2bdde..2227009 100644
--- a/tools/advertisement-example
+++ b/tools/advertisement-example
@@ -78,6 +78,11 @@ class Advertisement(dbus.service.Object):
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_specific_data:
self.manufacturer_specific_data = dict()
--
2.2.0.rc0.207.ga3a616c


2015-03-12 17:11:49

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 02/12] core: advertising: add LEAdvertisingManager stubs

Introducing src/advertising-manager 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-manager.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++
src/advertising-manager.h | 25 ++++++++++
4 files changed, 165 insertions(+)
create mode 100644 src/advertising-manager.c
create mode 100644 src/advertising-manager.h

diff --git a/Makefile.am b/Makefile.am
index d0da411..38f9420 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-manager.h src/advertising-manager.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 329e9b4..d4401ec 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-manager.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_manager *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;

@@ -6679,6 +6684,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 {
+ error("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-manager.c b/src/advertising-manager.c
new file mode 100644
index 0000000..754b6ff
--- /dev/null
+++ b/src/advertising-manager.c
@@ -0,0 +1,120 @@
+/*
+ *
+ * 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-manager.h"
+
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "adapter.h"
+#include "dbus-common.h"
+#include "log.h"
+#include "src/shared/util.h"
+
+#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
+
+struct btd_advertising_manager {
+ 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 struct btd_advertising_manager *
+advertising_manager_create(struct btd_adapter *adapter)
+{
+ struct btd_advertising_manager *manager;
+
+ manager = new0(struct btd_advertising_manager, 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, adapter,
+ NULL)) {
+ error("Failed to register " LE_ADVERTISING_MGR_IFACE);
+ free(manager);
+ return NULL;
+ }
+
+ return manager;
+}
+
+struct btd_advertising_manager *
+btd_advertising_manager_new(struct btd_adapter *adapter)
+{
+ struct btd_advertising_manager *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 *manager)
+{
+ if (!manager)
+ return;
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(manager->adapter),
+ LE_ADVERTISING_MGR_IFACE);
+
+ free(manager);
+}
diff --git a/src/advertising-manager.h b/src/advertising-manager.h
new file mode 100644
index 0000000..4046013
--- /dev/null
+++ b/src/advertising-manager.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_manager;
+
+struct btd_advertising_manager *btd_advertising_manager_new(
+ struct btd_adapter *adapter);
+void btd_advertising_manager_destroy(struct btd_advertising_manager *manager);
--
2.2.0.rc0.207.ga3a616c


2015-03-12 17:11:55

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 08/12] advertising-manager: Parse ServiceUUIDs

Parse the ServiceUUIDs property of the LEAdvertisement1 object.
---
src/advertising-manager.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
tools/advertisement-example | 3 ++-
2 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/src/advertising-manager.c b/src/advertising-manager.c
index 3f53637..92e5cad 100644
--- a/src/advertising-manager.c
+++ b/src/advertising-manager.c
@@ -174,6 +174,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);
@@ -189,7 +227,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 correctly");
+ return false;
+ }
+
+ /* TODO: parse the rest of the properties */

refresh_advertisement(ad);

diff --git a/tools/advertisement-example b/tools/advertisement-example
index dcd2ea2..fb2bdde 100644
--- a/tools/advertisement-example
+++ b/tools/advertisement-example
@@ -107,7 +107,8 @@ class TestAdvertisement(Advertisement):

def __init__(self, bus, index):
Advertisement.__init__(self, bus, index, 'broadcast')
- self.add_manufacturer_data(0x00ff, 'testing')
+ self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB')
+ self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB')


def register_ad_cb():
--
2.2.0.rc0.207.ga3a616c


2015-03-12 17:11:50

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 03/12] advertising-manager: Implement RegisterAdvertisement

Implement the RegisterAdvertisement method of the LEAdvertisingManager1
interface.
---
src/advertising-manager.c | 297 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 295 insertions(+), 2 deletions(-)

diff --git a/src/advertising-manager.c b/src/advertising-manager.c
index 754b6ff..76dcad9 100644
--- a/src/advertising-manager.c
+++ b/src/advertising-manager.c
@@ -19,27 +19,318 @@

#include "advertising-manager.h"

+#include <stdbool.h>
+#include <stdint.h>
+
#include <dbus/dbus.h>
#include <gdbus/gdbus.h>

+#include <lib/bluetooth.h>
+#include <lib/bluetooth/sdp.h>
+
#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"
+#define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"

struct btd_advertising_manager {
struct btd_adapter *adapter;
+ struct queue *adverts;
+};
+
+#define AD_TYPE_BROADCAST 0
+#define AD_TYPE_PERIPHERAL 1
+
+struct advertisement {
+ struct btd_advertising_manager *manager;
+ char *owner;
+ char *path;
+ DBusMessage *reg;
+ GDBusClient *client;
+ GDBusProxy *proxy;
+ uint8_t type; /* Advertising type */
+ bool random;
+ bool published;
};

+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(struct advertisement *ad)
+{
+ if (ad->client) {
+ g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+ g_dbus_client_set_proxy_handlers(ad->client, NULL, NULL, NULL,
+ NULL);
+ g_dbus_client_set_ready_watch(ad->client, NULL, NULL);
+ g_dbus_client_unref(ad->client);
+ }
+
+ if (ad->proxy)
+ g_dbus_proxy_unref(ad->proxy);
+
+ if (ad->reg)
+ dbus_message_unref(ad->reg);
+
+ 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_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 void proxy_added_cb(GDBusProxy *proxy, void *user_data)
+{
+ struct advertisement *ad = user_data;
+ const char *iface, *path;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (g_strcmp0(iface, LE_ADVERTISEMENT_IFACE))
+ return;
+
+ DBG("Object added to advertisement - path: %s iface: %s", path, iface);
+
+ ad->proxy = g_dbus_proxy_ref(proxy);
+}
+
+static void proxy_removed_cb(GDBusProxy *proxy, void *user_data)
+{
+ struct advertisement *ad = user_data;
+
+ DBG("Proxy removed, removing ad: %s", ad->path);
+
+ advertisement_remove(ad);
+}
+
+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);
+ if (!ad->published) {
+ /* TODO: MGMT API call to update the advertisement */
+ }
+}
+
+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 property_changed_cb(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct advertisement *ad = user_data;
+
+ if (!parse_advertisement(ad)) {
+ error("Failed to parse updated advertisement");
+ return;
+ }
+}
+
+static void client_ready_cb(GDBusClient *client, void *user_data)
+{
+ struct advertisement *ad = user_data;
+
+ DBusMessage *reply;
+
+ bool fail = false;
+
+ if (!ad->proxy) {
+ error("No advertisement object!");
+ fail = true;
+ reply = btd_error_failed(ad->reg,
+ "No advertisement object found");
+ goto reply;
+ }
+
+ if (!parse_advertisement(ad)) {
+ error("Failed to add advertisement to controller");
+ fail = true;
+ reply = btd_error_failed(ad->reg,
+ "Failed to add advertisement to controller");
+ goto reply;
+ }
+
+ if (g_dbus_proxy_set_property_watch(ad->proxy, property_changed_cb, ad)
+ == FALSE) {
+ error("Failed to set up property watch for advertisement");
+ reply = btd_error_failed(ad->reg,
+ "Can't watch advertisement object");
+ goto reply;
+ }
+
+ DBG("Advertisement registered: %s", ad->path);
+
+ reply = dbus_message_new_method_return(ad->reg);
+
+reply:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(ad->reg);
+ ad->reg = NULL;
+
+ if (fail)
+ advertisement_remove(ad);
+}
+
+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;
+
+ ad->reg = dbus_message_ref(msg);
+
+ g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
+ ad);
+
+ g_dbus_client_set_proxy_handlers(ad->client, proxy_added_cb,
+ proxy_removed_cb, NULL, ad);
+
+ g_dbus_client_set_ready_watch(ad->client, client_ready_cb, ad);
+
+ ad->random = false;
+ ad->published = false;
+
+ return ad;
+
+fail:
+ advertisement_free(ad);
+ return NULL;
+}
+
static DBusMessage *register_advertisement(DBusConnection *conn,
DBusMessage *msg,
void *user_data)
{
+ struct btd_advertising_manager *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;
}

@@ -79,13 +370,15 @@ advertising_manager_create(struct btd_adapter *adapter)
if (!g_dbus_register_interface(btd_get_dbus_connection(),
adapter_get_path(adapter),
LE_ADVERTISING_MGR_IFACE,
- methods, NULL, NULL, adapter,
+ methods, NULL, NULL, manager,
NULL)) {
error("Failed to register " LE_ADVERTISING_MGR_IFACE);
free(manager);
return NULL;
}

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

--
2.2.0.rc0.207.ga3a616c


2015-03-12 17:11:59

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 12/12] Update TODO for LE Advertising

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

diff --git a/TODO b/TODO
index 65e19ee..3bef10b 100644
--- a/TODO
+++ b/TODO
@@ -64,16 +64,17 @@ 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. A MGMT interface is needed for setting LE Advertisement Data
+ and Scan Response data. An adapter should be advertising whenever it is
+ appropriate to advertise, and it should be possible to determine what advertising
+ is occuring.

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


2015-03-12 17:11:51

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 04/12] 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 | 160 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
create mode 100644 tools/advertisement-example

diff --git a/tools/advertisement-example b/tools/advertisement-example
new file mode 100644
index 0000000..dcd2ea2
--- /dev/null
+++ b/tools/advertisement-example
@@ -0,0 +1,160 @@
+#!/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_specific_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_specific_data is not None:
+ properties['ManufacturerSpecificData'] = dbus.Dictionary(
+ self.manufacturer_specific_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_manufacturer_data(self, manuf_code, data):
+ if not self.manufacturer_specific_data:
+ self.manufacturer_specific_data = dict()
+ self.manufacturer_specific_data[manuf_code] = 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(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+ def GetManagedObjects(self):
+ response = {}
+ print 'GetManagedObjects'
+
+ print self.get_properties()
+ response[self.get_path()] = self.get_properties()
+ return response
+
+
+class TestAdvertisement(Advertisement):
+
+ def __init__(self, bus, index):
+ Advertisement.__init__(self, bus, index, 'broadcast')
+ self.add_manufacturer_data(0x00ff, 'testing')
+
+
+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-12 17:11:58

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 11/12] advertising-manager: Parse ServiceData

Parse the ServiceData property of LEAdvertisement1
---
src/advertising-manager.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
tools/advertisement-example | 7 ++++++
2 files changed, 67 insertions(+)

diff --git a/src/advertising-manager.c b/src/advertising-manager.c
index cb76482..73fab4e 100644
--- a/src/advertising-manager.c
+++ b/src/advertising-manager.c
@@ -302,6 +302,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);
@@ -333,6 +388,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 correctly");
+ return false;
+ }
+
/* TODO: parse the rest of the properties */

refresh_advertisement(ad);
diff --git a/tools/advertisement-example b/tools/advertisement-example
index 96554dd..7846f1d 100644
--- a/tools/advertisement-example
+++ b/tools/advertisement-example
@@ -88,6 +88,11 @@ class Advertisement(dbus.service.Object):
self.manufacturer_specific_data = dict()
self.manufacturer_specific_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}')
@@ -115,6 +120,8 @@ class TestAdvertisement(Advertisement):
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():
--
2.2.0.rc0.207.ga3a616c


2015-03-12 17:11:52

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 05/12] advertising-manager: implement UnregisterAdvertisement

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

diff --git a/src/advertising-manager.c b/src/advertising-manager.c
index 76dcad9..5045a06 100644
--- a/src/advertising-manager.c
+++ b/src/advertising-manager.c
@@ -104,6 +104,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);
@@ -338,10 +340,28 @@ static DBusMessage *unregister_advertisement(DBusConnection *conn,
DBusMessage *msg,
void *user_data)
{
+ struct btd_advertising_manager *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-12 17:11:53

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 06/12] core: 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 | 350 ++++++++++++++++++++++++++++++++++++++++++
src/shared/advertising-data.h | 69 +++++++++
3 files changed, 421 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 38f9420..bab1502 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..4f86a78
--- /dev/null
+++ b/src/shared/advertising-data.c
@@ -0,0 +1,350 @@
+/*
+ *
+ * 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-12 17:11:48

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 01/12] doc: Add LE Advertisement D-Bus API documentation

The LE Advertisement API allows external appications to define
persistent LE Advertisement Data packets.
---
doc/advertising-api.txt | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 doc/advertising-api.txt

diff --git a/doc/advertising-api.txt b/doc/advertising-api.txt
new file mode 100644
index 0000000..6bbe485
--- /dev/null
+++ b/doc/advertising-api.txt
@@ -0,0 +1,94 @@
+BlueZ D-Bus LE Advertising API Description
+******************************************
+
+Advertising packets are structured data which is broadcast on the LE Advertising
+channels and available for all devices in range. Because of the limited space
+available in LE Advertising packets (32 bytes) , each packet's contents must be
+controllable.
+
+BlueZ acts as a store for the multiple sets of Advertisement Data which is meant
+to be sent. It constructs the correct Advertisement Data from the structured
+data and communicates the sets of data to the kernel, where it is
+multiplexed.
+
+Advertisement Data objects are registered freely and then referenced by BlueZ
+when constructing the data sent to the kernel.
+
+LE Advertisement Data hierarchy
+===============================
+
+Specifies the Advertisement Data to be broadcast and some advertising
+parameters. Properties which are not present will not be included in the
+data. Required advertisement data types will always be included.
+All UUIDs are 128-bit versions in the API, and 16 or 32-bit
+versions of the same UUID will be used in the advertising data as appropriate.
+
+Service org.bluez
+Interface org.bluez.LEAdvertisement1
+Object path freely definable
+
+Properties string Type
+
+ Determines the type of advertising packet requested.
+
+ Possible values: "broadcast" or "peripheral"
+
+ array{string} ServiceUUIDs
+
+ List of UUIDs to include in the "Service UUID" field of
+ the Advertising Data.
+
+ dict ManufacturerSpecificData
+
+ Manufactuer Specific Data fields to include in
+ the Advertising Data. Keys are the Manufacturer ID
+ to associate with the data.
+
+ bool IncludePower
+
+ If present and true, the TX Power Level will be included
+ in the Advertising Data.
+
+ array{string} SolicitUUIDs
+
+ Array of UUIDs to include in "Service Solicitation"
+ Advertisement Data.
+
+ dict ServiceData
+
+ Service Data elements to include. The keys are the
+ UUID to associate with the data.
+
+
+LE Advertising Manager hierarchy
+================================
+
+The Advertising Manager allows external applications to register Advertisement
+Data which should be broadcast to devices. Advertisement Data elements must
+follow the API for LE Advertisement Data described above.
+
+Service org.bluez
+Interface org.bluez.LEAdvertisingManager1 [Experimental]
+Object path /org/bluez/{hci0,hci1,...}
+
+Methods RegisterAdvertisement(object advertisement, dict options)
+
+ Registers an advertisement object to be sent over the LE
+ Advertising channel. The service must be exported
+ under interface LEAdvertisement1. InvalidArguments
+ indicates that the object has invalid or conflicting
+ properties. InvalidLength indicates that the data
+ provided generates a data packet which is too long.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+ org.bluez.Error.InvalidLength
+
+ UnregisterAdvertisement(object advertisement)
+
+ This unregisters the advertisement that has been
+ prevously registered. The object path parameter must
+ match the same value that has been used on registration.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.DoesNotExist
--
2.2.0.rc0.207.ga3a616c


2015-03-12 17:11:54

by Marie Janssen

[permalink] [raw]
Subject: [BlueZ 07/12] advertising-manager: use advertising_data

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

diff --git a/src/advertising-manager.c b/src/advertising-manager.c
index 5045a06..3f53637 100644
--- a/src/advertising-manager.c
+++ b/src/advertising-manager.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"

@@ -56,6 +57,7 @@ struct advertisement {
uint8_t type; /* Advertising type */
bool random;
bool published;
+ struct advertising_data *data;
};

static bool match_advertisement_path(const void *a, const void *b)
@@ -76,6 +78,9 @@ static void advertisement_free(struct advertisement *ad)
g_dbus_client_unref(ad->client);
}

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

@@ -272,6 +277,10 @@ static struct advertisement *advertisement_create(DBusConnection *conn,
if (!ad->path)
goto fail;

+ ad->data = advertising_data_new();
+ if (!ad->data)
+ goto fail;
+
ad->reg = dbus_message_ref(msg);

g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
--
2.2.0.rc0.207.ga3a616c