2016-05-09 13:51:18

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue

From: Luiz Augusto von Dentz <[email protected]>

This adds the possibility to pass an offset to these operations, and
also in the server case to give the device object.
---
v2: Fix Vinicius comments, add necessary changes to other tools affected.

doc/gatt-api.txt | 20 ++++-
src/gatt-client.c | 180 ++++++++++++++++++++++++++++--------------
src/gatt-database.c | 219 ++++++++++++++++++++++++++++++++++++++--------------
3 files changed, 296 insertions(+), 123 deletions(-)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index ad2ab16..683b1b7 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -61,23 +61,29 @@ Service org.bluez
Interface org.bluez.GattCharacteristic1 [Experimental]
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY

-Methods array{byte} ReadValue()
+Methods array{byte} ReadValue(dict options)

Issues a request to read the value of the
characteristic and returns the value if the
operation was successful.

+ Possible options: "offset": uint16 offset
+ "device": Object Device (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
org.bluez.Error.NotAuthorized
org.bluez.Error.NotSupported

- void WriteValue(array{byte} value)
+ void WriteValue(array{byte} value, dict options)

Issues a request to write the value of the
characteristic.

+ Possible options: "offset": Start offset
+ "device": Device path (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
@@ -154,23 +160,29 @@ Service org.bluez
Interface org.bluez.GattDescriptor1 [Experimental]
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ

-Methods array{byte} ReadValue()
+Methods array{byte} ReadValue(dict flags)

Issues a request to read the value of the
characteristic and returns the value if the
operation was successful.

+ Possible options: "offset": Start offset
+ "device": Device path (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
org.bluez.Error.NotAuthorized
org.bluez.Error.NotSupported

- void WriteValue(array{byte} value)
+ void WriteValue(array{byte} value, dict flags)

Issues a request to write the value of the
characteristic.

+ Possible options: "offset": Start offset
+ "device": Device path (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 16a1f6c..0cbacca 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -23,6 +23,7 @@

#include <stdbool.h>
#include <stdint.h>
+#include <errno.h>

#include <dbus/dbus.h>

@@ -191,33 +192,17 @@ static gboolean descriptor_value_exists(const GDBusPropertyTable *property,
return ret;
}

-static bool parse_value_arg(DBusMessage *msg, uint8_t **value,
- size_t *value_len)
+static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
{
- DBusMessageIter iter, array;
- uint8_t *val;
- int len;
-
- if (!dbus_message_iter_init(msg, &iter))
- return false;
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
- return false;
-
- dbus_message_iter_recurse(&iter, &array);
- dbus_message_iter_get_fixed_array(&array, &val, &len);
- dbus_message_iter_next(&iter);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
- return false;
+ DBusMessageIter array;

- if (len < 0)
- return false;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;

- *value = val;
- *value_len = len;
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, value, len);

- return true;
+ return 0;
}

typedef bool (*async_dbus_op_complete_t)(void *data);
@@ -390,12 +375,60 @@ fail:
return;
}

+static int parse_options(DBusMessageIter *iter, uint16_t *offset)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "offset") == 0) {
+ if (var != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, offset);
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int read_value(struct bt_gatt_client *gatt, uint16_t handle,
+ bt_gatt_client_read_callback_t callback,
+ struct async_dbus_op *op)
+{
+ if (op->offset)
+ return bt_gatt_client_read_long_value(gatt, handle, op->offset,
+ callback,
+ async_dbus_op_ref(op),
+ async_dbus_op_unref);
+ else
+ return bt_gatt_client_read_value(gatt, handle, callback,
+ async_dbus_op_ref(op),
+ async_dbus_op_unref);
+}
+
static DBusMessage *descriptor_read_value(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct descriptor *desc = user_data;
struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+ DBusMessageIter iter;
struct async_dbus_op *op;
+ uint16_t offset = 0;

if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -403,14 +436,17 @@ static DBusMessage *descriptor_read_value(DBusConnection *conn,
if (desc->read_id)
return btd_error_in_progress(msg);

+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_options(&iter, &offset))
+ return btd_error_invalid_args(msg);
+
op = new0(struct async_dbus_op, 1);
op->msg = dbus_message_ref(msg);
op->data = desc;
+ op->offset = offset;

- desc->read_id = bt_gatt_client_read_value(gatt, desc->handle,
- desc_read_cb,
- async_dbus_op_ref(op),
- async_dbus_op_unref);
+ desc->read_id = read_value(gatt, desc->handle, desc_read_cb, op);
if (desc->read_id)
return NULL;

@@ -450,7 +486,6 @@ done:
g_dbus_send_message(btd_get_dbus_connection(), reply);
}

-
static void write_cb(bool success, uint8_t att_ecode, void *user_data)
{
write_result_cb(success, false, att_ecode, user_data);
@@ -459,7 +494,8 @@ static void write_cb(bool success, uint8_t att_ecode, void *user_data)
static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
struct bt_gatt_client *gatt,
bool reliable, const uint8_t *value,
- size_t value_len, void *data,
+ size_t value_len, uint16_t offset,
+ void *data,
async_dbus_op_complete_t complete)
{
struct async_dbus_op *op;
@@ -469,9 +505,10 @@ static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
op->msg = dbus_message_ref(msg);
op->data = data;
op->complete = complete;
+ op->offset = offset;

- id = bt_gatt_client_write_long_value(gatt, reliable, handle,
- 0, value, value_len,
+ id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset,
+ value, value_len,
write_result_cb, op,
async_dbus_op_free);

@@ -522,8 +559,10 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn,
{
struct descriptor *desc = user_data;
struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+ DBusMessageIter iter;
uint8_t *value = NULL;
- size_t value_len = 0;
+ int value_len = 0;
+ uint16_t offset = 0;

if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -531,7 +570,12 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn,
if (desc->write_id)
return btd_error_in_progress(msg);

- if (!parse_value_arg(msg, &value, &value_len))
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_value_arg(&iter, &value, &value_len))
+ return btd_error_invalid_args(msg);
+
+ if (parse_options(&iter, &offset))
return btd_error_invalid_args(msg);

/*
@@ -546,15 +590,15 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn,
* Based on the value length and the MTU, either use a write or a long
* write.
*/
- if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3)
+ if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset)
desc->write_id = start_write_request(msg, desc->handle,
gatt, value,
value_len, desc,
desc_write_complete);
else
- desc->write_id = start_long_write(msg, desc->handle,
- gatt, false, value,
- value_len, desc,
+ desc->write_id = start_long_write(msg, desc->handle, gatt,
+ false, value,
+ value_len, offset, desc,
desc_write_complete);

if (!desc->write_id)
@@ -574,13 +618,15 @@ static const GDBusPropertyTable descriptor_properties[] = {
};

static const GDBusMethodTable descriptor_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
- GDBUS_ARGS({ "value", "ay" }),
- descriptor_read_value) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
+ GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ descriptor_read_value) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
- GDBUS_ARGS({ "value", "ay" }),
- NULL,
- descriptor_write_value) },
+ GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL,
+ descriptor_write_value) },
{ }
};

@@ -837,7 +883,9 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn,
{
struct characteristic *chrc = user_data;
struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ DBusMessageIter iter;
struct async_dbus_op *op;
+ uint16_t offset = 0;

if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -845,14 +893,17 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn,
if (chrc->read_id)
return btd_error_in_progress(msg);

+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_options(&iter, &offset))
+ return btd_error_invalid_args(msg);
+
op = new0(struct async_dbus_op, 1);
op->msg = dbus_message_ref(msg);
op->data = chrc;
+ op->offset = offset;

- chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle,
- chrc_read_cb,
- async_dbus_op_ref(op),
- async_dbus_op_unref);
+ chrc->read_id = read_value(gatt, chrc->value_handle, chrc_read_cb, op);
if (chrc->read_id)
return NULL;

@@ -879,9 +930,11 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
{
struct characteristic *chrc = user_data;
struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ DBusMessageIter iter;
uint8_t *value = NULL;
- size_t value_len = 0;
+ int value_len = 0;
bool supported = false;
+ uint16_t offset = 0;

if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -889,7 +942,12 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
if (chrc->write_id)
return btd_error_in_progress(msg);

- if (!parse_value_arg(msg, &value, &value_len))
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_value_arg(&iter, &value, &value_len))
+ return btd_error_invalid_args(msg);
+
+ if (parse_options(&iter, &offset))
return btd_error_invalid_args(msg);

/*
@@ -906,7 +964,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
supported = true;
chrc->write_id = start_long_write(msg, chrc->value_handle, gatt,
- true, value, value_len,
+ true, value, value_len, offset,
chrc, chrc_write_complete);
if (chrc->write_id)
return NULL;
@@ -920,7 +978,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
if (!mtu)
return btd_error_failed(msg, "No ATT transport");

- if (value_len <= (unsigned) mtu - 3)
+ if (value_len <= mtu - 3 && !offset)
chrc->write_id = start_write_request(msg,
chrc->value_handle,
gatt, value, value_len,
@@ -928,7 +986,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
else
chrc->write_id = start_long_write(msg,
chrc->value_handle, gatt,
- false, value, value_len,
+ false, value, value_len, offset,
chrc, chrc_write_complete);

if (chrc->write_id)
@@ -1242,17 +1300,19 @@ static const GDBusPropertyTable characteristic_properties[] = {
};

static const GDBusMethodTable characteristic_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
- GDBUS_ARGS({ "value", "ay" }),
- characteristic_read_value) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
+ GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ characteristic_read_value) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
- GDBUS_ARGS({ "value", "ay" }),
- NULL,
- characteristic_write_value) },
+ GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL,
+ characteristic_write_value) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL,
- characteristic_start_notify) },
+ characteristic_start_notify) },
{ GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
- characteristic_stop_notify) },
+ characteristic_stop_notify) },
{ }
};

diff --git a/src/gatt-database.c b/src/gatt-database.c
index b8da955..99d084d 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -135,6 +135,7 @@ struct external_desc {
};

struct pending_op {
+ struct btd_device *device;
unsigned int id;
struct gatt_db_attribute *attrib;
struct queue *owner_queue;
@@ -1592,7 +1593,8 @@ static void pending_op_free(void *data)
free(op);
}

-static struct pending_op *pending_read_new(struct queue *owner_queue,
+static struct pending_op *pending_read_new(struct btd_device *device,
+ struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id)
{
@@ -1601,6 +1603,7 @@ static struct pending_op *pending_read_new(struct queue *owner_queue,
op = new0(struct pending_op, 1);

op->owner_queue = owner_queue;
+ op->device = device;
op->attrib = attrib;
op->id = id;
queue_push_tail(owner_queue, op);
@@ -1608,33 +1611,75 @@ static struct pending_op *pending_read_new(struct queue *owner_queue,
return op;
}

-static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
- struct queue *owner_queue,
- unsigned int id)
+static void append_options(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ const char *path = device_get_path(op->device);
+
+ dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void read_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ append_options(&dict, op);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static struct pending_op *send_read(struct btd_device *device,
+ struct gatt_db_attribute *attrib,
+ GDBusProxy *proxy,
+ struct queue *owner_queue,
+ unsigned int id)
{
struct pending_op *op;
- uint8_t ecode = BT_ATT_ERROR_UNLIKELY;

- op = pending_read_new(owner_queue, attrib, id);
+ op = pending_read_new(device, owner_queue, attrib, id);

- if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb,
- op, pending_op_free) == TRUE)
- return;
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb,
+ read_reply_cb, op, pending_op_free) == TRUE)
+ return op;

pending_op_free(op);

- gatt_db_attribute_read_result(attrib, id, ecode, NULL, 0);
+ return NULL;
}

static void write_setup_cb(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
- DBusMessageIter array;
+ DBusMessageIter array, dict;

dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&op->data.iov_base, op->data.iov_len);
dbus_message_iter_close_container(iter, &array);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ append_options(&dict, op);
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ if (!op->owner_queue) {
+ gatt_db_attribute_write_result(op->attrib, op->id, 0);
+ pending_op_free(op);
+ }
}

static void write_reply_cb(DBusMessage *message, void *user_data)
@@ -1673,7 +1718,8 @@ done:
gatt_db_attribute_write_result(op->attrib, op->id, ecode);
}

-static struct pending_op *pending_write_new(struct queue *owner_queue,
+static struct pending_op *pending_write_new(struct btd_device *device,
+ struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id,
const uint8_t *value,
@@ -1686,6 +1732,7 @@ static struct pending_op *pending_write_new(struct queue *owner_queue,
op->data.iov_base = (uint8_t *) value;
op->data.iov_len = len;

+ op->device = device;
op->owner_queue = owner_queue;
op->attrib = attrib;
op->id = id;
@@ -1694,24 +1741,25 @@ static struct pending_op *pending_write_new(struct queue *owner_queue,
return op;
}

-static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
+static struct pending_op *send_write(struct btd_device *device,
+ struct gatt_db_attribute *attrib,
+ GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id,
const uint8_t *value, size_t len)
{
struct pending_op *op;
- uint8_t ecode = BT_ATT_ERROR_UNLIKELY;

- op = pending_write_new(owner_queue, attrib, id, value, len);
+ op = pending_write_new(device, owner_queue, attrib, id, value, len);

if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
- write_reply_cb, op,
- pending_op_free) == TRUE)
- return;
+ owner_queue ? write_reply_cb : NULL,
+ op, pending_op_free) == TRUE)
+ return op;

pending_op_free(op);

- gatt_db_attribute_write_result(attrib, id, ecode);
+ return NULL;
}

static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props)
@@ -1895,19 +1943,65 @@ static bool database_add_cep(struct external_service *service,
return true;
}

+static struct btd_device *att_get_device(struct bt_att *att)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+ bdaddr_t src, dst;
+ uint8_t dst_type;
+ struct btd_adapter *adapter;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return NULL;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST_TYPE, &dst_type,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return NULL;
+ }
+
+ g_io_channel_unref(io);
+
+ adapter = adapter_find(&src);
+ if (!adapter) {
+ error("Unable to find adapter object");
+ return NULL;
+ }
+
+ return btd_adapter_find_device(adapter, &dst, dst_type);
+}
+
static void desc_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct external_desc *desc = user_data;
+ struct btd_device *device;

if (desc->attrib != attrib) {
error("Read callback called with incorrect attribute");
- return;
+ goto fail;
}

- send_read(attrib, desc->proxy, desc->pending_reads, id);
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
+ }
+
+ if (send_read(device, attrib, desc->proxy, desc->pending_reads, id))
+ return;
+
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}

static void desc_write_cb(struct gatt_db_attribute *attrib,
@@ -1917,13 +2011,26 @@ static void desc_write_cb(struct gatt_db_attribute *attrib,
void *user_data)
{
struct external_desc *desc = user_data;
+ struct btd_device *device;

if (desc->attrib != attrib) {
error("Read callback called with incorrect attribute");
- return;
+ goto fail;
}

- send_write(attrib, desc->proxy, desc->pending_writes, id, value, len);
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
+ }
+
+ if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
+ value, len))
+ return;
+
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}

static bool database_add_desc(struct external_service *service,
@@ -1956,43 +2063,25 @@ static void chrc_read_cb(struct gatt_db_attribute *attrib,
void *user_data)
{
struct external_chrc *chrc = user_data;
+ struct btd_device *device;

if (chrc->attrib != attrib) {
error("Read callback called with incorrect attribute");
- return;
+ goto fail;
}

- send_read(attrib, chrc->proxy, chrc->pending_reads, id);
-}
-
-static void write_without_response_setup_cb(DBusMessageIter *iter,
- void *user_data)
-{
- struct iovec *iov = user_data;
- DBusMessageIter array;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
- dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
- &iov->iov_base, iov->iov_len);
- dbus_message_iter_close_container(iter, &array);
-}
-
-static void send_write_without_response(struct gatt_db_attribute *attrib,
- GDBusProxy *proxy, unsigned int id,
- const uint8_t *value, size_t len)
-{
- struct iovec iov;
- uint8_t ecode = 0;
-
- iov.iov_base = (uint8_t *) value;
- iov.iov_len = len;
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
+ }

- if (!g_dbus_proxy_method_call(proxy, "WriteValue",
- write_without_response_setup_cb,
- NULL, &iov, NULL))
- ecode = BT_ATT_ERROR_UNLIKELY;
+ if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id))
+ return;

- gatt_db_attribute_write_result(attrib, id, ecode);
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}

static void chrc_write_cb(struct gatt_db_attribute *attrib,
@@ -2002,19 +2091,31 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
void *user_data)
{
struct external_chrc *chrc = user_data;
+ struct btd_device *device;
+ struct queue *queue;

if (chrc->attrib != attrib) {
error("Write callback called with incorrect attribute");
- return;
+ goto fail;
}

- if (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
- send_write_without_response(attrib, chrc->proxy, id, value,
- len);
- return;
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
}

- send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
+ if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+ queue = chrc->pending_writes;
+ else
+ queue = NULL;
+
+ if (send_write(device, attrib, chrc->proxy, queue, id, value, len))
+ return;
+
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}

static bool database_add_chrc(struct external_service *service,
--
2.5.5



Subject: RE: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue

Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz [mailto:[email protected]]
> Sent: Wednesday, May 18, 2016 3:56 PM
> To: Kasper Markus (ETAS-PSC/ECE1) <[email protected]>
> Cc: [email protected]
> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to
> ReadValue/WriteValue
>
> Hi,
>
> On Wed, May 18, 2016 at 4:55 PM, Luiz Augusto von Dentz
> <[email protected]> wrote:
> > Hi Markus,
> >
> > On Wed, May 18, 2016 at 10:59 AM, Kasper Markus (ETAS-PSC/ECE1)
> > <[email protected]> wrote:
> >> Hi,
> >>
> >>> -----Original Message-----
> >>> From: [email protected] [mailto:linux-bluetooth-
> >>> [email protected]] On Behalf Of Luiz Augusto von Dentz
> >>> Sent: Tuesday, May 17, 2016 4:22 PM
> >>> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options
> >>> dictionary to ReadValue/WriteValue
> >>>
> >>> Hi,
> >>>
> >>> On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
> >>> <[email protected]> wrote:
> >>> > Hi Luiz,
> >>> >
> >>> > Luiz Augusto von Dentz <[email protected]> writes:
> >>> >
> >>> >> From: Luiz Augusto von Dentz <[email protected]>
> >>> >>
> >>> >> This adds the possibility to pass an offset to these operations,
> >>> >> and also in the server case to give the device object.
> >>> >> ---
> >>> >> v2: Fix Vinicius comments, add necessary changes to other tools
> affected.
> >>> >>
> >>> >
> >>> > The series is looking good.
> >>>
> >>> If there are no comments to this set I will be pushing by the end of the day.
> >>
> >> Currently the write command inherently selects a write method based on the
> characteristics properties (write-without-response, reliable-write...).
> >> The option dictionary within the API allows for adding another option to
> override the default selection of the write method for characteristics supporting
> multiple write options.
> >> I thus consider this patch a more generic enabler within the given API to path
> the way for further improvements.
> >
> > Yes, I don't know where using a specific method would actually be
> > recommended since it the server should be able to indicate the proper
> > operation to use via properties but in case there is a real case where
> > the server has the properties somewhat broken then perhaps we can
> > include such feature.
> >
> > --
> > Luiz Augusto von Dentz
>
> Btw, these patches are now upstream.
>

I'm facing this problem for an iOS peripheral, where the properties of a characteristic are set to "write-without-response" only. The iOS stack adds a "reliable-write" descriptor to these characteristics, although write is not supported.
While I'm quite sure the Apple stack does not behave correctly when advertising the "reliable-write" extended property for a characteristic without "write" property, having control over bluez's writes would be the easiest way to handle this.
My current fix is a patched bluez: I modified the condition for a reliable write to check for both flags: "write" & "reliable-write" before using the reliable-write code.

Markus

> --
> Luiz Augusto von Dentz

2016-05-18 13:56:16

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue

Hi,

On Wed, May 18, 2016 at 4:55 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
> Hi Markus,
>
> On Wed, May 18, 2016 at 10:59 AM, Kasper Markus (ETAS-PSC/ECE1)
> <[email protected]> wrote:
>> Hi,
>>
>>> -----Original Message-----
>>> From: [email protected] [mailto:linux-bluetooth-
>>> [email protected]] On Behalf Of Luiz Augusto von Dentz
>>> Sent: Tuesday, May 17, 2016 4:22 PM
>>> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to
>>> ReadValue/WriteValue
>>>
>>> Hi,
>>>
>>> On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
>>> <[email protected]> wrote:
>>> > Hi Luiz,
>>> >
>>> > Luiz Augusto von Dentz <[email protected]> writes:
>>> >
>>> >> From: Luiz Augusto von Dentz <[email protected]>
>>> >>
>>> >> This adds the possibility to pass an offset to these operations, and
>>> >> also in the server case to give the device object.
>>> >> ---
>>> >> v2: Fix Vinicius comments, add necessary changes to other tools affected.
>>> >>
>>> >
>>> > The series is looking good.
>>>
>>> If there are no comments to this set I will be pushing by the end of the day.
>>
>> Currently the write command inherently selects a write method based on the characteristics properties (write-without-response, reliable-write...).
>> The option dictionary within the API allows for adding another option to override the default selection of the write method for characteristics supporting multiple write options.
>> I thus consider this patch a more generic enabler within the given API to path the way for further improvements.
>
> Yes, I don't know where using a specific method would actually be
> recommended since it the server should be able to indicate the proper
> operation to use via properties but in case there is a real case where
> the server has the properties somewhat broken then perhaps we can
> include such feature.
>
> --
> Luiz Augusto von Dentz

Btw, these patches are now upstream.

--
Luiz Augusto von Dentz

2016-05-18 13:55:51

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue

Hi Markus,

On Wed, May 18, 2016 at 10:59 AM, Kasper Markus (ETAS-PSC/ECE1)
<[email protected]> wrote:
> Hi,
>
>> -----Original Message-----
>> From: [email protected] [mailto:linux-bluetooth-
>> [email protected]] On Behalf Of Luiz Augusto von Dentz
>> Sent: Tuesday, May 17, 2016 4:22 PM
>> Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to
>> ReadValue/WriteValue
>>
>> Hi,
>>
>> On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
>> <[email protected]> wrote:
>> > Hi Luiz,
>> >
>> > Luiz Augusto von Dentz <[email protected]> writes:
>> >
>> >> From: Luiz Augusto von Dentz <[email protected]>
>> >>
>> >> This adds the possibility to pass an offset to these operations, and
>> >> also in the server case to give the device object.
>> >> ---
>> >> v2: Fix Vinicius comments, add necessary changes to other tools affected.
>> >>
>> >
>> > The series is looking good.
>>
>> If there are no comments to this set I will be pushing by the end of the day.
>
> Currently the write command inherently selects a write method based on the characteristics properties (write-without-response, reliable-write...).
> The option dictionary within the API allows for adding another option to override the default selection of the write method for characteristics supporting multiple write options.
> I thus consider this patch a more generic enabler within the given API to path the way for further improvements.

Yes, I don't know where using a specific method would actually be
recommended since it the server should be able to indicate the proper
operation to use via properties but in case there is a real case where
the server has the properties somewhat broken then perhaps we can
include such feature.

--
Luiz Augusto von Dentz

Subject: RE: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue

SGksIA0KDQo+IC0tLS0tT3JpZ2luYWwgTWVzc2FnZS0tLS0tDQo+IEZyb206IGxpbnV4LWJsdWV0
b290aC1vd25lckB2Z2VyLmtlcm5lbC5vcmcgW21haWx0bzpsaW51eC1ibHVldG9vdGgtDQo+IG93
bmVyQHZnZXIua2VybmVsLm9yZ10gT24gQmVoYWxmIE9mIEx1aXogQXVndXN0byB2b24gRGVudHoN
Cj4gU2VudDogVHVlc2RheSwgTWF5IDE3LCAyMDE2IDQ6MjIgUE0NCj4gU3ViamVjdDogUmU6IFtQ
QVRDSHYyIEJsdWVaIDEvNl0gZG9jL2dhdHQtYXBpOiBBZGQgb3B0aW9ucyBkaWN0aW9uYXJ5IHRv
DQo+IFJlYWRWYWx1ZS9Xcml0ZVZhbHVlDQo+IA0KPiBIaSwNCj4gDQo+IE9uIFR1ZSwgTWF5IDEw
LCAyMDE2IGF0IDE6NTcgQU0sIFZpbmljaXVzIENvc3RhIEdvbWVzDQo+IDx2aW5pY2l1cy5nb21l
c0BpbnRlbC5jb20+IHdyb3RlOg0KPiA+IEhpIEx1aXosDQo+ID4NCj4gPiBMdWl6IEF1Z3VzdG8g
dm9uIERlbnR6IDxsdWl6LmRlbnR6QGdtYWlsLmNvbT4gd3JpdGVzOg0KPiA+DQo+ID4+IEZyb206
IEx1aXogQXVndXN0byB2b24gRGVudHogPGx1aXoudm9uLmRlbnR6QGludGVsLmNvbT4NCj4gPj4N
Cj4gPj4gVGhpcyBhZGRzIHRoZSBwb3NzaWJpbGl0eSB0byBwYXNzIGFuIG9mZnNldCB0byB0aGVz
ZSBvcGVyYXRpb25zLCBhbmQNCj4gPj4gYWxzbyBpbiB0aGUgc2VydmVyIGNhc2UgdG8gZ2l2ZSB0
aGUgZGV2aWNlIG9iamVjdC4NCj4gPj4gLS0tDQo+ID4+IHYyOiBGaXggVmluaWNpdXMgY29tbWVu
dHMsIGFkZCBuZWNlc3NhcnkgY2hhbmdlcyB0byBvdGhlciB0b29scyBhZmZlY3RlZC4NCj4gPj4N
Cj4gPg0KPiA+IFRoZSBzZXJpZXMgaXMgbG9va2luZyBnb29kLg0KPiANCj4gSWYgdGhlcmUgYXJl
IG5vIGNvbW1lbnRzIHRvIHRoaXMgc2V0IEkgd2lsbCBiZSBwdXNoaW5nIGJ5IHRoZSBlbmQgb2Yg
dGhlIGRheS4NCg0KQ3VycmVudGx5IHRoZSB3cml0ZSBjb21tYW5kIGluaGVyZW50bHkgc2VsZWN0
cyBhIHdyaXRlIG1ldGhvZCBiYXNlZCBvbiB0aGUgY2hhcmFjdGVyaXN0aWNzIHByb3BlcnRpZXMg
KHdyaXRlLXdpdGhvdXQtcmVzcG9uc2UsIHJlbGlhYmxlLXdyaXRlLi4uKS4gDQpUaGUgb3B0aW9u
IGRpY3Rpb25hcnkgd2l0aGluIHRoZSBBUEkgYWxsb3dzIGZvciBhZGRpbmcgYW5vdGhlciBvcHRp
b24gdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgc2VsZWN0aW9uIG9mIHRoZSB3cml0ZSBtZXRob2Qg
Zm9yIGNoYXJhY3RlcmlzdGljcyBzdXBwb3J0aW5nIG11bHRpcGxlIHdyaXRlIG9wdGlvbnMuIA0K
SSB0aHVzIGNvbnNpZGVyIHRoaXMgcGF0Y2ggYSBtb3JlIGdlbmVyaWMgZW5hYmxlciB3aXRoaW4g
dGhlIGdpdmVuIEFQSSB0byBwYXRoIHRoZSB3YXkgZm9yIGZ1cnRoZXIgaW1wcm92ZW1lbnRzLg0K
DQpCZXN0IHJlZ2FyZHMsDQpNYXJrdXMNCg0K

2016-05-17 14:21:40

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue

Hi,

On Tue, May 10, 2016 at 1:57 AM, Vinicius Costa Gomes
<[email protected]> wrote:
> Hi Luiz,
>
> Luiz Augusto von Dentz <[email protected]> writes:
>
>> From: Luiz Augusto von Dentz <[email protected]>
>>
>> This adds the possibility to pass an offset to these operations, and
>> also in the server case to give the device object.
>> ---
>> v2: Fix Vinicius comments, add necessary changes to other tools affected.
>>
>
> The series is looking good.

If there are no comments to this set I will be pushing by the end of the day.

--
Luiz Augusto von Dentz

2016-05-09 22:57:24

by Vinicius Costa Gomes

[permalink] [raw]
Subject: Re: [PATCHv2 BlueZ 1/6] doc/gatt-api: Add options dictionary to ReadValue/WriteValue

Hi Luiz,

Luiz Augusto von Dentz <[email protected]> writes:

> From: Luiz Augusto von Dentz <[email protected]>
>
> This adds the possibility to pass an offset to these operations, and
> also in the server case to give the device object.
> ---
> v2: Fix Vinicius comments, add necessary changes to other tools affected.
>

The series is looking good.


Cheers,
--
Vinicius

2016-05-09 13:51:23

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCHv2 BlueZ 6/6] tools/gatt-service: Update to use new GATT API

From: Luiz Augusto von Dentz <[email protected]>

This update use of ReadValue and WriteValue to include the options
introduced in the API.
---
tools/gatt-service.c | 159 ++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 126 insertions(+), 33 deletions(-)

diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 329d1af..0c78c4d 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -29,6 +29,7 @@
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
+#include <string.h>
#include <sys/signalfd.h>

#include <glib.h>
@@ -127,32 +128,48 @@ static gboolean desc_get_value(const GDBusPropertyTable *property,
return desc_read(desc, iter);
}

-static void desc_write(struct descriptor *desc, DBusMessageIter *iter)
+static void desc_write(struct descriptor *desc, const uint8_t *value, int len)
{
- DBusMessageIter array;
- const uint8_t *value;
- int vlen;
-
- dbus_message_iter_recurse(iter, &array);
- dbus_message_iter_get_fixed_array(&array, &value, &vlen);
-
g_free(desc->value);
- desc->value = g_memdup(value, vlen);
- desc->vlen = vlen;
+ desc->value = g_memdup(value, len);
+ desc->vlen = len;

g_dbus_emit_property_changed(connection, desc->path,
GATT_DESCRIPTOR_IFACE, "Value");
}

+static int parse_value(DBusMessageIter *iter, const uint8_t **value, int *len)
+{
+ DBusMessageIter array;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, value, len);
+
+ return 0;
+}
+
static void desc_set_value(const GDBusPropertyTable *property,
DBusMessageIter *iter,
GDBusPendingPropertySet id, void *user_data)
{
struct descriptor *desc = user_data;
+ const uint8_t *value;
+ int len;

printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);

- desc_write(desc, iter);
+ if (parse_value(iter, &value, &len)) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ desc_write(desc, value, len);

g_dbus_pending_property_success(id);
}
@@ -249,15 +266,8 @@ static gboolean chr_get_props(const GDBusPropertyTable *property,
return TRUE;
}

-static void chr_write(struct characteristic *chr, DBusMessageIter *iter)
+static void chr_write(struct characteristic *chr, const uint8_t *value, int len)
{
- DBusMessageIter array;
- uint8_t *value;
- int len;
-
- dbus_message_iter_recurse(iter, &array);
- dbus_message_iter_get_fixed_array(&array, &value, &len);
-
g_free(chr->value);
chr->value = g_memdup(value, len);
chr->vlen = len;
@@ -271,10 +281,12 @@ static void chr_set_value(const GDBusPropertyTable *property,
GDBusPendingPropertySet id, void *user_data)
{
struct characteristic *chr = user_data;
+ const uint8_t *value;
+ int len;

printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);

- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ if (!parse_value(iter, &value, &len)) {
printf("Invalid value for Set('Value'...)\n");
g_dbus_pending_property_error(id,
ERROR_INTERFACE ".InvalidArguments",
@@ -282,7 +294,7 @@ static void chr_set_value(const GDBusPropertyTable *property,
return;
}

- chr_write(chr, iter);
+ chr_write(chr, value, len);

g_dbus_pending_property_success(id);
}
@@ -368,12 +380,53 @@ static void desc_iface_destroy(gpointer user_data)
g_free(desc);
}

+static int parse_options(DBusMessageIter *iter, const char **device)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "device") == 0) {
+ if (var != DBUS_TYPE_OBJECT_PATH)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, device);
+ printf("Device: %s\n", *device);
+ }
+ }
+
+ return 0;
+}
+
static DBusMessage *chr_read_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
struct characteristic *chr = user_data;
DBusMessage *reply;
DBusMessageIter iter;
+ const char *device;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");

reply = dbus_message_new_method_return(msg);
if (!reply)
@@ -392,10 +445,21 @@ static DBusMessage *chr_write_value(DBusConnection *conn, DBusMessage *msg,
{
struct characteristic *chr = user_data;
DBusMessageIter iter;
+ const uint8_t *value;
+ int len;
+ const char *device;

dbus_message_iter_init(msg, &iter);

- chr_write(chr, &iter);
+ if (parse_value(&iter, &value, &len))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ chr_write(chr, value, len);

return dbus_message_new_method_return(msg);
}
@@ -415,10 +479,12 @@ static DBusMessage *chr_stop_notify(DBusConnection *conn, DBusMessage *msg,
}

static const GDBusMethodTable chr_methods[] = {
- { GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
- chr_read_value) },
- { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
- NULL, chr_write_value) },
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ chr_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL, chr_write_value) },
{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify) },
{ GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify) },
{ }
@@ -430,6 +496,15 @@ static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
struct descriptor *desc = user_data;
DBusMessage *reply;
DBusMessageIter iter;
+ const char *device;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");

reply = dbus_message_new_method_return(msg);
if (!reply)
@@ -438,6 +513,10 @@ static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,

dbus_message_iter_init_append(reply, &iter);

+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
desc_read(desc, &iter);

return reply;
@@ -448,20 +527,34 @@ static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
{
struct descriptor *desc = user_data;
DBusMessageIter iter;
+ const char *device;
+ const uint8_t *value;
+ int len;

- dbus_message_iter_init(msg, &iter);
+ if (!dbus_message_iter_init(msg, &iter))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_value(&iter, &value, &len))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");

- desc_write(desc, &iter);
+ desc_write(desc, value, len);

return dbus_message_new_method_return(msg);
}

static const GDBusMethodTable desc_methods[] = {
- { GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
- desc_read_value) },
- { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
- NULL,
- desc_write_value) },
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ desc_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL, desc_write_value) },
{ }
};

--
2.5.5


2016-05-09 13:51:22

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCHv2 BlueZ 5/6] client: Update to use new GATT API

From: Luiz Augusto von Dentz <[email protected]>

This update use of ReadValue and WriteValue to include the options
introduced in the API.
---
client/gatt.c | 27 +++++++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 7dd3c94..fee1cf9 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -379,9 +379,23 @@ static void read_reply(DBusMessage *message, void *user_data)
rl_hexdump(value, len);
}

+static void read_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+ /* TODO: Add offset support */
+ dbus_message_iter_close_container(iter, &dict);
+}
+
static void read_attribute(GDBusProxy *proxy)
{
- if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply,
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply,
NULL, NULL) == FALSE) {
rl_printf("Failed to read\n");
return;
@@ -421,12 +435,21 @@ static void write_reply(DBusMessage *message, void *user_data)
static void write_setup(DBusMessageIter *iter, void *user_data)
{
struct iovec *iov = user_data;
- DBusMessageIter array;
+ DBusMessageIter array, dict;

dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&iov->iov_base, iov->iov_len);
dbus_message_iter_close_container(iter, &array);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+ /* TODO: Add offset support */
+ dbus_message_iter_close_container(iter, &dict);
}

static void write_attribute(GDBusProxy *proxy, char *arg)
--
2.5.5


2016-05-09 13:51:21

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCHv2 BlueZ 4/6] test: Update GATT examples with the new API

From: Luiz Augusto von Dentz <[email protected]>

---
test/example-gatt-client | 2 +-
test/example-gatt-server | 90 +++++++++++++++++++++++++-------
test/test-gatt-profile | 130 ++++++++++++++++++++++++++++++++++++-----------
3 files changed, 172 insertions(+), 50 deletions(-)

diff --git a/test/example-gatt-client b/test/example-gatt-client
index b77a627..4d1df2f 100755
--- a/test/example-gatt-client
+++ b/test/example-gatt-client
@@ -113,7 +113,7 @@ def hr_msrmt_changed_cb(iface, changed_props, invalidated_props):

def start_client():
# Read the Body Sensor Location value and print it asynchronously.
- body_snsr_loc_chrc[0].ReadValue(reply_handler=body_sensor_val_cb,
+ body_snsr_loc_chrc[0].ReadValue({}, reply_handler=body_sensor_val_cb,
error_handler=generic_error_cb,
dbus_interface=GATT_CHRC_IFACE)

diff --git a/test/example-gatt-server b/test/example-gatt-server
index 93cf068..71aeb1b 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -166,13 +166,15 @@ class Characteristic(dbus.service.Object):

return self.get_properties[GATT_CHRC_IFACE]

- @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
- def ReadValue(self):
+ @dbus.service.method(GATT_CHRC_IFACE,
+ in_signature='a{sv}',
+ out_signature='ay')
+ def ReadValue(self, options):
print('Default ReadValue called, returning error')
raise NotSupportedException()

- @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
- def WriteValue(self, value):
+ @dbus.service.method(GATT_CHRC_IFACE, in_signature='aya{sv}')
+ def WriteValue(self, value, options):
print('Default WriteValue called, returning error')
raise NotSupportedException()

@@ -222,13 +224,15 @@ class Descriptor(dbus.service.Object):

return self.get_properties[GATT_CHRC_IFACE]

- @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
- def ReadValue(self):
+ @dbus.service.method(GATT_DESC_IFACE,
+ in_signature='a{sv}',
+ out_signature='ay')
+ def ReadValue(self, options):
print ('Default ReadValue called, returning error')
raise NotSupportedException()

- @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
- def WriteValue(self, value):
+ @dbus.service.method(GATT_DESC_IFACE, in_signature='aya{sv}')
+ def WriteValue(self, value, options):
print('Default WriteValue called, returning error')
raise NotSupportedException()

@@ -317,7 +321,7 @@ class BodySensorLocationChrc(Characteristic):
['read'],
service)

- def ReadValue(self):
+ def ReadValue(self, options):
# Return 'Chest' as the sensor location.
return [ 0x01 ]

@@ -331,7 +335,7 @@ class HeartRateControlPointChrc(Characteristic):
['write'],
service)

- def WriteValue(self, value):
+ def WriteValue(self, value, options):
print('Heart Rate Control Point WriteValue called')

if len(value) != 1:
@@ -393,7 +397,7 @@ class BatteryLevelCharacteristic(Characteristic):
self.notify_battery_level()
return True

- def ReadValue(self):
+ def ReadValue(self, options):
print('Battery Level read: ' + repr(self.battery_lvl))
return [dbus.Byte(self.battery_lvl)]

@@ -425,6 +429,7 @@ class TestService(Service):
Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
self.add_characteristic(TestCharacteristic(bus, 0, self))
self.add_characteristic(TestEncryptCharacteristic(bus, 1, self))
+ self.add_characteristic(TestSecureCharacteristic(bus, 2, self))

class TestCharacteristic(Characteristic):
"""
@@ -445,11 +450,11 @@ class TestCharacteristic(Characteristic):
self.add_descriptor(
CharacteristicUserDescriptionDescriptor(bus, 1, self))

- def ReadValue(self):
+ def ReadValue(self, options):
print('TestCharacteristic Read: ' + repr(self.value))
return self.value

- def WriteValue(self, value):
+ def WriteValue(self, value, options):
print('TestCharacteristic Write: ' + repr(value))
self.value = value

@@ -468,7 +473,7 @@ class TestDescriptor(Descriptor):
['read', 'write'],
characteristic)

- def ReadValue(self):
+ def ReadValue(self, options):
return [
dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
]
@@ -491,10 +496,10 @@ class CharacteristicUserDescriptionDescriptor(Descriptor):
['read', 'write'],
characteristic)

- def ReadValue(self):
+ def ReadValue(self, options):
return self.value

- def WriteValue(self, value):
+ def WriteValue(self, value, options):
if not self.writable:
raise NotPermittedException()
self.value = value
@@ -517,11 +522,11 @@ class TestEncryptCharacteristic(Characteristic):
self.add_descriptor(
CharacteristicUserDescriptionDescriptor(bus, 3, self))

- def ReadValue(self):
+ def ReadValue(self, options):
print('TestCharacteristic Read: ' + repr(self.value))
return self.value

- def WriteValue(self, value):
+ def WriteValue(self, value, options):
print('TestCharacteristic Write: ' + repr(value))
self.value = value

@@ -539,7 +544,54 @@ class TestEncryptDescriptor(Descriptor):
['encrypt-read', 'encrypt-write'],
characteristic)

- def ReadValue(self):
+ def ReadValue(self, options):
+ return [
+ dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+ ]
+
+
+class TestSecureCharacteristic(Characteristic):
+ """
+ Dummy test characteristic requiring secure connection.
+
+ """
+ TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef5'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.TEST_CHRC_UUID,
+ ['secure-read', 'secure-write'],
+ service)
+ self.value = []
+ self.add_descriptor(TestEncryptDescriptor(bus, 2, self))
+ self.add_descriptor(
+ CharacteristicUserDescriptionDescriptor(bus, 3, self))
+
+ def ReadValue(self, options):
+ print('TestCharacteristic Read: ' + repr(self.value))
+ return self.value
+
+ def WriteValue(self, value, options):
+ print('TestCharacteristic Write: ' + repr(value))
+ self.value = value
+
+
+class TestSecureDescriptor(Descriptor):
+ """
+ Dummy test descriptor requiring secure connection. Returns a static value.
+
+ """
+ TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef6'
+
+ def __init__(self, bus, index, characteristic):
+ Descriptor.__init__(
+ self, bus, index,
+ self.TEST_DESC_UUID,
+ ['secure-read', 'secure-write'],
+ characteristic)
+
+ def ReadValue(self, options):
return [
dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
]
diff --git a/test/test-gatt-profile b/test/test-gatt-profile
index ad320b1..995a659 100755
--- a/test/test-gatt-profile
+++ b/test/test-gatt-profile
@@ -15,46 +15,116 @@ except ImportError:
import gobject as GObject
import bluezutils

-class GattProfile(dbus.service.Object):
- @dbus.service.method("org.bluez.GattProfile1",
- in_signature="", out_signature="")
- def Release(self):
- print("Release")
- mainloop.quit()
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'

-if __name__ == '__main__':
- dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+GATT_PROFILE_IFACE = 'org.bluez.GattProfile1'
+
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+
+class Application(dbus.service.Object):
+ def __init__(self, bus):
+ self.path = '/'
+ self.profiles = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_profile(self, profile):
+ self.profiles.append(profile)
+
+ @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+ def GetManagedObjects(self):
+ response = {}
+ print('GetManagedObjects')
+
+ for profile in self.profiles:
+ response[profile.get_path()] = profile.get_properties()
+
+ return response
+
+
+class Profile(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/profile'

- bus = dbus.SystemBus()
+ def __init__(self, bus, uuids):
+ self.path = self.PATH_BASE
+ self.bus = bus
+ self.uuids = uuids
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_PROFILE_IFACE: {
+ 'UUIDs': self.uuids,
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(GATT_PROFILE_IFACE,
+ in_signature="",
+ out_signature="")
+ def Release(self):
+ print("Release")
+ mainloop.quit()
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_PROFILE_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_PROFILE_IFACE]
+
+
+def register_app_cb():
+ print('GATT application registered')
+
+
+def register_app_error_cb(error):
+ print('Failed to register application: ' + str(error))
+ mainloop.quit()
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

- path = bluezutils.find_adapter().object_path
+ bus = dbus.SystemBus()

- manager = dbus.Interface(bus.get_object("org.bluez", path),
- "org.bluez.GattManager1")
+ path = bluezutils.find_adapter().object_path

- option_list = [
- make_option("-u", "--uuid", action="store",
- type="string", dest="uuid",
- default=None),
- make_option("-p", "--path", action="store",
- type="string", dest="path",
- default="/foo/bar/profile"),
- ]
+ manager = dbus.Interface(bus.get_object("org.bluez", path),
+ GATT_MANAGER_IFACE)

- opts = dbus.Dictionary({ }, signature='sv')
+ option_list = [make_option("-u", "--uuid", action="store",
+ type="string", dest="uuid",
+ default=None),
+ ]

- parser = OptionParser(option_list=option_list)
+ opts = dbus.Dictionary({}, signature='sv')

- (options, args) = parser.parse_args()
+ parser = OptionParser(option_list=option_list)

- profile = GattProfile(bus, options.path)
+ (options, args) = parser.parse_args()

- mainloop = GObject.MainLoop()
+ mainloop = GObject.MainLoop()

- if not options.uuid:
- options.uuid = str(uuid.uuid4())
+ if not options.uuid:
+ options.uuid = str(uuid.uuid4())

- uuids = { options.uuid }
- manager.RegisterProfile(options.path, uuids, opts)
+ app = Application(bus)
+ profile = Profile(bus, [options.uuid])
+ app.add_profile(profile)
+ manager.RegisterApplication(app.get_path(), {},
+ reply_handler=register_app_cb,
+ error_handler=register_app_error_cb)

- mainloop.run()
+ mainloop.run()
--
2.5.5


2016-05-09 13:51:20

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCHv2 BlueZ 3/6] doc/gatt-api: Add secure flags

From: Luiz Augusto von Dentz <[email protected]>

This add secure-{read,write} which shall be used by servers that want
to restrict attribute access to secure connection only (BT_SECURITY_FIPS)
---
doc/gatt-api.txt | 4 +++
lib/bluetooth.h | 1 +
src/gatt-database.c | 90 ++++++++++++++++++++----------------------------
src/shared/att-types.h | 5 +++
src/shared/gatt-server.c | 3 ++
5 files changed, 51 insertions(+), 52 deletions(-)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 9404986..cb4e196 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -150,6 +150,8 @@ Properties string UUID [read-only]
"encrypt-write"
"encrypt-authenticated-read"
"encrypt-authenticated-write"
+ "secure-read" (Server only)
+ "secure-write" (Server only)

Characteristic Descriptors hierarchy
====================================
@@ -217,6 +219,8 @@ Properties string UUID [read-only]
"encrypt-write"
"encrypt-authenticated-read"
"encrypt-authenticated-write"
+ "secure-read" (Server Only)
+ "secure-write" (Server Only)

GATT Profile hierarcy
=====================
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 852a6b2..eb27926 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -69,6 +69,7 @@ struct bt_security {
#define BT_SECURITY_LOW 1
#define BT_SECURITY_MEDIUM 2
#define BT_SECURITY_HIGH 3
+#define BT_SECURITY_FIPS 4

#define BT_DEFER_SETUP 7

diff --git a/src/gatt-database.c b/src/gatt-database.c
index 09bec0b..e287b98 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -116,6 +116,7 @@ struct external_chrc {
GDBusProxy *proxy;
uint8_t props;
uint8_t ext_props;
+ uint32_t perm;
struct gatt_db_attribute *attrib;
struct gatt_db_attribute *ccc;
struct queue *pending_reads;
@@ -1113,7 +1114,7 @@ static bool incr_attr_count(struct external_service *service, uint16_t incr)
}

static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
- uint8_t *ext_props)
+ uint8_t *ext_props, uint32_t *perm)
{
const char *flag;

@@ -1127,34 +1128,51 @@ static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,

if (!strcmp("broadcast", flag))
*props |= BT_GATT_CHRC_PROP_BROADCAST;
- else if (!strcmp("read", flag))
+ else if (!strcmp("read", flag)) {
*props |= BT_GATT_CHRC_PROP_READ;
- else if (!strcmp("write-without-response", flag))
+ *perm |= BT_ATT_PERM_READ;
+ } else if (!strcmp("write-without-response", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP;
- else if (!strcmp("write", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("write", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE;
- else if (!strcmp("notify", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("notify", flag)) {
*props |= BT_GATT_CHRC_PROP_NOTIFY;
- else if (!strcmp("indicate", flag))
+ } else if (!strcmp("indicate", flag)) {
*props |= BT_GATT_CHRC_PROP_INDICATE;
- else if (!strcmp("authenticated-signed-writes", flag))
+ } else if (!strcmp("authenticated-signed-writes", flag)) {
*props |= BT_GATT_CHRC_PROP_AUTH;
- else if (!strcmp("reliable-write", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("reliable-write", flag)) {
*ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
- else if (!strcmp("writable-auxiliaries", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("writable-auxiliaries", flag)) {
*ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX;
- else if (!strcmp("encrypt-read", flag)) {
+ } else if (!strcmp("encrypt-read", flag)) {
*props |= BT_GATT_CHRC_PROP_READ;
*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_READ;
+ *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT;
} else if (!strcmp("encrypt-write", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE;
*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_WRITE;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT;
} else if (!strcmp("encrypt-authenticated-read", flag)) {
*props |= BT_GATT_CHRC_PROP_READ;
*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+ *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
} else if (!strcmp("encrypt-authenticated-write", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE;
*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+ } else if (!strcmp("secure-read", flag)) {
+ *props |= BT_GATT_CHRC_PROP_READ;
+ *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_READ_SECURE;
+ } else if (!strcmp("secure-write", flag)) {
+ *props |= BT_GATT_CHRC_PROP_WRITE;
+ *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE;
} else {
error("Invalid characteristic flag: %s", flag);
return false;
@@ -1191,6 +1209,10 @@ static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm)
*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
else if (!strcmp("encrypt-authenticated-write", flag))
*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+ else if (!strcmp("secure-read", flag))
+ *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
+ else if (!strcmp("secure-write", flag))
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
else {
error("Invalid descriptor flag: %s", flag);
return false;
@@ -1204,6 +1226,7 @@ static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props,
uint32_t *perm)
{
DBusMessageIter iter, array;
+ const char *iface;

if (!g_dbus_proxy_get_property(proxy, "Flags", &iter))
return false;
@@ -1213,10 +1236,11 @@ static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props,

dbus_message_iter_recurse(&iter, &array);

- if (perm)
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, GATT_DESC_IFACE))
return parse_desc_flags(&array, perm);

- return parse_chrc_flags(&array, props, ext_props);
+ return parse_chrc_flags(&array, props, ext_props, perm);
}

static struct external_chrc *chrc_create(struct gatt_app *app,
@@ -1264,7 +1288,7 @@ static struct external_chrc *chrc_create(struct gatt_app *app,
* are used to determine if any special descriptors should be
* created.
*/
- if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) {
+ if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm)) {
error("Failed to parse characteristic properties");
goto fail;
}
@@ -1752,37 +1776,6 @@ static struct pending_op *send_write(struct btd_device *device,
return NULL;
}

-static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props)
-{
- uint32_t perm = 0;
-
- if (props & BT_GATT_CHRC_PROP_WRITE ||
- props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP ||
- ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE ||
- ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE ||
- ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
- perm |= BT_ATT_PERM_WRITE;
-
- if (props & BT_GATT_CHRC_PROP_READ ||
- ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ ||
- ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
- perm |= BT_ATT_PERM_READ;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ)
- perm |= BT_ATT_PERM_READ_ENCRYPT;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE)
- perm |= BT_ATT_PERM_WRITE_ENCRYPT;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
- perm |= BT_ATT_PERM_READ_AUTHEN;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
- perm |= BT_ATT_PERM_WRITE_AUTHEN;
-
- return perm;
-}
-
static uint8_t ccc_write_cb(uint16_t value, void *user_data)
{
struct external_chrc *chrc = user_data;
@@ -2112,7 +2105,6 @@ static bool database_add_chrc(struct external_service *service,
struct external_chrc *chrc)
{
bt_uuid_t uuid;
- uint32_t perm;
const struct queue_entry *entry;

if (!parse_uuid(chrc->proxy, &uuid)) {
@@ -2125,14 +2117,8 @@ static bool database_add_chrc(struct external_service *service,
return false;
}

- /*
- * TODO: Once shared/gatt-server properly supports permission checks,
- * set the permissions based on a D-Bus property of the external
- * characteristic.
- */
- perm = permissions_from_props(chrc->props, chrc->ext_props);
chrc->attrib = gatt_db_service_add_characteristic(service->attrib,
- &uuid, perm,
+ &uuid, chrc->perm,
chrc->props, chrc_read_cb,
chrc_write_cb, chrc);
if (!chrc->attrib) {
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index c3062c0..4a9b67f 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -31,6 +31,7 @@
#define BT_ATT_SECURITY_LOW 1
#define BT_ATT_SECURITY_MEDIUM 2
#define BT_ATT_SECURITY_HIGH 3
+#define BT_ATT_SECURITY_FIPS 4

#define BT_ATT_DEFAULT_LE_MTU 23
#define BT_ATT_MAX_LE_MTU 517
@@ -123,6 +124,10 @@ struct bt_att_pdu_error_rsp {
BT_ATT_PERM_WRITE_AUTHEN)
#define BT_ATT_PERM_AUTHOR 0x40
#define BT_ATT_PERM_NONE 0x80
+#define BT_ATT_PERM_READ_SECURE 0x0100
+#define BT_ATT_PERM_WRITE_SECURE 0x0200
+#define BT_ATT_PERM_SECURE (BT_ATT_PERM_READ_SECURE | \
+ BT_ATT_PERM_WRITE_SECURE)

/* GATT Characteristic Properties Bitfield values */
#define BT_GATT_CHRC_PROP_BROADCAST 0x01
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 123d9c1..79e01c8 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -398,6 +398,9 @@ static uint8_t check_permissions(struct bt_gatt_server *server,
return 0;

security = bt_att_get_security(server->att);
+ if (perm & BT_ATT_PERM_SECURE && security < BT_ATT_SECURITY_FIPS)
+ return BT_ATT_ERROR_AUTHENTICATION;
+
if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH)
return BT_ATT_ERROR_AUTHENTICATION;

--
2.5.5


2016-05-09 13:51:19

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCHv2 BlueZ 2/6] doc/gatt-api: Merge RegisterProfile with RegisterApplication

From: Luiz Augusto von Dentz <[email protected]>

Since RegisterApplication makes use of ObjectManager it is also possible
to verify the existance of GattProfile objects unifying the API for both
services (GATT server) and profiles (GATT client).
---
doc/gatt-api.txt | 37 ++---
src/gatt-database.c | 413 +++++++++++++++++++++-------------------------------
2 files changed, 171 insertions(+), 279 deletions(-)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 683b1b7..9404986 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -218,8 +218,8 @@ Properties string UUID [read-only]
"encrypt-authenticated-read"
"encrypt-authenticated-write"

-Profile hierarcy
-================
+GATT Profile hierarcy
+=====================

Local profile (GATT client) instance. By registering this type of object
an application effectively indicates support for a specific GATT profile
@@ -238,6 +238,10 @@ Methods void Release()
profile, because when this method gets called it has
already been unregistered.

+Properties array{string} UUIDs [read-only]
+
+ 128-bit GATT service UUIDs.
+

GATT Manager hierarchy
======================
@@ -306,11 +310,12 @@ Object path [variable prefix]/{hci0,hci1,...}
Methods void RegisterApplication(object application, dict options)

Registers a local GATT services hierarchy as described
- above.
+ above (GATT Server) and/or GATT profiles (GATT Client).

The application object path together with the D-Bus
system bus connection ID define the identification of
- the application registering a GATT based service.
+ the application registering a GATT based
+ service or profile.

Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.AlreadyExists
@@ -324,27 +329,3 @@ Methods void RegisterApplication(object application, dict options)

Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist
-
- void RegisterProfile(object profile, array{string} UUIDs,
- dict options)
-
- Registers a GATT (client role) profile exported
- under interface GattProfile1. The array of UUIDs
- specifies the mandatory set of remote service
- UUIDs that should all be available for the
- remote device to match this profile. Matching
- devices will be added to the auto-connection
- list and connected whenever available.
-
- Possible errors: org.bluez.Error.InvalidArguments
- org.bluez.Error.AlreadyExists
-
- void UnregisterProfile(object profile)
-
- This unregisters the profile that has been
- previously 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
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 99d084d..09bec0b 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -54,6 +54,7 @@
#endif

#define GATT_MANAGER_IFACE "org.bluez.GattManager1"
+#define GATT_PROFILE_IFACE "org.bluez.GattProfile1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHRC_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESC_IFACE "org.bluez.GattDescriptor1"
@@ -88,6 +89,7 @@ struct gatt_app {
DBusMessage *reg;
GDBusClient *client;
bool failed;
+ struct queue *profiles;
struct queue *services;
struct queue *proxies;
};
@@ -103,10 +105,8 @@ struct external_service {
};

struct external_profile {
- struct btd_gatt_database *database;
- char *owner;
- char *path; /* Path to GattProfile1 */
- unsigned int id;
+ struct gatt_app *app;
+ GDBusProxy *proxy;
struct queue *profiles; /* btd_profile list */
};

@@ -362,30 +362,6 @@ static void service_free(void *data)
free(service);
}

-static void app_free(void *data)
-{
- struct gatt_app *app = data;
-
- queue_destroy(app->services, service_free);
- queue_destroy(app->proxies, NULL);
-
- if (app->client) {
- g_dbus_client_set_disconnect_watch(app->client, NULL, NULL);
- g_dbus_client_set_proxy_handlers(app->client, NULL, NULL,
- NULL, NULL);
- g_dbus_client_set_ready_watch(app->client, NULL, NULL);
- g_dbus_client_unref(app->client);
- }
-
- if (app->reg)
- dbus_message_unref(app->reg);
-
- g_free(app->owner);
- g_free(app->path);
-
- free(app);
-}
-
static void profile_remove(void *data)
{
struct btd_profile *p = data;
@@ -403,20 +379,10 @@ static void profile_remove(void *data)

static void profile_release(struct external_profile *profile)
{
- DBusMessage *msg;
-
- if (!profile->id)
- return;
-
- DBG("Releasing \"%s\"", profile->owner);
-
- g_dbus_remove_watch(btd_get_dbus_connection(), profile->id);
+ DBG("Releasing \"%s\"", profile->app->owner);

- msg = dbus_message_new_method_call(profile->owner, profile->path,
- "org.bluez.GattProfile1",
- "Release");
- if (msg)
- g_dbus_send_message(btd_get_dbus_connection(), msg);
+ g_dbus_proxy_method_call(profile->proxy, "Release", NULL, NULL, NULL,
+ NULL);
}

static void profile_free(void *data)
@@ -427,12 +393,36 @@ static void profile_free(void *data)

profile_release(profile);

- g_free(profile->owner);
- g_free(profile->path);
+ g_dbus_proxy_unref(profile->proxy);

free(profile);
}

+static void app_free(void *data)
+{
+ struct gatt_app *app = data;
+
+ queue_destroy(app->profiles, profile_free);
+ queue_destroy(app->services, service_free);
+ queue_destroy(app->proxies, NULL);
+
+ if (app->client) {
+ g_dbus_client_set_disconnect_watch(app->client, NULL, NULL);
+ g_dbus_client_set_proxy_handlers(app->client, NULL, NULL,
+ NULL, NULL);
+ g_dbus_client_set_ready_watch(app->client, NULL, NULL);
+ g_dbus_client_unref(app->client);
+ }
+
+ if (app->reg)
+ dbus_message_unref(app->reg);
+
+ g_free(app->owner);
+ g_free(app->path);
+
+ free(app);
+}
+
static void gatt_database_free(void *data)
{
struct btd_gatt_database *database = data;
@@ -2235,9 +2225,6 @@ static bool database_add_app(struct gatt_app *app)
{
const struct queue_entry *entry;

- if (queue_isempty(app->services))
- return false;
-
entry = queue_get_entries(app->services);
while (entry) {
if (!database_add_service(entry->data)) {
@@ -2251,6 +2238,131 @@ static bool database_add_app(struct gatt_app *app)
return true;
}

+static int profile_device_probe(struct btd_service *service)
+{
+ struct btd_profile *p = btd_service_get_profile(service);
+
+ DBG("%s probed", p->name);
+
+ return 0;
+}
+
+static void profile_device_remove(struct btd_service *service)
+{
+ struct btd_profile *p = btd_service_get_profile(service);
+
+ DBG("%s removed", p->name);
+}
+
+static int profile_add(struct external_profile *profile, const char *uuid)
+{
+ struct btd_profile *p;
+
+ p = new0(struct btd_profile, 1);
+
+ /* Assign directly to avoid having extra fields */
+ p->name = (const void *) g_strdup_printf("%s%s/%s", profile->app->owner,
+ g_dbus_proxy_get_path(profile->proxy), uuid);
+ if (!p->name) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ p->remote_uuid = (const void *) g_strdup(uuid);
+ if (!p->remote_uuid) {
+ g_free((void *) p->name);
+ free(p);
+ return -ENOMEM;
+ }
+
+ p->device_probe = profile_device_probe;
+ p->device_remove = profile_device_remove;
+ p->auto_connect = true;
+ p->external = true;
+
+ queue_push_tail(profile->profiles, p);
+
+ DBG("Added \"%s\"", p->name);
+
+ return 0;
+}
+
+static void add_profile(void *data, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ btd_profile_register(data);
+ adapter_add_profile(adapter, data);
+}
+
+static struct external_profile *create_profile(struct gatt_app *app,
+ GDBusProxy *proxy,
+ const char *path)
+{
+ struct external_profile *profile;
+ DBusMessageIter iter, array;
+
+ if (!path || !g_str_has_prefix(path, "/"))
+ return NULL;
+
+ profile = new0(struct external_profile, 1);
+
+ profile->app = app;
+ profile->proxy = g_dbus_proxy_ref(proxy);
+ profile->profiles = queue_new();
+
+ if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) {
+ DBG("UUIDs property not found");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+ const char *uuid;
+
+ dbus_message_iter_get_basic(&array, &uuid);
+
+ if (profile_add(profile, uuid) < 0)
+ goto fail;
+
+ dbus_message_iter_next(&array);
+ }
+
+ if (queue_isempty(profile->profiles))
+ goto fail;
+
+ queue_foreach(profile->profiles, add_profile, app->database->adapter);
+ queue_push_tail(app->profiles, profile);
+
+ return profile;
+
+fail:
+ profile_free(profile);
+ return NULL;
+}
+
+static void register_profile(void *data, void *user_data)
+{
+ struct gatt_app *app = user_data;
+ GDBusProxy *proxy = data;
+ const char *iface = g_dbus_proxy_get_interface(proxy);
+ const char *path = g_dbus_proxy_get_path(proxy);
+
+ if (app->failed)
+ return;
+
+ if (g_strcmp0(iface, GATT_PROFILE_IFACE) == 0) {
+ struct external_profile *profile;
+
+ profile = create_profile(app, proxy, path);
+ if (!profile) {
+ app->failed = true;
+ return;
+ }
+ }
+}
+
static void register_service(void *data, void *user_data)
{
struct gatt_app *app = user_data;
@@ -2333,11 +2445,13 @@ static void client_ready_cb(GDBusClient *client, void *user_data)
goto reply;
}

+ queue_foreach(app->proxies, register_profile, app);
queue_foreach(app->proxies, register_service, app);
queue_foreach(app->proxies, register_characteristic, app);
queue_foreach(app->proxies, register_descriptor, app);

- if (!app->services || app->failed) {
+ if ((queue_isempty(app->services) && queue_isempty(app->profiles)) ||
+ app->failed) {
error("No valid external GATT objects found");
fail = true;
reply = btd_error_failed(app->reg,
@@ -2390,6 +2504,7 @@ static struct gatt_app *create_app(DBusConnection *conn, DBusMessage *msg,
goto fail;

app->services = queue_new();
+ app->profiles = queue_new();
app->proxies = queue_new();
app->reg = dbus_message_ref(msg);

@@ -2476,203 +2591,6 @@ static DBusMessage *manager_unregister_app(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

-static void profile_exited(DBusConnection *conn, void *user_data)
-{
- struct external_profile *profile = user_data;
-
- DBG("\"%s\" exited", profile->owner);
-
- profile->id = 0;
-
- queue_remove(profile->database->profiles, profile);
-
- profile_free(profile);
-}
-
-static int profile_device_probe(struct btd_service *service)
-{
- struct btd_profile *p = btd_service_get_profile(service);
-
- DBG("%s probed", p->name);
-
- return 0;
-}
-
-static void profile_device_remove(struct btd_service *service)
-{
- struct btd_profile *p = btd_service_get_profile(service);
-
- DBG("%s removed", p->name);
-}
-
-static int profile_add(struct external_profile *profile, const char *uuid)
-{
- struct btd_profile *p;
-
- p = new0(struct btd_profile, 1);
-
- /* Assign directly to avoid having extra fields */
- p->name = (const void *) g_strdup_printf("%s%s/%s", profile->owner,
- profile->path, uuid);
- if (!p->name) {
- free(p);
- return -ENOMEM;
- }
-
- p->remote_uuid = (const void *) g_strdup(uuid);
- if (!p->remote_uuid) {
- g_free((void *) p->name);
- free(p);
- return -ENOMEM;
- }
-
- p->device_probe = profile_device_probe;
- p->device_remove = profile_device_remove;
- p->auto_connect = true;
- p->external = true;
-
- queue_push_tail(profile->profiles, p);
-
- DBG("Added \"%s\"", p->name);
-
- return 0;
-}
-
-static void add_profile(void *data, void *user_data)
-{
- struct btd_adapter *adapter = user_data;
-
- btd_profile_register(data);
- adapter_add_profile(adapter, data);
-}
-
-static int profile_create(DBusConnection *conn,
- struct btd_gatt_database *database,
- const char *sender, const char *path,
- DBusMessageIter *iter)
-{
- struct external_profile *profile;
- DBusMessageIter uuids;
-
- if (!path || !g_str_has_prefix(path, "/"))
- return -EINVAL;
-
- profile = new0(struct external_profile, 1);
-
- profile->owner = g_strdup(sender);
- if (!profile->owner)
- goto fail;
-
- profile->path = g_strdup(path);
- if (!profile->path)
- goto fail;
-
- profile->profiles = queue_new();
- profile->database = database;
- profile->id = g_dbus_add_disconnect_watch(conn, sender, profile_exited,
- profile, NULL);
-
- dbus_message_iter_recurse(iter, &uuids);
-
- while (dbus_message_iter_get_arg_type(&uuids) == DBUS_TYPE_STRING) {
- const char *uuid;
-
- dbus_message_iter_get_basic(&uuids, &uuid);
-
- if (profile_add(profile, uuid) < 0)
- goto fail;
-
- dbus_message_iter_next(&uuids);
- }
-
- if (queue_isempty(profile->profiles))
- goto fail;
-
- queue_foreach(profile->profiles, add_profile, database->adapter);
- queue_push_tail(database->profiles, profile);
-
- return 0;
-
-fail:
- profile_free(profile);
- return -EINVAL;
-}
-
-static bool match_profile(const void *a, const void *b)
-{
- const struct external_profile *profile = a;
- const struct svc_match_data *data = b;
-
- return g_strcmp0(profile->path, data->path) == 0 &&
- g_strcmp0(profile->owner, data->sender) == 0;
-}
-
-static DBusMessage *manager_register_profile(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_gatt_database *database = user_data;
- const char *sender = dbus_message_get_sender(msg);
- DBusMessageIter args;
- const char *path;
- struct svc_match_data match_data;
-
- DBG("sender %s", sender);
-
- 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);
-
- match_data.path = path;
- match_data.sender = sender;
-
- if (queue_find(database->profiles, match_profile, &match_data))
- return btd_error_already_exists(msg);
-
- dbus_message_iter_next(&args);
- if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
- return btd_error_invalid_args(msg);
-
- if (profile_create(conn, database, sender, path, &args) < 0)
- return btd_error_failed(msg, "Failed to register profile");
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *manager_unregister_profile(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_gatt_database *database = user_data;
- const char *sender = dbus_message_get_sender(msg);
- const char *path;
- DBusMessageIter args;
- struct external_profile *profile;
- struct svc_match_data match_data;
-
- 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);
-
- match_data.path = path;
- match_data.sender = sender;
-
- profile = queue_remove_if(database->profiles, match_profile,
- &match_data);
- if (!profile)
- return btd_error_does_not_exist(msg);
-
- profile_free(profile);
-
- return dbus_message_new_method_return(msg);
-}
-
static const GDBusMethodTable manager_methods[] = {
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication",
GDBUS_ARGS({ "application", "o" },
@@ -2681,13 +2599,6 @@ static const GDBusMethodTable manager_methods[] = {
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication",
GDBUS_ARGS({ "application", "o" }),
NULL, manager_unregister_app) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile",
- GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
- { "options", "a{sv}" }), NULL,
- manager_register_profile) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterProfile",
- GDBUS_ARGS({ "profile", "o" }),
- NULL, manager_unregister_profile) },
{ }
};

--
2.5.5